[llvm] [llubi] Upstream existing floating-point intrinsics (PR #196034)

Zhige Chen via llvm-commits llvm-commits at lists.llvm.org
Thu May 7 23:20:33 PDT 2026


https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/196034

>From 4a5053621b086687fa87a7834ff6169019d61881 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 6 May 2026 17:23:46 +0800
Subject: [PATCH 1/6] [llubi] Floating-point intrinsics

---
 llvm/test/tools/llubi/intr_fp_fma.ll          |  30 ++
 llvm/test/tools/llubi/intr_fp_fptoi_sat.ll    |  32 ++
 llvm/test/tools/llubi/intr_fp_is_fpclass.ll   |  35 ++
 llvm/test/tools/llubi/intr_fp_minmax.ll       | 122 +++++++
 llvm/test/tools/llubi/intr_fp_unary.ll        |  28 ++
 .../test/tools/llubi/intr_fp_vector_reduce.ll |  78 +++++
 llvm/tools/llubi/lib/Context.h                |   3 +
 llvm/tools/llubi/lib/Interpreter.cpp          | 310 +++++++++++++++++-
 llvm/tools/llubi/llubi.cpp                    |   6 +
 9 files changed, 636 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/tools/llubi/intr_fp_fma.ll
 create mode 100644 llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
 create mode 100644 llvm/test/tools/llubi/intr_fp_is_fpclass.ll
 create mode 100644 llvm/test/tools/llubi/intr_fp_minmax.ll
 create mode 100644 llvm/test/tools/llubi/intr_fp_unary.ll
 create mode 100644 llvm/test/tools/llubi/intr_fp_vector_reduce.ll

diff --git a/llvm/test/tools/llubi/intr_fp_fma.ll b/llvm/test/tools/llubi/intr_fp_fma.ll
new file mode 100644
index 0000000000000..cd52f1ac7d0d4
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_fma.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %fma = call float @llvm.fma.f32(float 2.0, float 3.0, float 4.0)
+  %fma_poison_lhs = call float @llvm.fma.f32(float poison, float 3.0, float 4.0)
+  %fma_poison_rhs = call float @llvm.fma.f32(float 2.0, float poison, float 4.0)
+  %fma_poison_addend = call float @llvm.fma.f32(float 2.0, float 3.0, float poison)
+  %fma_vec = call <4 x float> @llvm.fma.v4f32(<4 x float> <float 2.0, float poison, float 2.0, float 2.0>, <4 x float> <float 3.0, float 3.0, float 5.0, float 3.0>, <4 x float> <float 4.0, float 4.0, float 7.0, float poison>)
+
+  %fmuladd = call float @llvm.fmuladd.f32(float 2.0, float 3.0, float 4.0)
+  %fmuladd_poison_lhs = call float @llvm.fmuladd.f32(float poison, float 3.0, float 4.0)
+  %fmuladd_poison_rhs = call float @llvm.fmuladd.f32(float 2.0, float poison, float 4.0)
+  %fmuladd_poison_addend = call float @llvm.fmuladd.f32(float 2.0, float 3.0, float poison)
+  %fmuladd_vec = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> <float 2.0, float poison, float 2.0, float 2.0>, <4 x float> <float 3.0, float 3.0, float 5.0, float 3.0>, <4 x float> <float 4.0, float 4.0, float 7.0, float poison>)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %fma = call float @llvm.fma.f32(float 2.000000e+00, float 3.000000e+00, float 4.000000e+00) => float 1.000000e+01
+; CHECK-NEXT:   %fma_poison_lhs = call float @llvm.fma.f32(float poison, float 3.000000e+00, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fma_poison_rhs = call float @llvm.fma.f32(float 2.000000e+00, float poison, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fma_poison_addend = call float @llvm.fma.f32(float 2.000000e+00, float 3.000000e+00, float poison) => poison
+; CHECK-NEXT:   %fma_vec = call <4 x float> @llvm.fma.v4f32(<4 x float> <float 2.000000e+00, float poison, float 2.000000e+00, float 2.000000e+00>, <4 x float> <float 3.000000e+00, float 3.000000e+00, float 5.000000e+00, float 3.000000e+00>, <4 x float> <float 4.000000e+00, float 4.000000e+00, float 7.000000e+00, float poison>) => { float 1.000000e+01, poison, float 1.700000e+01, poison }
+; CHECK-NEXT:   %fmuladd = call float @llvm.fmuladd.f32(float 2.000000e+00, float 3.000000e+00, float 4.000000e+00) => float 1.000000e+01
+; CHECK-NEXT:   %fmuladd_poison_lhs = call float @llvm.fmuladd.f32(float poison, float 3.000000e+00, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fmuladd_poison_rhs = call float @llvm.fmuladd.f32(float 2.000000e+00, float poison, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fmuladd_poison_addend = call float @llvm.fmuladd.f32(float 2.000000e+00, float 3.000000e+00, float poison) => poison
+; CHECK-NEXT:   %fmuladd_vec = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> <float 2.000000e+00, float poison, float 2.000000e+00, float 2.000000e+00>, <4 x float> <float 3.000000e+00, float 3.000000e+00, float 5.000000e+00, float 3.000000e+00>, <4 x float> <float 4.000000e+00, float 4.000000e+00, float 7.000000e+00, float poison>) => { float 1.000000e+01, poison, float 1.700000e+01, poison }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll b/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
new file mode 100644
index 0000000000000..fa93a5b1338d4
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %spos = call i8 @llvm.fptosi.sat.i8.f32(float 127.75)
+  %sneg = call i8 @llvm.fptosi.sat.i8.f32(float -128.75)
+  %slarge = call i8 @llvm.fptosi.sat.i8.f32(float 1.0e+10)
+  %slarge_neg = call i8 @llvm.fptosi.sat.i8.f32(float -1.0e+10)
+  %spoison = call i8 @llvm.fptosi.sat.i8.f32(float poison)
+  %svec = call <4 x i8> @llvm.fptosi.sat.v4i8.v4f32(<4 x float> <float 1.25, float poison, float 1.0e+10, float -1.0e+10>)
+
+  %upos = call i8 @llvm.fptoui.sat.i8.f32(float 255.75)
+  %uneg = call i8 @llvm.fptoui.sat.i8.f32(float -1.25)
+  %ularge = call i8 @llvm.fptoui.sat.i8.f32(float 1.0e+10)
+  %upoison = call i8 @llvm.fptoui.sat.i8.f32(float poison)
+  %uvec = call <4 x i8> @llvm.fptoui.sat.v4i8.v4f32(<4 x float> <float 1.25, float poison, float 1.0e+10, float -1.25>)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %spos = call i8 @llvm.fptosi.sat.i8.f32(float 1.277500e+02) => i8 127
+; CHECK-NEXT:   %sneg = call i8 @llvm.fptosi.sat.i8.f32(float -1.287500e+02) => i8 -128
+; CHECK-NEXT:   %slarge = call i8 @llvm.fptosi.sat.i8.f32(float 1.000000e+10) => i8 127
+; CHECK-NEXT:   %slarge_neg = call i8 @llvm.fptosi.sat.i8.f32(float -1.000000e+10) => i8 -128
+; CHECK-NEXT:   %spoison = call i8 @llvm.fptosi.sat.i8.f32(float poison) => poison
+; CHECK-NEXT:   %svec = call <4 x i8> @llvm.fptosi.sat.v4i8.v4f32(<4 x float> <float 1.250000e+00, float poison, float 1.000000e+10, float -1.000000e+10>) => { i8 1, poison, i8 127, i8 -128 }
+; CHECK-NEXT:   %upos = call i8 @llvm.fptoui.sat.i8.f32(float 2.557500e+02) => i8 -1
+; CHECK-NEXT:   %uneg = call i8 @llvm.fptoui.sat.i8.f32(float -1.250000e+00) => i8 0
+; CHECK-NEXT:   %ularge = call i8 @llvm.fptoui.sat.i8.f32(float 1.000000e+10) => i8 -1
+; CHECK-NEXT:   %upoison = call i8 @llvm.fptoui.sat.i8.f32(float poison) => poison
+; CHECK-NEXT:   %uvec = call <4 x i8> @llvm.fptoui.sat.v4i8.v4f32(<4 x float> <float 1.250000e+00, float poison, float 1.000000e+10, float -1.250000e+00>) => { i8 1, poison, i8 -1, i8 0 }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_is_fpclass.ll b/llvm/test/tools/llubi/intr_fp_is_fpclass.ll
new file mode 100644
index 0000000000000..c781fbeae281e
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_is_fpclass.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %is_snan = call i1 @llvm.is.fpclass.f32(float 0x7FF0000020000000, i32 1)
+  %is_qnan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 2)
+  %is_nan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 3)
+  %is_neg_inf = call i1 @llvm.is.fpclass.f32(float 0xFFF0000000000000, i32 4)
+  %is_neg_normal = call i1 @llvm.is.fpclass.f32(float -1.0, i32 8)
+  %is_neg_subnormal = call i1 @llvm.is.fpclass.f32(float -1.434929627468612680625899e-42, i32 16)
+  %is_neg_zero = call i1 @llvm.is.fpclass.f32(float -0.0, i32 32)
+  %is_pos_zero = call i1 @llvm.is.fpclass.f32(float 0.0, i32 64)
+  %is_pos_subnormal = call i1 @llvm.is.fpclass.f32(float 1.434929627468612680625899e-42, i32 128)
+  %is_pos_normal = call i1 @llvm.is.fpclass.f32(float 1.0, i32 256)
+  %is_pos_inf = call i1 @llvm.is.fpclass.f32(float 0x7FF0000000000000, i32 512)
+  %is_poison = call i1 @llvm.is.fpclass.f32(float poison, i32 256)
+  %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float 0x7FF8000020000000, float poison, float 1.0, float -0.0>, i32 291)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %is_snan = call i1 @llvm.is.fpclass.f32(float 0x7FF0000020000000, i32 1) => T
+; CHECK-NEXT:   %is_qnan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 2) => T
+; CHECK-NEXT:   %is_nan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 3) => T
+; CHECK-NEXT:   %is_neg_inf = call i1 @llvm.is.fpclass.f32(float 0xFFF0000000000000, i32 4) => T
+; CHECK-NEXT:   %is_neg_normal = call i1 @llvm.is.fpclass.f32(float -1.000000e+00, i32 8) => T
+; CHECK-NEXT:   %is_neg_subnormal = call i1 @llvm.is.fpclass.f32(float 0xB740000000000000, i32 16) => T
+; CHECK-NEXT:   %is_neg_zero = call i1 @llvm.is.fpclass.f32(float -0.000000e+00, i32 32) => T
+; CHECK-NEXT:   %is_pos_zero = call i1 @llvm.is.fpclass.f32(float 0.000000e+00, i32 64) => T
+; CHECK-NEXT:   %is_pos_subnormal = call i1 @llvm.is.fpclass.f32(float 0x3740000000000000, i32 128) => T
+; CHECK-NEXT:   %is_pos_normal = call i1 @llvm.is.fpclass.f32(float 1.000000e+00, i32 256) => T
+; CHECK-NEXT:   %is_pos_inf = call i1 @llvm.is.fpclass.f32(float 0x7FF0000000000000, i32 512) => T
+; CHECK-NEXT:   %is_poison = call i1 @llvm.is.fpclass.f32(float poison, i32 256) => poison
+; CHECK-NEXT:   %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float 0x7FF8000020000000, float poison, float 1.000000e+00, float -0.000000e+00>, i32 291) => { T, poison, T, T }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_minmax.ll b/llvm/test/tools/llubi/intr_fp_minmax.ll
new file mode 100644
index 0000000000000..7195c5bc5ca67
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_minmax.ll
@@ -0,0 +1,122 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %maxnum = call float @llvm.maxnum.f32(float 1.0, float 2.0)
+  %maxnum_neg_zero = call float @llvm.maxnum.f32(float -0.0, float 0.0)
+  %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000020000000, float 2.0)
+  %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.0, float 0x7FF8000020000000)
+  %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000020000000, float 2.0)
+  %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.0, float 0x7FF0000020000000)
+  %maxnum_poison_lhs = call float @llvm.maxnum.f32(float poison, float 2.0)
+  %maxnum_poison_rhs = call float @llvm.maxnum.f32(float 1.0, float poison)
+  %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+
+  %minnum = call float @llvm.minnum.f32(float 1.0, float 2.0)
+  %minnum_neg_zero = call float @llvm.minnum.f32(float -0.0, float 0.0)
+  %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000020000000, float 2.0)
+  %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.0, float 0x7FF8000020000000)
+  %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000020000000, float 2.0)
+  %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.0, float 0x7FF0000020000000)
+  %minnum_poison_lhs = call float @llvm.minnum.f32(float poison, float 2.0)
+  %minnum_poison_rhs = call float @llvm.minnum.f32(float 1.0, float poison)
+  %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+
+  %maximum = call float @llvm.maximum.f32(float 1.0, float 2.0)
+  %maximum_neg_zero = call float @llvm.maximum.f32(float -0.0, float 0.0)
+  %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000020000000, float 2.0)
+  %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.0, float 0x7FF8000020000000)
+  %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000020000000, float 2.0)
+  %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.0, float 0x7FF0000020000000)
+  %maximum_poison_lhs = call float @llvm.maximum.f32(float poison, float 2.0)
+  %maximum_poison_rhs = call float @llvm.maximum.f32(float 1.0, float poison)
+  %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+
+  %minimum = call float @llvm.minimum.f32(float 1.0, float 2.0)
+  %minimum_neg_zero = call float @llvm.minimum.f32(float -0.0, float 0.0)
+  %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000020000000, float 2.0)
+  %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.0, float 0x7FF8000020000000)
+  %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000020000000, float 2.0)
+  %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.0, float 0x7FF0000020000000)
+  %minimum_poison_lhs = call float @llvm.minimum.f32(float poison, float 2.0)
+  %minimum_poison_rhs = call float @llvm.minimum.f32(float 1.0, float poison)
+  %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+
+  %maximumnum = call float @llvm.maximumnum.f32(float 1.0, float 2.0)
+  %maximumnum_neg_zero = call float @llvm.maximumnum.f32(float -0.0, float 0.0)
+  %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000020000000, float 2.0)
+  %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.0, float 0x7FF8000020000000)
+  %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float 0x7FF0000020000000, float 2.0)
+  %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.0, float 0x7FF0000020000000)
+  %maximumnum_poison_lhs = call float @llvm.maximumnum.f32(float poison, float 2.0)
+  %maximumnum_poison_rhs = call float @llvm.maximumnum.f32(float 1.0, float poison)
+  %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+
+  %minimumnum = call float @llvm.minimumnum.f32(float 1.0, float 2.0)
+  %minimumnum_neg_zero = call float @llvm.minimumnum.f32(float -0.0, float 0.0)
+  %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float 0x7FF8000020000000, float 2.0)
+  %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.0, float 0x7FF8000020000000)
+  %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float 0x7FF0000020000000, float 2.0)
+  %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.0, float 0x7FF0000020000000)
+  %minimumnum_poison_lhs = call float @llvm.minimumnum.f32(float poison, float 2.0)
+  %minimumnum_poison_rhs = call float @llvm.minimumnum.f32(float 1.0, float poison)
+  %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %maxnum = call float @llvm.maxnum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_neg_zero = call float @llvm.maxnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
+; CHECK-NEXT:   %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
+; CHECK-NEXT:   %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0xFFC00000
+; CHECK-NEXT:   %maxnum_poison_lhs = call float @llvm.maxnum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %maxnum_poison_rhs = call float @llvm.maxnum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %minnum = call float @llvm.minnum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
+; CHECK-NEXT:   %minnum_neg_zero = call float @llvm.minnum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
+; CHECK-NEXT:   %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
+; CHECK-NEXT:   %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0xFFC00000
+; CHECK-NEXT:   %minnum_poison_lhs = call float @llvm.minnum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %minnum_poison_rhs = call float @llvm.minnum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %maximum = call float @llvm.maximum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximum_neg_zero = call float @llvm.maximum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
+; CHECK-NEXT:   %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 0x7FC00001
+; CHECK-NEXT:   %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 0xFFC00000
+; CHECK-NEXT:   %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
+; CHECK-NEXT:   %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0x7F800001
+; CHECK-NEXT:   %maximum_poison_lhs = call float @llvm.maximum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %maximum_poison_rhs = call float @llvm.maximum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 0xFFC00001, poison }
+; CHECK-NEXT:   %minimum = call float @llvm.minimum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
+; CHECK-NEXT:   %minimum_neg_zero = call float @llvm.minimum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
+; CHECK-NEXT:   %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 0xFFC00000
+; CHECK-NEXT:   %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 0xFFC00001
+; CHECK-NEXT:   %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
+; CHECK-NEXT:   %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0x7FC00001
+; CHECK-NEXT:   %minimum_poison_lhs = call float @llvm.minimum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %minimum_poison_rhs = call float @llvm.minimum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float NaN, poison }
+; CHECK-NEXT:   %maximumnum = call float @llvm.maximumnum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_neg_zero = call float @llvm.maximumnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
+; CHECK-NEXT:   %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_poison_lhs = call float @llvm.maximumnum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %maximumnum_poison_rhs = call float @llvm.maximumnum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %minimumnum = call float @llvm.minimumnum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
+; CHECK-NEXT:   %minimumnum_neg_zero = call float @llvm.minimumnum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
+; CHECK-NEXT:   %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_poison_lhs = call float @llvm.minimumnum.f32(float poison, float 2.000000e+00) => poison
+; CHECK-NEXT:   %minimumnum_poison_rhs = call float @llvm.minimumnum.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_unary.ll b/llvm/test/tools/llubi/intr_fp_unary.ll
new file mode 100644
index 0000000000000..269664bf14c34
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_unary.ll
@@ -0,0 +1,28 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %fabs = call float @llvm.fabs.f32(float -1.5)
+  %fabs_neg_zero = call float @llvm.fabs.f32(float -0.0)
+  %fabs_poison = call float @llvm.fabs.f32(float poison)
+  %fabs_vec = call <4 x float> @llvm.fabs.v4f32(<4 x float> <float -1.0, float poison, float 2.0, float -0.0>)
+
+  %copysign = call float @llvm.copysign.f32(float 1.0, float -2.0)
+  %copysign_neg_zero = call float @llvm.copysign.f32(float 1.0, float -0.0)
+  %copysign_poison_mag = call float @llvm.copysign.f32(float poison, float -2.0)
+  %copysign_poison_sign = call float @llvm.copysign.f32(float 1.0, float poison)
+  %copysign_vec = call <4 x float> @llvm.copysign.v4f32(<4 x float> <float 1.0, float poison, float 3.0, float -0.0>, <4 x float> <float -2.0, float -2.0, float 2.0, float 2.0>)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %fabs = call float @llvm.fabs.f32(float -1.500000e+00) => float 1.500000e+00
+; CHECK-NEXT:   %fabs_neg_zero = call float @llvm.fabs.f32(float -0.000000e+00) => float 0.000000e+00
+; CHECK-NEXT:   %fabs_poison = call float @llvm.fabs.f32(float poison) => poison
+; CHECK-NEXT:   %fabs_vec = call <4 x float> @llvm.fabs.v4f32(<4 x float> <float -1.000000e+00, float poison, float 2.000000e+00, float -0.000000e+00>) => { float 1.000000e+00, poison, float 2.000000e+00, float 0.000000e+00 }
+; CHECK-NEXT:   %copysign = call float @llvm.copysign.f32(float 1.000000e+00, float -2.000000e+00) => float -1.000000e+00
+; CHECK-NEXT:   %copysign_neg_zero = call float @llvm.copysign.f32(float 1.000000e+00, float -0.000000e+00) => float -1.000000e+00
+; CHECK-NEXT:   %copysign_poison_mag = call float @llvm.copysign.f32(float poison, float -2.000000e+00) => poison
+; CHECK-NEXT:   %copysign_poison_sign = call float @llvm.copysign.f32(float 1.000000e+00, float poison) => poison
+; CHECK-NEXT:   %copysign_vec = call <4 x float> @llvm.copysign.v4f32(<4 x float> <float 1.000000e+00, float poison, float 3.000000e+00, float -0.000000e+00>, <4 x float> <float -2.000000e+00, float -2.000000e+00, float 2.000000e+00, float 2.000000e+00>) => { float -1.000000e+00, poison, float 3.000000e+00, float 0.000000e+00 }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_vector_reduce.ll b/llvm/test/tools/llubi/intr_fp_vector_reduce.ll
new file mode 100644
index 0000000000000..57050438c0e2b
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_vector_reduce.ll
@@ -0,0 +1,78 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --vscale=4 --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %fadd = call float @llvm.vector.reduce.fadd.v4f32(float 1.0, <4 x float> <float 2.0, float 3.0, float 4.0, float 5.0>)
+  %fadd_poison_acc = call float @llvm.vector.reduce.fadd.v4f32(float poison, <4 x float> <float 2.0, float 3.0, float 4.0, float 5.0>)
+  %fadd_poison_vec = call float @llvm.vector.reduce.fadd.v4f32(float 1.0, <4 x float> <float 2.0, float poison, float 4.0, float 5.0>)
+
+  %fmul = call float @llvm.vector.reduce.fmul.v4f32(float 2.0, <4 x float> <float 2.0, float 3.0, float 4.0, float 5.0>)
+  %fmul_poison_acc = call float @llvm.vector.reduce.fmul.v4f32(float poison, <4 x float> <float 2.0, float 3.0, float 4.0, float 5.0>)
+  %fmul_poison_vec = call float @llvm.vector.reduce.fmul.v4f32(float 2.0, <4 x float> <float 2.0, float poison, float 4.0, float 5.0>)
+
+  %fmax = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
+  %fmax_poison = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+
+  %fmin = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
+  %fmin_poison = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+
+  %fmaximum = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
+  %fmaximum_poison = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+
+  %fminimum = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
+  %fminimum_poison = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+
+  %sv_poison = insertelement <vscale x 4 x float> splat (float 2.0), float poison, i64 1
+  %sv_fadd = call float @llvm.vector.reduce.fadd.nxv4f32(float 1.0, <vscale x 4 x float> splat (float 2.0))
+  %sv_fadd_poison_acc = call float @llvm.vector.reduce.fadd.nxv4f32(float poison, <vscale x 4 x float> splat (float 2.0))
+  %sv_fadd_poison_vec = call float @llvm.vector.reduce.fadd.nxv4f32(float 1.0, <vscale x 4 x float> %sv_poison)
+
+  %sv_fmul = call float @llvm.vector.reduce.fmul.nxv4f32(float 2.0, <vscale x 4 x float> splat (float 2.0))
+  %sv_fmul_poison_acc = call float @llvm.vector.reduce.fmul.nxv4f32(float poison, <vscale x 4 x float> splat (float 2.0))
+  %sv_fmul_poison_vec = call float @llvm.vector.reduce.fmul.nxv4f32(float 2.0, <vscale x 4 x float> %sv_poison)
+
+  %sv_fmax = call float @llvm.vector.reduce.fmax.nxv4f32(<vscale x 4 x float> splat (float 2.0))
+  %sv_fmax_poison = call float @llvm.vector.reduce.fmax.nxv4f32(<vscale x 4 x float> %sv_poison)
+
+  %sv_fmin = call float @llvm.vector.reduce.fmin.nxv4f32(<vscale x 4 x float> splat (float 2.0))
+  %sv_fmin_poison = call float @llvm.vector.reduce.fmin.nxv4f32(<vscale x 4 x float> %sv_poison)
+
+  %sv_fmaximum = call float @llvm.vector.reduce.fmaximum.nxv4f32(<vscale x 4 x float> splat (float 2.0))
+  %sv_fmaximum_poison = call float @llvm.vector.reduce.fmaximum.nxv4f32(<vscale x 4 x float> %sv_poison)
+
+  %sv_fminimum = call float @llvm.vector.reduce.fminimum.nxv4f32(<vscale x 4 x float> splat (float 2.0))
+  %sv_fminimum_poison = call float @llvm.vector.reduce.fminimum.nxv4f32(<vscale x 4 x float> %sv_poison)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %fadd = call float @llvm.vector.reduce.fadd.v4f32(float 1.000000e+00, <4 x float> <float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 1.500000e+01
+; CHECK-NEXT:   %fadd_poison_acc = call float @llvm.vector.reduce.fadd.v4f32(float poison, <4 x float> <float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fadd_poison_vec = call float @llvm.vector.reduce.fadd.v4f32(float 1.000000e+00, <4 x float> <float 2.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmul = call float @llvm.vector.reduce.fmul.v4f32(float 2.000000e+00, <4 x float> <float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 2.400000e+02
+; CHECK-NEXT:   %fmul_poison_acc = call float @llvm.vector.reduce.fmul.v4f32(float poison, <4 x float> <float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmul_poison_vec = call float @llvm.vector.reduce.fmul.v4f32(float 2.000000e+00, <4 x float> <float 2.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmax = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 5.000000e+00
+; CHECK-NEXT:   %fmax_poison = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmin = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 1.000000e+00
+; CHECK-NEXT:   %fmin_poison = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmaximum = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 5.000000e+00
+; CHECK-NEXT:   %fmaximum_poison = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fminimum = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 1.000000e+00
+; CHECK-NEXT:   %fminimum_poison = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %sv_poison = insertelement <vscale x 4 x float> splat (float 2.000000e+00), float poison, i64 1 => { float 2.000000e+00, poison, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00 }
+; CHECK-NEXT:   %sv_fadd = call float @llvm.vector.reduce.fadd.nxv4f32(float 1.000000e+00, <vscale x 4 x float> splat (float 2.000000e+00)) => float 3.300000e+01
+; CHECK-NEXT:   %sv_fadd_poison_acc = call float @llvm.vector.reduce.fadd.nxv4f32(float poison, <vscale x 4 x float> splat (float 2.000000e+00)) => poison
+; CHECK-NEXT:   %sv_fadd_poison_vec = call float @llvm.vector.reduce.fadd.nxv4f32(float 1.000000e+00, <vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   %sv_fmul = call float @llvm.vector.reduce.fmul.nxv4f32(float 2.000000e+00, <vscale x 4 x float> splat (float 2.000000e+00)) => float 1.310720e+05
+; CHECK-NEXT:   %sv_fmul_poison_acc = call float @llvm.vector.reduce.fmul.nxv4f32(float poison, <vscale x 4 x float> splat (float 2.000000e+00)) => poison
+; CHECK-NEXT:   %sv_fmul_poison_vec = call float @llvm.vector.reduce.fmul.nxv4f32(float 2.000000e+00, <vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   %sv_fmax = call float @llvm.vector.reduce.fmax.nxv4f32(<vscale x 4 x float> splat (float 2.000000e+00)) => float 2.000000e+00
+; CHECK-NEXT:   %sv_fmax_poison = call float @llvm.vector.reduce.fmax.nxv4f32(<vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   %sv_fmin = call float @llvm.vector.reduce.fmin.nxv4f32(<vscale x 4 x float> splat (float 2.000000e+00)) => float 2.000000e+00
+; CHECK-NEXT:   %sv_fmin_poison = call float @llvm.vector.reduce.fmin.nxv4f32(<vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   %sv_fmaximum = call float @llvm.vector.reduce.fmaximum.nxv4f32(<vscale x 4 x float> splat (float 2.000000e+00)) => float 2.000000e+00
+; CHECK-NEXT:   %sv_fmaximum_poison = call float @llvm.vector.reduce.fmaximum.nxv4f32(<vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   %sv_fminimum = call float @llvm.vector.reduce.fminimum.nxv4f32(<vscale x 4 x float> splat (float 2.000000e+00)) => float 2.000000e+00
+; CHECK-NEXT:   %sv_fminimum_poison = call float @llvm.vector.reduce.fminimum.nxv4f32(<vscale x 4 x float> %sv_poison) => poison
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 177778aa1f691..af5849caeb8d6 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -208,6 +208,7 @@ class Context {
   bool Deterministic = false;
   UndefValueBehavior UndefBehavior = UndefValueBehavior::NonDeterministic;
   NaNPropagationBehavior NaNBehavior = NaNPropagationBehavior::NonDeterministic;
+  bool fusedMultiplyAdd = false;
 
   std::mt19937_64 Rng;
 
@@ -260,6 +261,7 @@ class Context {
   void setVScale(uint32_t VS) { VScale = VS; }
   void setMaxSteps(uint32_t MS) { MaxSteps = MS; }
   void setMaxStackDepth(uint32_t Depth) { MaxStackDepth = Depth; }
+  void setFusedMultiplyAdd(bool F) { fusedMultiplyAdd = F; }
   uint64_t getMemoryLimit() const { return MaxMem; }
   uint32_t getVScale() const { return VScale; }
   uint32_t getMaxSteps() const { return MaxSteps; }
@@ -269,6 +271,7 @@ class Context {
   bool mayUseNonDeterminism() const { return !Deterministic; }
   UndefValueBehavior getEffectiveUndefValueBehavior() const;
   NaNPropagationBehavior getEffectiveNaNPropagationBehavior() const;
+  bool fuseMultiplyAdd() const { return fusedMultiplyAdd; }
   void setUndefValueBehavior(UndefValueBehavior UB) { UndefBehavior = UB; }
   void setNaNPropagationBehavior(NaNPropagationBehavior NaNBehav) {
     NaNBehavior = NaNBehav;
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 212e1626c1b91..acde46b5b3c01 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -165,7 +165,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   }
 
   APFloat handleDenormal(APFloat Val, DenormalMode::DenormalModeKind Mode,
-                         bool IsInput = false) {
+                         bool IsInput) {
     if (!Val.isDenormal())
       return Val;
     if (IsInput) {
@@ -394,6 +394,31 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                        });
   }
 
+  AnyValue
+  visitFPUnOpWithResult(Type *RetTy, const FastMathFlags &FMF,
+                        const AnyValue &Operand,
+                        function_ref<APFloat(const APFloat &)> ScalarFn) {
+    return computeUnOp(
+        RetTy, Operand, [&](const AnyValue &OperandInner) -> AnyValue {
+          if (OperandInner.isPoison())
+            return AnyValue::poison();
+
+          // We don't flush denormals here since the only
+          // floating-point unary operation is fneg. And fneg is
+          // specified as a bitwise operation which only flips
+          // the sign bit of the input.
+
+          AnyValue ValidatedOperand =
+              handleFMFFlags(OperandInner, FMF, /*IsInput=*/true);
+          if (ValidatedOperand.isPoison())
+            return ValidatedOperand;
+
+          APFloat Result = ScalarFn(ValidatedOperand.asFloat());
+
+          return handleFMFFlags(Result, FMF, /*IsInput=*/false);
+        });
+  }
+
   AnyValue computeBinOp(
       Type *Ty, const AnyValue &LHS, const AnyValue &RHS,
       function_ref<AnyValue(const AnyValue &, const AnyValue &)> ScalarFn) {
@@ -448,14 +473,16 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         return ValidatedRHS;
 
       // Flush input denormals
-      APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(), DenormMode.Input);
-      APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(), DenormMode.Input);
+      APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(), DenormMode.Input,
+                                    /*IsInput=*/true);
+      APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(), DenormMode.Input,
+                                    /*IsInput=*/true);
 
       APFloat RawResult = ScalarFn(FLHS, FRHS);
 
       // Flush output denormals and handle fast-math flags.
       AnyValue FResult = handleFMFFlags(
-          handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/true), FMF,
+          handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/false), FMF,
           /*IsInput=*/false);
 
       if (FResult.isPoison())
@@ -510,6 +537,53 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                        AnyValue(std::move(OverflowVec))};
   }
 
+  AnyValue visitFPBinOpWithResult(
+      Type *RetTy, const FastMathFlags &FMF, const AnyValue &LHS,
+      const AnyValue &RHS,
+      function_ref<APFloat(const APFloat &, const APFloat &)> ScalarFn) {
+    DenormalMode DenormMode = getCurrentDenormalMode(RetTy);
+
+    if (!Ctx.isDefaultFPEnv())
+      reportImmediateUB() << "Non-constrained floating-point operation assumes "
+                             "default floating-point environment";
+
+    return computeBinOp(
+        RetTy, LHS, RHS,
+        [&](const AnyValue &LHSInner, const AnyValue &RHSInner) -> AnyValue {
+          if (LHSInner.isPoison() || RHSInner.isPoison())
+            return AnyValue::poison();
+
+          AnyValue ValidatedLHS =
+              handleFMFFlags(LHSInner, FMF, /*IsInput=*/true);
+          AnyValue ValidatedRHS =
+              handleFMFFlags(RHSInner, FMF, /*IsInput=*/true);
+          if (ValidatedLHS.isPoison())
+            return ValidatedLHS;
+          if (ValidatedRHS.isPoison())
+            return ValidatedRHS;
+
+          // Flush input denormals
+          APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
+          APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
+
+          APFloat RawResult = ScalarFn(FLHS, FRHS);
+
+          // Flush output denormals and handle fast-math flags.
+          AnyValue FResult = handleFMFFlags(
+              handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/false),
+              FMF,
+              /*IsInput=*/false);
+
+          if (FResult.isPoison())
+            return FResult;
+
+          APFloat Result = FResult.asFloat();
+          return applyNaNPropagation(Result, {&FLHS, &FRHS});
+        });
+  }
+
   AnyValue
   computeTriOp(Type *Ty, const AnyValue &Op1, const AnyValue &Op2,
                const AnyValue &Op3,
@@ -569,6 +643,61 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         });
   }
 
+  AnyValue visitFPTriOpWithResult(
+      Type *RetTy, const FastMathFlags &FMF, const AnyValue &Op1,
+      const AnyValue &Op2, const AnyValue &Op3,
+      function_ref<APFloat(const APFloat &, const APFloat &, const APFloat &)>
+          ScalarFn) {
+    DenormalMode DenormMode = getCurrentDenormalMode(RetTy);
+
+    if (!Ctx.isDefaultFPEnv())
+      reportImmediateUB() << "Non-constrained floating-point operation assumes "
+                             "default floating-point environment";
+
+    return computeTriOp(
+        RetTy, Op1, Op2, Op3,
+        [&](const AnyValue &Op1Inner, const AnyValue &Op2Inner,
+            const AnyValue &Op3Inner) -> AnyValue {
+          if (Op1Inner.isPoison() || Op2Inner.isPoison() || Op3Inner.isPoison())
+            return AnyValue::poison();
+
+          AnyValue ValidatedOp1 =
+              handleFMFFlags(Op1Inner, FMF, /*IsInput=*/true);
+          AnyValue ValidatedOp2 =
+              handleFMFFlags(Op2Inner, FMF, /*IsInput=*/true);
+          AnyValue ValidatedOp3 =
+              handleFMFFlags(Op3Inner, FMF, /*IsInput=*/true);
+          if (ValidatedOp1.isPoison())
+            return ValidatedOp1;
+          if (ValidatedOp2.isPoison())
+            return ValidatedOp2;
+          if (ValidatedOp3.isPoison())
+            return ValidatedOp3;
+
+          // Flush input denormals
+          APFloat FOp1 = handleDenormal(ValidatedOp1.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
+          APFloat FOp2 = handleDenormal(ValidatedOp2.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
+          APFloat FOp3 = handleDenormal(ValidatedOp3.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
+
+          APFloat RawResult = ScalarFn(FOp1, FOp2, FOp3);
+
+          // Flush output denormals and handle fast-math flags.
+          AnyValue FResult = handleFMFFlags(
+              handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/false),
+              FMF,
+              /*IsInput=*/false);
+
+          if (FResult.isPoison())
+            return FResult;
+
+          APFloat Result = FResult.asFloat();
+          return applyNaNPropagation(Result, {&FOp1, &FOp2, &FOp3});
+        });
+  }
+
   void jumpTo(Instruction &Terminator, BasicBlock *DestBB) {
     if (!Handler.onBBJump(Terminator, *DestBB)) {
       setFailed();
@@ -696,6 +825,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
   }
 
+  DenormalMode getCurrentDenormalMode(Type *Ty) {
+    if (Ty->isFPOrFPVectorTy()) {
+      return CurrentFrame->Func.getDenormalMode(
+          Ty->getScalarType()->getFltSemantics());
+    }
+    return DenormalMode::getDefault();
+  }
+
   // Helper function to convert BooleanKind to bool. Report an immediate UB if
   // a poison is found.
   bool getBooleanNonPoison(BooleanKind Boolean) {
@@ -816,6 +953,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   AnyValue callIntrinsic(CallBase &CB, ArrayRef<AnyValue> Args) {
     Intrinsic::ID IID = CB.getIntrinsicID();
     Type *RetTy = CB.getType();
+    const FastMathFlags FMF =
+        isa<FPMathOperator>(CB) ? CB.getFastMathFlags() : FastMathFlags();
 
     switch (IID) {
     case Intrinsic::assume:
@@ -1304,6 +1443,159 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       }
       return std::move(Res);
     }
+    case Intrinsic::vector_reduce_fadd:
+    case Intrinsic::vector_reduce_fmul:
+    case Intrinsic::vector_reduce_fmax:
+    case Intrinsic::vector_reduce_fmin:
+    case Intrinsic::vector_reduce_fmaximum:
+    case Intrinsic::vector_reduce_fminimum: {
+      const auto DenormMode = getCurrentDenormalMode(RetTy);
+      const bool HasStart =
+          IID == Intrinsic::vector_reduce_fadd ||
+          IID == Intrinsic::vector_reduce_fmul;
+      const AnyValue &Vector = HasStart ? Args[1] : Args[0];
+      std::optional<APFloat> Res;
+      if (HasStart) {
+        if (Args[0].isPoison())
+          return AnyValue::poison();
+        const AnyValue ValidatedStart =
+            handleFMFFlags(Args[0], FMF, /*IsInput=*/true);
+        if (ValidatedStart.isPoison())
+          return AnyValue::poison();
+        Res = handleDenormal(ValidatedStart.asFloat(), DenormMode.Input,
+                             /*IsInput=*/true);
+      }
+      for (const auto &V : Vector.asAggregate()) {
+        if (V.isPoison()) {
+          Res.reset();
+          break;
+        }
+        const AnyValue ValidatedOp =
+            handleFMFFlags(V, FMF, /*IsInput=*/true);
+        if (ValidatedOp.isPoison()) {
+          Res.reset();
+          break;
+        }
+        APFloat Op = handleDenormal(ValidatedOp.asFloat(), DenormMode.Input,
+                                    /*IsInput=*/true);
+        if (!Res) {
+          Res = std::move(Op);
+          continue;
+        }
+        switch (IID) {
+        case Intrinsic::vector_reduce_fadd:
+          *Res = *Res + Op;
+          break;
+        case Intrinsic::vector_reduce_fmul:
+          *Res = *Res * Op;
+          break;
+        case Intrinsic::vector_reduce_fmax:
+          *Res = maxnum(*Res, Op);
+          break;
+        case Intrinsic::vector_reduce_fmin:
+          *Res = minnum(*Res, Op);
+          break;
+        case Intrinsic::vector_reduce_fmaximum:
+          *Res = maximum(*Res, Op);
+          break;
+        case Intrinsic::vector_reduce_fminimum:
+          *Res = minimum(*Res, Op);
+          break;
+        default:
+          llvm_unreachable("Unexpected intrinsic ID");
+        }
+      }
+      return Res.has_value()
+                 ? handleDenormal(handleFMFFlags(std::move(*Res), FMF,
+                                                 /*IsInput=*/false)
+                                      .asFloat(),
+                                  DenormMode.Output,
+                                  /*IsInput=*/false)
+                 : AnyValue::poison();
+    }
+    case Intrinsic::fabs: {
+      return visitFPUnOpWithResult(
+          RetTy, FMF, Args[0],
+          [](const APFloat &Operand) -> APFloat { return abs(Operand); });
+    }
+    case Intrinsic::fma: {
+      return visitFPTriOpWithResult(
+          RetTy, FMF, Args[0], Args[1], Args[2],
+          [](const APFloat &Op1, const APFloat &Op2,
+             const APFloat &Op3) -> APFloat {
+            auto Res = Op1;
+            Res.fusedMultiplyAdd(Op2, Op3, RoundingMode::NearestTiesToEven);
+            return Res;
+          });
+    }
+    case Intrinsic::fmuladd: {
+      return visitFPTriOpWithResult(
+          RetTy, FMF, Args[0], Args[1], Args[2],
+          [&](const APFloat &Op1, const APFloat &Op2,
+              const APFloat &Op3) -> APFloat {
+            if (Ctx.fuseMultiplyAdd()) {
+              auto Res = Op1;
+              Res.fusedMultiplyAdd(Op2, Op3, RoundingMode::NearestTiesToEven);
+              return Res;
+            }
+            return Op1 * Op2 + Op3;
+          });
+    }
+    case Intrinsic::is_fpclass: {
+      if (Args[1].isPoison())
+        return AnyValue::poison();
+      const FPClassTest Mask =
+          static_cast<FPClassTest>(Args[1].asInteger().getZExtValue());
+      return computeUnOp(RetTy, Args[0], [&](const AnyValue &Op) -> AnyValue {
+        if (Op.isPoison())
+          return AnyValue::poison();
+        return AnyValue::boolean(
+            static_cast<bool>(Op.asFloat().classify() & Mask));
+      });
+    }
+    case Intrinsic::copysign:
+    case Intrinsic::maxnum:
+    case Intrinsic::minnum:
+    case Intrinsic::maximum:
+    case Intrinsic::minimum:
+    case Intrinsic::maximumnum:
+    case Intrinsic::minimumnum: {
+      return visitFPBinOpWithResult(
+          RetTy, FMF, Args[0], Args[1],
+          [IID](const APFloat &LHS, const APFloat &RHS) -> APFloat {
+            switch (IID) {
+            case Intrinsic::copysign:
+              return APFloat::copySign(LHS, RHS);
+            case Intrinsic::maxnum:
+              return maxnum(LHS, RHS);
+            case Intrinsic::minnum:
+              return minnum(LHS, RHS);
+            case Intrinsic::maximum:
+              return maximum(LHS, RHS);
+            case Intrinsic::minimum:
+              return minimum(LHS, RHS);
+            case Intrinsic::maximumnum:
+              return maximumnum(LHS, RHS);
+            case Intrinsic::minimumnum:
+              return minimumnum(LHS, RHS);
+            default:
+              llvm_unreachable("Unexpected intrinsic ID");
+            }
+          });
+    }
+    case Intrinsic::fptosi_sat:
+    case Intrinsic::fptoui_sat: {
+      const auto BitWidth = RetTy->getScalarSizeInBits();
+      return computeUnOp(RetTy, Args[0], [&](const AnyValue &Op) -> AnyValue {
+        if (Op.isPoison())
+          return AnyValue::poison();
+        const APFloat &Operand = Op.asFloat();
+        APSInt V(BitWidth, IID == Intrinsic::fptoui_sat);
+        [[maybe_unused]] bool IsExact;
+        Operand.convertToInteger(V, APFloat::rmTowardZero, &IsExact);
+        return V;
+      });
+    }
     default:
       Handler.onUnrecognizedInstruction(CB);
       setFailed();
@@ -1776,8 +2068,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       if (ValidatedOperand.isPoison())
         return ValidatedOperand;
 
-      APFloat FOperand =
-          handleDenormal(ValidatedOperand.asFloat(), DenormMode.Input);
+      APFloat FOperand = handleDenormal(ValidatedOperand.asFloat(),
+                                        DenormMode.Input, /*IsInput=*/true);
       APFloat SourceNaN = FOperand;
 
       bool LosesInfo;
@@ -1931,8 +2223,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           ValidateRes.isPoison())
         return ValidateRes;
 
-      APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
-      APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
+      APFloat FLHS =
+          handleDenormal(LHS.asFloat(), DenormMode.Input, /*IsInput=*/true);
+      APFloat FRHS =
+          handleDenormal(RHS.asFloat(), DenormMode.Input, /*IsInput=*/true);
 
       return AnyValue::boolean(FCmpInst::compare(FLHS, FRHS, I.getPredicate()));
     });
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 396ace887d0fc..d92e4de691e14 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -84,6 +84,11 @@ static cl::opt<bool>
                   cl::desc("Disable interpreter-introduced non-determinism."),
                   cl::init(false), cl::cat(InterpreterCategory));
 
+static cl::opt<bool>
+    FuseMultiplyAdd("fuse-multiply-add",
+                  cl::desc("Fuse floating-point multiply add operation"),
+                  cl::init(false), cl::cat(InterpreterCategory));
+
 cl::opt<ubi::UndefValueBehavior> UndefBehavior(
     "", cl::desc("Choose undef value behavior:"),
     cl::values(clEnumVal(ubi::UndefValueBehavior::NonDeterministic,
@@ -234,6 +239,7 @@ int main(int argc, char **argv) {
   Ctx.setVScale(VScale);
   Ctx.setMaxSteps(MaxSteps);
   Ctx.setMaxStackDepth(MaxStackDepth);
+  Ctx.setFusedMultiplyAdd(FuseMultiplyAdd);
   Ctx.setDeterministic(Deterministic);
   Ctx.setUndefValueBehavior(UndefBehavior);
   Ctx.setNaNPropagationBehavior(NaNPropagationBehavior);

>From be86eca689b44a0e71a1fffb9628463cb6d9d0c2 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 6 May 2026 17:43:42 +0800
Subject: [PATCH 2/6] [llubi] Fix copysign and NaN propagation in FP vector
 reduction

---
 llvm/tools/llubi/lib/Interpreter.cpp | 54 ++++++++++++++++++++--------
 1 file changed, 39 insertions(+), 15 deletions(-)

diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index acde46b5b3c01..7df6ff9acd202 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -1450,9 +1450,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     case Intrinsic::vector_reduce_fmaximum:
     case Intrinsic::vector_reduce_fminimum: {
       const auto DenormMode = getCurrentDenormalMode(RetTy);
-      const bool HasStart =
-          IID == Intrinsic::vector_reduce_fadd ||
-          IID == Intrinsic::vector_reduce_fmul;
+      const bool HasStart = IID == Intrinsic::vector_reduce_fadd ||
+                            IID == Intrinsic::vector_reduce_fmul;
       const AnyValue &Vector = HasStart ? Args[1] : Args[0];
       std::optional<APFloat> Res;
       if (HasStart) {
@@ -1470,8 +1469,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           Res.reset();
           break;
         }
-        const AnyValue ValidatedOp =
-            handleFMFFlags(V, FMF, /*IsInput=*/true);
+        const AnyValue ValidatedOp = handleFMFFlags(V, FMF, /*IsInput=*/true);
         if (ValidatedOp.isPoison()) {
           Res.reset();
           break;
@@ -1505,13 +1503,24 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           llvm_unreachable("Unexpected intrinsic ID");
         }
       }
-      return Res.has_value()
-                 ? handleDenormal(handleFMFFlags(std::move(*Res), FMF,
-                                                 /*IsInput=*/false)
-                                      .asFloat(),
-                                  DenormMode.Output,
-                                  /*IsInput=*/false)
-                 : AnyValue::poison();
+      if (Res.has_value()) {
+        const AnyValue ValidatedRes =
+            handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+        if (ValidatedRes.isPoison()) {
+          Res.reset();
+        }
+        const APFloat FRes =
+            handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
+                           /*IsInput=*/false);
+        std::vector<AnyValue> RawInputVec = Vector.asAggregate();
+        std::vector<const APFloat *> InputVec;
+        InputVec.reserve(RawInputVec.size());
+        transform(
+            RawInputVec, std::back_inserter(InputVec),
+            [](const AnyValue &V) -> const APFloat * { return &V.asFloat(); });
+        return applyNaNPropagation(FRes, InputVec);
+      }
+      return AnyValue::poison();
     }
     case Intrinsic::fabs: {
       return visitFPUnOpWithResult(
@@ -1553,7 +1562,24 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
             static_cast<bool>(Op.asFloat().classify() & Mask));
       });
     }
-    case Intrinsic::copysign:
+    case Intrinsic::copysign: {
+      return computeBinOp(
+          RetTy, Args[0], Args[1],
+          [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+            if (LHS.isPoison() || RHS.isPoison())
+              return AnyValue::poison();
+            const AnyValue ValidatedLHS =
+                handleFMFFlags(LHS, FMF, /*IsInput=*/true);
+            const AnyValue ValidatedRHS =
+                handleFMFFlags(RHS, FMF, /*IsInput=*/true);
+            if (ValidatedLHS.isPoison() || ValidatedRHS.isPoison())
+              return AnyValue::poison();
+
+            return handleFMFFlags(APFloat::copySign(ValidatedLHS.asFloat(),
+                                                    ValidatedRHS.asFloat()),
+                                  FMF, /*IsInput=*/false);
+          });
+    }
     case Intrinsic::maxnum:
     case Intrinsic::minnum:
     case Intrinsic::maximum:
@@ -1564,8 +1590,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           RetTy, FMF, Args[0], Args[1],
           [IID](const APFloat &LHS, const APFloat &RHS) -> APFloat {
             switch (IID) {
-            case Intrinsic::copysign:
-              return APFloat::copySign(LHS, RHS);
             case Intrinsic::maxnum:
               return maxnum(LHS, RHS);
             case Intrinsic::minnum:

>From 5be460427202c3ae2aaad4e8f83c788d9be9d6dd Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 6 May 2026 17:47:29 +0800
Subject: [PATCH 3/6] [llubi] Format code

---
 llvm/tools/llubi/llubi.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index d92e4de691e14..673eb0d26702b 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -86,8 +86,8 @@ static cl::opt<bool>
 
 static cl::opt<bool>
     FuseMultiplyAdd("fuse-multiply-add",
-                  cl::desc("Fuse floating-point multiply add operation"),
-                  cl::init(false), cl::cat(InterpreterCategory));
+                    cl::desc("Fuse floating-point multiply add operation"),
+                    cl::init(false), cl::cat(InterpreterCategory));
 
 cl::opt<ubi::UndefValueBehavior> UndefBehavior(
     "", cl::desc("Choose undef value behavior:"),

>From 93830e9018dabdacb4616b4e5574d391fae14cc6 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Wed, 6 May 2026 19:51:41 +0800
Subject: [PATCH 4/6] [llubi] Implement non-det sNaN behavior for minnum/maxnum
 & update tests

---
 llvm/test/tools/llubi/intr_fp_fptoi_sat.ll  |   8 +
 llvm/test/tools/llubi/intr_fp_is_fpclass.ll |  20 +-
 llvm/test/tools/llubi/intr_fp_minmax.ll     | 120 +++++------
 llvm/tools/llubi/lib/Context.h              |   6 +-
 llvm/tools/llubi/lib/Interpreter.cpp        | 208 +++++++++++++-------
 llvm/tools/llubi/llubi.cpp                  |   9 +-
 6 files changed, 224 insertions(+), 147 deletions(-)

diff --git a/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll b/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
index fa93a5b1338d4..80c12ddd663ee 100644
--- a/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
+++ b/llvm/test/tools/llubi/intr_fp_fptoi_sat.ll
@@ -8,12 +8,16 @@ define void @main() {
   %slarge_neg = call i8 @llvm.fptosi.sat.i8.f32(float -1.0e+10)
   %spoison = call i8 @llvm.fptosi.sat.i8.f32(float poison)
   %svec = call <4 x i8> @llvm.fptosi.sat.v4i8.v4f32(<4 x float> <float 1.25, float poison, float 1.0e+10, float -1.0e+10>)
+  %sqnan = call i8 @llvm.fptosi.sat.i8.f32(float +nan(0x42))
+  %ssnan = call i8 @llvm.fptosi.sat.i8.f32(float +snan(0x42))
 
   %upos = call i8 @llvm.fptoui.sat.i8.f32(float 255.75)
   %uneg = call i8 @llvm.fptoui.sat.i8.f32(float -1.25)
   %ularge = call i8 @llvm.fptoui.sat.i8.f32(float 1.0e+10)
   %upoison = call i8 @llvm.fptoui.sat.i8.f32(float poison)
   %uvec = call <4 x i8> @llvm.fptoui.sat.v4i8.v4f32(<4 x float> <float 1.25, float poison, float 1.0e+10, float -1.25>)
+  %uqnan = call i8 @llvm.fptoui.sat.i8.f32(float +nan(0x42))
+  %usnan = call i8 @llvm.fptoui.sat.i8.f32(float +snan(0x42))
   ret void
 }
 ; CHECK: Entering function: main
@@ -23,10 +27,14 @@ define void @main() {
 ; CHECK-NEXT:   %slarge_neg = call i8 @llvm.fptosi.sat.i8.f32(float -1.000000e+10) => i8 -128
 ; CHECK-NEXT:   %spoison = call i8 @llvm.fptosi.sat.i8.f32(float poison) => poison
 ; CHECK-NEXT:   %svec = call <4 x i8> @llvm.fptosi.sat.v4i8.v4f32(<4 x float> <float 1.250000e+00, float poison, float 1.000000e+10, float -1.000000e+10>) => { i8 1, poison, i8 127, i8 -128 }
+; CHECK-NEXT:   %sqnan = call i8 @llvm.fptosi.sat.i8.f32(float 0x7FF8000840000000) => i8 0
+; CHECK-NEXT:   %ssnan = call i8 @llvm.fptosi.sat.i8.f32(float 0x7FF0000840000000) => i8 0
 ; CHECK-NEXT:   %upos = call i8 @llvm.fptoui.sat.i8.f32(float 2.557500e+02) => i8 -1
 ; CHECK-NEXT:   %uneg = call i8 @llvm.fptoui.sat.i8.f32(float -1.250000e+00) => i8 0
 ; CHECK-NEXT:   %ularge = call i8 @llvm.fptoui.sat.i8.f32(float 1.000000e+10) => i8 -1
 ; CHECK-NEXT:   %upoison = call i8 @llvm.fptoui.sat.i8.f32(float poison) => poison
 ; CHECK-NEXT:   %uvec = call <4 x i8> @llvm.fptoui.sat.v4i8.v4f32(<4 x float> <float 1.250000e+00, float poison, float 1.000000e+10, float -1.250000e+00>) => { i8 1, poison, i8 -1, i8 0 }
+; CHECK-NEXT:   %uqnan = call i8 @llvm.fptoui.sat.i8.f32(float 0x7FF8000840000000) => i8 0
+; CHECK-NEXT:   %usnan = call i8 @llvm.fptoui.sat.i8.f32(float 0x7FF0000840000000) => i8 0
 ; CHECK-NEXT:   ret void
 ; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_is_fpclass.ll b/llvm/test/tools/llubi/intr_fp_is_fpclass.ll
index c781fbeae281e..7800a772cc1be 100644
--- a/llvm/test/tools/llubi/intr_fp_is_fpclass.ll
+++ b/llvm/test/tools/llubi/intr_fp_is_fpclass.ll
@@ -2,25 +2,25 @@
 ; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
 
 define void @main() {
-  %is_snan = call i1 @llvm.is.fpclass.f32(float 0x7FF0000020000000, i32 1)
-  %is_qnan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 2)
-  %is_nan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 3)
-  %is_neg_inf = call i1 @llvm.is.fpclass.f32(float 0xFFF0000000000000, i32 4)
+  %is_snan = call i1 @llvm.is.fpclass.f32(float +snan(0x42), i32 1)
+  %is_qnan = call i1 @llvm.is.fpclass.f32(float +nan(0x42), i32 2)
+  %is_nan = call i1 @llvm.is.fpclass.f32(float -nan(0x42), i32 3)
+  %is_neg_inf = call i1 @llvm.is.fpclass.f32(float -inf, i32 4)
   %is_neg_normal = call i1 @llvm.is.fpclass.f32(float -1.0, i32 8)
   %is_neg_subnormal = call i1 @llvm.is.fpclass.f32(float -1.434929627468612680625899e-42, i32 16)
   %is_neg_zero = call i1 @llvm.is.fpclass.f32(float -0.0, i32 32)
   %is_pos_zero = call i1 @llvm.is.fpclass.f32(float 0.0, i32 64)
   %is_pos_subnormal = call i1 @llvm.is.fpclass.f32(float 1.434929627468612680625899e-42, i32 128)
   %is_pos_normal = call i1 @llvm.is.fpclass.f32(float 1.0, i32 256)
-  %is_pos_inf = call i1 @llvm.is.fpclass.f32(float 0x7FF0000000000000, i32 512)
+  %is_pos_inf = call i1 @llvm.is.fpclass.f32(float +inf, i32 512)
   %is_poison = call i1 @llvm.is.fpclass.f32(float poison, i32 256)
-  %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float 0x7FF8000020000000, float poison, float 1.0, float -0.0>, i32 291)
+  %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float +snan(0x42), float poison, float 1.0, float -0.0>, i32 291)
   ret void
 }
 ; CHECK: Entering function: main
-; CHECK-NEXT:   %is_snan = call i1 @llvm.is.fpclass.f32(float 0x7FF0000020000000, i32 1) => T
-; CHECK-NEXT:   %is_qnan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 2) => T
-; CHECK-NEXT:   %is_nan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000020000000, i32 3) => T
+; CHECK-NEXT:   %is_snan = call i1 @llvm.is.fpclass.f32(float 0x7FF0000840000000, i32 1) => T
+; CHECK-NEXT:   %is_qnan = call i1 @llvm.is.fpclass.f32(float 0x7FF8000840000000, i32 2) => T
+; CHECK-NEXT:   %is_nan = call i1 @llvm.is.fpclass.f32(float 0xFFF8000840000000, i32 3) => T
 ; CHECK-NEXT:   %is_neg_inf = call i1 @llvm.is.fpclass.f32(float 0xFFF0000000000000, i32 4) => T
 ; CHECK-NEXT:   %is_neg_normal = call i1 @llvm.is.fpclass.f32(float -1.000000e+00, i32 8) => T
 ; CHECK-NEXT:   %is_neg_subnormal = call i1 @llvm.is.fpclass.f32(float 0xB740000000000000, i32 16) => T
