[llvm] [llubi] Initial support for floating-point numbers (PR #188453)
Zhige Chen via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 29 00:04:58 PDT 2026
https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/188453
>From 0b1c62180b78d09449f14f5a8f575be5b02147c1 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 25 Mar 2026 14:46:09 +0800
Subject: [PATCH 01/28] [llubi] Initial support for floating-point numbers
---
llvm/test/tools/llubi/fp_arith.ll | 33 +++
llvm/test/tools/llubi/fp_cast.ll | 29 +++
llvm/test/tools/llubi/fp_cmp.ll | 25 +++
llvm/test/tools/llubi/fp_denorm.ll | 15 ++
llvm/test/tools/llubi/fp_fastmath.ll | 18 ++
llvm/tools/llubi/lib/Interpreter.cpp | 296 ++++++++++++++++++++++++++-
6 files changed, 415 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/tools/llubi/fp_arith.ll
create mode 100644 llvm/test/tools/llubi/fp_cast.ll
create mode 100644 llvm/test/tools/llubi/fp_cmp.ll
create mode 100644 llvm/test/tools/llubi/fp_denorm.ll
create mode 100644 llvm/test/tools/llubi/fp_fastmath.ll
diff --git a/llvm/test/tools/llubi/fp_arith.ll b/llvm/test/tools/llubi/fp_arith.ll
new file mode 100644
index 0000000000000..2fd09ac53b1a2
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith.ll
@@ -0,0 +1,33 @@
+; 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() {
+ %add = fadd float 1.5, 2.5
+ %sub = fsub double 5.0, 1.0
+ %mul = fmul float 2.0, 3.0
+ %div = fdiv double 1.0, 2.0
+ %neg = fneg float -1.5
+
+ %vadd = fadd <2 x float> <float 1.0, float 2.0>, <float 3.0, float 4.0>
+
+ %p1 = fadd float 1.0, poison
+ %p2 = fneg float poison
+
+ %inf = fdiv float 1.0, 0.0
+ %nan = fdiv float 0.0, 0.0
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd float 1.500000e+00, 2.500000e+00 => float 4.000000e+00
+; CHECK-NEXT: %sub = fsub double 5.000000e+00, 1.000000e+00 => double 4.000000e+00
+; CHECK-NEXT: %mul = fmul float 2.000000e+00, 3.000000e+00 => float 6.000000e+00
+; CHECK-NEXT: %div = fdiv double 1.000000e+00, 2.000000e+00 => double 5.000000e-01
+; CHECK-NEXT: %neg = fneg float -1.500000e+00 => float 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x float> <float 1.000000e+00, float 2.000000e+00>, <float 3.000000e+00, float 4.000000e+00> => { float 4.000000e+00, float 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd float 1.000000e+00, poison => poison
+; CHECK-NEXT: %p2 = fneg float poison => poison
+; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_cast.ll b/llvm/test/tools/llubi/fp_cast.ll
new file mode 100644
index 0000000000000..bab205f1c5891
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_cast.ll
@@ -0,0 +1,29 @@
+; 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() {
+ %ext = fpext float 2.0 to double
+ %trunc = fptrunc double 3.5 to float
+
+ %si2fp = sitofp i32 -2 to float
+ %ui2fp = uitofp i32 255 to float
+
+ %fp2si = fptosi double -4.75 to i32
+ %fp2ui = fptoui double 4.75 to i32
+
+ %oob_ui = fptoui float -1.0 to i32
+ %nan_si = fptosi float 0x7FF8000000000000 to i32
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %ext = fpext float 2.000000e+00 to double => double 2.000000e+00
+; CHECK-NEXT: %trunc = fptrunc double 3.500000e+00 to float => float 3.500000e+00
+; CHECK-NEXT: %si2fp = sitofp i32 -2 to float => float -2.000000e+00
+; CHECK-NEXT: %ui2fp = uitofp i32 255 to float => float 2.550000e+02
+; CHECK-NEXT: %fp2si = fptosi double -4.750000e+00 to i32 => i32 -4
+; CHECK-NEXT: %fp2ui = fptoui double 4.750000e+00 to i32 => i32 4
+; CHECK-NEXT: %oob_ui = fptoui float -1.000000e+00 to i32 => poison
+; CHECK-NEXT: %nan_si = fptosi float 0x7FF8000000000000 to i32 => poison
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_cmp.ll b/llvm/test/tools/llubi/fp_cmp.ll
new file mode 100644
index 0000000000000..489a91f256e91
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_cmp.ll
@@ -0,0 +1,25 @@
+; 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() {
+ %eq = fcmp oeq float 1.0, 1.0
+ %ne = fcmp one float 1.0, 2.0
+ %gt = fcmp ogt float 2.0, 1.0
+
+ %nan = fdiv float 0.0, 0.0
+ %ord_nan = fcmp oeq float %nan, 1.0
+ %uno_nan = fcmp uno float %nan, 1.0
+ %une_nan = fcmp une float %nan, 1.0
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %eq = fcmp oeq float 1.000000e+00, 1.000000e+00 => T
+; CHECK-NEXT: %ne = fcmp one float 1.000000e+00, 2.000000e+00 => T
+; CHECK-NEXT: %gt = fcmp ogt float 2.000000e+00, 1.000000e+00 => T
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: %ord_nan = fcmp oeq float %nan, 1.000000e+00 => F
+; CHECK-NEXT: %uno_nan = fcmp uno float %nan, 1.000000e+00 => T
+; CHECK-NEXT: %une_nan = fcmp une float %nan, 1.000000e+00 => T
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_denorm.ll b/llvm/test/tools/llubi/fp_denorm.ll
new file mode 100644
index 0000000000000..982c172baf6c7
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_denorm.ll
@@ -0,0 +1,15 @@
+; 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() denormal_fpenv(positivezero|positivezero) {
+ %input_flush = fadd float 1.434929627468612680625899e-42, 0.0
+
+ %output_flush = fmul float 5.421010862427522170037264e-20, 5.421010862427522170037264e-20
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %input_flush = fadd float 0x3740000000000000, 0.000000e+00 => float 0.000000e+00
+; CHECK-NEXT: %output_flush = fmul float 0x3BF0000000000000, 0x3BF0000000000000 => float 0.000000e+00
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_fastmath.ll b/llvm/test/tools/llubi/fp_fastmath.ll
new file mode 100644
index 0000000000000..475f40d2b0457
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_fastmath.ll
@@ -0,0 +1,18 @@
+; 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() {
+ %bad_nan = fdiv nnan float 0.0, 0.0
+
+ %bad_inf = fdiv ninf float 1.0, 0.0
+
+ %safe = fadd nnan ninf float 1.0, 2.0
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %bad_nan = fdiv nnan float 0.000000e+00, 0.000000e+00 => poison
+; CHECK-NEXT: %bad_inf = fdiv ninf float 1.000000e+00, 0.000000e+00 => poison
+; CHECK-NEXT: %safe = fadd nnan ninf float 1.000000e+00, 2.000000e+00 => float 3.000000e+00
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 393391d52c89c..009f4aedeec2b 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -118,6 +118,29 @@ static AnyValue mulNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
return Res;
}
+static APFloat handleDenormal(APFloat Val,
+ DenormalMode::DenormalModeKind Mode) {
+ if (!Val.isDenormal())
+ return Val;
+ if (Mode == DenormalMode::PositiveZero)
+ return APFloat::getZero(Val.getSemantics(), false);
+ if (Mode == DenormalMode::PreserveSign)
+ return APFloat::getZero(Val.getSemantics(), Val.isNegative());
+ // Default case for IEEE, Dynamic, and Invalid
+ return Val;
+}
+
+static std::optional<APFloat> validateFMF(APFloat Val, FastMathFlags FMF) {
+ // The returned std::nullopt indicates that a poison value is produced.
+ if (FMF.noNaNs() && Val.isNaN())
+ return std::nullopt;
+ if (FMF.noInfs() && Val.isInfinity())
+ return std::nullopt;
+ // Since nsz (no signed zeros) is an optimization hint, so it doesn't requires
+ // runtime modification to maintain semantic correctness.
+ return Val;
+}
+
/// Instruction executor using the visitor pattern.
/// Unlike the Context class that manages the global state,
/// InstExecutor only maintains the state for call frames.
@@ -130,6 +153,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
bool Status;
Frame *CurrentFrame = nullptr;
AnyValue None;
+ APFloat::roundingMode CurrentRoundingMode;
void reportImmediateUB(StringRef Msg) {
// Check if we have already reported an immediate UB.
@@ -187,6 +211,33 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
});
}
+ void visitFPUnOp(Instruction &I,
+ function_ref<AnyValue(const APFloat &)> ScalarFn) {
+ FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
+ DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
+ I.getType()->getScalarType()->getFltSemantics());
+
+ visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
+ if (Operand.isPoison())
+ return AnyValue::poison();
+
+ // Flush input denormals
+ APFloat FInput = handleDenormal(Operand.asFloat(), DenormMode.Input);
+
+ APFloat Result = ScalarFn(FInput).asFloat();
+
+ // Flush output denormals
+ APFloat FResult = handleDenormal(Result, DenormMode.Output);
+
+ if (auto ValidateRes = validateFMF(FResult, FMF)) {
+ return *ValidateRes;
+ }
+
+ // A poison value is produced when FMFs are violated
+ return AnyValue::poison();
+ });
+ }
+
AnyValue computeBinOp(
Type *Ty, const AnyValue &LHS, const AnyValue &RHS,
function_ref<AnyValue(const AnyValue &, const AnyValue &)> ScalarFn) {
@@ -219,6 +270,42 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
});
}
+ void visitFPBinOp(
+ Instruction &I,
+ function_ref<AnyValue(const APFloat &, const APFloat &)> ScalarFn) {
+ FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
+ DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
+ I.getType()->getScalarType()->getFltSemantics());
+
+ visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+ if (LHS.isPoison() || RHS.isPoison())
+ return AnyValue::poison();
+
+ if (auto ValidateRes = validateFMF(LHS.asFloat(), FMF); !ValidateRes) {
+ return AnyValue::poison();
+ }
+ if (auto ValidateRes = validateFMF(RHS.asFloat(), FMF); !ValidateRes) {
+ return AnyValue::poison();
+ }
+
+ // Flush input denormals
+ APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
+ APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
+
+ APFloat Result = ScalarFn(FLHS, FRHS).asFloat();
+
+ // Flush output denormals
+ APFloat FResult = handleDenormal(Result, DenormMode.Output);
+
+ if (auto ValidateRes = validateFMF(FResult, FMF)) {
+ return *ValidateRes;
+ }
+
+ // A poison value is produced when FMFs are violated
+ return AnyValue::poison();
+ });
+ }
+
void jumpTo(Instruction &Terminator, BasicBlock *DestBB) {
if (!Handler.onBBJump(Terminator, *DestBB)) {
Status = false;
@@ -418,7 +505,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
- : Ctx(C), DL(Ctx.getDataLayout()), Handler(H), Status(true) {
+ : Ctx(C), DL(Ctx.getDataLayout()), Handler(H), Status(true),
+ CurrentRoundingMode(APFloat::rmNearestTiesToEven) {
CallStack.emplace_back(F, /*CallSite=*/nullptr, /*LastFrame=*/nullptr, Args,
RetVal, Ctx.getTLIImpl());
}
@@ -767,6 +855,50 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
});
}
+ void visitFAdd(BinaryOperator &I) {
+ visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ APFloat Res = LHS;
+ Res.add(RHS, CurrentRoundingMode);
+ return Res;
+ });
+ }
+
+ void visitFSub(BinaryOperator &I) {
+ visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ APFloat Res = LHS;
+ Res.subtract(RHS, CurrentRoundingMode);
+ return Res;
+ });
+ }
+
+ void visitFMul(BinaryOperator &I) {
+ visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ APFloat Res = LHS;
+ Res.multiply(RHS, CurrentRoundingMode);
+ return Res;
+ });
+ }
+
+ void visitFDiv(BinaryOperator &I) {
+ visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ APFloat Res = LHS;
+ Res.divide(RHS, CurrentRoundingMode);
+ return Res;
+ });
+ }
+
+ void visitFRem(BinaryOperator &I) {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ APFloat Res = LHS;
+ Res.mod(RHS);
+ return Res;
+ });
+ }
+
+ void visitFNeg(UnaryOperator &I) {
+ visitFPUnOp(I, [](const APFloat &Operand) -> AnyValue { return -Operand; });
+ }
+
void visitTruncInst(TruncInst &Trunc) {
visitIntUnOp(Trunc, [&](const APInt &Operand) -> AnyValue {
unsigned DestBW = Trunc.getType()->getScalarSizeInBits();
@@ -794,6 +926,77 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
});
}
+ void visitFPExtInst(FPExtInst &FPExt) { visitFPConvInst(FPExt); }
+
+ void visitFPTruncInst(FPTruncInst &FPTrunc) { visitFPConvInst(FPTrunc); }
+
+ void visitFPConvInst(Instruction &I) {
+ const fltSemantics &DstSem =
+ I.getType()->getScalarType()->getFltSemantics();
+
+ visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
+ if (Operand.isPoison())
+ return AnyValue::poison();
+
+ APFloat Res = Operand.asFloat();
+ bool LosesInfo;
+ Res.convert(DstSem, CurrentRoundingMode, &LosesInfo);
+ return AnyValue(Res);
+ });
+ }
+
+ void visitFPToSIInst(FPToSIInst &FPToSI) {
+ visitFPToIntInst(FPToSI, /*IsUnsigned=*/false);
+ }
+
+ void visitFPToUIInst(FPToUIInst &FPToUI) {
+ visitFPToIntInst(FPToUI, /*IsUnsigned=*/true);
+ }
+
+ void visitFPToIntInst(Instruction &I, bool IsUnsigned) {
+ // Note: We DO NOT use CurrentRoundingMode here.
+ // Language specs require truncation towards zero for FP-to-Int conversions.
+ visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
+ if (Operand.isPoison())
+ return AnyValue::poison();
+
+ APSInt Res(I.getType()->getScalarSizeInBits(), /*isUnsigned=*/IsUnsigned);
+ bool IsExact;
+ APFloat::opStatus Status = Operand.asFloat().convertToInteger(
+ Res, APFloat::rmTowardZero, &IsExact);
+
+ if (Status == APFloat::opInvalidOp)
+ return AnyValue::poison();
+
+ return AnyValue(Res);
+ });
+ }
+
+ void visitSIToFPInst(SIToFPInst &SIToFP) {
+ visitIntToFPInst(SIToFP, /*IsSigned=*/true);
+ }
+
+ void visitUIToFPInst(UIToFPInst &UIToFP) {
+ visitIntToFPInst(UIToFP, /*IsSigned=*/false);
+ }
+
+ void visitIntToFPInst(Instruction &I, bool IsSigned) {
+ const fltSemantics &DstSem =
+ I.getType()->getScalarType()->getFltSemantics();
+
+ visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
+ if (Operand.isPoison())
+ return AnyValue::poison();
+
+ APFloat Res(DstSem);
+
+ Res.convertFromAPInt(Operand.asInteger(), /*IsSigned=*/IsSigned,
+ CurrentRoundingMode);
+
+ return AnyValue(Res);
+ });
+ }
+
void visitAnd(BinaryOperator &I) {
visitIntBinOp(I, [](const APInt &LHS, const APInt &RHS) -> AnyValue {
return LHS & RHS;
@@ -860,6 +1063,97 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
});
}
+ void visitFCmpInst(FCmpInst &I) {
+ DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
+ I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
+ FastMathFlags FMF = I.getFastMathFlags();
+
+ visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+ if (LHS.isPoison() || RHS.isPoison()) {
+ return AnyValue::poison();
+ }
+
+ if (auto ValidateRes = validateFMF(LHS.asFloat(), FMF); !ValidateRes) {
+ return AnyValue::poison();
+ }
+ if (auto ValidateRes = validateFMF(RHS.asFloat(), FMF); !ValidateRes) {
+ return AnyValue::poison();
+ }
+
+ APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
+ APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
+
+ APFloat::cmpResult CmpResult = FLHS.compare(FRHS);
+
+ bool Result = false;
+
+ switch (I.getPredicate()) {
+ case FCmpInst::FCMP_FALSE:
+ Result = false;
+ break;
+ case FCmpInst::FCMP_OEQ:
+ Result = (CmpResult == APFloat::cmpEqual);
+ break;
+ case FCmpInst::FCMP_OGT:
+ Result = (CmpResult == APFloat::cmpGreaterThan);
+ break;
+ case FCmpInst::FCMP_OGE:
+ Result = (CmpResult == APFloat::cmpEqual ||
+ CmpResult == APFloat::cmpGreaterThan);
+ break;
+ case FCmpInst::FCMP_OLT:
+ Result = (CmpResult == APFloat::cmpLessThan);
+ break;
+ case FCmpInst::FCMP_OLE:
+ Result = (CmpResult == APFloat::cmpEqual ||
+ CmpResult == APFloat::cmpLessThan);
+ break;
+ case FCmpInst::FCMP_ONE:
+ Result = (CmpResult == APFloat::cmpGreaterThan ||
+ CmpResult == APFloat::cmpLessThan);
+ break;
+ case FCmpInst::FCMP_ORD:
+ Result = (CmpResult != APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_UNO:
+ Result = (CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_UEQ:
+ Result = (CmpResult == APFloat::cmpEqual ||
+ CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_UGT:
+ Result = (CmpResult == APFloat::cmpGreaterThan ||
+ CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_UGE:
+ Result = (CmpResult == APFloat::cmpEqual ||
+ CmpResult == APFloat::cmpGreaterThan ||
+ CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_ULT:
+ Result = (CmpResult == APFloat::cmpLessThan ||
+ CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_ULE:
+ Result = (CmpResult == APFloatBase::cmpEqual ||
+ CmpResult == APFloat::cmpLessThan ||
+ CmpResult == APFloat::cmpUnordered);
+ break;
+ case FCmpInst::FCMP_UNE:
+ Result = (CmpResult != APFloatBase::cmpEqual);
+ break;
+ case FCmpInst::FCMP_TRUE:
+ Result = true;
+ break;
+ default:
+ llvm_unreachable("Invalid FCmp predicate");
+ }
+
+ return AnyValue::boolean(Result);
+ });
+ }
+
void visitSelect(SelectInst &SI) {
// TODO: handle fast-math flags.
if (SI.getCondition()->getType()->isIntegerTy(1)) {
>From de1a32af17e08b4c24bc69372272eabde531a31c Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 25 Mar 2026 16:36:27 +0800
Subject: [PATCH 02/28] [llubi] Handle fast-math flags in phi nodes and select
---
llvm/test/tools/llubi/fp_phi_select.ll | 33 +++++++
llvm/tools/llubi/lib/Interpreter.cpp | 124 ++++++++++++++-----------
2 files changed, 104 insertions(+), 53 deletions(-)
create mode 100644 llvm/test/tools/llubi/fp_phi_select.ll
diff --git a/llvm/test/tools/llubi/fp_phi_select.ll b/llvm/test/tools/llubi/fp_phi_select.ll
new file mode 100644
index 0000000000000..9c0685a4d1cbc
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_phi_select.ll
@@ -0,0 +1,33 @@
+; 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() {
+entry:
+ %nan = fdiv float 0.0, 0.0
+ %inf = fdiv float 1.0, 0.0
+
+ %safe_sel = select i1 true, float %nan, float 1.0
+ %bad_sel_nan = select nnan i1 true, float %nan, float 1.0
+ %bad_sel_inf = select ninf i1 false, float 1.0, float %inf
+
+ br label %phi.test
+
+phi.test:
+ %safe_phi = phi float [ %nan, %entry ]
+ %bad_phi_nan = phi nnan float [ %nan, %entry ]
+ %bad_phi_inf = phi ninf float [ %inf, %entry ]
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
+; CHECK-NEXT: %safe_sel = select i1 true, float %nan, float 1.000000e+00 => float NaN
+; CHECK-NEXT: %bad_sel_nan = select nnan i1 true, float %nan, float 1.000000e+00 => poison
+; CHECK-NEXT: %bad_sel_inf = select ninf i1 false, float 1.000000e+00, float %inf => poison
+; CHECK-NEXT: br label %phi.test jump to %phi.test
+; CHECK-NEXT: %safe_phi = phi float [ %nan, %entry ] => float NaN
+; CHECK-NEXT: %bad_phi_nan = phi nnan float [ %nan, %entry ] => poison
+; CHECK-NEXT: %bad_phi_inf = phi ninf float [ %inf, %entry ] => poison
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 009f4aedeec2b..e187cd348384f 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -130,12 +130,24 @@ static APFloat handleDenormal(APFloat Val,
return Val;
}
-static std::optional<APFloat> validateFMF(APFloat Val, FastMathFlags FMF) {
- // The returned std::nullopt indicates that a poison value is produced.
- if (FMF.noNaNs() && Val.isNaN())
- return std::nullopt;
- if (FMF.noInfs() && Val.isInfinity())
- return std::nullopt;
+static AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF) {
+ if (Val.isPoison() || Val.isNone())
+ return AnyValue::poison();
+
+ if (Val.isAggregate()) {
+ std::vector<AnyValue> ResVec;
+ ResVec.reserve(Val.asAggregate().size());
+ for (const auto &A : Val.asAggregate()) {
+ ResVec.push_back(handleFMFFlags(A, FMF));
+ }
+ return AnyValue(ResVec);
+ }
+
+ const APFloat &APVal = Val.asFloat();
+ if (FMF.noNaNs() && APVal.isNaN())
+ return AnyValue::poison();
+ if (FMF.noInfs() && APVal.isInfinity())
+ return AnyValue::poison();
// Since nsz (no signed zeros) is an optimization hint, so it doesn't requires
// runtime modification to maintain semantic correctness.
return Val;
@@ -229,12 +241,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
// Flush output denormals
APFloat FResult = handleDenormal(Result, DenormMode.Output);
- if (auto ValidateRes = validateFMF(FResult, FMF)) {
- return *ValidateRes;
- }
-
- // A poison value is produced when FMFs are violated
- return AnyValue::poison();
+ return handleFMFFlags(FResult, FMF);
});
}
@@ -281,11 +288,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- if (auto ValidateRes = validateFMF(LHS.asFloat(), FMF); !ValidateRes) {
- return AnyValue::poison();
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison()) {
+ return ValidateRes;
}
- if (auto ValidateRes = validateFMF(RHS.asFloat(), FMF); !ValidateRes) {
- return AnyValue::poison();
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison()) {
+ return ValidateRes;
}
// Flush input denormals
@@ -297,12 +304,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
// Flush output denormals
APFloat FResult = handleDenormal(Result, DenormMode.Output);
- if (auto ValidateRes = validateFMF(FResult, FMF)) {
- return *ValidateRes;
- }
-
- // A poison value is produced when FMFs are violated
- return AnyValue::poison();
+ return handleFMFFlags(FResult, FMF);
});
}
@@ -321,9 +323,16 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
SmallVector<std::pair<PHINode *, AnyValue>> IncomingValues;
PHINode *PHI = nullptr;
while ((PHI = dyn_cast<PHINode>(CurrentFrame->PC))) {
- Value *Incoming = PHI->getIncomingValueForBlock(From);
- // TODO: handle fast-math flags.
- IncomingValues.emplace_back(PHI, getValue(Incoming));
+ AnyValue IncomingVal = getValue(PHI->getIncomingValueForBlock(From));
+
+ // Fast-math flags validation
+ if (isa<FPMathOperator>(PHI)) {
+ FastMathFlags FMF = PHI->getFastMathFlags();
+ if (FMF.any())
+ IncomingVal = handleFMFFlags(std::move(IncomingVal), FMF);
+ }
+
+ IncomingValues.emplace_back(PHI, IncomingVal);
++CurrentFrame->PC;
}
for (auto &[K, V] : IncomingValues)
@@ -1073,11 +1082,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return AnyValue::poison();
}
- if (auto ValidateRes = validateFMF(LHS.asFloat(), FMF); !ValidateRes) {
- return AnyValue::poison();
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison()) {
+ return ValidateRes;
}
- if (auto ValidateRes = validateFMF(RHS.asFloat(), FMF); !ValidateRes) {
- return AnyValue::poison();
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison()) {
+ return ValidateRes;
}
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
@@ -1155,40 +1164,49 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
}
void visitSelect(SelectInst &SI) {
- // TODO: handle fast-math flags.
+ AnyValue Res;
+
if (SI.getCondition()->getType()->isIntegerTy(1)) {
switch (getValue(SI.getCondition()).asBoolean()) {
case BooleanKind::True:
- setResult(SI, getValue(SI.getTrueValue()));
- return;
- case BooleanKind::False:
- setResult(SI, getValue(SI.getFalseValue()));
- return;
- case BooleanKind::Poison:
- setResult(SI, AnyValue::getPoisonValue(Ctx, SI.getType()));
- return;
- }
- }
-
- auto &Cond = getValue(SI.getCondition()).asAggregate();
- auto &TV = getValue(SI.getTrueValue()).asAggregate();
- auto &FV = getValue(SI.getFalseValue()).asAggregate();
- std::vector<AnyValue> Res;
- size_t Len = Cond.size();
- Res.reserve(Len);
- for (uint32_t I = 0; I != Len; ++I) {
- switch (Cond[I].asBoolean()) {
- case BooleanKind::True:
- Res.push_back(TV[I]);
+ Res = getValue(SI.getTrueValue());
break;
case BooleanKind::False:
- Res.push_back(FV[I]);
+ Res = getValue(SI.getFalseValue());
break;
case BooleanKind::Poison:
- Res.push_back(AnyValue::poison());
+ Res = AnyValue::getPoisonValue(Ctx, SI.getType());
break;
}
+ } else {
+ auto &Cond = getValue(SI.getCondition()).asAggregate();
+ auto &TV = getValue(SI.getTrueValue()).asAggregate();
+ auto &FV = getValue(SI.getFalseValue()).asAggregate();
+ std::vector<AnyValue> ResVec;
+ size_t Len = Cond.size();
+ ResVec.reserve(Len);
+ for (uint32_t I = 0; I != Len; ++I) {
+ switch (Cond[I].asBoolean()) {
+ case BooleanKind::True:
+ ResVec.push_back(TV[I]);
+ break;
+ case BooleanKind::False:
+ ResVec.push_back(FV[I]);
+ break;
+ case BooleanKind::Poison:
+ ResVec.push_back(AnyValue::poison());
+ break;
+ }
+ }
+ Res = AnyValue(std::move(ResVec));
}
+
+ // Handle fast-math flags
+ if (auto *FPMO = dyn_cast<FPMathOperator>(&SI)) {
+ if (FastMathFlags FMF = FPMO->getFastMathFlags(); FMF.any())
+ Res = handleFMFFlags(std::move(Res), FMF);
+ }
+
setResult(SI, std::move(Res));
}
>From 5c505b3d1b86d4a9b18d734ad1b36aa7aeef2b4f Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 13:37:11 +0800
Subject: [PATCH 03/28] [llubi] Minor fixes to floating-point numbers
---
llvm/tools/llubi/lib/Context.cpp | 5 +
llvm/tools/llubi/lib/Context.h | 2 +
llvm/tools/llubi/lib/Interpreter.cpp | 145 +++++++--------------------
3 files changed, 46 insertions(+), 106 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 968ac4f561558..ca7105153a34c 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -499,6 +499,11 @@ uint64_t Context::getEffectiveTypeStoreSize(Type *Ty) {
return getEffectiveTypeSize(DL.getTypeStoreSize(Ty));
}
+bool Context::getRandomBool() {
+ // We use the lowest bit of the raw bits from RNG as the result:
+ return static_cast<bool>(Rng() & 1);
+}
+
void MemoryObject::markAsFreed() {
State = MemoryObjectState::Freed;
Bytes.clear();
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index d1960b270d9bd..fcd6a3ad4485a 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -262,6 +262,8 @@ class Context {
/// error occurred during execution.
bool runFunction(Function &F, ArrayRef<AnyValue> Args, AnyValue &RetVal,
EventHandler &Handler);
+
+ bool getRandomBool();
};
} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index e187cd348384f..e743193630957 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -127,29 +127,8 @@ static APFloat handleDenormal(APFloat Val,
if (Mode == DenormalMode::PreserveSign)
return APFloat::getZero(Val.getSemantics(), Val.isNegative());
// Default case for IEEE, Dynamic, and Invalid
- return Val;
-}
-
-static AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF) {
- if (Val.isPoison() || Val.isNone())
- return AnyValue::poison();
-
- if (Val.isAggregate()) {
- std::vector<AnyValue> ResVec;
- ResVec.reserve(Val.asAggregate().size());
- for (const auto &A : Val.asAggregate()) {
- ResVec.push_back(handleFMFFlags(A, FMF));
- }
- return AnyValue(ResVec);
- }
-
- const APFloat &APVal = Val.asFloat();
- if (FMF.noNaNs() && APVal.isNaN())
- return AnyValue::poison();
- if (FMF.noInfs() && APVal.isInfinity())
- return AnyValue::poison();
- // Since nsz (no signed zeros) is an optimization hint, so it doesn't requires
- // runtime modification to maintain semantic correctness.
+ // Currently we treat Dynamic the same as IEEE, since we don't support
+ // changing the mode at this point.
return Val;
}
@@ -196,6 +175,31 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
CurrentFrame->ValueMap.insert_or_assign(&I, std::move(V));
}
+ AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF) {
+ if (Val.isPoison())
+ return AnyValue::poison();
+
+ if (Val.isAggregate()) {
+ std::vector<AnyValue> ResVec;
+ ResVec.reserve(Val.asAggregate().size());
+ for (const auto &A : Val.asAggregate())
+ ResVec.push_back(handleFMFFlags(A, FMF));
+ return AnyValue(ResVec);
+ }
+
+ const APFloat &APVal = Val.asFloat();
+ if (FMF.noNaNs() && APVal.isNaN())
+ return AnyValue::poison();
+ if (FMF.noInfs() && APVal.isInfinity())
+ return AnyValue::poison();
+ // Non-deterministically flip the sign of the input.
+ if (FMF.noSignedZeros() && APVal.isZero()) {
+ return AnyValue(
+ APFloat::getZero(APVal.getSemantics(), Ctx.getRandomBool()));
+ }
+ return Val;
+ }
+
AnyValue computeUnOp(Type *Ty, const AnyValue &Operand,
function_ref<AnyValue(const AnyValue &)> ScalarFn) {
if (Ty->isVectorTy()) {
@@ -233,15 +237,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
if (Operand.isPoison())
return AnyValue::poison();
- // Flush input denormals
- APFloat FInput = handleDenormal(Operand.asFloat(), DenormMode.Input);
-
- APFloat Result = ScalarFn(FInput).asFloat();
+ // 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.
- // Flush output denormals
- APFloat FResult = handleDenormal(Result, DenormMode.Output);
+ APFloat Result = ScalarFn(Operand.asFloat()).asFloat();
- return handleFMFFlags(FResult, FMF);
+ return handleFMFFlags(Result, FMF);
});
}
@@ -288,12 +290,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison()) {
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison())
return ValidateRes;
- }
- if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison()) {
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison())
return ValidateRes;
- }
// Flush input denormals
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
@@ -867,7 +867,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
void visitFAdd(BinaryOperator &I) {
visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
- Res.add(RHS, CurrentRoundingMode);
+ Res.add(RHS, APFloat::rmNearestTiesToEven);
return Res;
});
}
@@ -875,7 +875,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
void visitFSub(BinaryOperator &I) {
visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
- Res.subtract(RHS, CurrentRoundingMode);
+ Res.subtract(RHS, APFloat::rmNearestTiesToEven);
return Res;
});
}
@@ -883,7 +883,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
void visitFMul(BinaryOperator &I) {
visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
- Res.multiply(RHS, CurrentRoundingMode);
+ Res.multiply(RHS, APFloat::rmNearestTiesToEven);
return Res;
});
}
@@ -891,7 +891,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
void visitFDiv(BinaryOperator &I) {
visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
- Res.divide(RHS, CurrentRoundingMode);
+ Res.divide(RHS, APFloat::rmNearestTiesToEven);
return Res;
});
}
@@ -1092,74 +1092,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
- APFloat::cmpResult CmpResult = FLHS.compare(FRHS);
-
- bool Result = false;
-
- switch (I.getPredicate()) {
- case FCmpInst::FCMP_FALSE:
- Result = false;
- break;
- case FCmpInst::FCMP_OEQ:
- Result = (CmpResult == APFloat::cmpEqual);
- break;
- case FCmpInst::FCMP_OGT:
- Result = (CmpResult == APFloat::cmpGreaterThan);
- break;
- case FCmpInst::FCMP_OGE:
- Result = (CmpResult == APFloat::cmpEqual ||
- CmpResult == APFloat::cmpGreaterThan);
- break;
- case FCmpInst::FCMP_OLT:
- Result = (CmpResult == APFloat::cmpLessThan);
- break;
- case FCmpInst::FCMP_OLE:
- Result = (CmpResult == APFloat::cmpEqual ||
- CmpResult == APFloat::cmpLessThan);
- break;
- case FCmpInst::FCMP_ONE:
- Result = (CmpResult == APFloat::cmpGreaterThan ||
- CmpResult == APFloat::cmpLessThan);
- break;
- case FCmpInst::FCMP_ORD:
- Result = (CmpResult != APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_UNO:
- Result = (CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_UEQ:
- Result = (CmpResult == APFloat::cmpEqual ||
- CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_UGT:
- Result = (CmpResult == APFloat::cmpGreaterThan ||
- CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_UGE:
- Result = (CmpResult == APFloat::cmpEqual ||
- CmpResult == APFloat::cmpGreaterThan ||
- CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_ULT:
- Result = (CmpResult == APFloat::cmpLessThan ||
- CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_ULE:
- Result = (CmpResult == APFloatBase::cmpEqual ||
- CmpResult == APFloat::cmpLessThan ||
- CmpResult == APFloat::cmpUnordered);
- break;
- case FCmpInst::FCMP_UNE:
- Result = (CmpResult != APFloatBase::cmpEqual);
- break;
- case FCmpInst::FCMP_TRUE:
- Result = true;
- break;
- default:
- llvm_unreachable("Invalid FCmp predicate");
- }
-
- return AnyValue::boolean(Result);
+ return AnyValue::boolean(FCmpInst::compare(FLHS, FRHS, I.getPredicate()));
});
}
>From 1d1d54e6754f5980a515e4e0304dfe69f4e91408 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 14:17:14 +0800
Subject: [PATCH 04/28] [llubi] NaN payload propagation for floating-point
operations
---
llvm/tools/llubi/lib/Context.h | 1 +
llvm/tools/llubi/lib/Interpreter.cpp | 63 +++++++++++++++++++++++++---
2 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index fcd6a3ad4485a..68deef1ef5951 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -202,6 +202,7 @@ class Context {
uint32_t getMaxStackDepth() const { return MaxStackDepth; }
void setUndefValueBehavior(UndefValueBehavior UB) { UndefBehavior = UB; }
void reseed(uint32_t Seed) { Rng.seed(Seed); }
+ std::mt19937_64 &getRng() { return Rng; }
LLVMContext &getContext() const { return Ctx; }
const DataLayout &getDataLayout() const { return DL; }
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index e743193630957..2b885b45df022 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -299,12 +299,63 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
- APFloat Result = ScalarFn(FLHS, FRHS).asFloat();
-
- // Flush output denormals
- APFloat FResult = handleDenormal(Result, DenormMode.Output);
-
- return handleFMFFlags(FResult, FMF);
+ APFloat RawResult = ScalarFn(FLHS, FRHS).asFloat();
+
+ // Flush output denormals and handle fast-math flags.
+ AnyValue FResult =
+ handleFMFFlags(handleDenormal(RawResult, DenormMode.Output), FMF);
+
+ if (FResult.isPoison())
+ return FResult;
+
+ APFloat Result = FResult.asFloat();
+ // NaN payload propagation
+ if (Result.isNaN()) {
+ // We non-deterministically choose from the four cases as specified in
+ // the language reference. The sign is also non-deterministic
+ const int8_t Choice = static_cast<int8_t>(Ctx.getRng()() % 4);
+ const bool Sign = Ctx.getRandomBool();
+ if (Choice == 0) {
+ // Preferred NaN: the quiet bit is set and the payload is all-zero
+ return APFloat::getQNaN(Result.getSemantics(), Sign);
+ }
+ if (Choice == 1) {
+ // Quieting NaN propagation: the quiet bit is set and the payload is
+ // copied from any input operand that is a NaN. We implement this by
+ // directly set the quiet bit of the input NaN, and
+ // non-deterministically flip its sign bit
+ auto QuietNaN = [&](APFloat &Input) {
+ APFloat Quieted = Input.makeQuiet();
+ if (Sign)
+ Quieted.changeSign();
+ return Quieted;
+ };
+ if (FLHS.isNaN())
+ return QuietNaN(FLHS);
+ if (FRHS.isNaN())
+ return QuietNaN(FRHS);
+ }
+ if (Choice == 2) {
+ // Unchanged NaN propagation: the quiet bit and payload are copied
+ // from any input operand that is a NaN
+ auto FlipSign = [&](APFloat &Input) {
+ if (Sign)
+ Input.changeSign();
+ return Input;
+ };
+ if (FLHS.isNaN())
+ return FlipSign(FLHS);
+ if (FRHS.isNaN())
+ return FlipSign(FRHS);
+ }
+ if (Choice == 3) {
+ // Target-specific NaN: the quiet bit is set and the payload is picked
+ // from a target-specific set of "extra" possible NaN payloads. We
+ // approximate this by filling the payload with random values.
+ auto Payload = APInt(64, Ctx.getRng()());
+ return APFloat::getQNaN(Result.getSemantics(), Sign, &Payload);
+ }
+ }
});
}
>From 2878ac0a40f723db299d6acab232b805b9738af9 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 14:19:28 +0800
Subject: [PATCH 05/28] [llubi] Small fix to visitBinOp
---
llvm/tools/llubi/lib/Interpreter.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 2b885b45df022..11c38afa02a59 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -334,6 +334,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return QuietNaN(FLHS);
if (FRHS.isNaN())
return QuietNaN(FRHS);
+ return QuietNaN(Result);
}
if (Choice == 2) {
// Unchanged NaN propagation: the quiet bit and payload are copied
@@ -347,6 +348,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return FlipSign(FLHS);
if (FRHS.isNaN())
return FlipSign(FRHS);
+ return FlipSign(Result);
}
if (Choice == 3) {
// Target-specific NaN: the quiet bit is set and the payload is picked
>From c55d92cb731d9d60c794ff85abda443425af8d72 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 14:21:28 +0800
Subject: [PATCH 06/28] [llubi] Small fix to visitBinOp
---
llvm/tools/llubi/lib/Interpreter.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 11c38afa02a59..f9de9c8ca463e 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -358,6 +358,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return APFloat::getQNaN(Result.getSemantics(), Sign, &Payload);
}
}
+
+ return Result;
});
}
>From b967a7bb8c6f201e4f14f7c3b6f16c6e2fe7ef70 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 14:51:18 +0800
Subject: [PATCH 07/28] [llubi] Add new CLI option to control the behavior of
NaN propagation
---
llvm/tools/llubi/lib/Context.cpp | 10 +++++++++-
llvm/tools/llubi/lib/Context.h | 22 +++++++++++++++++++++-
llvm/tools/llubi/lib/Interpreter.cpp | 28 ++++++++++++++--------------
llvm/tools/llubi/llubi.cpp | 21 +++++++++++++++++++++
4 files changed, 65 insertions(+), 16 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index ca7105153a34c..2f2788f7b50fb 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -501,7 +501,15 @@ uint64_t Context::getEffectiveTypeStoreSize(Type *Ty) {
bool Context::getRandomBool() {
// We use the lowest bit of the raw bits from RNG as the result:
- return static_cast<bool>(Rng() & 1);
+ if (UndefBehavior == UndefValueBehavior::NonDeterministic)
+ return static_cast<bool>(Rng() & 1);
+ return false;
+}
+
+uint64_t Context::getRandomUInt64() {
+ if (UndefBehavior == UndefValueBehavior::NonDeterministic)
+ return Rng();
+ return 0;
}
void MemoryObject::markAsFreed() {
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 68deef1ef5951..28a4583dace1d 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -47,6 +47,19 @@ enum class UndefValueBehavior {
Zero, // All uses of the undef value yield zero.
};
+enum class NaNPropagationBehavior {
+ NonDeterministic, // Non-deterministically choose from the following 4 cases
+ PreferredNaN, // The quiet bit is set and the payload is all-zero
+ QuietingNaN, // The quiet bit is set and the payload is copied from any input
+ // operand that is a NaN
+ UnchangedNaN, // The quiet bit and payload are copied from any input operand
+ // that is a NaN
+ TargetSpecificNaN // The quiet bit is set and the payload is picked from a
+ // target-specific set of “extra” possible NaN payloads,
+ // currently we implement it by filling payload with random
+ // values
+};
+
class MemoryObject : public RefCountedBase<MemoryObject> {
uint64_t Address;
uint64_t Size;
@@ -149,6 +162,7 @@ class Context {
uint32_t MaxSteps = 0;
uint32_t MaxStackDepth = 256;
UndefValueBehavior UndefBehavior = UndefValueBehavior::NonDeterministic;
+ NaNPropagationBehavior NaNBehavior = NaNPropagationBehavior::NonDeterministic;
std::mt19937_64 Rng;
@@ -200,9 +214,14 @@ class Context {
uint32_t getVScale() const { return VScale; }
uint32_t getMaxSteps() const { return MaxSteps; }
uint32_t getMaxStackDepth() const { return MaxStackDepth; }
+ NaNPropagationBehavior getNaNPropagationBehavior() const {
+ return NaNBehavior;
+ }
void setUndefValueBehavior(UndefValueBehavior UB) { UndefBehavior = UB; }
+ void setNaNPropagationBehavior(NaNPropagationBehavior NaNBehav) {
+ NaNBehavior = NaNBehav;
+ }
void reseed(uint32_t Seed) { Rng.seed(Seed); }
- std::mt19937_64 &getRng() { return Rng; }
LLVMContext &getContext() const { return Ctx; }
const DataLayout &getDataLayout() const { return DL; }
@@ -265,6 +284,7 @@ class Context {
EventHandler &Handler);
bool getRandomBool();
+ uint64_t getRandomUInt64();
};
} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index f9de9c8ca463e..6433e41eb1c53 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -230,8 +230,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
void visitFPUnOp(Instruction &I,
function_ref<AnyValue(const APFloat &)> ScalarFn) {
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
- DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
- I.getType()->getScalarType()->getFltSemantics());
visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
if (Operand.isPoison())
@@ -311,15 +309,17 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
APFloat Result = FResult.asFloat();
// NaN payload propagation
if (Result.isNaN()) {
- // We non-deterministically choose from the four cases as specified in
- // the language reference. The sign is also non-deterministic
- const int8_t Choice = static_cast<int8_t>(Ctx.getRng()() % 4);
+ NaNPropagationBehavior Choice = Ctx.getNaNPropagationBehavior();
+ if (Choice == NaNPropagationBehavior::NonDeterministic) {
+ uint64_t NonDetChoice = Ctx.getRandomUInt64() % 4 + 1;
+ Choice = static_cast<NaNPropagationBehavior>(NonDetChoice);
+ }
const bool Sign = Ctx.getRandomBool();
- if (Choice == 0) {
+ if (Choice == NaNPropagationBehavior::PreferredNaN) {
// Preferred NaN: the quiet bit is set and the payload is all-zero
return APFloat::getQNaN(Result.getSemantics(), Sign);
}
- if (Choice == 1) {
+ if (Choice == NaNPropagationBehavior::QuietingNaN) {
// Quieting NaN propagation: the quiet bit is set and the payload is
// copied from any input operand that is a NaN. We implement this by
// directly set the quiet bit of the input NaN, and
@@ -336,7 +336,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return QuietNaN(FRHS);
return QuietNaN(Result);
}
- if (Choice == 2) {
+ if (Choice == NaNPropagationBehavior::UnchangedNaN) {
// Unchanged NaN propagation: the quiet bit and payload are copied
// from any input operand that is a NaN
auto FlipSign = [&](APFloat &Input) {
@@ -350,11 +350,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
return FlipSign(FRHS);
return FlipSign(Result);
}
- if (Choice == 3) {
+ if (Choice == NaNPropagationBehavior::TargetSpecificNaN) {
// Target-specific NaN: the quiet bit is set and the payload is picked
// from a target-specific set of "extra" possible NaN payloads. We
// approximate this by filling the payload with random values.
- auto Payload = APInt(64, Ctx.getRng()());
+ auto Payload = APInt(64, Ctx.getRandomUInt64());
return APFloat::getQNaN(Result.getSemantics(), Sign, &Payload);
}
}
@@ -920,7 +920,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
}
void visitFAdd(BinaryOperator &I) {
- visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
Res.add(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -928,7 +928,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
}
void visitFSub(BinaryOperator &I) {
- visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
Res.subtract(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -936,7 +936,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
}
void visitFMul(BinaryOperator &I) {
- visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
Res.multiply(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -944,7 +944,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
}
void visitFDiv(BinaryOperator &I) {
- visitFPBinOp(I, [this](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
APFloat Res = LHS;
Res.divide(RHS, APFloat::rmNearestTiesToEven);
return Res;
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index de76a7e64c27b..4200c9246e8d8 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -87,6 +87,26 @@ cl::opt<ubi::UndefValueBehavior> UndefBehavior(
clEnumVal(ubi::UndefValueBehavior::Zero,
"All uses of an uninitialized byte yield zero.")));
+cl::opt<ubi::NaNPropagationBehavior> NaNPropagationBehavior(
+ "", cl::desc("Choose NaN propagation behavior:"),
+ cl::values(
+ clEnumVal(ubi::NaNPropagationBehavior::NonDeterministic,
+ "Non-deterministically choose from 4 cases as specified by "
+ "language reference."),
+ clEnumVal(ubi::NaNPropagationBehavior::PreferredNaN,
+ "The quiet bit is set and the payload is all-zero."),
+ clEnumVal(
+ ubi::NaNPropagationBehavior::QuietingNaN,
+ "The quiet bit is set and the payload is copied from any input"
+ "operand that is a NaN."),
+ clEnumVal(ubi::NaNPropagationBehavior::UnchangedNaN,
+ "The quiet bit and payload are copied from any input operand"
+ "that is a NaN"),
+ clEnumVal(ubi::NaNPropagationBehavior::TargetSpecificNaN,
+ "The quiet bit is set and the payload is picked from a"
+ "target-specific set of “extra” possible NaN payloads."
+ "Implemented by filling payload with random values")));
+
class VerboseEventHandler : public ubi::EventHandler {
public:
bool onInstructionExecuted(Instruction &I,
@@ -178,6 +198,7 @@ int main(int argc, char **argv) {
Ctx.setMaxSteps(MaxSteps);
Ctx.setMaxStackDepth(MaxStackDepth);
Ctx.setUndefValueBehavior(UndefBehavior);
+ Ctx.setNaNPropagationBehavior(NaNPropagationBehavior);
Ctx.reseed(Seed);
if (!Ctx.initGlobalValues()) {
>From ddceccdfc39000bfc069868d2b19ea155549c87d Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 15:15:45 +0800
Subject: [PATCH 08/28] [llubi] Update test cases
---
llvm/test/tools/llubi/fp_arith.ll | 2 +-
llvm/test/tools/llubi/fp_cmp.ll | 2 +-
llvm/test/tools/llubi/fp_phi_select.ll | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/test/tools/llubi/fp_arith.ll b/llvm/test/tools/llubi/fp_arith.ll
index 2fd09ac53b1a2..69b091844ce8d 100644
--- a/llvm/test/tools/llubi/fp_arith.ll
+++ b/llvm/test/tools/llubi/fp_arith.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd float 1.000000e+00, poison => poison
; CHECK-NEXT: %p2 = fneg float poison => poison
; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_cmp.ll b/llvm/test/tools/llubi/fp_cmp.ll
index 489a91f256e91..da6d7d550479b 100644
--- a/llvm/test/tools/llubi/fp_cmp.ll
+++ b/llvm/test/tools/llubi/fp_cmp.ll
@@ -17,7 +17,7 @@ define void @main() {
; CHECK-NEXT: %eq = fcmp oeq float 1.000000e+00, 1.000000e+00 => T
; CHECK-NEXT: %ne = fcmp one float 1.000000e+00, 2.000000e+00 => T
; CHECK-NEXT: %gt = fcmp ogt float 2.000000e+00, 1.000000e+00 => T
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
; CHECK-NEXT: %ord_nan = fcmp oeq float %nan, 1.000000e+00 => F
; CHECK-NEXT: %uno_nan = fcmp uno float %nan, 1.000000e+00 => T
; CHECK-NEXT: %une_nan = fcmp une float %nan, 1.000000e+00 => T
diff --git a/llvm/test/tools/llubi/fp_phi_select.ll b/llvm/test/tools/llubi/fp_phi_select.ll
index 9c0685a4d1cbc..d3492e84ea880 100644
--- a/llvm/test/tools/llubi/fp_phi_select.ll
+++ b/llvm/test/tools/llubi/fp_phi_select.ll
@@ -20,13 +20,13 @@ phi.test:
ret void
}
; CHECK: Entering function: main
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
-; CHECK-NEXT: %safe_sel = select i1 true, float %nan, float 1.000000e+00 => float NaN
+; CHECK-NEXT: %safe_sel = select i1 true, float %nan, float 1.000000e+00 => float 0xFFC00000
; CHECK-NEXT: %bad_sel_nan = select nnan i1 true, float %nan, float 1.000000e+00 => poison
; CHECK-NEXT: %bad_sel_inf = select ninf i1 false, float 1.000000e+00, float %inf => poison
; CHECK-NEXT: br label %phi.test jump to %phi.test
-; CHECK-NEXT: %safe_phi = phi float [ %nan, %entry ] => float NaN
+; CHECK-NEXT: %safe_phi = phi float [ %nan, %entry ] => float 0xFFC00000
; CHECK-NEXT: %bad_phi_nan = phi nnan float [ %nan, %entry ] => poison
; CHECK-NEXT: %bad_phi_inf = phi ninf float [ %inf, %entry ] => poison
; CHECK-NEXT: ret void
>From 2f8b3057d0dcfc1e32e2fe5b116e0825eeb7f5eb Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 27 Mar 2026 15:21:04 +0800
Subject: [PATCH 09/28] [llubi] Non-deterministically flushing the output FP
numbers
---
llvm/tools/llubi/lib/Interpreter.cpp | 38 ++++++++++++++++------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 6433e41eb1c53..c3568f687174f 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -118,20 +118,6 @@ static AnyValue mulNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
return Res;
}
-static APFloat handleDenormal(APFloat Val,
- DenormalMode::DenormalModeKind Mode) {
- if (!Val.isDenormal())
- return Val;
- if (Mode == DenormalMode::PositiveZero)
- return APFloat::getZero(Val.getSemantics(), false);
- if (Mode == DenormalMode::PreserveSign)
- return APFloat::getZero(Val.getSemantics(), Val.isNegative());
- // Default case for IEEE, Dynamic, and Invalid
- // Currently we treat Dynamic the same as IEEE, since we don't support
- // changing the mode at this point.
- return Val;
-}
-
/// Instruction executor using the visitor pattern.
/// Unlike the Context class that manages the global state,
/// InstExecutor only maintains the state for call frames.
@@ -175,6 +161,26 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
CurrentFrame->ValueMap.insert_or_assign(&I, std::move(V));
}
+ APFloat handleDenormal(APFloat Val, DenormalMode::DenormalModeKind Mode,
+ bool NonDet = false) {
+ if (!Val.isDenormal())
+ return Val;
+ if (NonDet) {
+ // Non-deterministically choose between flushing or preserving the
+ // denormal value.
+ if (Ctx.getRandomBool())
+ return Val;
+ }
+ if (Mode == DenormalMode::PositiveZero)
+ return APFloat::getZero(Val.getSemantics(), false);
+ if (Mode == DenormalMode::PreserveSign)
+ return APFloat::getZero(Val.getSemantics(), Val.isNegative());
+ // Default case for IEEE, Dynamic, and Invalid
+ // Currently we treat Dynamic the same as IEEE, since we don't support
+ // changing the mode at this point.
+ return Val;
+ }
+
AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF) {
if (Val.isPoison())
return AnyValue::poison();
@@ -300,8 +306,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
APFloat RawResult = ScalarFn(FLHS, FRHS).asFloat();
// Flush output denormals and handle fast-math flags.
- AnyValue FResult =
- handleFMFFlags(handleDenormal(RawResult, DenormMode.Output), FMF);
+ AnyValue FResult = handleFMFFlags(
+ handleDenormal(RawResult, DenormMode.Output, true), FMF);
if (FResult.isPoison())
return FResult;
>From e5cd154a2e678bd2d80553a44f59c185e84f28f3 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 14:06:27 +0800
Subject: [PATCH 10/28] [llubi] Some FP-related fixes
---
llvm/tools/llubi/lib/Interpreter.cpp | 49 ++++++++++++++++++----------
1 file changed, 31 insertions(+), 18 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index ee711a59fc025..48ebd53d2c504 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -155,7 +155,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFPUnOp(Instruction &I,
- function_ref<AnyValue(const APFloat &)> ScalarFn) {
+ function_ref<APFloat(const APFloat &)> ScalarFn) {
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
visitUnOp(I, [&](const AnyValue &Operand) -> AnyValue {
@@ -166,7 +166,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// operation is fneg. And fneg is specified as a bitwise operation which
// only flips the sign bit of the input.
- APFloat Result = ScalarFn(Operand.asFloat()).asFloat();
+ APFloat Result = ScalarFn(Operand.asFloat());
return handleFMFFlags(Result, FMF);
});
@@ -206,10 +206,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
void visitFPBinOp(
Instruction &I,
- function_ref<AnyValue(const APFloat &, const APFloat &)> ScalarFn) {
+ function_ref<APFloat(const APFloat &, const APFloat &)> ScalarFn) {
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
- DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
- I.getType()->getScalarType()->getFltSemantics());
+ DenormalMode DenormMode = getCurrentDenormalMode(I);
visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
if (LHS.isPoison() || RHS.isPoison())
@@ -224,7 +223,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
- APFloat RawResult = ScalarFn(FLHS, FRHS).asFloat();
+ APFloat RawResult = ScalarFn(FLHS, FRHS);
// Flush output denormals and handle fast-math flags.
AnyValue FResult = handleFMFFlags(
@@ -281,7 +280,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// Target-specific NaN: the quiet bit is set and the payload is picked
// from a target-specific set of "extra" possible NaN payloads. We
// approximate this by filling the payload with random values.
- auto Payload = APInt(64, Ctx.getRandomUInt64());
+ APInt Payload(64, Ctx.getRandomUInt64());
return APFloat::getQNaN(Result.getSemantics(), Sign, &Payload);
}
}
@@ -411,6 +410,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return IdxInt.sext(IndexBitWidth);
}
+ DenormalMode getCurrentDenormalMode(Instruction &I) {
+ return CurrentFrame->Func.getDenormalMode(
+ I.getType()->getScalarType()->getFltSemantics());
+ }
+
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -765,7 +769,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFAdd(BinaryOperator &I) {
- visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.add(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -773,7 +777,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFSub(BinaryOperator &I) {
- visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.subtract(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -781,7 +785,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFMul(BinaryOperator &I) {
- visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.multiply(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -789,7 +793,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFDiv(BinaryOperator &I) {
- visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.divide(RHS, APFloat::rmNearestTiesToEven);
return Res;
@@ -797,7 +801,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFRem(BinaryOperator &I) {
- visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> AnyValue {
+ visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.mod(RHS);
return Res;
@@ -805,7 +809,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFNeg(UnaryOperator &I) {
- visitFPUnOp(I, [](const APFloat &Operand) -> AnyValue { return -Operand; });
+ visitFPUnOp(I, [](const APFloat &Operand) -> APFloat { return -Operand; });
}
void visitTruncInst(TruncInst &Trunc) {
@@ -847,10 +851,20 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (Operand.isPoison())
return AnyValue::poison();
- APFloat Res = Operand.asFloat();
+ FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
+ APFloat FOperand = Operand.asFloat();
+ if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
+ ValidateRes.isPoison())
+ return ValidateRes;
+
bool LosesInfo;
- Res.convert(DstSem, CurrentRoundingMode, &LosesInfo);
- return AnyValue(Res);
+ FOperand.convert(DstSem, CurrentRoundingMode, &LosesInfo);
+
+ if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
+ ValidateRes.isPoison())
+ return ValidateRes;
+
+ return AnyValue(FOperand);
});
}
@@ -973,8 +987,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFCmpInst(FCmpInst &I) {
- DenormalMode DenormMode = CurrentFrame->Func.getDenormalMode(
- I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
+ DenormalMode DenormMode = getCurrentDenormalMode(I);
FastMathFlags FMF = I.getFastMathFlags();
visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
>From 6f70afe607d965c906d49b825b34152e1d08cc91 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 14:13:45 +0800
Subject: [PATCH 11/28] [llubi] Small format fix
---
llvm/tools/llubi/lib/Interpreter.cpp | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 48ebd53d2c504..20981dad6c143 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -991,16 +991,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FastMathFlags FMF = I.getFastMathFlags();
visitBinOp(I, [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
- if (LHS.isPoison() || RHS.isPoison()) {
+ if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- }
- if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison()) {
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison())
return ValidateRes;
- }
- if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison()) {
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison())
return ValidateRes;
- }
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
>From 1256652434d5b31c2ea04054d5dd70dfd2d8f3ee Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 14:42:18 +0800
Subject: [PATCH 12/28] [llubi] Small FP-related fixes
---
llvm/tools/llubi/lib/Interpreter.cpp | 60 +++++++++++++++++++++++++---
llvm/tools/llubi/llubi.cpp | 3 +-
2 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 20981dad6c143..5d62d8cd69ec5 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -68,7 +68,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
const DataLayout &DL;
std::list<Frame> CallStack;
AnyValue None;
- APFloat::roundingMode CurrentRoundingMode;
+ RoundingMode CurrentRoundingMode;
+ fp::ExceptionBehavior CurrentExceptionBehavior;
const AnyValue &getValue(Value *V) {
if (auto *C = dyn_cast<Constant>(V))
@@ -121,8 +122,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue::poison();
// Non-deterministically flip the sign of the input.
if (FMF.noSignedZeros() && APVal.isZero()) {
- return AnyValue(
- APFloat::getZero(APVal.getSemantics(), Ctx.getRandomBool()));
+ return AnyValue(APFloat::getZero(
+ APVal.getSemantics(), APVal.isNegative() ^ Ctx.getRandomBool()));
}
return Val;
}
@@ -419,7 +420,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
: ExecutorBase(C, H), DL(Ctx.getDataLayout()),
- CurrentRoundingMode(APFloat::rmNearestTiesToEven) {
+ CurrentRoundingMode(APFloat::rmNearestTiesToEven),
+ CurrentExceptionBehavior(fp::ebIgnore) {
CallStack.emplace_back(F, /*CallSite=*/nullptr, /*LastFrame=*/nullptr, Args,
RetVal, Ctx.getTLIImpl());
}
@@ -769,6 +771,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFAdd(BinaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.add(RHS, APFloat::rmNearestTiesToEven);
@@ -777,6 +785,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFSub(BinaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.subtract(RHS, APFloat::rmNearestTiesToEven);
@@ -785,6 +799,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFMul(BinaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.multiply(RHS, APFloat::rmNearestTiesToEven);
@@ -793,6 +813,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFDiv(BinaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.divide(RHS, APFloat::rmNearestTiesToEven);
@@ -801,6 +827,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFRem(BinaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.mod(RHS);
@@ -809,6 +841,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFNeg(UnaryOperator &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
visitFPUnOp(I, [](const APFloat &Operand) -> APFloat { return -Operand; });
}
@@ -844,6 +882,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
void visitFPTruncInst(FPTruncInst &FPTrunc) { visitFPConvInst(FPTrunc); }
void visitFPConvInst(Instruction &I) {
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ reportImmediateUB("Non-constrained floating-point operation assumes "
+ "default floating-point environment");
+ }
+
const fltSemantics &DstSem =
I.getType()->getScalarType()->getFltSemantics();
@@ -852,7 +897,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue::poison();
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
- APFloat FOperand = Operand.asFloat();
+ DenormalMode DenormMode = getCurrentDenormalMode(I);
+
+ APFloat FOperand = handleDenormal(Operand.asFloat(), DenormMode.Input);
+
if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
ValidateRes.isPoison())
return ValidateRes;
@@ -864,6 +912,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
ValidateRes.isPoison())
return ValidateRes;
+ FOperand = handleDenormal(std::move(FOperand), DenormMode.Output, true);
+
return AnyValue(FOperand);
});
}
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 4200c9246e8d8..825d8e17149e6 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -105,7 +105,8 @@ cl::opt<ubi::NaNPropagationBehavior> NaNPropagationBehavior(
clEnumVal(ubi::NaNPropagationBehavior::TargetSpecificNaN,
"The quiet bit is set and the payload is picked from a"
"target-specific set of “extra” possible NaN payloads."
- "Implemented by filling payload with random values")));
+ "Implemented by filling payload with random values")),
+ cl::init(ubi::NaNPropagationBehavior::NonDeterministic));
class VerboseEventHandler : public ubi::EventHandler {
public:
>From 322880654b6d5dff023f2f3bdbaa18da876d0119 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 15:01:54 +0800
Subject: [PATCH 13/28] [llubi] Refactor out NaN propagation logic & add NaN
propagation to fpext/fptrunc
---
llvm/tools/llubi/lib/Interpreter.cpp | 176 +++++++++++++++++++--------
1 file changed, 122 insertions(+), 54 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 5d62d8cd69ec5..787fa1fc88849 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -128,6 +128,122 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Val;
}
+ enum class NaNPayloadCastMode {
+ None,
+ FPExt,
+ FPTrunc,
+ };
+
+ static unsigned getNaNPayloadBitWidth(const fltSemantics &Sem) {
+ unsigned Precision = APFloat::semanticsPrecision(Sem);
+ return Precision > 2 ? Precision - 2 : 0;
+ }
+
+ NaNPropagationBehavior resolveNaNPropagationBehavior() {
+ NaNPropagationBehavior Choice = Ctx.getNaNPropagationBehavior();
+ if (Choice == NaNPropagationBehavior::NonDeterministic) {
+ uint64_t NonDetChoice = Ctx.getRandomUInt64() % 4 + 1;
+ Choice = static_cast<NaNPropagationBehavior>(NonDetChoice);
+ }
+ return Choice;
+ }
+
+ const APFloat &pickNaNSource(ArrayRef<const APFloat *> Inputs,
+ const APFloat &Fallback) {
+ for (const APFloat *Input : Inputs) {
+ if (Input && Input->isNaN())
+ return *Input;
+ }
+ return Fallback;
+ }
+
+ APInt getNaNPayload(const APFloat &NaNVal) {
+ unsigned PayloadBits = getNaNPayloadBitWidth(NaNVal.getSemantics());
+ if (!PayloadBits)
+ return APInt(1, 0);
+ return NaNVal.bitcastToAPInt().extractBits(PayloadBits, 0);
+ }
+
+ APInt castNaNPayload(const APInt &SrcPayload, unsigned SrcPayloadBits,
+ unsigned DstPayloadBits, NaNPayloadCastMode CastMode) {
+ if (!DstPayloadBits)
+ return APInt(1, 0);
+
+ APInt CanonicalSrc = SrcPayload.zextOrTrunc(SrcPayloadBits);
+ if (CastMode == NaNPayloadCastMode::FPExt &&
+ SrcPayloadBits < DstPayloadBits)
+ return CanonicalSrc.zext(DstPayloadBits)
+ .shl(DstPayloadBits - SrcPayloadBits);
+ if (CastMode == NaNPayloadCastMode::FPTrunc &&
+ SrcPayloadBits > DstPayloadBits)
+ return CanonicalSrc.lshr(SrcPayloadBits - DstPayloadBits)
+ .trunc(DstPayloadBits);
+ return CanonicalSrc.zextOrTrunc(DstPayloadBits);
+ }
+
+ APFloat buildNaN(const fltSemantics &Sem, bool IsNegative, bool IsSignaling,
+ const APInt &Payload) {
+ unsigned SignificandBits = APFloat::semanticsPrecision(Sem) - 1;
+ APInt Fill = APInt::getZero(SignificandBits);
+ unsigned PayloadBits = getNaNPayloadBitWidth(Sem);
+ if (PayloadBits)
+ Fill.insertBits(Payload.zextOrTrunc(PayloadBits), 0);
+
+ if (IsSignaling)
+ return APFloat::getSNaN(Sem, IsNegative, &Fill);
+ return APFloat::getQNaN(Sem, IsNegative, &Fill);
+ }
+
+ APFloat propagateInputNaN(const APFloat &InputNaN, const fltSemantics &DstSem,
+ bool QuietingMode, bool FlipSign,
+ NaNPayloadCastMode CastMode) {
+ unsigned SrcPayloadBits = getNaNPayloadBitWidth(InputNaN.getSemantics());
+ unsigned DstPayloadBits = getNaNPayloadBitWidth(DstSem);
+ APInt MappedPayload = castNaNPayload(
+ getNaNPayload(InputNaN), SrcPayloadBits, DstPayloadBits, CastMode);
+
+ bool IsSignaling = InputNaN.isSignaling();
+ bool OutputIsSignaling = !QuietingMode && IsSignaling;
+ if (OutputIsSignaling &&
+ (DstPayloadBits == 0 ||
+ (CastMode == NaNPayloadCastMode::FPTrunc && MappedPayload.isZero())))
+ OutputIsSignaling = false;
+
+ bool IsNegative = InputNaN.isNegative();
+ if (FlipSign)
+ IsNegative = !IsNegative;
+ return buildNaN(DstSem, IsNegative, OutputIsSignaling, MappedPayload);
+ }
+
+ APFloat
+ applyNaNPropagation(const APFloat &Result, ArrayRef<const APFloat *> Inputs,
+ NaNPayloadCastMode CastMode = NaNPayloadCastMode::None) {
+ if (!Result.isNaN())
+ return Result;
+
+ NaNPropagationBehavior Choice = resolveNaNPropagationBehavior();
+ const bool SignChoice = Ctx.getRandomBool();
+ switch (Choice) {
+ case NaNPropagationBehavior::PreferredNaN:
+ return APFloat::getQNaN(Result.getSemantics(), SignChoice);
+ case NaNPropagationBehavior::QuietingNaN:
+ return propagateInputNaN(pickNaNSource(Inputs, Result),
+ Result.getSemantics(), /*QuietingMode=*/true,
+ SignChoice, CastMode);
+ case NaNPropagationBehavior::UnchangedNaN:
+ return propagateInputNaN(pickNaNSource(Inputs, Result),
+ Result.getSemantics(), /*QuietingMode=*/false,
+ SignChoice, CastMode);
+ case NaNPropagationBehavior::TargetSpecificNaN: {
+ APInt Payload(64, Ctx.getRandomUInt64());
+ return APFloat::getQNaN(Result.getSemantics(), SignChoice, &Payload);
+ }
+ case NaNPropagationBehavior::NonDeterministic:
+ llvm_unreachable("NonDeterministic should be resolved earlier.");
+ }
+ llvm_unreachable("Unhandled NaN propagation behavior.");
+ }
+
AnyValue computeUnOp(Type *Ty, const AnyValue &Operand,
function_ref<AnyValue(const AnyValue &)> ScalarFn) {
if (Ty->isVectorTy()) {
@@ -234,59 +350,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return FResult;
APFloat Result = FResult.asFloat();
- // NaN payload propagation
- if (Result.isNaN()) {
- NaNPropagationBehavior Choice = Ctx.getNaNPropagationBehavior();
- if (Choice == NaNPropagationBehavior::NonDeterministic) {
- uint64_t NonDetChoice = Ctx.getRandomUInt64() % 4 + 1;
- Choice = static_cast<NaNPropagationBehavior>(NonDetChoice);
- }
- const bool Sign = Ctx.getRandomBool();
- if (Choice == NaNPropagationBehavior::PreferredNaN) {
- // Preferred NaN: the quiet bit is set and the payload is all-zero
- return APFloat::getQNaN(Result.getSemantics(), Sign);
- }
- if (Choice == NaNPropagationBehavior::QuietingNaN) {
- // Quieting NaN propagation: the quiet bit is set and the payload is
- // copied from any input operand that is a NaN. We implement this by
- // directly set the quiet bit of the input NaN, and
- // non-deterministically flip its sign bit
- auto QuietNaN = [&](APFloat &Input) {
- APFloat Quieted = Input.makeQuiet();
- if (Sign)
- Quieted.changeSign();
- return Quieted;
- };
- if (FLHS.isNaN())
- return QuietNaN(FLHS);
- if (FRHS.isNaN())
- return QuietNaN(FRHS);
- return QuietNaN(Result);
- }
- if (Choice == NaNPropagationBehavior::UnchangedNaN) {
- // Unchanged NaN propagation: the quiet bit and payload are copied
- // from any input operand that is a NaN
- auto FlipSign = [&](APFloat &Input) {
- if (Sign)
- Input.changeSign();
- return Input;
- };
- if (FLHS.isNaN())
- return FlipSign(FLHS);
- if (FRHS.isNaN())
- return FlipSign(FRHS);
- return FlipSign(Result);
- }
- if (Choice == NaNPropagationBehavior::TargetSpecificNaN) {
- // Target-specific NaN: the quiet bit is set and the payload is picked
- // from a target-specific set of "extra" possible NaN payloads. We
- // approximate this by filling the payload with random values.
- APInt Payload(64, Ctx.getRandomUInt64());
- return APFloat::getQNaN(Result.getSemantics(), Sign, &Payload);
- }
- }
-
- return Result;
+ return applyNaNPropagation(Result, {&FLHS, &FRHS});
});
}
@@ -900,6 +964,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
DenormalMode DenormMode = getCurrentDenormalMode(I);
APFloat FOperand = handleDenormal(Operand.asFloat(), DenormMode.Input);
+ APFloat SourceNaN = FOperand;
if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
ValidateRes.isPoison())
@@ -914,7 +979,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FOperand = handleDenormal(std::move(FOperand), DenormMode.Output, true);
- return AnyValue(FOperand);
+ NaNPayloadCastMode CastMode = isa<FPExtInst>(I)
+ ? NaNPayloadCastMode::FPExt
+ : NaNPayloadCastMode::FPTrunc;
+ return AnyValue(applyNaNPropagation(FOperand, {&SourceNaN}, CastMode));
});
}
>From 25f4828c32de9bbb95c04e9721db8302b419d3b4 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 15:39:17 +0800
Subject: [PATCH 14/28] [llubi] Add basic arithmetic tests for
half/bfloat/double/x86_fp80/fp128/ppc_fp128
---
llvm/test/tools/llubi/fp_arith.ll | 33 ---------------------
llvm/test/tools/llubi/fp_arith_bfloat.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_double.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_float.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_fp128.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_fp80.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_half.ll | 20 +++++++++++++
llvm/test/tools/llubi/fp_arith_ppc_fp128.ll | 20 +++++++++++++
8 files changed, 140 insertions(+), 33 deletions(-)
delete mode 100644 llvm/test/tools/llubi/fp_arith.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_bfloat.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_double.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_float.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_fp128.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_fp80.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_half.ll
create mode 100644 llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
diff --git a/llvm/test/tools/llubi/fp_arith.ll b/llvm/test/tools/llubi/fp_arith.ll
deleted file mode 100644
index 69b091844ce8d..0000000000000
--- a/llvm/test/tools/llubi/fp_arith.ll
+++ /dev/null
@@ -1,33 +0,0 @@
-; 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() {
- %add = fadd float 1.5, 2.5
- %sub = fsub double 5.0, 1.0
- %mul = fmul float 2.0, 3.0
- %div = fdiv double 1.0, 2.0
- %neg = fneg float -1.5
-
- %vadd = fadd <2 x float> <float 1.0, float 2.0>, <float 3.0, float 4.0>
-
- %p1 = fadd float 1.0, poison
- %p2 = fneg float poison
-
- %inf = fdiv float 1.0, 0.0
- %nan = fdiv float 0.0, 0.0
-
- ret void
-}
-; CHECK: Entering function: main
-; CHECK-NEXT: %add = fadd float 1.500000e+00, 2.500000e+00 => float 4.000000e+00
-; CHECK-NEXT: %sub = fsub double 5.000000e+00, 1.000000e+00 => double 4.000000e+00
-; CHECK-NEXT: %mul = fmul float 2.000000e+00, 3.000000e+00 => float 6.000000e+00
-; CHECK-NEXT: %div = fdiv double 1.000000e+00, 2.000000e+00 => double 5.000000e-01
-; CHECK-NEXT: %neg = fneg float -1.500000e+00 => float 1.500000e+00
-; CHECK-NEXT: %vadd = fadd <2 x float> <float 1.000000e+00, float 2.000000e+00>, <float 3.000000e+00, float 4.000000e+00> => { float 4.000000e+00, float 6.000000e+00 }
-; CHECK-NEXT: %p1 = fadd float 1.000000e+00, poison => poison
-; CHECK-NEXT: %p2 = fneg float poison => poison
-; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
-; CHECK-NEXT: ret void
-; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_bfloat.ll b/llvm/test/tools/llubi/fp_arith_bfloat.ll
new file mode 100644
index 0000000000000..c546a65ccf21e
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_bfloat.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd bfloat 1.5, 2.5
+ %sub = fsub bfloat 5.0, 1.0
+ %mul = fmul bfloat 2.0, 3.0
+ %div = fdiv bfloat 1.0, 2.0
+ %neg = fneg bfloat -1.5
+
+ %vadd = fadd <2 x bfloat> <bfloat 1.0, bfloat 2.0>, <bfloat 3.0, bfloat 4.0>
+
+ %p1 = fadd bfloat 1.0, poison
+ %p2 = fneg bfloat poison
+
+ %inf = fdiv bfloat 1.0, 0.0
+ %nan = fdiv bfloat 0.0, 0.0
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_double.ll b/llvm/test/tools/llubi/fp_arith_double.ll
new file mode 100644
index 0000000000000..7201058392b6e
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_double.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd double 1.5, 2.5
+ %sub = fsub double 5.0, 1.0
+ %mul = fmul double 2.0, 3.0
+ %div = fdiv double 1.0, 2.0
+ %neg = fneg double -1.5
+
+ %vadd = fadd <2 x double> <double 1.0, double 2.0>, <double 3.0, double 4.0>
+
+ %p1 = fadd double 1.0, poison
+ %p2 = fneg double poison
+
+ %inf = fdiv double 1.0, 0.0
+ %nan = fdiv double 0.0, 0.0
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_float.ll b/llvm/test/tools/llubi/fp_arith_float.ll
new file mode 100644
index 0000000000000..3dff7003c1f70
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_float.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd float 1.5, 2.5
+ %sub = fsub float 5.0, 1.0
+ %mul = fmul float 2.0, 3.0
+ %div = fdiv float 1.0, 2.0
+ %neg = fneg float -1.5
+
+ %vadd = fadd <2 x float> <float 1.0, float 2.0>, <float 3.0, float 4.0>
+
+ %p1 = fadd float 1.0, poison
+ %p2 = fneg float poison
+
+ %inf = fdiv float 1.0, 0.0
+ %nan = fdiv float 0.0, 0.0
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_fp128.ll b/llvm/test/tools/llubi/fp_arith_fp128.ll
new file mode 100644
index 0000000000000..1fb3b5b060ed6
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_fp128.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd fp128 0xL00000000000000003FFF800000000000, 0xL00000000000000004000400000000000
+ %sub = fsub fp128 0xL00000000000000004001400000000000, 0xL00000000000000003FFF000000000000
+ %mul = fmul fp128 0xL00000000000000004000000000000000, 0xL00000000000000004000800000000000
+ %div = fdiv fp128 0xL00000000000000003FFF000000000000, 0xL00000000000000004000000000000000
+ %neg = fneg fp128 0xL0000000000000000BFFF800000000000
+
+ %vadd = fadd <2 x fp128> <fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000004000000000000000>, <fp128 0xL00000000000000004000800000000000, fp128 0xL00000000000000004001000000000000>
+
+ %p1 = fadd fp128 0xL00000000000000003FFF000000000000, poison
+ %p2 = fneg fp128 poison
+
+ %inf = fdiv fp128 0xL00000000000000003FFF000000000000, 0xL00000000000000000000000000000000
+ %nan = fdiv fp128 0xL00000000000000000000000000000000, 0xL00000000000000000000000000000000
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_fp80.ll b/llvm/test/tools/llubi/fp_arith_fp80.ll
new file mode 100644
index 0000000000000..78c58fdae0d54
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_fp80.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd x86_fp80 0xK3FFFC000000000000000, 0xK4000A000000000000000
+ %sub = fsub x86_fp80 0xK4001A000000000000000, 0xK3FFF8000000000000000
+ %mul = fmul x86_fp80 0xK40008000000000000000, 0xK4000C000000000000000
+ %div = fdiv x86_fp80 0xK3FFF8000000000000000, 0xK40008000000000000000
+ %neg = fneg x86_fp80 0xKBFFFC000000000000000
+
+ %vadd = fadd <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, <x86_fp80 0xK4000C000000000000000, x86_fp80 0xK40018000000000000000>
+
+ %p1 = fadd x86_fp80 0xK3FFF8000000000000000, poison
+ %p2 = fneg x86_fp80 poison
+
+ %inf = fdiv x86_fp80 0xK3FFF8000000000000000, 0xK00000000000000000000
+ %nan = fdiv x86_fp80 0xK00000000000000000000, 0xK00000000000000000000
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_half.ll b/llvm/test/tools/llubi/fp_arith_half.ll
new file mode 100644
index 0000000000000..49e1dbc57498c
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_half.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd half 1.5, 2.5
+ %sub = fsub half 5.0, 1.0
+ %mul = fmul half 2.0, 3.0
+ %div = fdiv half 1.0, 2.0
+ %neg = fneg half -1.5
+
+ %vadd = fadd <2 x half> <half 1.0, half 2.0>, <half 3.0, half 4.0>
+
+ %p1 = fadd half 1.0, poison
+ %p2 = fneg half poison
+
+ %inf = fdiv half 1.0, 0.0
+ %nan = fdiv half 0.0, 0.0
+
+ ret void
+}
diff --git a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
new file mode 100644
index 0000000000000..d2a8a5c06dd4a
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
@@ -0,0 +1,20 @@
+; 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() {
+ %add = fadd ppc_fp128 0xM3FF80000000000000000000000000000, 0xM40040000000000000000000000000000
+ %sub = fsub ppc_fp128 0xM40140000000000000000000000000000, 0xM3FF00000000000000000000000000000
+ %mul = fmul ppc_fp128 0xM40000000000000000000000000000000, 0xM40080000000000000000000000000000
+ %div = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM40000000000000000000000000000000
+ %neg = fneg ppc_fp128 0xMBFF80000000000000000000000000000
+
+ %vadd = fadd <2 x ppc_fp128> <ppc_fp128 0xM3FF00000000000000000000000000000, ppc_fp128 0xM40000000000000000000000000000000>, <ppc_fp128 0xM40080000000000000000000000000000, ppc_fp128 0xM40100000000000000000000000000000>
+
+ %p1 = fadd ppc_fp128 0xM3FF00000000000000000000000000000, poison
+ %p2 = fneg ppc_fp128 poison
+
+ %inf = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM00000000000000000000000000000000
+ %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000
+
+ ret void
+}
>From 5777b35ea2b054dcce956fe1e8e96e17914c3cdc Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 15:40:16 +0800
Subject: [PATCH 15/28] [llubi] Update tests
---
llvm/test/tools/llubi/fp_arith_bfloat.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_double.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_float.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_fp128.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_fp80.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_half.ll | 13 +++++++++++++
llvm/test/tools/llubi/fp_arith_ppc_fp128.ll | 13 +++++++++++++
7 files changed, 91 insertions(+)
diff --git a/llvm/test/tools/llubi/fp_arith_bfloat.ll b/llvm/test/tools/llubi/fp_arith_bfloat.ll
index c546a65ccf21e..e386261261f94 100644
--- a/llvm/test/tools/llubi/fp_arith_bfloat.ll
+++ b/llvm/test/tools/llubi/fp_arith_bfloat.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd bfloat 0xR3FC0, 0xR4020 => bfloat 4.000000e+00
+; CHECK-NEXT: %sub = fsub bfloat 0xR40A0, 0xR3F80 => bfloat 4.000000e+00
+; CHECK-NEXT: %mul = fmul bfloat 0xR4000, 0xR4040 => bfloat 6.000000e+00
+; CHECK-NEXT: %div = fdiv bfloat 0xR3F80, 0xR4000 => bfloat 5.000000e-01
+; CHECK-NEXT: %neg = fneg bfloat 0xRBFC0 => bfloat 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x bfloat> <bfloat 0xR3F80, bfloat 0xR4000>, <bfloat 0xR4040, bfloat 0xR4080> => { bfloat 4.000000e+00, bfloat 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd bfloat 0xR3F80, poison => poison
+; CHECK-NEXT: %p2 = fneg bfloat poison => poison
+; CHECK-NEXT: %inf = fdiv bfloat 0xR3F80, 0xR0000 => bfloat +Inf
+; CHECK-NEXT: %nan = fdiv bfloat 0xR0000, 0xR0000 => bfloat 0xFFC0
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_double.ll b/llvm/test/tools/llubi/fp_arith_double.ll
index 7201058392b6e..755af05cbbf9d 100644
--- a/llvm/test/tools/llubi/fp_arith_double.ll
+++ b/llvm/test/tools/llubi/fp_arith_double.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd double 1.500000e+00, 2.500000e+00 => double 4.000000e+00
+; CHECK-NEXT: %sub = fsub double 5.000000e+00, 1.000000e+00 => double 4.000000e+00
+; CHECK-NEXT: %mul = fmul double 2.000000e+00, 3.000000e+00 => double 6.000000e+00
+; CHECK-NEXT: %div = fdiv double 1.000000e+00, 2.000000e+00 => double 5.000000e-01
+; CHECK-NEXT: %neg = fneg double -1.500000e+00 => double 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x double> <double 1.000000e+00, double 2.000000e+00>, <double 3.000000e+00, double 4.000000e+00> => { double 4.000000e+00, double 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd double 1.000000e+00, poison => poison
+; CHECK-NEXT: %p2 = fneg double poison => poison
+; CHECK-NEXT: %inf = fdiv double 1.000000e+00, 0.000000e+00 => double +Inf
+; CHECK-NEXT: %nan = fdiv double 0.000000e+00, 0.000000e+00 => double 0xFFF8000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_float.ll b/llvm/test/tools/llubi/fp_arith_float.ll
index 3dff7003c1f70..28ee59f3d7fe2 100644
--- a/llvm/test/tools/llubi/fp_arith_float.ll
+++ b/llvm/test/tools/llubi/fp_arith_float.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd float 1.500000e+00, 2.500000e+00 => float 4.000000e+00
+; CHECK-NEXT: %sub = fsub float 5.000000e+00, 1.000000e+00 => float 4.000000e+00
+; CHECK-NEXT: %mul = fmul float 2.000000e+00, 3.000000e+00 => float 6.000000e+00
+; CHECK-NEXT: %div = fdiv float 1.000000e+00, 2.000000e+00 => float 5.000000e-01
+; CHECK-NEXT: %neg = fneg float -1.500000e+00 => float 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x float> <float 1.000000e+00, float 2.000000e+00>, <float 3.000000e+00, float 4.000000e+00> => { float 4.000000e+00, float 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd float 1.000000e+00, poison => poison
+; CHECK-NEXT: %p2 = fneg float poison => poison
+; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_fp128.ll b/llvm/test/tools/llubi/fp_arith_fp128.ll
index 1fb3b5b060ed6..83a2e7295d879 100644
--- a/llvm/test/tools/llubi/fp_arith_fp128.ll
+++ b/llvm/test/tools/llubi/fp_arith_fp128.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd fp128 0xL00000000000000003FFF800000000000, 0xL00000000000000004000400000000000 => fp128 4.000000e+00
+; CHECK-NEXT: %sub = fsub fp128 0xL00000000000000004001400000000000, 0xL00000000000000003FFF000000000000 => fp128 4.000000e+00
+; CHECK-NEXT: %mul = fmul fp128 0xL00000000000000004000000000000000, 0xL00000000000000004000800000000000 => fp128 6.000000e+00
+; CHECK-NEXT: %div = fdiv fp128 0xL00000000000000003FFF000000000000, 0xL00000000000000004000000000000000 => fp128 5.000000e-01
+; CHECK-NEXT: %neg = fneg fp128 0xL0000000000000000BFFF800000000000 => fp128 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x fp128> <fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000004000000000000000>, <fp128 0xL00000000000000004000800000000000, fp128 0xL00000000000000004001000000000000> => { fp128 4.000000e+00, fp128 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd fp128 0xL00000000000000003FFF000000000000, poison => poison
+; CHECK-NEXT: %p2 = fneg fp128 poison => poison
+; CHECK-NEXT: %inf = fdiv fp128 0xL00000000000000003FFF000000000000, 0xL00000000000000000000000000000000 => fp128 +Inf
+; CHECK-NEXT: %nan = fdiv fp128 0xL00000000000000000000000000000000, 0xL00000000000000000000000000000000 => fp128 0xFFFF8000000000000000000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_fp80.ll b/llvm/test/tools/llubi/fp_arith_fp80.ll
index 78c58fdae0d54..759e162773cd4 100644
--- a/llvm/test/tools/llubi/fp_arith_fp80.ll
+++ b/llvm/test/tools/llubi/fp_arith_fp80.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd x86_fp80 0xK3FFFC000000000000000, 0xK4000A000000000000000 => x86_fp80 4.000000e+00
+; CHECK-NEXT: %sub = fsub x86_fp80 0xK4001A000000000000000, 0xK3FFF8000000000000000 => x86_fp80 4.000000e+00
+; CHECK-NEXT: %mul = fmul x86_fp80 0xK40008000000000000000, 0xK4000C000000000000000 => x86_fp80 6.000000e+00
+; CHECK-NEXT: %div = fdiv x86_fp80 0xK3FFF8000000000000000, 0xK40008000000000000000 => x86_fp80 5.000000e-01
+; CHECK-NEXT: %neg = fneg x86_fp80 0xKBFFFC000000000000000 => x86_fp80 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x x86_fp80> <x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK40008000000000000000>, <x86_fp80 0xK4000C000000000000000, x86_fp80 0xK40018000000000000000> => { x86_fp80 4.000000e+00, x86_fp80 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd x86_fp80 0xK3FFF8000000000000000, poison => poison
+; CHECK-NEXT: %p2 = fneg x86_fp80 poison => poison
+; CHECK-NEXT: %inf = fdiv x86_fp80 0xK3FFF8000000000000000, 0xK00000000000000000000 => x86_fp80 +Inf
+; CHECK-NEXT: %nan = fdiv x86_fp80 0xK00000000000000000000, 0xK00000000000000000000 => x86_fp80 0xFFFFC000000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_half.ll b/llvm/test/tools/llubi/fp_arith_half.ll
index 49e1dbc57498c..093ec2b1e92da 100644
--- a/llvm/test/tools/llubi/fp_arith_half.ll
+++ b/llvm/test/tools/llubi/fp_arith_half.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd half 0xH3E00, 0xH4100 => half 4.000000e+00
+; CHECK-NEXT: %sub = fsub half 0xH4500, 0xH3C00 => half 4.000000e+00
+; CHECK-NEXT: %mul = fmul half 0xH4000, 0xH4200 => half 6.000000e+00
+; CHECK-NEXT: %div = fdiv half 0xH3C00, 0xH4000 => half 5.000000e-01
+; CHECK-NEXT: %neg = fneg half 0xHBE00 => half 1.500000e+00
+; CHECK-NEXT: %vadd = fadd <2 x half> <half 0xH3C00, half 0xH4000>, <half 0xH4200, half 0xH4400> => { half 4.000000e+00, half 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd half 0xH3C00, poison => poison
+; CHECK-NEXT: %p2 = fneg half poison => poison
+; CHECK-NEXT: %inf = fdiv half 0xH3C00, 0xH0000 => half +Inf
+; CHECK-NEXT: %nan = fdiv half 0xH0000, 0xH0000 => half 0xFE00
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
index d2a8a5c06dd4a..14dabc9bc1753 100644
--- a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
+++ b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
@@ -18,3 +18,16 @@ define void @main() {
ret void
}
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = fadd ppc_fp128 0xM3FF80000000000000000000000000000, 0xM40040000000000000000000000000000 => ppc_fp128 4.000000e+00
+; CHECK-NEXT: %sub = fsub ppc_fp128 0xM40140000000000000000000000000000, 0xM3FF00000000000000000000000000000 => ppc_fp128 0x80000000000000004010000000000000
+; CHECK-NEXT: %mul = fmul ppc_fp128 0xM40000000000000000000000000000000, 0xM40080000000000000000000000000000 => ppc_fp128 6.000000e+00
+; CHECK-NEXT: %div = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM40000000000000000000000000000000 => ppc_fp128 5.000000e-01
+; CHECK-NEXT: %neg = fneg ppc_fp128 0xMBFF80000000000000000000000000000 => ppc_fp128 0x80000000000000003FF8000000000000
+; CHECK-NEXT: %vadd = fadd <2 x ppc_fp128> <ppc_fp128 0xM3FF00000000000000000000000000000, ppc_fp128 0xM40000000000000000000000000000000>, <ppc_fp128 0xM40080000000000000000000000000000, ppc_fp128 0xM40100000000000000000000000000000> => { ppc_fp128 4.000000e+00, ppc_fp128 6.000000e+00 }
+; CHECK-NEXT: %p1 = fadd ppc_fp128 0xM3FF00000000000000000000000000000, poison => poison
+; CHECK-NEXT: %p2 = fneg ppc_fp128 poison => poison
+; CHECK-NEXT: %inf = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 +Inf
+; CHECK-NEXT: %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 0x0000000000000000FFF8000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
>From 7d20161ca50dd952b244c3f5c72dfa8bd9bbf10d Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 15:55:56 +0800
Subject: [PATCH 16/28] [llubi] Add nneg handling to uitofp & related test
update
---
llvm/test/tools/llubi/fp_cast.ll | 10 ++++++++++
llvm/tools/llubi/lib/Interpreter.cpp | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/llvm/test/tools/llubi/fp_cast.ll b/llvm/test/tools/llubi/fp_cast.ll
index bab205f1c5891..00e1578d72395 100644
--- a/llvm/test/tools/llubi/fp_cast.ll
+++ b/llvm/test/tools/llubi/fp_cast.ll
@@ -8,12 +8,18 @@ define void @main() {
%si2fp = sitofp i32 -2 to float
%ui2fp = uitofp i32 255 to float
+ %ui2fp_nneg_pos = uitofp nneg i32 255 to float
+ %ui2fp_nneg_neg = uitofp nneg i32 -255 to float
+
%fp2si = fptosi double -4.75 to i32
%fp2ui = fptoui double 4.75 to i32
%oob_ui = fptoui float -1.0 to i32
%nan_si = fptosi float 0x7FF8000000000000 to i32
+ %oob_si_half = sitofp i32 -100000 to half
+ %oob_ui_half = uitofp i32 100000 to half
+
ret void
}
; CHECK: Entering function: main
@@ -21,9 +27,13 @@ define void @main() {
; CHECK-NEXT: %trunc = fptrunc double 3.500000e+00 to float => float 3.500000e+00
; CHECK-NEXT: %si2fp = sitofp i32 -2 to float => float -2.000000e+00
; CHECK-NEXT: %ui2fp = uitofp i32 255 to float => float 2.550000e+02
+; CHECK-NEXT: %ui2fp_nneg_pos = uitofp nneg i32 255 to float => float 2.550000e+02
+; CHECK-NEXT: %ui2fp_nneg_neg = uitofp nneg i32 -255 to float => poison
; CHECK-NEXT: %fp2si = fptosi double -4.750000e+00 to i32 => i32 -4
; CHECK-NEXT: %fp2ui = fptoui double 4.750000e+00 to i32 => i32 4
; CHECK-NEXT: %oob_ui = fptoui float -1.000000e+00 to i32 => poison
; CHECK-NEXT: %nan_si = fptosi float 0x7FF8000000000000 to i32 => poison
+; CHECK-NEXT: %oob_si_half = sitofp i32 -100000 to half => half -Inf
+; CHECK-NEXT: %oob_ui_half = uitofp i32 100000 to half => half +Inf
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 787fa1fc88849..a0230f48c3ef6 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -1029,6 +1029,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (Operand.isPoison())
return AnyValue::poison();
+ APInt IOperand = Operand.asInteger();
+
+ if (isa<UIToFPInst>(I) && I.hasNonNeg() && IOperand.isNegative())
+ return AnyValue::poison();
+
APFloat Res(DstSem);
Res.convertFromAPInt(Operand.asInteger(), /*IsSigned=*/IsSigned,
>From 966f4f282c31f431b3a4b6c8f9e43b5317738ebe Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 16:05:40 +0800
Subject: [PATCH 17/28] [llubi] Test update for the nsz flag
---
llvm/test/tools/llubi/fp_fastmath.ll | 5 +++++
llvm/tools/llubi/lib/Interpreter.cpp | 4 +---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/llvm/test/tools/llubi/fp_fastmath.ll b/llvm/test/tools/llubi/fp_fastmath.ll
index 475f40d2b0457..ba1cf3fca9c42 100644
--- a/llvm/test/tools/llubi/fp_fastmath.ll
+++ b/llvm/test/tools/llubi/fp_fastmath.ll
@@ -8,11 +8,16 @@ define void @main() {
%safe = fadd nnan ninf float 1.0, 2.0
+ %nsz_1 = fneg nsz float -0.0
+ %nsz_2 = fneg nsz float -0.0
+
ret void
}
; CHECK: Entering function: main
; CHECK-NEXT: %bad_nan = fdiv nnan float 0.000000e+00, 0.000000e+00 => poison
; CHECK-NEXT: %bad_inf = fdiv ninf float 1.000000e+00, 0.000000e+00 => poison
; CHECK-NEXT: %safe = fadd nnan ninf float 1.000000e+00, 2.000000e+00 => float 3.000000e+00
+; CHECK-NEXT: %nsz_1 = fneg nsz float -0.000000e+00 => float 0.000000e+00
+; CHECK-NEXT: %nsz_2 = fneg nsz float -0.000000e+00 => float -0.000000e+00
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index a0230f48c3ef6..d872d73598fdc 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -120,11 +120,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue::poison();
if (FMF.noInfs() && APVal.isInfinity())
return AnyValue::poison();
- // Non-deterministically flip the sign of the input.
- if (FMF.noSignedZeros() && APVal.isZero()) {
+ if (FMF.noSignedZeros() && APVal.isZero())
return AnyValue(APFloat::getZero(
APVal.getSemantics(), APVal.isNegative() ^ Ctx.getRandomBool()));
- }
return Val;
}
>From 0b0b2a7e23973d4792049cc6471b6449895eccd0 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 16:52:06 +0800
Subject: [PATCH 18/28] [llubi] New tests for NaN propagation
---
.../test/tools/llubi/fp_nan_platform_specific.ll | 16 ++++++++++++++++
llvm/test/tools/llubi/fp_nan_preferred.ll | 16 ++++++++++++++++
llvm/test/tools/llubi/fp_nan_quieting.ll | 16 ++++++++++++++++
llvm/test/tools/llubi/fp_nan_unchanged.ll | 16 ++++++++++++++++
4 files changed, 64 insertions(+)
create mode 100644 llvm/test/tools/llubi/fp_nan_platform_specific.ll
create mode 100644 llvm/test/tools/llubi/fp_nan_preferred.ll
create mode 100644 llvm/test/tools/llubi/fp_nan_quieting.ll
create mode 100644 llvm/test/tools/llubi/fp_nan_unchanged.ll
diff --git a/llvm/test/tools/llubi/fp_nan_platform_specific.ll b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
new file mode 100644
index 0000000000000..74665faf1a224
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --ubi::NaNPropagationBehavior::TargetSpecificNaN --verbose < %s 2>&1 | FileCheck %s\
+
+define void @main() {
+ %qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double 0x7FFD3A7C3E40F98B
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFFD6903CEE3FCEE
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0x7FFA062AFD5FE9EE
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_preferred.ll b/llvm/test/tools/llubi/fp_nan_preferred.ll
new file mode 100644
index 0000000000000..f85784868e796
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_preferred.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --ubi::NaNPropagationBehavior::PreferredNaN --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF8000000000000
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF8000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_quieting.ll b/llvm/test/tools/llubi/fp_nan_quieting.ll
new file mode 100644
index 0000000000000..cf6db52e57a38
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_quieting.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --ubi::NaNPropagationBehavior::QuietingNaN --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF8000000000001
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF8000000000001
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_unchanged.ll b/llvm/test/tools/llubi/fp_nan_unchanged.ll
new file mode 100644
index 0000000000000..63edb6859ce65
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_unchanged.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --ubi::NaNPropagationBehavior::UnchangedNaN --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF8000000000001
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF0000000000001
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
>From 8c2a6690581c61aedab5dadc52d267d789b9e1af Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 16:58:15 +0800
Subject: [PATCH 19/28] [llubi] Fix getCurrentDenormalMode()
---
llvm/tools/llubi/lib/Interpreter.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index d872d73598fdc..e54b1271b5bef 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -475,7 +475,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
DenormalMode getCurrentDenormalMode(Instruction &I) {
return CurrentFrame->Func.getDenormalMode(
- I.getType()->getScalarType()->getFltSemantics());
+ I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
}
public:
>From 1785cf3170e7c14906e2b56e60ca360066e0d22f Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Thu, 2 Apr 2026 19:05:52 +0800
Subject: [PATCH 20/28] [llubi] Fix incorrect RUN command in test
---
llvm/test/tools/llubi/fp_nan_platform_specific.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/tools/llubi/fp_nan_platform_specific.ll b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
index 74665faf1a224..dd59663b8e76d 100644
--- a/llvm/test/tools/llubi/fp_nan_platform_specific.ll
+++ b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
-; RUN: llubi --ubi::NaNPropagationBehavior::TargetSpecificNaN --verbose < %s 2>&1 | FileCheck %s\
+; RUN: llubi --ubi::NaNPropagationBehavior::TargetSpecificNaN --verbose < %s 2>&1 | FileCheck %s
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
>From 12ba11d27dcfa7bbf04378ce7e7833ea4175ee26 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Fri, 3 Apr 2026 15:19:32 +0800
Subject: [PATCH 21/28] [llubi] Shorten the NaN propagation flags
---
.../tools/llubi/fp_nan_platform_specific.ll | 2 +-
llvm/test/tools/llubi/fp_nan_preferred.ll | 2 +-
llvm/test/tools/llubi/fp_nan_quieting.ll | 2 +-
llvm/test/tools/llubi/fp_nan_unchanged.ll | 2 +-
llvm/tools/llubi/lib/Interpreter.cpp | 48 +++++--------------
llvm/tools/llubi/llubi.cpp | 29 +++++------
6 files changed, 31 insertions(+), 54 deletions(-)
diff --git a/llvm/test/tools/llubi/fp_nan_platform_specific.ll b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
index dd59663b8e76d..43361f95a92fc 100644
--- a/llvm/test/tools/llubi/fp_nan_platform_specific.ll
+++ b/llvm/test/tools/llubi/fp_nan_platform_specific.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
-; RUN: llubi --ubi::NaNPropagationBehavior::TargetSpecificNaN --verbose < %s 2>&1 | FileCheck %s
+; RUN: llubi --nan-target-specific --verbose < %s 2>&1 | FileCheck %s
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
diff --git a/llvm/test/tools/llubi/fp_nan_preferred.ll b/llvm/test/tools/llubi/fp_nan_preferred.ll
index f85784868e796..232ad0795f14b 100644
--- a/llvm/test/tools/llubi/fp_nan_preferred.ll
+++ b/llvm/test/tools/llubi/fp_nan_preferred.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
-; RUN: llubi --ubi::NaNPropagationBehavior::PreferredNaN --verbose < %s 2>&1 | FileCheck %s
+; RUN: llubi --nan-preferred --verbose < %s 2>&1 | FileCheck %s
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
diff --git a/llvm/test/tools/llubi/fp_nan_quieting.ll b/llvm/test/tools/llubi/fp_nan_quieting.ll
index cf6db52e57a38..1864bc03d571f 100644
--- a/llvm/test/tools/llubi/fp_nan_quieting.ll
+++ b/llvm/test/tools/llubi/fp_nan_quieting.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
-; RUN: llubi --ubi::NaNPropagationBehavior::QuietingNaN --verbose < %s 2>&1 | FileCheck %s
+; RUN: llubi --nan-quieting --verbose < %s 2>&1 | FileCheck %s
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
diff --git a/llvm/test/tools/llubi/fp_nan_unchanged.ll b/llvm/test/tools/llubi/fp_nan_unchanged.ll
index 63edb6859ce65..e9f0c94e4fd46 100644
--- a/llvm/test/tools/llubi/fp_nan_unchanged.ll
+++ b/llvm/test/tools/llubi/fp_nan_unchanged.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
-; RUN: llubi --ubi::NaNPropagationBehavior::UnchangedNaN --verbose < %s 2>&1 | FileCheck %s
+; RUN: llubi --nan-unchanged --verbose < %s 2>&1 | FileCheck %s
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index e54b1271b5bef..0ccd827e9ff22 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -325,6 +325,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
DenormalMode DenormMode = getCurrentDenormalMode(I);
+ if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode)) {
+ Status = false;
+ 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();
@@ -478,6 +485,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
}
+ bool isDefaultFPEnv() {
+ return isDefaultFPEnvironment(CurrentExceptionBehavior,
+ CurrentRoundingMode);
+ }
+
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -833,12 +845,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFAdd(BinaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.add(RHS, APFloat::rmNearestTiesToEven);
@@ -847,12 +853,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFSub(BinaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.subtract(RHS, APFloat::rmNearestTiesToEven);
@@ -861,12 +861,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFMul(BinaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.multiply(RHS, APFloat::rmNearestTiesToEven);
@@ -875,12 +869,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFDiv(BinaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.divide(RHS, APFloat::rmNearestTiesToEven);
@@ -889,12 +877,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFRem(BinaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPBinOp(I, [](const APFloat &LHS, const APFloat &RHS) -> APFloat {
APFloat Res = LHS;
Res.mod(RHS);
@@ -903,12 +885,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitFNeg(UnaryOperator &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
- reportImmediateUB("Non-constrained floating-point operation assumes "
- "default floating-point environment");
- }
visitFPUnOp(I, [](const APFloat &Operand) -> APFloat { return -Operand; });
}
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 825d8e17149e6..f82d70c0d6d8d 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -90,22 +90,23 @@ cl::opt<ubi::UndefValueBehavior> UndefBehavior(
cl::opt<ubi::NaNPropagationBehavior> NaNPropagationBehavior(
"", cl::desc("Choose NaN propagation behavior:"),
cl::values(
- clEnumVal(ubi::NaNPropagationBehavior::NonDeterministic,
- "Non-deterministically choose from 4 cases as specified by "
- "language reference."),
- clEnumVal(ubi::NaNPropagationBehavior::PreferredNaN,
- "The quiet bit is set and the payload is all-zero."),
- clEnumVal(
- ubi::NaNPropagationBehavior::QuietingNaN,
+ clEnumValN(ubi::NaNPropagationBehavior::NonDeterministic, "nan-nodet",
+ "Non-deterministically choose from 4 cases as specified by "
+ "language reference."),
+ clEnumValN(ubi::NaNPropagationBehavior::PreferredNaN, "nan-preferred",
+ "The quiet bit is set and the payload is all-zero."),
+ clEnumValN(
+ ubi::NaNPropagationBehavior::QuietingNaN, "nan-quieting",
"The quiet bit is set and the payload is copied from any input"
"operand that is a NaN."),
- clEnumVal(ubi::NaNPropagationBehavior::UnchangedNaN,
- "The quiet bit and payload are copied from any input operand"
- "that is a NaN"),
- clEnumVal(ubi::NaNPropagationBehavior::TargetSpecificNaN,
- "The quiet bit is set and the payload is picked from a"
- "target-specific set of “extra” possible NaN payloads."
- "Implemented by filling payload with random values")),
+ clEnumValN(ubi::NaNPropagationBehavior::UnchangedNaN, "nan-unchanged",
+ "The quiet bit and payload are copied from any input operand"
+ "that is a NaN"),
+ clEnumValN(ubi::NaNPropagationBehavior::TargetSpecificNaN,
+ "nan-target-specific",
+ "The quiet bit is set and the payload is picked from a"
+ "target-specific set of “extra” possible NaN payloads."
+ "Implemented by filling payload with random values")),
cl::init(ubi::NaNPropagationBehavior::NonDeterministic));
class VerboseEventHandler : public ubi::EventHandler {
>From 340a6d001565b8660c0816c527e96c8509b9b455 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Fri, 3 Apr 2026 15:40:58 +0800
Subject: [PATCH 22/28] [llubi] Refactor the NaN propagation logics
---
llvm/test/tools/llubi/fp_arith_ppc_fp128.ll | 2 +-
llvm/tools/llubi/lib/Interpreter.cpp | 87 +++------------------
2 files changed, 14 insertions(+), 75 deletions(-)
diff --git a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
index 14dabc9bc1753..7d26c6e1e972a 100644
--- a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
+++ b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd ppc_fp128 0xM3FF00000000000000000000000000000, poison => poison
; CHECK-NEXT: %p2 = fneg ppc_fp128 poison => poison
; CHECK-NEXT: %inf = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 +Inf
-; CHECK-NEXT: %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 0x0000000000000000FFF8000000000000
+; CHECK-NEXT: %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 0x8000000000000000FFF8000000000000
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 0ccd827e9ff22..7da4c70cf718a 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -126,17 +126,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Val;
}
- enum class NaNPayloadCastMode {
- None,
- FPExt,
- FPTrunc,
- };
-
- static unsigned getNaNPayloadBitWidth(const fltSemantics &Sem) {
- unsigned Precision = APFloat::semanticsPrecision(Sem);
- return Precision > 2 ? Precision - 2 : 0;
- }
-
NaNPropagationBehavior resolveNaNPropagationBehavior() {
NaNPropagationBehavior Choice = Ctx.getNaNPropagationBehavior();
if (Choice == NaNPropagationBehavior::NonDeterministic) {
@@ -155,67 +144,20 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Fallback;
}
- APInt getNaNPayload(const APFloat &NaNVal) {
- unsigned PayloadBits = getNaNPayloadBitWidth(NaNVal.getSemantics());
- if (!PayloadBits)
- return APInt(1, 0);
- return NaNVal.bitcastToAPInt().extractBits(PayloadBits, 0);
- }
-
- APInt castNaNPayload(const APInt &SrcPayload, unsigned SrcPayloadBits,
- unsigned DstPayloadBits, NaNPayloadCastMode CastMode) {
- if (!DstPayloadBits)
- return APInt(1, 0);
-
- APInt CanonicalSrc = SrcPayload.zextOrTrunc(SrcPayloadBits);
- if (CastMode == NaNPayloadCastMode::FPExt &&
- SrcPayloadBits < DstPayloadBits)
- return CanonicalSrc.zext(DstPayloadBits)
- .shl(DstPayloadBits - SrcPayloadBits);
- if (CastMode == NaNPayloadCastMode::FPTrunc &&
- SrcPayloadBits > DstPayloadBits)
- return CanonicalSrc.lshr(SrcPayloadBits - DstPayloadBits)
- .trunc(DstPayloadBits);
- return CanonicalSrc.zextOrTrunc(DstPayloadBits);
- }
-
- APFloat buildNaN(const fltSemantics &Sem, bool IsNegative, bool IsSignaling,
- const APInt &Payload) {
- unsigned SignificandBits = APFloat::semanticsPrecision(Sem) - 1;
- APInt Fill = APInt::getZero(SignificandBits);
- unsigned PayloadBits = getNaNPayloadBitWidth(Sem);
- if (PayloadBits)
- Fill.insertBits(Payload.zextOrTrunc(PayloadBits), 0);
-
- if (IsSignaling)
- return APFloat::getSNaN(Sem, IsNegative, &Fill);
- return APFloat::getQNaN(Sem, IsNegative, &Fill);
- }
-
APFloat propagateInputNaN(const APFloat &InputNaN, const fltSemantics &DstSem,
- bool QuietingMode, bool FlipSign,
- NaNPayloadCastMode CastMode) {
- unsigned SrcPayloadBits = getNaNPayloadBitWidth(InputNaN.getSemantics());
- unsigned DstPayloadBits = getNaNPayloadBitWidth(DstSem);
- APInt MappedPayload = castNaNPayload(
- getNaNPayload(InputNaN), SrcPayloadBits, DstPayloadBits, CastMode);
-
- bool IsSignaling = InputNaN.isSignaling();
- bool OutputIsSignaling = !QuietingMode && IsSignaling;
- if (OutputIsSignaling &&
- (DstPayloadBits == 0 ||
- (CastMode == NaNPayloadCastMode::FPTrunc && MappedPayload.isZero())))
- OutputIsSignaling = false;
-
- bool IsNegative = InputNaN.isNegative();
+ bool QuietingMode, bool FlipSign) {
+ APFloat Res = InputNaN;
+ bool LosesInfo;
+ Res.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo);
if (FlipSign)
- IsNegative = !IsNegative;
- return buildNaN(DstSem, IsNegative, OutputIsSignaling, MappedPayload);
+ Res.changeSign();
+ if (QuietingMode && Res.isSignaling())
+ Res = Res.makeQuiet();
+ return Res;
}
- APFloat
- applyNaNPropagation(const APFloat &Result, ArrayRef<const APFloat *> Inputs,
- NaNPayloadCastMode CastMode = NaNPayloadCastMode::None) {
+ APFloat applyNaNPropagation(const APFloat &Result,
+ ArrayRef<const APFloat *> Inputs) {
if (!Result.isNaN())
return Result;
@@ -227,11 +169,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
case NaNPropagationBehavior::QuietingNaN:
return propagateInputNaN(pickNaNSource(Inputs, Result),
Result.getSemantics(), /*QuietingMode=*/true,
- SignChoice, CastMode);
+ SignChoice);
case NaNPropagationBehavior::UnchangedNaN:
return propagateInputNaN(pickNaNSource(Inputs, Result),
Result.getSemantics(), /*QuietingMode=*/false,
- SignChoice, CastMode);
+ SignChoice);
case NaNPropagationBehavior::TargetSpecificNaN: {
APInt Payload(64, Ctx.getRandomUInt64());
return APFloat::getQNaN(Result.getSemantics(), SignChoice, &Payload);
@@ -953,10 +895,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FOperand = handleDenormal(std::move(FOperand), DenormMode.Output, true);
- NaNPayloadCastMode CastMode = isa<FPExtInst>(I)
- ? NaNPayloadCastMode::FPExt
- : NaNPayloadCastMode::FPTrunc;
- return AnyValue(applyNaNPropagation(FOperand, {&SourceNaN}, CastMode));
+ return AnyValue(applyNaNPropagation(FOperand, {&SourceNaN}));
});
}
>From 4b7e7bad9bdf4ba228983100a185c0e325940bc1 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Tue, 7 Apr 2026 16:26:19 +0800
Subject: [PATCH 23/28] [llubi] Small fixes to FP implementation
---
llvm/tools/llubi/lib/Context.cpp | 20 +++++++
llvm/tools/llubi/lib/Context.h | 13 ++++-
llvm/tools/llubi/lib/Interpreter.cpp | 78 +++++++++++++---------------
3 files changed, 68 insertions(+), 43 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 2f2788f7b50fb..f3c1bfeb06648 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -499,6 +499,26 @@ uint64_t Context::getEffectiveTypeStoreSize(Type *Ty) {
return getEffectiveTypeSize(DL.getTypeStoreSize(Ty));
}
+RoundingMode Context::getCurrentRoundingMode() const {
+ return CurrentRoundingMode;
+}
+
+fp::ExceptionBehavior Context::getCurrentExceptionBehavior() const {
+ return CurrentExceptionBehavior;
+}
+
+void Context::setCurrentRoundingMode(RoundingMode RM) {
+ CurrentRoundingMode = RM;
+}
+
+void Context::setCurrentExceptionBehavior(fp::ExceptionBehavior EB) {
+ CurrentExceptionBehavior = EB;
+}
+
+bool Context::isDefaultFPEnv() const {
+ return isDefaultFPEnvironment(CurrentExceptionBehavior, CurrentRoundingMode);
+}
+
bool Context::getRandomBool() {
// We use the lowest bit of the raw bits from RNG as the result:
if (UndefBehavior == UndefValueBehavior::NonDeterministic)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 28a4583dace1d..6c8e9fe658a7a 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -196,7 +196,12 @@ class Context {
ValidBlockTargets;
AnyValue getConstantValueImpl(Constant *C);
- // TODO: errno and fpenv
+ // Floating-point environment
+ RoundingMode CurrentRoundingMode = RoundingMode::NearestTiesToEven;
+ fp::ExceptionBehavior CurrentExceptionBehavior =
+ fp::ExceptionBehavior::ebIgnore;
+
+ // TODO: errno
public:
explicit Context(Module &M);
@@ -283,6 +288,12 @@ class Context {
bool runFunction(Function &F, ArrayRef<AnyValue> Args, AnyValue &RetVal,
EventHandler &Handler);
+ RoundingMode getCurrentRoundingMode() const;
+ fp::ExceptionBehavior getCurrentExceptionBehavior() const;
+ void setCurrentRoundingMode(RoundingMode RM);
+ void setCurrentExceptionBehavior(fp::ExceptionBehavior EB);
+ bool isDefaultFPEnv() const;
+
bool getRandomBool();
uint64_t getRandomUInt64();
};
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 7da4c70cf718a..80971fced12b7 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -68,8 +68,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
const DataLayout &DL;
std::list<Frame> CallStack;
AnyValue None;
- RoundingMode CurrentRoundingMode;
- fp::ExceptionBehavior CurrentExceptionBehavior;
const AnyValue &getValue(Value *V) {
if (auto *C = dyn_cast<Constant>(V))
@@ -103,7 +101,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Val;
}
- AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF) {
+ AnyValue handleFMFFlags(AnyValue Val, FastMathFlags FMF, bool IsInput) {
if (Val.isPoison())
return AnyValue::poison();
@@ -111,7 +109,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
std::vector<AnyValue> ResVec;
ResVec.reserve(Val.asAggregate().size());
for (const auto &A : Val.asAggregate())
- ResVec.push_back(handleFMFFlags(A, FMF));
+ ResVec.push_back(handleFMFFlags(A, FMF, IsInput));
return AnyValue(ResVec);
}
@@ -120,7 +118,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue::poison();
if (FMF.noInfs() && APVal.isInfinity())
return AnyValue::poison();
- if (FMF.noSignedZeros() && APVal.isZero())
+ if (FMF.noSignedZeros() && APVal.isZero() && IsInput)
return AnyValue(APFloat::getZero(
APVal.getSemantics(), APVal.isNegative() ^ Ctx.getRandomBool()));
return Val;
@@ -223,9 +221,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// operation is fneg. And fneg is specified as a bitwise operation which
// only flips the sign bit of the input.
- APFloat Result = ScalarFn(Operand.asFloat());
+ AnyValue ValidatedOperand = handleFMFFlags(Operand, FMF, true);
+ if (ValidatedOperand.isPoison())
+ return ValidatedOperand;
- return handleFMFFlags(Result, FMF);
+ APFloat Result = ScalarFn(ValidatedOperand.asFloat());
+
+ return handleFMFFlags(Result, FMF, false);
});
}
@@ -267,31 +269,30 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
DenormalMode DenormMode = getCurrentDenormalMode(I);
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
+ 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();
- if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison())
- return ValidateRes;
- if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison())
- return ValidateRes;
+ AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, true);
+ AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, false);
+ if (ValidatedLHS.isPoison())
+ return ValidatedLHS;
+ if (ValidatedRHS.isPoison())
+ return ValidatedRHS;
// Flush input denormals
- APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
- APFloat FRHS = handleDenormal(RHS.asFloat(), DenormMode.Input);
+ APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(), DenormMode.Input);
+ APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(), DenormMode.Input);
APFloat RawResult = ScalarFn(FLHS, FRHS);
// Flush output denormals and handle fast-math flags.
AnyValue FResult = handleFMFFlags(
- handleDenormal(RawResult, DenormMode.Output, true), FMF);
+ handleDenormal(RawResult, DenormMode.Output, true), FMF, false);
if (FResult.isPoison())
return FResult;
@@ -322,7 +323,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (isa<FPMathOperator>(PHI)) {
FastMathFlags FMF = PHI->getFastMathFlags();
if (FMF.any())
- IncomingVal = handleFMFFlags(std::move(IncomingVal), FMF);
+ IncomingVal = handleFMFFlags(std::move(IncomingVal), FMF, true);
}
IncomingValues.emplace_back(PHI, IncomingVal);
@@ -427,17 +428,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
}
- bool isDefaultFPEnv() {
- return isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode);
- }
-
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
- : ExecutorBase(C, H), DL(Ctx.getDataLayout()),
- CurrentRoundingMode(APFloat::rmNearestTiesToEven),
- CurrentExceptionBehavior(fp::ebIgnore) {
+ : ExecutorBase(C, H), DL(Ctx.getDataLayout()) {
CallStack.emplace_back(F, /*CallSite=*/nullptr, /*LastFrame=*/nullptr, Args,
RetVal, Ctx.getTLIImpl());
}
@@ -862,12 +856,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
void visitFPTruncInst(FPTruncInst &FPTrunc) { visitFPConvInst(FPTrunc); }
void visitFPConvInst(Instruction &I) {
- if (!isDefaultFPEnvironment(CurrentExceptionBehavior,
- CurrentRoundingMode)) {
- Status = false;
+ if (!Ctx.isDefaultFPEnv())
reportImmediateUB("Non-constrained floating-point operation assumes "
"default floating-point environment");
- }
const fltSemantics &DstSem =
I.getType()->getScalarType()->getFltSemantics();
@@ -879,17 +870,18 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
DenormalMode DenormMode = getCurrentDenormalMode(I);
- APFloat FOperand = handleDenormal(Operand.asFloat(), DenormMode.Input);
- APFloat SourceNaN = FOperand;
+ auto ValidatedOperand = handleFMFFlags(Operand, FMF, true);
+ if (ValidatedOperand.isPoison())
+ return ValidatedOperand;
- if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
- ValidateRes.isPoison())
- return ValidateRes;
+ APFloat FOperand =
+ handleDenormal(ValidatedOperand.asFloat(), DenormMode.Input);
+ APFloat SourceNaN = FOperand;
bool LosesInfo;
- FOperand.convert(DstSem, CurrentRoundingMode, &LosesInfo);
+ FOperand.convert(DstSem, Ctx.getCurrentRoundingMode(), &LosesInfo);
- if (auto ValidateRes = handleFMFFlags(FOperand, FMF);
+ if (auto ValidateRes = handleFMFFlags(FOperand, FMF, false);
ValidateRes.isPoison())
return ValidateRes;
@@ -950,7 +942,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
APFloat Res(DstSem);
Res.convertFromAPInt(Operand.asInteger(), /*IsSigned=*/IsSigned,
- CurrentRoundingMode);
+ Ctx.getCurrentRoundingMode());
return AnyValue(Res);
});
@@ -1030,9 +1022,11 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- if (auto ValidateRes = handleFMFFlags(LHS, FMF); ValidateRes.isPoison())
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF, true);
+ ValidateRes.isPoison())
return ValidateRes;
- if (auto ValidateRes = handleFMFFlags(RHS, FMF); ValidateRes.isPoison())
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF, true);
+ ValidateRes.isPoison())
return ValidateRes;
APFloat FLHS = handleDenormal(LHS.asFloat(), DenormMode.Input);
@@ -1083,7 +1077,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// Handle fast-math flags
if (auto *FPMO = dyn_cast<FPMathOperator>(&SI)) {
if (FastMathFlags FMF = FPMO->getFastMathFlags(); FMF.any())
- Res = handleFMFFlags(std::move(Res), FMF);
+ Res = handleFMFFlags(std::move(Res), FMF, true);
}
setResult(SI, std::move(Res));
>From b4e2fb3a07aec9cc9f81f97d2bf28dbcec5217ab Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Tue, 7 Apr 2026 16:57:29 +0800
Subject: [PATCH 24/28] [llubi] Add missing header
---
llvm/tools/llubi/lib/Context.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 6c8e9fe658a7a..f4c62aeab73a2 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -12,6 +12,7 @@
#include "Value.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/IR/FPEnv.h"
#include "llvm/IR/Module.h"
#include <map>
#include <random>
>From 6aedec2ebf44bdd520f5f1dd5aad63f16ef620cc Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Fri, 10 Apr 2026 12:55:36 +0800
Subject: [PATCH 25/28] [llubi] Format fixes
---
llvm/tools/llubi/lib/Interpreter.cpp | 29 +++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 80971fced12b7..99f590ca9f25a 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -118,7 +118,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return AnyValue::poison();
if (FMF.noInfs() && APVal.isInfinity())
return AnyValue::poison();
- if (FMF.noSignedZeros() && APVal.isZero() && IsInput)
+ if (IsInput && FMF.noSignedZeros() && APVal.isZero())
return AnyValue(APFloat::getZero(
APVal.getSemantics(), APVal.isNegative() ^ Ctx.getRandomBool()));
return Val;
@@ -136,7 +136,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
const APFloat &pickNaNSource(ArrayRef<const APFloat *> Inputs,
const APFloat &Fallback) {
for (const APFloat *Input : Inputs) {
- if (Input && Input->isNaN())
+ if (Input->isNaN())
return *Input;
}
return Fallback;
@@ -221,13 +221,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// 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, true);
+ AnyValue ValidatedOperand =
+ handleFMFFlags(Operand, FMF, /*IsInput=*/true);
if (ValidatedOperand.isPoison())
return ValidatedOperand;
APFloat Result = ScalarFn(ValidatedOperand.asFloat());
- return handleFMFFlags(Result, FMF, false);
+ return handleFMFFlags(Result, FMF, /*IsInput=*/false);
});
}
@@ -277,8 +278,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, true);
- AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, false);
+ AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, /*IsInput=*/true);
+ AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, /*IsInput=*/true);
if (ValidatedLHS.isPoison())
return ValidatedLHS;
if (ValidatedRHS.isPoison())
@@ -292,7 +293,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// Flush output denormals and handle fast-math flags.
AnyValue FResult = handleFMFFlags(
- handleDenormal(RawResult, DenormMode.Output, true), FMF, false);
+ handleDenormal(RawResult, DenormMode.Output, /*NonDet=*/true), FMF,
+ /*IsInput=*/false);
if (FResult.isPoison())
return FResult;
@@ -323,7 +325,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (isa<FPMathOperator>(PHI)) {
FastMathFlags FMF = PHI->getFastMathFlags();
if (FMF.any())
- IncomingVal = handleFMFFlags(std::move(IncomingVal), FMF, true);
+ IncomingVal =
+ handleFMFFlags(std::move(IncomingVal), FMF, /*IsInput=*/true);
}
IncomingValues.emplace_back(PHI, IncomingVal);
@@ -870,7 +873,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
FastMathFlags FMF = cast<FPMathOperator>(I).getFastMathFlags();
DenormalMode DenormMode = getCurrentDenormalMode(I);
- auto ValidatedOperand = handleFMFFlags(Operand, FMF, true);
+ auto ValidatedOperand = handleFMFFlags(Operand, FMF, /*IsInput=*/true);
if (ValidatedOperand.isPoison())
return ValidatedOperand;
@@ -881,7 +884,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
bool LosesInfo;
FOperand.convert(DstSem, Ctx.getCurrentRoundingMode(), &LosesInfo);
- if (auto ValidateRes = handleFMFFlags(FOperand, FMF, false);
+ if (auto ValidateRes = handleFMFFlags(FOperand, FMF, /*IsInput=*/false);
ValidateRes.isPoison())
return ValidateRes;
@@ -1022,10 +1025,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (LHS.isPoison() || RHS.isPoison())
return AnyValue::poison();
- if (auto ValidateRes = handleFMFFlags(LHS, FMF, true);
+ if (auto ValidateRes = handleFMFFlags(LHS, FMF, /*IsInput=*/true);
ValidateRes.isPoison())
return ValidateRes;
- if (auto ValidateRes = handleFMFFlags(RHS, FMF, true);
+ if (auto ValidateRes = handleFMFFlags(RHS, FMF, /*IsInput=*/true);
ValidateRes.isPoison())
return ValidateRes;
@@ -1077,7 +1080,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// Handle fast-math flags
if (auto *FPMO = dyn_cast<FPMathOperator>(&SI)) {
if (FastMathFlags FMF = FPMO->getFastMathFlags(); FMF.any())
- Res = handleFMFFlags(std::move(Res), FMF, true);
+ Res = handleFMFFlags(std::move(Res), FMF, /*IsInput=*/true);
}
setResult(SI, std::move(Res));
>From 3f6ae154f55c1ffa46116e23f9d161dbffec3d17 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Sat, 11 Apr 2026 23:07:40 +0800
Subject: [PATCH 26/28] [llubi] Format fix
---
llvm/tools/llubi/lib/Context.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index e68ea4cc33671..e40c3916273b9 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -68,7 +68,7 @@ enum class NaNPropagationBehavior {
// currently we implement it by filling payload with random
// values
};
-
+
struct ProgramExitInfo {
enum class ProgramExitKind {
// Program exited via a normal return
>From 5e7064422e95c84f76b70d683e1a1e652df8f704 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Tue, 28 Apr 2026 11:44:48 +0800
Subject: [PATCH 27/28] [llubi] Format fix
---
llvm/tools/llubi/lib/Interpreter.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 88ef2d5bf3b99..9b3b2e63e1abd 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -232,8 +232,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return handleFMFFlags(Result, FMF, /*IsInput=*/false);
});
- }
-
+ }
+
AnyValue
visitIntUnOpWithResult(Instruction &I,
function_ref<AnyValue(const APInt &)> ScalarFn) {
@@ -315,8 +315,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
APFloat Result = FResult.asFloat();
return applyNaNPropagation(Result, {&FLHS, &FRHS});
});
- }
-
+ }
+
AnyValue visitIntBinOpWithResult(
Instruction &I,
function_ref<AnyValue(const APInt &, const APInt &)> ScalarFn) {
@@ -546,8 +546,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
DenormalMode getCurrentDenormalMode(Instruction &I) {
return CurrentFrame->Func.getDenormalMode(
I.getOperand(0)->getType()->getScalarType()->getFltSemantics());
- }
-
+ }
+
// Helper function to convert BooleanKind to bool. Report an immediate UB if
// a poison is found.
bool getBooleanNonPoison(BooleanKind Boolean) {
>From 2d120613a2eaf4994634eb6931684c2cf3679f41 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Wed, 29 Apr 2026 15:01:38 +0800
Subject: [PATCH 28/28] [llubi] Refactor target-specific and non-deterministic
NaN propagation
---
llvm/test/tools/llubi/deterministic.ll | 15 ++
llvm/test/tools/llubi/fp_arith_bfloat.ll | 2 +-
llvm/test/tools/llubi/fp_arith_double.ll | 2 +-
llvm/test/tools/llubi/fp_arith_float.ll | 2 +-
llvm/test/tools/llubi/fp_arith_fp128.ll | 2 +-
llvm/test/tools/llubi/fp_arith_fp80.ll | 2 +-
llvm/test/tools/llubi/fp_arith_half.ll | 2 +-
llvm/test/tools/llubi/fp_arith_ppc_fp128.ll | 2 +-
llvm/test/tools/llubi/fp_cmp.ll | 2 +-
llvm/test/tools/llubi/fp_nan_quieting.ll | 2 +-
.../llubi/fp_nan_target_specific_sparc.ll | 18 +++
...ic.ll => fp_nan_target_specific_wasm32.ll} | 6 +-
.../fp_nan_target_specific_x86_64_linux.ll | 16 +++
llvm/test/tools/llubi/fp_nan_unchanged.ll | 2 +-
llvm/test/tools/llubi/fp_phi_select.ll | 6 +-
llvm/tools/llubi/lib/Context.cpp | 25 +++-
llvm/tools/llubi/lib/Context.h | 17 ++-
llvm/tools/llubi/lib/Interpreter.cpp | 128 ++++++++++++++----
llvm/tools/llubi/llubi.cpp | 16 ++-
19 files changed, 206 insertions(+), 61 deletions(-)
create mode 100644 llvm/test/tools/llubi/deterministic.ll
create mode 100644 llvm/test/tools/llubi/fp_nan_target_specific_sparc.ll
rename llvm/test/tools/llubi/{fp_nan_platform_specific.ll => fp_nan_target_specific_wasm32.ll} (85%)
create mode 100644 llvm/test/tools/llubi/fp_nan_target_specific_x86_64_linux.ll
diff --git a/llvm/test/tools/llubi/deterministic.ll b/llvm/test/tools/llubi/deterministic.ll
new file mode 100644
index 0000000000000..a8947f551e5aa
--- /dev/null
+++ b/llvm/test/tools/llubi/deterministic.ll
@@ -0,0 +1,15 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose --deterministic < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %alloc = alloca i32
+ call void @llvm.lifetime.start.p0(ptr %alloc)
+ %undef_load = load i32, ptr %alloc
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %alloc = alloca i32, align 4 => ptr 0x8 [alloc]
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %alloc)
+; CHECK-NEXT: %undef_load = load i32, ptr %alloc, align 4 => i32 0
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_bfloat.ll b/llvm/test/tools/llubi/fp_arith_bfloat.ll
index e386261261f94..35cd9e24f91db 100644
--- a/llvm/test/tools/llubi/fp_arith_bfloat.ll
+++ b/llvm/test/tools/llubi/fp_arith_bfloat.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd bfloat 0xR3F80, poison => poison
; CHECK-NEXT: %p2 = fneg bfloat poison => poison
; CHECK-NEXT: %inf = fdiv bfloat 0xR3F80, 0xR0000 => bfloat +Inf
-; CHECK-NEXT: %nan = fdiv bfloat 0xR0000, 0xR0000 => bfloat 0xFFC0
+; CHECK-NEXT: %nan = fdiv bfloat 0xR0000, 0xR0000 => bfloat NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_double.ll b/llvm/test/tools/llubi/fp_arith_double.ll
index 755af05cbbf9d..33dead23862ae 100644
--- a/llvm/test/tools/llubi/fp_arith_double.ll
+++ b/llvm/test/tools/llubi/fp_arith_double.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd double 1.000000e+00, poison => poison
; CHECK-NEXT: %p2 = fneg double poison => poison
; CHECK-NEXT: %inf = fdiv double 1.000000e+00, 0.000000e+00 => double +Inf
-; CHECK-NEXT: %nan = fdiv double 0.000000e+00, 0.000000e+00 => double 0xFFF8000000000000
+; CHECK-NEXT: %nan = fdiv double 0.000000e+00, 0.000000e+00 => double NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_float.ll b/llvm/test/tools/llubi/fp_arith_float.ll
index 28ee59f3d7fe2..9e163a2673234 100644
--- a/llvm/test/tools/llubi/fp_arith_float.ll
+++ b/llvm/test/tools/llubi/fp_arith_float.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd float 1.000000e+00, poison => poison
; CHECK-NEXT: %p2 = fneg float poison => poison
; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_fp128.ll b/llvm/test/tools/llubi/fp_arith_fp128.ll
index 83a2e7295d879..c00ea53e9f670 100644
--- a/llvm/test/tools/llubi/fp_arith_fp128.ll
+++ b/llvm/test/tools/llubi/fp_arith_fp128.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd fp128 0xL00000000000000003FFF000000000000, poison => poison
; CHECK-NEXT: %p2 = fneg fp128 poison => poison
; CHECK-NEXT: %inf = fdiv fp128 0xL00000000000000003FFF000000000000, 0xL00000000000000000000000000000000 => fp128 +Inf
-; CHECK-NEXT: %nan = fdiv fp128 0xL00000000000000000000000000000000, 0xL00000000000000000000000000000000 => fp128 0xFFFF8000000000000000000000000000
+; CHECK-NEXT: %nan = fdiv fp128 0xL00000000000000000000000000000000, 0xL00000000000000000000000000000000 => fp128 NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_fp80.ll b/llvm/test/tools/llubi/fp_arith_fp80.ll
index 759e162773cd4..7a6e764957949 100644
--- a/llvm/test/tools/llubi/fp_arith_fp80.ll
+++ b/llvm/test/tools/llubi/fp_arith_fp80.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd x86_fp80 0xK3FFF8000000000000000, poison => poison
; CHECK-NEXT: %p2 = fneg x86_fp80 poison => poison
; CHECK-NEXT: %inf = fdiv x86_fp80 0xK3FFF8000000000000000, 0xK00000000000000000000 => x86_fp80 +Inf
-; CHECK-NEXT: %nan = fdiv x86_fp80 0xK00000000000000000000, 0xK00000000000000000000 => x86_fp80 0xFFFFC000000000000000
+; CHECK-NEXT: %nan = fdiv x86_fp80 0xK00000000000000000000, 0xK00000000000000000000 => x86_fp80 NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_half.ll b/llvm/test/tools/llubi/fp_arith_half.ll
index 093ec2b1e92da..3ffdc388047e1 100644
--- a/llvm/test/tools/llubi/fp_arith_half.ll
+++ b/llvm/test/tools/llubi/fp_arith_half.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd half 0xH3C00, poison => poison
; CHECK-NEXT: %p2 = fneg half poison => poison
; CHECK-NEXT: %inf = fdiv half 0xH3C00, 0xH0000 => half +Inf
-; CHECK-NEXT: %nan = fdiv half 0xH0000, 0xH0000 => half 0xFE00
+; CHECK-NEXT: %nan = fdiv half 0xH0000, 0xH0000 => half NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
index 7d26c6e1e972a..c9d20aaa32101 100644
--- a/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
+++ b/llvm/test/tools/llubi/fp_arith_ppc_fp128.ll
@@ -28,6 +28,6 @@ define void @main() {
; CHECK-NEXT: %p1 = fadd ppc_fp128 0xM3FF00000000000000000000000000000, poison => poison
; CHECK-NEXT: %p2 = fneg ppc_fp128 poison => poison
; CHECK-NEXT: %inf = fdiv ppc_fp128 0xM3FF00000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 +Inf
-; CHECK-NEXT: %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 0x8000000000000000FFF8000000000000
+; CHECK-NEXT: %nan = fdiv ppc_fp128 0xM00000000000000000000000000000000, 0xM00000000000000000000000000000000 => ppc_fp128 NaN
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_cmp.ll b/llvm/test/tools/llubi/fp_cmp.ll
index da6d7d550479b..489a91f256e91 100644
--- a/llvm/test/tools/llubi/fp_cmp.ll
+++ b/llvm/test/tools/llubi/fp_cmp.ll
@@ -17,7 +17,7 @@ define void @main() {
; CHECK-NEXT: %eq = fcmp oeq float 1.000000e+00, 1.000000e+00 => T
; CHECK-NEXT: %ne = fcmp one float 1.000000e+00, 2.000000e+00 => T
; CHECK-NEXT: %gt = fcmp ogt float 2.000000e+00, 1.000000e+00 => T
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
; CHECK-NEXT: %ord_nan = fcmp oeq float %nan, 1.000000e+00 => F
; CHECK-NEXT: %uno_nan = fcmp uno float %nan, 1.000000e+00 => T
; CHECK-NEXT: %une_nan = fcmp une float %nan, 1.000000e+00 => T
diff --git a/llvm/test/tools/llubi/fp_nan_quieting.ll b/llvm/test/tools/llubi/fp_nan_quieting.ll
index 1864bc03d571f..02254f7201e1d 100644
--- a/llvm/test/tools/llubi/fp_nan_quieting.ll
+++ b/llvm/test/tools/llubi/fp_nan_quieting.ll
@@ -11,6 +11,6 @@ define void @main() {
; CHECK: Entering function: main
; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF8000000000001
-; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF8000000000001
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0x7FF8000000000001
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_target_specific_sparc.ll b/llvm/test/tools/llubi/fp_nan_target_specific_sparc.ll
new file mode 100644
index 0000000000000..b308cee865bee
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_target_specific_sparc.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --nan-target-specific --verbose < %s 2>&1 | FileCheck %s
+
+target triple = "sparc-unknown-linux-gnu"
+
+define void @main() {
+ %qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double 0x7FFFFFFFFFFFFFFF
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFFFFFFFFFFFFFFF
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0x7FFFFFFFFFFFFFFF
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_platform_specific.ll b/llvm/test/tools/llubi/fp_nan_target_specific_wasm32.ll
similarity index 85%
rename from llvm/test/tools/llubi/fp_nan_platform_specific.ll
rename to llvm/test/tools/llubi/fp_nan_target_specific_wasm32.ll
index 43361f95a92fc..5fad18c6688f5 100644
--- a/llvm/test/tools/llubi/fp_nan_platform_specific.ll
+++ b/llvm/test/tools/llubi/fp_nan_target_specific_wasm32.ll
@@ -1,6 +1,8 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
; RUN: llubi --nan-target-specific --verbose < %s 2>&1 | FileCheck %s
+target triple = "wasm32-unknown-unknown"
+
define void @main() {
%qnan_zero_payload = fadd double 1.0, 0x7FF8000000000000
%qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
@@ -9,8 +11,8 @@ define void @main() {
ret void
}
; CHECK: Entering function: main
-; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double 0x7FFD3A7C3E40F98B
-; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFFD6903CEE3FCEE
+; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF93217F032E8B9
; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0x7FFA062AFD5FE9EE
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_target_specific_x86_64_linux.ll b/llvm/test/tools/llubi/fp_nan_target_specific_x86_64_linux.ll
new file mode 100644
index 0000000000000..c8f20d9faa2a3
--- /dev/null
+++ b/llvm/test/tools/llubi/fp_nan_target_specific_x86_64_linux.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --nan-target-specific --verbose < %s 2>&1 | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @main() {
+ %qnan_nonzero_payload = fadd double 1.0, 0x7FF8000000000001
+ %snan = fadd double 1.0, 0x7FF0000000000001
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double NaN
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF8000000000000
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_nan_unchanged.ll b/llvm/test/tools/llubi/fp_nan_unchanged.ll
index e9f0c94e4fd46..22926c9fb7de4 100644
--- a/llvm/test/tools/llubi/fp_nan_unchanged.ll
+++ b/llvm/test/tools/llubi/fp_nan_unchanged.ll
@@ -11,6 +11,6 @@ define void @main() {
; CHECK: Entering function: main
; CHECK-NEXT: %qnan_zero_payload = fadd double 1.000000e+00, 0x7FF8000000000000 => double NaN
; CHECK-NEXT: %qnan_nonzero_payload = fadd double 1.000000e+00, 0x7FF8000000000001 => double 0xFFF8000000000001
-; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0xFFF0000000000001
+; CHECK-NEXT: %snan = fadd double 1.000000e+00, 0x7FF0000000000001 => double 0x7FF0000000000001
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/fp_phi_select.ll b/llvm/test/tools/llubi/fp_phi_select.ll
index d3492e84ea880..9c0685a4d1cbc 100644
--- a/llvm/test/tools/llubi/fp_phi_select.ll
+++ b/llvm/test/tools/llubi/fp_phi_select.ll
@@ -20,13 +20,13 @@ phi.test:
ret void
}
; CHECK: Entering function: main
-; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float 0xFFC00000
+; CHECK-NEXT: %nan = fdiv float 0.000000e+00, 0.000000e+00 => float NaN
; CHECK-NEXT: %inf = fdiv float 1.000000e+00, 0.000000e+00 => float +Inf
-; CHECK-NEXT: %safe_sel = select i1 true, float %nan, float 1.000000e+00 => float 0xFFC00000
+; CHECK-NEXT: %safe_sel = select i1 true, float %nan, float 1.000000e+00 => float NaN
; CHECK-NEXT: %bad_sel_nan = select nnan i1 true, float %nan, float 1.000000e+00 => poison
; CHECK-NEXT: %bad_sel_inf = select ninf i1 false, float 1.000000e+00, float %inf => poison
; CHECK-NEXT: br label %phi.test jump to %phi.test
-; CHECK-NEXT: %safe_phi = phi float [ %nan, %entry ] => float 0xFFC00000
+; CHECK-NEXT: %safe_phi = phi float [ %nan, %entry ] => float NaN
; CHECK-NEXT: %bad_phi_nan = phi nnan float [ %nan, %entry ] => poison
; CHECK-NEXT: %bad_phi_inf = phi ninf float [ %inf, %entry ] => poison
; CHECK-NEXT: ret void
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 9eb2fdb6b5a97..e9c25f9be97b4 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -141,13 +141,14 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
return AnyValue::poison();
}
uint8_t RandomBits = 0;
- if (UndefBehavior == UndefValueBehavior::NonDeterministic &&
+ if (getEffectiveUndefValueBehavior() ==
+ UndefValueBehavior::NonDeterministic &&
(~LogicalByte.ConcreteMask & Mask)) {
// This byte contains undef bits.
// We don't use std::uniform_int_distribution here because it produces
// different results across different library implementations. Instead,
// we directly use the low bits from Rng.
- RandomBits = static_cast<uint8_t>(Rng());
+ RandomBits = static_cast<uint8_t>(getRandomUInt64());
}
uint8_t ActualBits = ((LogicalByte.Value & LogicalByte.ConcreteMask) |
(RandomBits & ~LogicalByte.ConcreteMask)) &
@@ -380,7 +381,7 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
if (Val.isPoison()) {
uint32_t Bits = DL.getTypeSizeInBits(Ty);
APInt RandomVal = APInt::getZero(Bits);
- if (UndefBehavior == UndefValueBehavior::NonDeterministic) {
+ if (mayUseNonDeterminism()) {
SmallVector<APInt::WordType> RandomWords;
uint32_t NumWords = APInt::getNumWords(Bits);
RandomWords.reserve(NumWords);
@@ -388,7 +389,7 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
std::numeric_limits<APInt::WordType>::digits,
"Unexpected Rng result type.");
for (uint32_t I = 0; I != NumWords; ++I)
- RandomWords.push_back(static_cast<APInt::WordType>(Rng()));
+ RandomWords.push_back(static_cast<APInt::WordType>(getRandomUInt64()));
RandomVal = APInt(Bits, RandomWords);
}
if (Ty->isIntegerTy())
@@ -523,15 +524,27 @@ bool Context::isDefaultFPEnv() const {
return isDefaultFPEnvironment(CurrentExceptionBehavior, CurrentRoundingMode);
}
+UndefValueBehavior Context::getEffectiveUndefValueBehavior() const {
+ if (isDeterministic())
+ return UndefValueBehavior::Zero;
+ return UndefBehavior;
+}
+
+NaNPropagationBehavior Context::getEffectiveNaNPropagationBehavior() const {
+ if (isDeterministic())
+ return NaNPropagationBehavior::PreferredNaN;
+ return NaNBehavior;
+}
+
bool Context::getRandomBool() {
// We use the lowest bit of the raw bits from RNG as the result:
- if (UndefBehavior == UndefValueBehavior::NonDeterministic)
+ if (mayUseNonDeterminism())
return static_cast<bool>(Rng() & 1);
return false;
}
uint64_t Context::getRandomUInt64() {
- if (UndefBehavior == UndefValueBehavior::NonDeterministic)
+ if (mayUseNonDeterminism())
return Rng();
return 0;
}
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index e40c3916273b9..f558d9cc0f9ff 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -57,16 +57,15 @@ enum class UndefValueBehavior {
};
enum class NaNPropagationBehavior {
- NonDeterministic, // Non-deterministically choose from the following 4 cases
+ NonDeterministic, // Non-deterministically choose from valid NaN results
PreferredNaN, // The quiet bit is set and the payload is all-zero
QuietingNaN, // The quiet bit is set and the payload is copied from any input
// operand that is a NaN
UnchangedNaN, // The quiet bit and payload are copied from any input operand
// that is a NaN
TargetSpecificNaN // The quiet bit is set and the payload is picked from a
- // target-specific set of “extra” possible NaN payloads,
- // currently we implement it by filling payload with random
- // values
+ // known target-specific set of "extra" possible NaN
+ // payloads
};
struct ProgramExitInfo {
@@ -206,6 +205,7 @@ class Context {
uint32_t VScale = 4;
uint32_t MaxSteps = 0;
uint32_t MaxStackDepth = 256;
+ bool Deterministic = false;
UndefValueBehavior UndefBehavior = UndefValueBehavior::NonDeterministic;
NaNPropagationBehavior NaNBehavior = NaNPropagationBehavior::NonDeterministic;
@@ -264,9 +264,11 @@ class Context {
uint32_t getVScale() const { return VScale; }
uint32_t getMaxSteps() const { return MaxSteps; }
uint32_t getMaxStackDepth() const { return MaxStackDepth; }
- NaNPropagationBehavior getNaNPropagationBehavior() const {
- return NaNBehavior;
- }
+ void setDeterministic(bool D) { Deterministic = D; }
+ bool isDeterministic() const { return Deterministic; }
+ bool mayUseNonDeterminism() const { return !Deterministic; }
+ UndefValueBehavior getEffectiveUndefValueBehavior() const;
+ NaNPropagationBehavior getEffectiveNaNPropagationBehavior() const;
void setUndefValueBehavior(UndefValueBehavior UB) { UndefBehavior = UB; }
void setNaNPropagationBehavior(NaNPropagationBehavior NaNBehav) {
NaNBehavior = NaNBehav;
@@ -275,6 +277,7 @@ class Context {
LLVMContext &getContext() const { return Ctx; }
const DataLayout &getDataLayout() const { return DL; }
+ const Triple &getTargetTriple() const { return M.getTargetTriple(); }
const TargetLibraryInfoImpl &getTLIImpl() const { return TLIImpl; }
/// Get the effective vector length for a vector type.
uint32_t getEVL(ElementCount EC) const {
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 9b3b2e63e1abd..aa8a9b0e23d7c 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -14,12 +14,15 @@
#include "ExecutorBase.h"
#include "Library.h"
#include "Value.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Support/Allocator.h"
+#include "llvm/TargetParser/Triple.h"
namespace llvm::ubi {
@@ -84,10 +87,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
APFloat handleDenormal(APFloat Val, DenormalMode::DenormalModeKind Mode,
- bool NonDet = false) {
+ bool IsInput = false) {
if (!Val.isDenormal())
return Val;
- if (NonDet) {
+ if (IsInput) {
// Non-deterministically choose between flushing or preserving the
// denormal value.
if (Ctx.getRandomBool())
@@ -126,22 +129,46 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Val;
}
- NaNPropagationBehavior resolveNaNPropagationBehavior() {
- NaNPropagationBehavior Choice = Ctx.getNaNPropagationBehavior();
- if (Choice == NaNPropagationBehavior::NonDeterministic) {
- uint64_t NonDetChoice = Ctx.getRandomUInt64() % 4 + 1;
- Choice = static_cast<NaNPropagationBehavior>(NonDetChoice);
- }
- return Choice;
+ void addNaNCandidate(SmallVectorImpl<APFloat> &Candidates,
+ APFloat Candidate) {
+ APInt Bits = Candidate.bitcastToAPInt();
+ if (any_of(Candidates, [&](const APFloat &Existing) {
+ return Existing.bitcastToAPInt() == Bits;
+ }))
+ return;
+ Candidates.push_back(std::move(Candidate));
+ }
+
+ APFloat pickNaNCandidate(ArrayRef<APFloat> Candidates) {
+ assert(!Candidates.empty() && "Need at least one NaN candidate.");
+ return Candidates[Ctx.getRandomUInt64() % Candidates.size()];
}
- const APFloat &pickNaNSource(ArrayRef<const APFloat *> Inputs,
- const APFloat &Fallback) {
+ APInt getRandomNaNPayload(const fltSemantics &Sem) {
+ const unsigned NumBits = Sem.precision - 1;
+ SmallVector<APInt::WordType, 2> RandomWords;
+ const unsigned NumWords = APInt::getNumWords(NumBits);
+ RandomWords.reserve(NumWords);
+ for (unsigned I = 0; I != NumWords; ++I)
+ RandomWords.push_back(Ctx.getRandomUInt64());
+ return APInt(NumBits, RandomWords);
+ }
+
+ bool isPreferredNaN(const APFloat &Val) {
+ assert(Val.isNaN() && "Expected NaN.");
+ const APFloat Preferred =
+ APFloat::getQNaN(Val.getSemantics(), Val.isNegative());
+ return Val.bitcastToAPInt() == Preferred.bitcastToAPInt();
+ }
+
+ bool wasmMayProduceExtraNaNPayload(ArrayRef<const APFloat *> Inputs) {
for (const APFloat *Input : Inputs) {
- if (Input->isNaN())
- return *Input;
+ if (!Input->isNaN())
+ continue;
+ if (Input->isSignaling() || !isPreferredNaN(*Input))
+ return true;
}
- return Fallback;
+ return false;
}
APFloat propagateInputNaN(const APFloat &InputNaN, const fltSemantics &DstSem,
@@ -156,30 +183,75 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Res;
}
+ void addPropagatedNaNCandidates(SmallVectorImpl<APFloat> &Candidates,
+ ArrayRef<const APFloat *> Inputs,
+ const fltSemantics &DstSem, bool QuietingMode,
+ bool SignChoice) {
+ for (const APFloat *Input : Inputs) {
+ if (!Input->isNaN())
+ continue;
+ addNaNCandidate(Candidates, propagateInputNaN(*Input, DstSem,
+ QuietingMode, SignChoice));
+ }
+ }
+
+ void addTargetSpecificNaNCandidates(SmallVectorImpl<APFloat> &Candidates,
+ const APFloat &Result,
+ ArrayRef<const APFloat *> Inputs,
+ bool SignChoice) {
+ const Triple &TT = Ctx.getTargetTriple();
+ if (TT.isWasm()) {
+ if (!wasmMayProduceExtraNaNPayload(Inputs))
+ return;
+ APInt Payload = getRandomNaNPayload(Result.getSemantics());
+ addNaNCandidate(Candidates, APFloat::getQNaN(Result.getSemantics(),
+ SignChoice, &Payload));
+ return;
+ }
+
+ if (TT.isSPARC32() || TT.isSPARC64()) {
+ APInt Payload = APInt::getAllOnes(Result.getSemantics().precision - 1);
+ addNaNCandidate(Candidates, APFloat::getQNaN(Result.getSemantics(),
+ SignChoice, &Payload));
+ }
+ }
+
APFloat applyNaNPropagation(const APFloat &Result,
ArrayRef<const APFloat *> Inputs) {
if (!Result.isNaN())
return Result;
- NaNPropagationBehavior Choice = resolveNaNPropagationBehavior();
+ const NaNPropagationBehavior Choice =
+ Ctx.getEffectiveNaNPropagationBehavior();
const bool SignChoice = Ctx.getRandomBool();
+ const fltSemantics &ResultSem = Result.getSemantics();
+ auto PreferredNaN = [&]() {
+ return APFloat::getQNaN(ResultSem, SignChoice);
+ };
+
+ SmallVector<APFloat, 4> Candidates;
switch (Choice) {
case NaNPropagationBehavior::PreferredNaN:
- return APFloat::getQNaN(Result.getSemantics(), SignChoice);
+ return PreferredNaN();
case NaNPropagationBehavior::QuietingNaN:
- return propagateInputNaN(pickNaNSource(Inputs, Result),
- Result.getSemantics(), /*QuietingMode=*/true,
- SignChoice);
+ addPropagatedNaNCandidates(Candidates, Inputs, ResultSem,
+ /*QuietingMode=*/true, SignChoice);
+ return Candidates.empty() ? PreferredNaN() : pickNaNCandidate(Candidates);
case NaNPropagationBehavior::UnchangedNaN:
- return propagateInputNaN(pickNaNSource(Inputs, Result),
- Result.getSemantics(), /*QuietingMode=*/false,
- SignChoice);
- case NaNPropagationBehavior::TargetSpecificNaN: {
- APInt Payload(64, Ctx.getRandomUInt64());
- return APFloat::getQNaN(Result.getSemantics(), SignChoice, &Payload);
- }
+ addPropagatedNaNCandidates(Candidates, Inputs, ResultSem,
+ /*QuietingMode=*/false, SignChoice);
+ return Candidates.empty() ? PreferredNaN() : pickNaNCandidate(Candidates);
+ case NaNPropagationBehavior::TargetSpecificNaN:
+ addTargetSpecificNaNCandidates(Candidates, Result, Inputs, SignChoice);
+ return Candidates.empty() ? PreferredNaN() : pickNaNCandidate(Candidates);
case NaNPropagationBehavior::NonDeterministic:
- llvm_unreachable("NonDeterministic should be resolved earlier.");
+ addNaNCandidate(Candidates, PreferredNaN());
+ addPropagatedNaNCandidates(Candidates, Inputs, ResultSem,
+ /*QuietingMode=*/true, SignChoice);
+ addPropagatedNaNCandidates(Candidates, Inputs, ResultSem,
+ /*QuietingMode=*/false, SignChoice);
+ addTargetSpecificNaNCandidates(Candidates, Result, Inputs, SignChoice);
+ return pickNaNCandidate(Candidates);
}
llvm_unreachable("Unhandled NaN propagation behavior.");
}
@@ -306,7 +378,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
// Flush output denormals and handle fast-math flags.
AnyValue FResult = handleFMFFlags(
- handleDenormal(RawResult, DenormMode.Output, /*NonDet=*/true), FMF,
+ handleDenormal(RawResult, DenormMode.Output, /*IsInput=*/true), FMF,
/*IsInput=*/false);
if (FResult.isPoison())
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 83b956fdf7b1e..ccbbdf3e38e83 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -79,6 +79,11 @@ static cl::opt<unsigned>
cl::desc("Random seed for non-deterministic behavior (default = 0)"),
cl::value_desc("N"), cl::init(0), cl::cat(InterpreterCategory));
+static cl::opt<bool>
+ Deterministic("deterministic",
+ cl::desc("Disable interpreter-introduced non-determinism."),
+ cl::init(false), cl::cat(InterpreterCategory));
+
cl::opt<ubi::UndefValueBehavior> UndefBehavior(
"", cl::desc("Choose undef value behavior:"),
cl::values(clEnumVal(ubi::UndefValueBehavior::NonDeterministic,
@@ -91,8 +96,8 @@ cl::opt<ubi::NaNPropagationBehavior> NaNPropagationBehavior(
"", cl::desc("Choose NaN propagation behavior:"),
cl::values(
clEnumValN(ubi::NaNPropagationBehavior::NonDeterministic, "nan-nodet",
- "Non-deterministically choose from 4 cases as specified by "
- "language reference."),
+ "Non-deterministically choose from valid NaN results as "
+ "specified by language reference."),
clEnumValN(ubi::NaNPropagationBehavior::PreferredNaN, "nan-preferred",
"The quiet bit is set and the payload is all-zero."),
clEnumValN(
@@ -104,9 +109,9 @@ cl::opt<ubi::NaNPropagationBehavior> NaNPropagationBehavior(
"that is a NaN"),
clEnumValN(ubi::NaNPropagationBehavior::TargetSpecificNaN,
"nan-target-specific",
- "The quiet bit is set and the payload is picked from a"
- "target-specific set of “extra” possible NaN payloads."
- "Implemented by filling payload with random values")),
+ "The quiet bit is set and the payload is picked from a "
+ "known target-specific set of \"extra\" possible NaN "
+ "payloads.")),
cl::init(ubi::NaNPropagationBehavior::NonDeterministic));
class VerboseEventHandler : public ubi::EventHandler {
@@ -219,6 +224,7 @@ int main(int argc, char **argv) {
Ctx.setVScale(VScale);
Ctx.setMaxSteps(MaxSteps);
Ctx.setMaxStackDepth(MaxStackDepth);
+ Ctx.setDeterministic(Deterministic);
Ctx.setUndefValueBehavior(UndefBehavior);
Ctx.setNaNPropagationBehavior(NaNPropagationBehavior);
Ctx.reseed(Seed);
More information about the llvm-commits
mailing list