[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