@@ -30,6 +30,6 @@ define void @main() {
 ; CHECK-NEXT:   %is_pos_normal = call i1 @llvm.is.fpclass.f32(float 1.000000e+00, i32 256) => T
 ; CHECK-NEXT:   %is_pos_inf = call i1 @llvm.is.fpclass.f32(float 0x7FF0000000000000, i32 512) => T
 ; CHECK-NEXT:   %is_poison = call i1 @llvm.is.fpclass.f32(float poison, i32 256) => poison
-; CHECK-NEXT:   %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float 0x7FF8000020000000, float poison, float 1.000000e+00, float -0.000000e+00>, i32 291) => { T, poison, T, T }
+; CHECK-NEXT:   %is_vec = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> <float 0x7FF0000840000000, float poison, float 1.000000e+00, float -0.000000e+00>, i32 291) => { T, poison, T, T }
 ; CHECK-NEXT:   ret void
 ; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_minmax.ll b/llvm/test/tools/llubi/intr_fp_minmax.ll
index 7195c5bc5ca67..e33aff27a368e 100644
--- a/llvm/test/tools/llubi/intr_fp_minmax.ll
+++ b/llvm/test/tools/llubi/intr_fp_minmax.ll
@@ -4,119 +4,119 @@
 define void @main() {
   %maxnum = call float @llvm.maxnum.f32(float 1.0, float 2.0)
   %maxnum_neg_zero = call float @llvm.maxnum.f32(float -0.0, float 0.0)
-  %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000020000000, float 2.0)
-  %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.0, float 0x7FF8000020000000)
-  %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000020000000, float 2.0)
-  %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.0, float 0x7FF0000020000000)
+  %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float +nan(0x42), float 2.0)
+  %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.0, float +nan(0x42))
+  %maxnum_snan_lhs = call float @llvm.maxnum.f32(float +snan(0x42), float 2.0)
+  %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.0, float +snan(0x42))
   %maxnum_poison_lhs = call float @llvm.maxnum.f32(float poison, float 2.0)
   %maxnum_poison_rhs = call float @llvm.maxnum.f32(float 1.0, float poison)
