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

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Wed May 6 05:06:33 PDT 2026


================
@@ -1304,6 +1406,290 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       }
       return std::move(Res);
     }
+    case Intrinsic::vector_reduce_fadd:
+    case Intrinsic::vector_reduce_fmul:
+    case Intrinsic::vector_reduce_fmaximum:
+    case Intrinsic::vector_reduce_fminimum: {
+      const auto DenormMode = getCurrentDenormalMode(RetTy);
+      const bool HasStart = IID == Intrinsic::vector_reduce_fadd ||
+                            IID == Intrinsic::vector_reduce_fmul;
+      const AnyValue &Vector = HasStart ? Args[1] : Args[0];
+      std::optional<APFloat> Res;
+      if (HasStart) {
+        if (Args[0].isPoison())
+          return AnyValue::poison();
+        const AnyValue ValidatedStart =
+            handleFMFFlags(Args[0], FMF, /*IsInput=*/true);
+        if (ValidatedStart.isPoison())
+          return AnyValue::poison();
+        Res = handleDenormal(ValidatedStart.asFloat(), DenormMode.Input,
+                             /*IsInput=*/true);
+      }
+      for (const auto &V : Vector.asAggregate()) {
+        if (V.isPoison())
+          return AnyValue::poison();
+        const AnyValue ValidatedOp = handleFMFFlags(V, FMF, /*IsInput=*/true);
+        if (ValidatedOp.isPoison())
+          return AnyValue::poison();
+        APFloat Op = handleDenormal(ValidatedOp.asFloat(), DenormMode.Input,
+                                    /*IsInput=*/true);
+        if (!Res) {
+          Res = std::move(Op);
+          continue;
+        }
+        switch (IID) {
+        case Intrinsic::vector_reduce_fadd:
+          *Res = *Res + Op;
+          break;
+        case Intrinsic::vector_reduce_fmul:
+          *Res = *Res * Op;
+          break;
+        case Intrinsic::vector_reduce_fmaximum:
+          *Res = maximum(*Res, Op);
+          break;
+        case Intrinsic::vector_reduce_fminimum:
+          *Res = minimum(*Res, Op);
+          break;
+        default:
+          llvm_unreachable("Unexpected intrinsic ID");
+        }
+      }
+      if (Res.has_value()) {
+        const AnyValue ValidatedRes =
+            handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+        if (ValidatedRes.isPoison()) {
+          Res.reset();
+        }
+        const APFloat FRes =
+            handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
+                           /*IsInput=*/false);
+        std::vector<AnyValue> RawInputVec = Vector.asAggregate();
+        std::vector<const APFloat *> InputVec;
+        InputVec.reserve(RawInputVec.size());
+        transform(
+            RawInputVec, std::back_inserter(InputVec),
+            [](const AnyValue &V) -> const APFloat * { return &V.asFloat(); });
+        return applyNaNPropagation(FRes, InputVec);
+      }
+      return AnyValue::poison();
+    }
+    case Intrinsic::vector_reduce_fmax:
+    case Intrinsic::vector_reduce_fmin: {
+      const auto DenormMode = getCurrentDenormalMode(RetTy);
+      const auto &Vector = Args[0].asAggregate();
+      std::optional<APFloat> Res;
+      SmallVector<APFloat, 16> InputFloats;
+      std::vector<const APFloat *> InputVec;
+      InputFloats.reserve(Vector.size());
+      InputVec.reserve(Vector.size());
+      for (const auto &V : Vector) {
+        if (V.isPoison())
+          return AnyValue::poison();
+        const AnyValue ValidatedOp = handleFMFFlags(V, FMF, /*IsInput=*/true);
+        if (ValidatedOp.isPoison())
+          return AnyValue::poison();
+        InputFloats.push_back(handleDenormal(ValidatedOp.asFloat(),
+                                             DenormMode.Input,
+                                             /*IsInput=*/true));
+        InputVec.push_back(&InputFloats.back());
+        if (!Res) {
+          Res = InputFloats.back();
+          continue;
+        }
+        *Res = IID == Intrinsic::vector_reduce_fmax
+                   ? maxnum(*Res, InputFloats.back())
+                   : minnum(*Res, InputFloats.back());
+      }
+      if (!Res)
+        return AnyValue::poison();
+
+      AnyValue ValidatedRes = handleFMFFlags(*Res, FMF, /*IsInput=*/false);
+      if (ValidatedRes.isPoison())
+        return AnyValue::poison();
+      APFloat FRes = handleDenormal(ValidatedRes.asFloat(), DenormMode.Output,
+                                    /*IsInput=*/false);
+
+      if (!any_of(InputVec, [](const APFloat *V) { return V->isSignaling(); }))
+        return applyNaNPropagation(FRes, InputVec);
+
+      if (Ctx.mayUseNonDeterminism() && Ctx.getRandomBool())
+        return applyNaNPropagation(APFloat::getQNaN(FRes.getSemantics()),
+                                   InputVec);
+
+      SmallVector<APFloat, 16> QuietedInputFloats;
+      QuietedInputFloats.reserve(InputVec.size());
+      for (const APFloat *V : InputVec)
+        QuietedInputFloats.push_back(propagateInputNaN(*V, V->getSemantics(),
+                                                       /*QuietingMode=*/true,
+                                                       /*FlipSign=*/false));
+      APFloat QuietRes = QuietedInputFloats.front();
+      for (const APFloat &V :
+           ArrayRef<APFloat>(QuietedInputFloats).drop_front())
+        QuietRes = IID == Intrinsic::vector_reduce_fmax ? maxnum(QuietRes, V)
+                                                        : minnum(QuietRes, V);
+      QuietRes = handleDenormal(QuietRes, DenormMode.Output,
+                                /*IsInput=*/false);
+      AnyValue FinalRes = handleFMFFlags(QuietRes, FMF, /*IsInput=*/false);
+      if (FinalRes.isPoison())
+        return AnyValue::poison();
+      QuietRes = FinalRes.asFloat();
+
+      SmallVector<const APFloat *, 16> QuietedInputVec;
+      QuietedInputVec.reserve(QuietedInputFloats.size());
+      for (const APFloat &V : QuietedInputFloats)
+        QuietedInputVec.push_back(&V);
+      return applyNaNPropagation(QuietRes, QuietedInputVec);
+    }
+    case Intrinsic::fabs: {
+      return visitBitwiseFPUnOpWithResult(
+          RetTy, FMF, Args[0],
+          [](const APFloat &Operand) -> APFloat { return abs(Operand); });
+    }
+    case Intrinsic::fma: {
+      return visitFPTriOpWithResult(
+          RetTy, FMF, Args[0], Args[1], Args[2],
+          [](const APFloat &Op1, const APFloat &Op2,
+             const APFloat &Op3) -> APFloat {
+            auto Res = Op1;
+            Res.fusedMultiplyAdd(Op2, Op3, RoundingMode::NearestTiesToEven);
+            return Res;
+          });
+    }
+    case Intrinsic::fmuladd: {
+      return visitFPTriOpWithResult(
+          RetTy, FMF, Args[0], Args[1], Args[2],
+          [&](const APFloat &Op1, const APFloat &Op2,
+              const APFloat &Op3) -> APFloat {
+            if (Ctx.fuseMultiplyAdd()) {
+              auto Res = Op1;
+              Res.fusedMultiplyAdd(Op2, Op3, RoundingMode::NearestTiesToEven);
+              return Res;
+            }
+            return Op1 * Op2 + Op3;
+          });
+    }
+    case Intrinsic::is_fpclass: {
+      if (Args[1].isPoison())
+        return AnyValue::poison();
+      const FPClassTest Mask =
+          static_cast<FPClassTest>(Args[1].asInteger().getZExtValue());
+      return computeUnOp(RetTy, Args[0], [&](const AnyValue &Op) -> AnyValue {
+        if (Op.isPoison())
+          return AnyValue::poison();
+        return AnyValue::boolean(
+            static_cast<bool>(Op.asFloat().classify() & Mask));
+      });
+    }
+    case Intrinsic::copysign: {
+      return computeBinOp(
+          RetTy, Args[0], Args[1],
+          [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+            if (LHS.isPoison() || RHS.isPoison())
+              return AnyValue::poison();
+            const AnyValue ValidatedLHS =
+                handleFMFFlags(LHS, FMF, /*IsInput=*/true);
+            const AnyValue ValidatedRHS =
+                handleFMFFlags(RHS, FMF, /*IsInput=*/true);
+            if (ValidatedLHS.isPoison() || ValidatedRHS.isPoison())
+              return AnyValue::poison();
+
+            return handleFMFFlags(APFloat::copySign(ValidatedLHS.asFloat(),
+                                                    ValidatedRHS.asFloat()),
+                                  FMF, /*IsInput=*/false);
+          });
+    }
+    case Intrinsic::maxnum:
+    case Intrinsic::minnum: {
+      DenormalMode DenormMode = getCurrentDenormalMode(RetTy);
+
+      if (!Ctx.isDefaultFPEnv())
+        reportImmediateUB()
+            << "Non-constrained floating-point operation assumes default "
+               "floating-point environment";
+
+      return computeBinOp(
+          RetTy, Args[0], Args[1],
+          [&](const AnyValue &LHS, const AnyValue &RHS) -> AnyValue {
+            if (LHS.isPoison() || RHS.isPoison())
+              return AnyValue::poison();
+            AnyValue ValidatedLHS = handleFMFFlags(LHS, FMF, /*IsInput=*/true);
+            AnyValue ValidatedRHS = handleFMFFlags(RHS, FMF, /*IsInput=*/true);
+            if (ValidatedLHS.isPoison())
+              return ValidatedLHS;
+            if (ValidatedRHS.isPoison())
+              return ValidatedRHS;
+
+            APFloat FLHS = handleDenormal(ValidatedLHS.asFloat(),
+                                          DenormMode.Input, /*IsInput=*/true);
+            APFloat FRHS = handleDenormal(ValidatedRHS.asFloat(),
+                                          DenormMode.Input, /*IsInput=*/true);
+
+            APFloat RawResult = IID == Intrinsic::maxnum ? maxnum(FLHS, FRHS)
+                                                         : minnum(FLHS, FRHS);
+            APFloat Result = handleDenormal(RawResult, DenormMode.Output,
+                                            /*IsInput=*/false);
+            AnyValue FResult = handleFMFFlags(Result, FMF, /*IsInput=*/false);
+            if (FResult.isPoison())
+              return FResult;
+            Result = FResult.asFloat();
+
+            if ((FLHS.isSignaling() || FRHS.isSignaling()) &&
+                (!Ctx.mayUseNonDeterminism() || !Ctx.getRandomBool())) {
+              APFloat QuietLHS = propagateInputNaN(FLHS, FLHS.getSemantics(),
+                                                   /*QuietingMode=*/true,
+                                                   /*FlipSign=*/false);
+              APFloat QuietRHS = propagateInputNaN(FRHS, FRHS.getSemantics(),
+                                                   /*QuietingMode=*/true,
+                                                   /*FlipSign=*/false);
+              APFloat QuietResult = IID == Intrinsic::maxnum
+                                        ? maxnum(QuietLHS, QuietRHS)
+                                        : minnum(QuietLHS, QuietRHS);
+              QuietResult = handleDenormal(QuietResult, DenormMode.Output,
+                                           /*IsInput=*/false);
+              AnyValue FinalResult =
+                  handleFMFFlags(QuietResult, FMF, /*IsInput=*/false);
+              if (FinalResult.isPoison())
+                return FinalResult;
+              QuietResult = FinalResult.asFloat();
+              return applyNaNPropagation(QuietResult, {&QuietLHS, &QuietRHS});
----------------
nikic wrote:

This isn't quite right. The NaN propagation should still be from the original NaN (importantly, sNaN is a valid result for an sNaN input).

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


More information about the llvm-commits mailing list