[llvm] [llubi] Initial support for floating-point numbers (PR #188453)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 28 06:39:03 PDT 2026


================
@@ -83,6 +83,107 @@ 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, bool IsInput) {
+    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, IsInput));
+      return AnyValue(ResVec);
+    }
+
+    const APFloat &APVal = Val.asFloat();
+    if (FMF.noNaNs() && APVal.isNaN())
+      return AnyValue::poison();
+    if (FMF.noInfs() && APVal.isInfinity())
+      return AnyValue::poison();
+    if (IsInput && FMF.noSignedZeros() && APVal.isZero())
+      return AnyValue(APFloat::getZero(
+          APVal.getSemantics(), APVal.isNegative() ^ Ctx.getRandomBool()));
+    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;
+  }
+
+  const APFloat &pickNaNSource(ArrayRef<const APFloat *> Inputs,
+                               const APFloat &Fallback) {
+    for (const APFloat *Input : Inputs) {
+      if (Input->isNaN())
+        return *Input;
+    }
+    return Fallback;
+  }
+
+  APFloat propagateInputNaN(const APFloat &InputNaN, const fltSemantics &DstSem,
+                            bool QuietingMode, bool FlipSign) {
+    APFloat Res = InputNaN;
+    bool LosesInfo;
+    Res.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo);
+    if (FlipSign)
+      Res.changeSign();
+    if (QuietingMode && Res.isSignaling())
+      Res = Res.makeQuiet();
+    return Res;
+  }
+
+  APFloat applyNaNPropagation(const APFloat &Result,
+                              ArrayRef<const APFloat *> Inputs) {
+    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);
+    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);
----------------
nikic wrote:

LangRef says:

> The quiet bit is set and the payload is picked from a target-specific set of “extra” possible NaN payloads. The set can depend on the input operand values. This set is empty on x86 and ARM, but can be non-empty on other architectures. (For instance, on wasm, if any input NaN does not have the preferred all-zero payload or any input NaN is an SNaN, then this set contains all possible payloads; otherwise, it is empty. On SPARC, this set consists of the all-one payload.)

I'm not sure it makes sense to model the precise target-specific behavior in llubi, but I think just doing a completely random payload here is not right.

https://github.com/llvm/llvm-project/pull/188453


More information about the llvm-commits mailing list