-  %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
 
   %minnum = call float @llvm.minnum.f32(float 1.0, float 2.0)
   %minnum_neg_zero = call float @llvm.minnum.f32(float -0.0, float 0.0)
-  %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000020000000, float 2.0)
-  %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.0, float 0x7FF8000020000000)
-  %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000020000000, float 2.0)
-  %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.0, float 0x7FF0000020000000)
+  %minnum_qnan_lhs = call float @llvm.minnum.f32(float +nan(0x42), float 2.0)
+  %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.0, float +nan(0x42))
+  %minnum_snan_lhs = call float @llvm.minnum.f32(float +snan(0x42), float 2.0)
+  %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.0, float +snan(0x42))
   %minnum_poison_lhs = call float @llvm.minnum.f32(float poison, float 2.0)
   %minnum_poison_rhs = call float @llvm.minnum.f32(float 1.0, float poison)
-  %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
 
   %maximum = call float @llvm.maximum.f32(float 1.0, float 2.0)
   %maximum_neg_zero = call float @llvm.maximum.f32(float -0.0, float 0.0)
-  %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000020000000, float 2.0)
-  %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.0, float 0x7FF8000020000000)
-  %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000020000000, float 2.0)
-  %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.0, float 0x7FF0000020000000)
+  %maximum_qnan_lhs = call float @llvm.maximum.f32(float +nan(0x42), float 2.0)
+  %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.0, float +nan(0x42))
+  %maximum_snan_lhs = call float @llvm.maximum.f32(float +snan(0x42), float 2.0)
+  %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.0, float +snan(0x42))
   %maximum_poison_lhs = call float @llvm.maximum.f32(float poison, float 2.0)
   %maximum_poison_rhs = call float @llvm.maximum.f32(float 1.0, float poison)
