[llvm] [llubi] Upstream existing floating-point intrinsics (PR #196034)
Zhige Chen via llvm-commits
llvm-commits at lists.llvm.org
Wed May 6 02:47:43 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/3] [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/3] [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/3] [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:"),
More information about the llvm-commits
mailing list