-  %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
 
   %minimum = call float @llvm.minimum.f32(float 1.0, float 2.0)
   %minimum_neg_zero = call float @llvm.minimum.f32(float -0.0, float 0.0)
-  %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000020000000, float 2.0)
-  %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.0, float 0x7FF8000020000000)
-  %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000020000000, float 2.0)
-  %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.0, float 0x7FF0000020000000)
+  %minimum_qnan_lhs = call float @llvm.minimum.f32(float +nan(0x42), float 2.0)
+  %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.0, float +nan(0x42))
+  %minimum_snan_lhs = call float @llvm.minimum.f32(float +snan(0x42), float 2.0)
+  %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.0, float +snan(0x42))
   %minimum_poison_lhs = call float @llvm.minimum.f32(float poison, float 2.0)
   %minimum_poison_rhs = call float @llvm.minimum.f32(float 1.0, float poison)
-  %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
 
   %maximumnum = call float @llvm.maximumnum.f32(float 1.0, float 2.0)
   %maximumnum_neg_zero = call float @llvm.maximumnum.f32(float -0.0, float 0.0)
-  %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000020000000, float 2.0)
-  %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.0, float 0x7FF8000020000000)
-  %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float 0x7FF0000020000000, float 2.0)
-  %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.0, float 0x7FF0000020000000)
+  %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float +nan(0x42), float 2.0)
+  %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.0, float +nan(0x42))
+  %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float +snan(0x42), float 2.0)
+  %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.0, float +snan(0x42))
   %maximumnum_poison_lhs = call float @llvm.maximumnum.f32(float poison, float 2.0)
   %maximumnum_poison_rhs = call float @llvm.maximumnum.f32(float 1.0, float poison)
-  %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
 
   %minimumnum = call float @llvm.minimumnum.f32(float 1.0, float 2.0)
   %minimumnum_neg_zero = call float @llvm.minimumnum.f32(float -0.0, float 0.0)
-  %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float 0x7FF8000020000000, float 2.0)
-  %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.0, float 0x7FF8000020000000)
-  %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float 0x7FF0000020000000, float 2.0)
-  %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.0, float 0x7FF0000020000000)
+  %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float +nan(0x42), float 2.0)
+  %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.0, float +nan(0x42))
+  %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float +snan(0x42), float 2.0)
+  %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.0, float +snan(0x42))
   %minimumnum_poison_lhs = call float @llvm.minimumnum.f32(float poison, float 2.0)
   %minimumnum_poison_rhs = call float @llvm.minimumnum.f32(float 1.0, float poison)
-  %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.0, float poison, float 0x7FF8000020000000, float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
+  %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.0, float poison, float +nan(0x42), float 4.0>, <4 x float> <float 2.0, float 2.0, float 2.0, float poison>)
   ret void
 }
 ; CHECK: Entering function: main
 ; CHECK-NEXT:   %maxnum = call float @llvm.maxnum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maxnum_neg_zero = call float @llvm.maxnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
-; CHECK-NEXT:   %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
-; CHECK-NEXT:   %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
-; CHECK-NEXT:   %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0xFFC00000
+; CHECK-NEXT:   %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0xFFC00000
 ; CHECK-NEXT:   %maxnum_poison_lhs = call float @llvm.maxnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %maxnum_poison_rhs = call float @llvm.maxnum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
 ; CHECK-NEXT:   %minnum = call float @llvm.minnum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
 ; CHECK-NEXT:   %minnum_neg_zero = call float @llvm.minnum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
-; CHECK-NEXT:   %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
-; CHECK-NEXT:   %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
-; CHECK-NEXT:   %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0xFFC00000
+; CHECK-NEXT:   %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 2.000000e+00
 ; CHECK-NEXT:   %minnum_poison_lhs = call float @llvm.minnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %minnum_poison_rhs = call float @llvm.minnum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
 ; CHECK-NEXT:   %maximum = call float @llvm.maximum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maximum_neg_zero = call float @llvm.maximum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
-; CHECK-NEXT:   %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 0x7FC00001
-; CHECK-NEXT:   %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 0xFFC00000
-; CHECK-NEXT:   %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
-; CHECK-NEXT:   %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0x7F800001
+; CHECK-NEXT:   %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 0xFFC00000
+; CHECK-NEXT:   %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 0x7FC00042
+; CHECK-NEXT:   %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0xFFC00000
+; CHECK-NEXT:   %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
 ; CHECK-NEXT:   %maximum_poison_lhs = call float @llvm.maximum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %maximum_poison_rhs = call float @llvm.maximum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 0xFFC00001, poison }
+; CHECK-NEXT:   %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 0x7FC00042, poison }
 ; CHECK-NEXT:   %minimum = call float @llvm.minimum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
 ; CHECK-NEXT:   %minimum_neg_zero = call float @llvm.minimum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
-; CHECK-NEXT:   %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 0xFFC00000
-; CHECK-NEXT:   %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 0xFFC00001
-; CHECK-NEXT:   %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 0x7F800001
-; CHECK-NEXT:   %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 0x7FC00001
+; CHECK-NEXT:   %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 0xFFC00042
+; CHECK-NEXT:   %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 0xFFC00000
+; CHECK-NEXT:   %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0xFFC00000
+; CHECK-NEXT:   %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
 ; CHECK-NEXT:   %minimum_poison_lhs = call float @llvm.minimum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %minimum_poison_rhs = call float @llvm.minimum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float NaN, poison }
+; CHECK-NEXT:   %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 0x7FC00042, poison }
 ; CHECK-NEXT:   %maximumnum = call float @llvm.maximumnum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maximumnum_neg_zero = call float @llvm.maximumnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
-; CHECK-NEXT:   %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
-; CHECK-NEXT:   %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_qnan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_snan_lhs = call float @llvm.maximumnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %maximumnum_snan_rhs = call float @llvm.maximumnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 2.000000e+00
 ; CHECK-NEXT:   %maximumnum_poison_lhs = call float @llvm.maximumnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %maximumnum_poison_rhs = call float @llvm.maximumnum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %maximumnum_vec = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
 ; CHECK-NEXT:   %minimumnum = call float @llvm.minimumnum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
 ; CHECK-NEXT:   %minimumnum_neg_zero = call float @llvm.minimumnum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
-; CHECK-NEXT:   %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float 0x7FF8000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF8000020000000) => float 2.000000e+00
-; CHECK-NEXT:   %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float 0x7FF0000020000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF0000020000000) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_qnan_lhs = call float @llvm.minimumnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_qnan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_snan_lhs = call float @llvm.minimumnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
+; CHECK-NEXT:   %minimumnum_snan_rhs = call float @llvm.minimumnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 2.000000e+00
 ; CHECK-NEXT:   %minimumnum_poison_lhs = call float @llvm.minimumnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %minimumnum_poison_rhs = call float @llvm.minimumnum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000020000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
+; CHECK-NEXT:   %minimumnum_vec = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
 ; CHECK-NEXT:   ret void
 ; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index af5849caeb8d6..12a9924c70633 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -208,7 +208,7 @@ class Context {
   bool Deterministic = false;
   UndefValueBehavior UndefBehavior = UndefValueBehavior::NonDeterministic;
   NaNPropagationBehavior NaNBehavior = NaNPropagationBehavior::NonDeterministic;
-  bool fusedMultiplyAdd = false;
+  bool FusedMultiplyAdd = false;
 
   std::mt19937_64 Rng;
 
@@ -261,7 +261,7 @@ class Context {
   void setVScale(uint32_t VS) { VScale = VS; }
   void setMaxSteps(uint32_t MS) { MaxSteps = MS; }
   void setMaxStackDepth(uint32_t Depth) { MaxStackDepth = Depth; }
-  void setFusedMultiplyAdd(bool F) { fusedMultiplyAdd = F; }
+  void setFusedMultiplyAdd(bool F) { FusedMultiplyAdd = F; }
   uint64_t getMemoryLimit() const { return MaxMem; }
   uint32_t getVScale() const { return VScale; }
   uint32_t getMaxSteps() const { return MaxSteps; }
@@ -271,7 +271,7 @@ class Context {
   bool mayUseNonDeterminism() const { return !Deterministic; }
   UndefValueBehavior getEffectiveUndefValueBehavior() const;
   NaNPropagationBehavior getEffectiveNaNPropagationBehavior() const;
-  bool fuseMultiplyAdd() const { return fusedMultiplyAdd; }
+  bool fuseMultiplyAdd() const { return FusedMultiplyAdd; }
   void setUndefValueBehavior(UndefValueBehavior UB) { UndefBehavior = UB; }
   void setNaNPropagationBehavior(NaNPropagationBehavior NaNBehav) {
     NaNBehavior = NaNBehav;
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 7df6ff9acd202..a4f183d9b4896 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -394,17 +394,16 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                        });
   }
 
-  AnyValue
-  visitFPUnOpWithResult(Type *RetTy, const FastMathFlags &FMF,
-                        const AnyValue &Operand,
-                        function_ref<APFloat(const APFloat &)> ScalarFn) {
+  AnyValue visitBitwiseFPUnOpWithResult(
+      Type *RetTy, const FastMathFlags &FMF, const AnyValue &Operand,
+      function_ref<APFloat(const APFloat &)> ScalarFn) {
     return computeUnOp(
         RetTy, Operand, [&](const AnyValue &OperandInner) -> AnyValue {
           if (OperandInner.isPoison())
             return AnyValue::poison();
 
           // We don't flush denormals here since the only
-          // floating-point unary operation is fneg. And fneg is
+          // floating-point unary operation is llvm.fabs. And llvm.fabs is
           // specified as a bitwise operation which only flips
           // the sign bit of the input.
 
@@ -454,43 +453,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   void visitFPBinOp(
       Instruction &I,
       function_ref<APFloat(const APFloat &, const APFloat &)> ScalarFn) {
-    FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
-    DenormalMode DenormMode = getCurrentDenormalMode(I);
-
-    if (!Ctx.isDefaultFPEnv())
-      reportImmediateUB() << "Non-constrained floating-point operation assumes "
-                             "default floating-point environment";
-
-    visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
-      if (LHS.isPoison() || RHS.isPoison())
-        return AnyValue::poison();
-
-      AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, /*IsInput=*/true);
-      AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, /*IsInput=*/true);
-      if (ValidatedLHS.isPoison())
-        return ValidatedLHS;
-      if (ValidatedRHS.isPoison())
-        return ValidatedRHS;
-
-      // Flush input denormals
-      APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(), DenormMode.Input,
-                                    /*IsInput=*/true);
-      APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(), DenormMode.Input,
-                                    /*IsInput=*/true);
-
-      APFloat RawResult = ScalarFn(FLHS, FRHS);
-
-      // Flush output denormals and handle fast-math flags.
-      AnyValue FResult = handleFMFFlags(
-          handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/false), FMF,
-          /*IsInput=*/false);
-
-      if (FResult.isPoison())
-        return FResult;
-
-      APFloat Result = FResult.asFloat();
-      return applyNaNPropagation(Result, {&FLHS, &FRHS});
-    });
+    setResult(I, visitFPBinOpWithResult(
+                     I.getType(), cast<FPMathOperator>(I).getFastMathFlags(),
+                     getValue(I.getOperand(0)), getValue(I.getOperand(1)),
+                     ScalarFn));
   }
 
   AnyValue visitIntBinOpWithResult(
@@ -826,11 +792,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   }
 
   DenormalMode getCurrentDenormalMode(Type *Ty) {
-    if (Ty->isFPOrFPVectorTy()) {
-      return CurrentFrame->Func.getDenormalMode(
-          Ty->getScalarType()->getFltSemantics());
-    }
-    return DenormalMode::getDefault();
+    return CurrentFrame->Func.getDenormalMode(
+        Ty->getScalarType()->getFltSemantics());
   }
 
   // Helper function to convert BooleanKind to bool. Report an immediate UB if
@@ -1445,8 +1408,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
     case Intrinsic::vector_reduce_fadd:
     case Intrinsic::vector_reduce_fmul:
-    case Intrinsic::vector_reduce_fmax:
-    case Intrinsic::vector_reduce_fmin:
     case Intrinsic::vector_reduce_fmaximum:
     case Intrinsic::vector_reduce_fminimum: {
       const auto DenormMode = getCurrentDenormalMode(RetTy);
@@ -1465,15 +1426,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                              /*IsInput=*/true);
       }
       for (const auto &V : Vector.asAggregate()) {
-        if (V.isPoison()) {
-          Res.reset();
-          break;
-        }
+        if (V.isPoison())
+          return AnyValue::poison();
         const AnyValue ValidatedOp = handleFMFFlags(V, FMF, /*IsInput=*/true);
-        if (ValidatedOp.isPoison()) {
-          Res.reset();
-          break;
-        }
+        if (ValidatedOp.isPoison())
+          return AnyValue::poison();
         APFloat Op = handleDenormal(ValidatedOp.asFloat(), DenormMode.Input,
                                     /*IsInput=*/true);
         if (!Res) {
@@ -1487,12 +1444,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         case Intrinsic::vector_reduce_fmul:
           *Res = *Res * Op;
           break;
-        case Intrinsic::vector_reduce_fmax:
-          *Res = maxnum(*Res, Op);
-          break;
-        case Intrinsic::vector_reduce_fmin:
-          *Res = minnum(*Res, Op);
-          break;
         case Intrinsic::vector_reduce_fmaximum:
           *Res = maximum(*Res, Op);
           break;
@@ -1522,8 +1473,75 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       }
       return AnyValue::poison();
     }
+    case Intrinsic::vector_reduce_fmax:
+    case Intrinsic::vector_reduce_fmin: {
+      const auto DenormMode = getCurrentDenormalMode(RetTy);
+      const auto &Vector = Args[0].asAggregate();
+      std::optional<APFloat> Res;
+      SmallVector<APFloat, 16> InputFloats;
+      std::vector<const APFloat *> InputVec;
+      InputFloats.reserve(Vector.size());
+      InputVec.reserve(Vector.size());
+      for (const auto &V : Vector) {
+        if (V.isPoison())
+          return AnyValue::poison();
+        const AnyValue ValidatedOp = handleFMFFlags(V, FMF, /*IsInput=*/true);
+        if (ValidatedOp.isPoison())
+          return AnyValue::poison();
+        InputFloats.push_back(handleDenormal(ValidatedOp.asFloat(),
+                                             DenormMode.Input,
+                                             /*IsInput=*/true));
+        InputVec.push_back(&InputFloats.back());
+        if (!Res) {
+          Res = InputFloats.back();
+          continue;
+        }
+        *Res = IID == Intrinsic::vector_reduce_fmax
+                   ? maxnum(*Res, InputFloats.back())
+                   : minnum(*Res, InputFloats.back());
+      }
+      if (!Res)
+        return AnyValue::poison();
+
+      AnyValue ValidatedRes = handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+      if (ValidatedRes.isPoison())
+        return AnyValue::poison();
+      APFloat FRes = handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
+                                    /*IsInput=*/false);
+
+      if (!any_of(InputVec, [](const APFloat *V) { return V->isSignaling(); }))
+        return applyNaNPropagation(FRes, InputVec);
+
+      if (Ctx.mayUseNonDeterminism() && Ctx.getRandomBool())
+        return applyNaNPropagation(APFloat::getQNaN(FRes.getSemantics()),
+                                   InputVec);
+
+      SmallVector<APFloat, 16> QuietedInputFloats;
+      QuietedInputFloats.reserve(InputVec.size());
+      for (const APFloat *V : InputVec)
+        QuietedInputFloats.push_back(propagateInputNaN(*V, V->getSemantics(),
+                                                       /*QuietingMode=*/true,
+                                                       /*FlipSign=*/false));
+      APFloat QuietRes = QuietedInputFloats.front();
+      for (const APFloat &V :
+           ArrayRef<APFloat>(QuietedInputFloats).drop_front())
+        QuietRes = IID == Intrinsic::vector_reduce_fmax ? maxnum(QuietRes, V)
+                                                        : minnum(QuietRes, V);
+      QuietRes = handleDenormal(QuietRes, DenormMode.Output,
+                                /*IsInput=*/false);
+      AnyValue FinalRes = handleFMFFlags(QuietRes, FMF, /*IsInput=*/false);
+      if (FinalRes.isPoison())
+        return AnyValue::poison();
+      QuietRes = FinalRes.asFloat();
+
+      SmallVector<const APFloat *, 16> QuietedInputVec;
+      QuietedInputVec.reserve(QuietedInputFloats.size());
+      for (const APFloat &V : QuietedInputFloats)
+        QuietedInputVec.push_back(&V);
+      return applyNaNPropagation(QuietRes, QuietedInputVec);
+    }
     case Intrinsic::fabs: {
-      return visitFPUnOpWithResult(
+      return visitBitwiseFPUnOpWithResult(
           RetTy, FMF, Args[0],
           [](const APFloat &Operand) -> APFloat { return abs(Operand); });
     }
@@ -1581,7 +1599,63 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           });
     }
     case Intrinsic::maxnum:
-    case Intrinsic::minnum:
+    case Intrinsic::minnum: {
+      DenormalMode DenormMode = getCurrentDenormalMode(RetTy);
+
+      if (!Ctx.isDefaultFPEnv())
+        reportImmediateUB()
+            << "Non-constrained floating-point operation assumes default "
+               "floating-point environment";
+
+      return computeBinOp(
+          RetTy, Args[0], Args[1],
+          [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+            if (LHS.isPoison() || RHS.isPoison())
+              return AnyValue::poison();
+            AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, /*IsInput=*/true);
+            AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, /*IsInput=*/true);
+            if (ValidatedLHS.isPoison())
+              return ValidatedLHS;
+            if (ValidatedRHS.isPoison())
+              return ValidatedRHS;
+
+            APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(),
+                                          DenormMode.Input, /*IsInput=*/true);
+            APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(),
+                                          DenormMode.Input, /*IsInput=*/true);
+
+            APFloat RawResult = IID == Intrinsic::maxnum ? maxnum(FLHS, FRHS)
+                                                         : minnum(FLHS, FRHS);
+            APFloat Result = handleDenormal(RawResult, DenormMode.Output,
+                                            /*IsInput=*/false);
+            AnyValue FResult = handleFMFFlags(Result, FMF, /*IsInput=*/false);
+            if (FResult.isPoison())
+              return FResult;
+            Result = FResult.asFloat();
+
+            if ((FLHS.isSignaling() || FRHS.isSignaling()) &&
+                (!Ctx.mayUseNonDeterminism() || !Ctx.getRandomBool())) {
+              APFloat QuietLHS = propagateInputNaN(FLHS, FLHS.getSemantics(),
+                                                   /*QuietingMode=*/true,
+                                                   /*FlipSign=*/false);
+              APFloat QuietRHS = propagateInputNaN(FRHS, FRHS.getSemantics(),
+                                                   /*QuietingMode=*/true,
+                                                   /*FlipSign=*/false);
+              APFloat QuietResult = IID == Intrinsic::maxnum
+                                        ? maxnum(QuietLHS, QuietRHS)
+                                        : minnum(QuietLHS, QuietRHS);
+              QuietResult = handleDenormal(QuietResult, DenormMode.Output,
+                                           /*IsInput=*/false);
+              AnyValue FinalResult =
+                  handleFMFFlags(QuietResult, FMF, /*IsInput=*/false);
+              if (FinalResult.isPoison())
+                return FinalResult;
+              QuietResult = FinalResult.asFloat();
+              return applyNaNPropagation(QuietResult, {&QuietLHS, &QuietRHS});
+            }
+            return applyNaNPropagation(Result, {&FLHS, &FRHS});
+          });
+    }
     case Intrinsic::maximum:
     case Intrinsic::minimum:
     case Intrinsic::maximumnum:
@@ -1590,10 +1664,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           RetTy, FMF, Args[0], Args[1],
           [IID](const APFloat &LHS, const APFloat &RHS) -> APFloat {
             switch (IID) {
-            case Intrinsic::maxnum:
-              return maxnum(LHS, RHS);
-            case Intrinsic::minnum:
-              return minnum(LHS, RHS);
             case Intrinsic::maximum:
               return maximum(LHS, RHS);
             case Intrinsic::minimum:
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 673eb0d26702b..53946b4cb08f8 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -84,10 +84,9 @@ static cl::opt<bool>
                   cl::desc("Disable interpreter-introduced non-determinism."),
                   cl::init(false), cl::cat(InterpreterCategory));
 
-static cl::opt<bool>
-    FuseMultiplyAdd("fuse-multiply-add",
-                    cl::desc("Fuse floating-point multiply add operation"),
-                    cl::init(false), cl::cat(InterpreterCategory));
+static cl::opt<bool> FuseFMulAdd("fuse-fmuladd",
+                                 cl::desc("Fuse llvm.fmuladd.* intrinsic"),
+                                 cl::init(true), cl::cat(InterpreterCategory));
 
 cl::opt<ubi::UndefValueBehavior> UndefBehavior(
     "", cl::desc("Choose undef value behavior:"),
@@ -239,7 +238,7 @@ int main(int argc, char **argv) {
   Ctx.setVScale(VScale);
   Ctx.setMaxSteps(MaxSteps);
   Ctx.setMaxStackDepth(MaxStackDepth);
-  Ctx.setFusedMultiplyAdd(FuseMultiplyAdd);
+  Ctx.setFusedMultiplyAdd(FuseFMulAdd);
   Ctx.setDeterministic(Deterministic);
   Ctx.setUndefValueBehavior(UndefBehavior);
   Ctx.setNaNPropagationBehavior(NaNPropagationBehavior);

>From 26872414c1d90bbcb0a9f12f7d15fcc89a6008ea Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Thu, 7 May 2026 14:08:42 +0800
Subject: [PATCH 5/6] [llubi] Small fix

---
 llvm/test/tools/llubi/intr_fp_minmax.ll | 22 ++++++------
 llvm/tools/llubi/lib/Interpreter.cpp    | 45 +++++++------------------
 2 files changed, 23 insertions(+), 44 deletions(-)

diff --git a/llvm/test/tools/llubi/intr_fp_minmax.ll b/llvm/test/tools/llubi/intr_fp_minmax.ll
index e33aff27a368e..0214741116b9a 100644
--- a/llvm/test/tools/llubi/intr_fp_minmax.ll
+++ b/llvm/test/tools/llubi/intr_fp_minmax.ll
@@ -68,8 +68,8 @@ define void @main() {
 ; CHECK-NEXT:   %maxnum_neg_zero = call float @llvm.maxnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
 ; CHECK-NEXT:   %maxnum_qnan_lhs = call float @llvm.maxnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maxnum_qnan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
-; CHECK-NEXT:   %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0xFFC00000
+; CHECK-NEXT:   %maxnum_snan_lhs = call float @llvm.maxnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0xFFC00042
+; CHECK-NEXT:   %maxnum_snan_rhs = call float @llvm.maxnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
 ; CHECK-NEXT:   %maxnum_poison_lhs = call float @llvm.maxnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %maxnum_poison_rhs = call float @llvm.maxnum.f32(float 1.000000e+00, float poison) => poison
 ; CHECK-NEXT:   %maxnum_vec = call <4 x float> @llvm.maxnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 2.000000e+00, poison }
@@ -78,28 +78,28 @@ define void @main() {
 ; CHECK-NEXT:   %minnum_qnan_lhs = call float @llvm.minnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %minnum_qnan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 2.000000e+00
 ; CHECK-NEXT:   %minnum_snan_lhs = call float @llvm.minnum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 2.000000e+00
-; CHECK-NEXT:   %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 2.000000e+00
+; CHECK-NEXT:   %minnum_snan_rhs = call float @llvm.minnum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
 ; CHECK-NEXT:   %minnum_poison_lhs = call float @llvm.minnum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %minnum_poison_rhs = call float @llvm.minnum.f32(float 1.000000e+00, float poison) => poison
 ; CHECK-NEXT:   %minnum_vec = call <4 x float> @llvm.minnum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 2.000000e+00, poison }
 ; CHECK-NEXT:   %maximum = call float @llvm.maximum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maximum_neg_zero = call float @llvm.maximum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
 ; CHECK-NEXT:   %maximum_qnan_lhs = call float @llvm.maximum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 0xFFC00000
-; CHECK-NEXT:   %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 0x7FC00042
-; CHECK-NEXT:   %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0xFFC00000
-; CHECK-NEXT:   %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
+; CHECK-NEXT:   %maximum_qnan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float NaN
+; CHECK-NEXT:   %maximum_snan_lhs = call float @llvm.maximum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0x7F800042
+; CHECK-NEXT:   %maximum_snan_rhs = call float @llvm.maximum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0xFF800042
 ; CHECK-NEXT:   %maximum_poison_lhs = call float @llvm.maximum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %maximum_poison_rhs = call float @llvm.maximum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 0x7FC00042, poison }
+; CHECK-NEXT:   %maximum_vec = call <4 x float> @llvm.maximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 2.000000e+00, poison, float 0xFFC00000, poison }
 ; CHECK-NEXT:   %minimum = call float @llvm.minimum.f32(float 1.000000e+00, float 2.000000e+00) => float 1.000000e+00
 ; CHECK-NEXT:   %minimum_neg_zero = call float @llvm.minimum.f32(float -0.000000e+00, float 0.000000e+00) => float -0.000000e+00
 ; CHECK-NEXT:   %minimum_qnan_lhs = call float @llvm.minimum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 0xFFC00042
-; CHECK-NEXT:   %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float 0xFFC00000
-; CHECK-NEXT:   %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0xFFC00000
-; CHECK-NEXT:   %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float 0x7F800042
+; CHECK-NEXT:   %minimum_qnan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF8000840000000) => float NaN
+; CHECK-NEXT:   %minimum_snan_lhs = call float @llvm.minimum.f32(float 0x7FF0000840000000, float 2.000000e+00) => float 0x7FC00042
+; CHECK-NEXT:   %minimum_snan_rhs = call float @llvm.minimum.f32(float 2.000000e+00, float 0x7FF0000840000000) => float NaN
 ; CHECK-NEXT:   %minimum_poison_lhs = call float @llvm.minimum.f32(float poison, float 2.000000e+00) => poison
 ; CHECK-NEXT:   %minimum_poison_rhs = call float @llvm.minimum.f32(float 1.000000e+00, float poison) => poison
-; CHECK-NEXT:   %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 0x7FC00042, poison }
+; CHECK-NEXT:   %minimum_vec = call <4 x float> @llvm.minimum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 0x7FF8000840000000, float 4.000000e+00>, <4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float poison>) => { float 1.000000e+00, poison, float 0xFFC00042, poison }
 ; CHECK-NEXT:   %maximumnum = call float @llvm.maximumnum.f32(float 1.000000e+00, float 2.000000e+00) => float 2.000000e+00
 ; CHECK-NEXT:   %maximumnum_neg_zero = call float @llvm.maximumnum.f32(float -0.000000e+00, float 0.000000e+00) => float 0.000000e+00
 ; CHECK-NEXT:   %maximumnum_qnan_lhs = call float @llvm.maximumnum.f32(float 0x7FF8000840000000, float 2.000000e+00) => float 2.000000e+00
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index a4f183d9b4896..05a76d19f978b 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -360,27 +360,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     });
   }
 
-  void visitFPUnOp(Instruction &I,
-                   function_ref<APFloat(const APFloat &)> ScalarFn) {
-    FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
-
-    visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
-      if (Operand.isPoison())
-        return AnyValue::poison();
-
-      // We don't flush denormals here since the only floating-point unary
-      // operation is fneg. And fneg is specified as a bitwise operation which
-      // only flips the sign bit of the input.
-
-      AnyValue ValidatedOperand =
-          handleFMFFlags(Operand, FMF, /*IsInput=*/true);
-      if (ValidatedOperand.isPoison())
-        return ValidatedOperand;
-
-      APFloat Result = ScalarFn(ValidatedOperand.asFloat());
-
-      return handleFMFFlags(Result, FMF, /*IsInput=*/false);
-    });
+  void visitBitwiseFPUnOp(Instruction &I,
+                          function_ref<APFloat(const APFloat &)> ScalarFn) {
+    setResult(I, visitBitwiseFPUnOpWithResult(
+                     I.getType(), cast<FPMathOperator>(I).getFastMathFlags(),
+                     getValue(I.getOperand(0)), ScalarFn));
   }
 
   AnyValue
@@ -402,10 +386,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           if (OperandInner.isPoison())
             return AnyValue::poison();
 
-          // We don't flush denormals here since the only
-          // floating-point unary operation is llvm.fabs. And llvm.fabs is
-          // specified as a bitwise operation which only flips
-          // the sign bit of the input.
+          // We don't flush denormals here since bitwise floating-point
+          // operations only manipulate on certain bits of the operand.
 
           AnyValue ValidatedOperand =
               handleFMFFlags(OperandInner, FMF, /*IsInput=*/true);
@@ -1534,11 +1516,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         return AnyValue::poison();
       QuietRes = FinalRes.asFloat();
 
-      SmallVector<const APFloat *, 16> QuietedInputVec;
-      QuietedInputVec.reserve(QuietedInputFloats.size());
-      for (const APFloat &V : QuietedInputFloats)
-        QuietedInputVec.push_back(&V);
-      return applyNaNPropagation(QuietRes, QuietedInputVec);
+      return applyNaNPropagation(QuietRes, InputVec);
     }
     case Intrinsic::fabs: {
       return visitBitwiseFPUnOpWithResult(
@@ -1634,7 +1612,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
             Result = FResult.asFloat();
 
             if ((FLHS.isSignaling() || FRHS.isSignaling()) &&
-                (!Ctx.mayUseNonDeterminism() || !Ctx.getRandomBool())) {
+                Ctx.getRandomBool()) {
               APFloat QuietLHS = propagateInputNaN(FLHS, FLHS.getSemantics(),
                                                    /*QuietingMode=*/true,
                                                    /*FlipSign=*/false);
@@ -1651,7 +1629,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
               if (FinalResult.isPoison())
                 return FinalResult;
               QuietResult = FinalResult.asFloat();
-              return applyNaNPropagation(QuietResult, {&QuietLHS, &QuietRHS});
+              return applyNaNPropagation(QuietResult, {&FLHS, &FRHS});
             }
             return applyNaNPropagation(Result, {&FLHS, &FRHS});
           });
@@ -2109,7 +2087,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   }
 
   void visitFNeg(UnaryOperator &I) {
-    visitFPUnOp(I, [](const APFloat &Operand) -> APFloat { return -Operand; });
+    visitBitwiseFPUnOp(
+        I, [](const APFloat &Operand) -> APFloat { return -Operand; });
   }
 
   void visitTruncInst(TruncInst &Trunc) {

>From d862a2f4a7140f1b057c12c7f919684dfb4e946f Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 8 May 2026 14:20:12 +0800
Subject: [PATCH 6/6] [llubi] llvm.vector.reduce.fmin/fmax update

---
 llvm/test/tools/llubi/intr_fp_fmuladd.ll      |  19 +++
 .../test/tools/llubi/intr_fp_vector_reduce.ll |   4 +
 llvm/tools/llubi/lib/Interpreter.cpp          | 115 ++++++++----------
 3 files changed, 71 insertions(+), 67 deletions(-)
 create mode 100644 llvm/test/tools/llubi/intr_fp_fmuladd.ll

diff --git a/llvm/test/tools/llubi/intr_fp_fmuladd.ll b/llvm/test/tools/llubi/intr_fp_fmuladd.ll
new file mode 100644
index 0000000000000..dd504a3e71a97
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_fp_fmuladd.ll
@@ -0,0 +1,19 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose --fuse-fmuladd=false < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %fmuladd = call float @llvm.fmuladd.f32(float 2.0, float 3.0, float 4.0)
+  %fmuladd_poison_lhs = call float @llvm.fmuladd.f32(float poison, float 3.0, float 4.0)
+  %fmuladd_poison_rhs = call float @llvm.fmuladd.f32(float 2.0, float poison, float 4.0)
+  %fmuladd_poison_addend = call float @llvm.fmuladd.f32(float 2.0, float 3.0, float poison)
+  %fmuladd_vec = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> <float 2.0, float poison, float 2.0, float 2.0>, <4 x float> <float 3.0, float 3.0, float 5.0, float 3.0>, <4 x float> <float 4.0, float 4.0, float 7.0, float poison>)
+  ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT:   %fmuladd = call float @llvm.fmuladd.f32(float 2.000000e+00, float 3.000000e+00, float 4.000000e+00) => float 1.000000e+01
+; CHECK-NEXT:   %fmuladd_poison_lhs = call float @llvm.fmuladd.f32(float poison, float 3.000000e+00, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fmuladd_poison_rhs = call float @llvm.fmuladd.f32(float 2.000000e+00, float poison, float 4.000000e+00) => poison
+; CHECK-NEXT:   %fmuladd_poison_addend = call float @llvm.fmuladd.f32(float 2.000000e+00, float 3.000000e+00, float poison) => poison
+; CHECK-NEXT:   %fmuladd_vec = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> <float 2.000000e+00, float poison, float 2.000000e+00, float 2.000000e+00>, <4 x float> <float 3.000000e+00, float 3.000000e+00, float 5.000000e+00, float 3.000000e+00>, <4 x float> <float 4.000000e+00, float 4.000000e+00, float 7.000000e+00, float poison>) => { float 1.000000e+01, poison, float 1.700000e+01, poison }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_fp_vector_reduce.ll b/llvm/test/tools/llubi/intr_fp_vector_reduce.ll
index 57050438c0e2b..c1364545f1cb8 100644
--- a/llvm/test/tools/llubi/intr_fp_vector_reduce.ll
+++ b/llvm/test/tools/llubi/intr_fp_vector_reduce.ll
@@ -12,9 +12,11 @@ define void @main() {
 
   %fmax = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
   %fmax_poison = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+  %fmax_snan = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.0, float +snan(0x42), float 4.0, float 5.0>)
 
   %fmin = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
   %fmin_poison = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
+  %fmin_snan = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.0, float +snan(0x42), float 4.0, float 5.0>)
 
   %fmaximum = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.0, float 2.0, float 4.0, float 5.0>)
   %fmaximum_poison = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.0, float poison, float 4.0, float 5.0>)
@@ -53,8 +55,10 @@ define void @main() {
 ; CHECK-NEXT:   %fmul_poison_vec = call float @llvm.vector.reduce.fmul.v4f32(float 2.000000e+00, <4 x float> <float 2.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
 ; CHECK-NEXT:   %fmax = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 5.000000e+00
 ; CHECK-NEXT:   %fmax_poison = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmax_snan = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> <float 1.000000e+00, float 0x7FF0000840000000, float 4.000000e+00, float 5.000000e+00>) => float 5.000000e+00
 ; CHECK-NEXT:   %fmin = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 1.000000e+00
 ; CHECK-NEXT:   %fmin_poison = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
+; CHECK-NEXT:   %fmin_snan = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> <float 1.000000e+00, float 0x7FF0000840000000, float 4.000000e+00, float 5.000000e+00>) => float 0x7F800042
 ; CHECK-NEXT:   %fmaximum = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 5.000000e+00
 ; CHECK-NEXT:   %fmaximum_poison = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> <float 1.000000e+00, float poison, float 4.000000e+00, float 5.000000e+00>) => poison
 ; CHECK-NEXT:   %fminimum = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> <float 1.000000e+00, float 2.000000e+00, float 4.000000e+00, float 5.000000e+00>) => float 1.000000e+00
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 05a76d19f978b..5800c66774adf 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -768,11 +768,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     return IdxInt.sext(IndexBitWidth);
   }
 
-  DenormalMode getCurrentDenormalMode(Instruction &I) {
-    return CurrentFrame->Func.getDenormalMode(
-        I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
-  }
-
   DenormalMode getCurrentDenormalMode(Type *Ty) {
     return CurrentFrame->Func.getDenormalMode(
         Ty->getScalarType()->getFltSemantics());
@@ -1436,32 +1431,27 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           llvm_unreachable("Unexpected intrinsic ID");
         }
       }
-      if (Res.has_value()) {
-        const AnyValue ValidatedRes =
-            handleFMFFlags(*Res, FMF, /*IsInput=*/false);
-        if (ValidatedRes.isPoison()) {
-          Res.reset();
-        }
-        const APFloat FRes =
-            handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
-                           /*IsInput=*/false);
-        std::vector<AnyValue> RawInputVec = Vector.asAggregate();
-        std::vector<const APFloat *> InputVec;
-        InputVec.reserve(RawInputVec.size());
-        transform(
-            RawInputVec, std::back_inserter(InputVec),
-            [](const AnyValue &V) -> const APFloat * { return &V.asFloat(); });
-        return applyNaNPropagation(FRes, InputVec);
-      }
-      return AnyValue::poison();
+      assert(Res.has_value());
+      const AnyValue ValidatedRes =
+          handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+      if (ValidatedRes.isPoison())
+        return AnyValue::poison();
+      const APFloat FRes =
+          handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
+                         /*IsInput=*/false);
+      SmallVector<const APFloat *, 8> InputVec;
+      InputVec.reserve(Vector.asAggregate().size());
+      transform(
+          Vector.asAggregate(), std::back_inserter(InputVec),
+          [](const AnyValue &V) -> const APFloat * { return &V.asFloat(); });
+      return applyNaNPropagation(FRes, InputVec);
     }
     case Intrinsic::vector_reduce_fmax:
     case Intrinsic::vector_reduce_fmin: {
       const auto DenormMode = getCurrentDenormalMode(RetTy);
       const auto &Vector = Args[0].asAggregate();
-      std::optional<APFloat> Res;
-      SmallVector<APFloat, 16> InputFloats;
-      std::vector<const APFloat *> InputVec;
+      SmallVector<APFloat, 8> InputFloats;
+      SmallVector<const APFloat *, 8> InputVec;
       InputFloats.reserve(Vector.size());
       InputVec.reserve(Vector.size());
       for (const auto &V : Vector) {
@@ -1474,49 +1464,41 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                                              DenormMode.Input,
                                              /*IsInput=*/true));
         InputVec.push_back(&InputFloats.back());
-        if (!Res) {
-          Res = InputFloats.back();
-          continue;
-        }
-        *Res = IID == Intrinsic::vector_reduce_fmax
-                   ? maxnum(*Res, InputFloats.back())
-                   : minnum(*Res, InputFloats.back());
       }
-      if (!Res)
+      if (InputVec.empty())
         return AnyValue::poison();
 
-      AnyValue ValidatedRes = handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+      SmallVector<APFloat, 8> Worklist(InputFloats);
+      const bool HasSNaN =
+          any_of(InputVec, [](const APFloat *V) { return V->isSignaling(); });
+      while (Worklist.size() > 1) {
+        size_t LHSIdx = 0;
+        size_t RHSIdx = 1;
+        if (HasSNaN) {
+          LHSIdx = Ctx.getRandomUInt64() % Worklist.size();
+          RHSIdx = Ctx.getRandomUInt64() % (Worklist.size() - 1);
+          if (RHSIdx >= LHSIdx)
+            ++RHSIdx;
+        }
+
+        APFloat Res = IID == Intrinsic::vector_reduce_fmax
+                          ? maxnum(Worklist[LHSIdx], Worklist[RHSIdx])
+                          : minnum(Worklist[LHSIdx], Worklist[RHSIdx]);
+        if (LHSIdx < RHSIdx)
+          std::swap(LHSIdx, RHSIdx);
+        Worklist.erase(Worklist.begin() + LHSIdx);
+        Worklist.erase(Worklist.begin() + RHSIdx);
+        Worklist.push_back(std::move(Res));
+      }
+
+      AnyValue ValidatedRes =
+          handleFMFFlags(Worklist.front(), FMF, /*IsInput=*/false);
       if (ValidatedRes.isPoison())
         return AnyValue::poison();
       APFloat FRes = handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
                                     /*IsInput=*/false);
 
-      if (!any_of(InputVec, [](const APFloat *V) { return V->isSignaling(); }))
-        return applyNaNPropagation(FRes, InputVec);
-
-      if (Ctx.mayUseNonDeterminism() && Ctx.getRandomBool())
-        return applyNaNPropagation(APFloat::getQNaN(FRes.getSemantics()),
-                                   InputVec);
-
-      SmallVector<APFloat, 16> QuietedInputFloats;
-      QuietedInputFloats.reserve(InputVec.size());
-      for (const APFloat *V : InputVec)
-        QuietedInputFloats.push_back(propagateInputNaN(*V, V->getSemantics(),
-                                                       /*QuietingMode=*/true,
-                                                       /*FlipSign=*/false));
-      APFloat QuietRes = QuietedInputFloats.front();
-      for (const APFloat &V :
-           ArrayRef<APFloat>(QuietedInputFloats).drop_front())
-        QuietRes = IID == Intrinsic::vector_reduce_fmax ? maxnum(QuietRes, V)
-                                                        : minnum(QuietRes, V);
-      QuietRes = handleDenormal(QuietRes, DenormMode.Output,
-                                /*IsInput=*/false);
-      AnyValue FinalRes = handleFMFFlags(QuietRes, FMF, /*IsInput=*/false);
-      if (FinalRes.isPoison())
-        return AnyValue::poison();
-      QuietRes = FinalRes.asFloat();
-
-      return applyNaNPropagation(QuietRes, InputVec);
+      return applyNaNPropagation(FRes, InputVec);
     }
     case Intrinsic::fabs: {
       return visitBitwiseFPUnOpWithResult(
@@ -1547,8 +1529,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
           });
     }
     case Intrinsic::is_fpclass: {
-      if (Args[1].isPoison())
-        return AnyValue::poison();
       const FPClassTest Mask =
           static_cast<FPClassTest>(Args[1].asInteger().getZExtValue());
       return computeUnOp(RetTy, Args[0], [&](const AnyValue &Op) -> AnyValue {
@@ -1628,8 +1608,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
                   handleFMFFlags(QuietResult, FMF, /*IsInput=*/false);
               if (FinalResult.isPoison())
                 return FinalResult;
-              QuietResult = FinalResult.asFloat();
-              return applyNaNPropagation(QuietResult, {&FLHS, &FRHS});
+              Result = FinalResult.asFloat();
             }
             return applyNaNPropagation(Result, {&FLHS, &FRHS});
           });
@@ -2135,7 +2114,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         return AnyValue::poison();
 
       FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
-      DenormalMode DenormMode = getCurrentDenormalMode(I);
+      DenormalMode DenormMode =
+          getCurrentDenormalMode(I.getOperand(0)->getType());
 
       auto ValidatedOperand = handleFMFFlags(Operand, FMF, /*IsInput=*/true);
       if (ValidatedOperand.isPoison())
@@ -2282,7 +2262,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   }
 
   void visitFCmpInst(FCmpInst &I) {
-    DenormalMode DenormMode = getCurrentDenormalMode(I);
+    DenormalMode DenormMode =
+        getCurrentDenormalMode(I.getOperand(0)->getType());
     FastMathFlags FMF = I.getFastMathFlags();
 
     visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {



More information about the llvm-commits mailing list