[llvm] add `frexp`/`ldexp` expansion in `ExpandIrInsts` (PR #208556)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 12:31:34 PDT 2026
================
@@ -546,6 +548,220 @@ static bool expandFRem(BinaryOperator &I, std::optional<SimplifyQuery> &SQ) {
return true;
}
+
+static void expandLdexp(IntrinsicInst *II) {
+ LLVM_DEBUG(dbgs() << "Expanding instruction: " << *II << '\n');
+
+ IRBuilder<> B(II);
+ B.SetCurrentDebugLocation(II->getDebugLoc());
+ LLVMContext &Ctx = II->getContext();
+
+ Type *VT = II->getType();
+ Value *X = II->getArgOperand(0);
+ Value *N = II->getArgOperand(1);
+ Type *ExpVT = N->getType();
+ Type *AsIntVT = B.getIntNTy(VT->getScalarSizeInBits());
+
+ const fltSemantics &FltSem = VT->getFltSemantics();
+
+ const APFloat::ExponentType MaxExpVal = APFloat::semanticsMaxExponent(FltSem);
+ const APFloat::ExponentType MinExpVal = APFloat::semanticsMinExponent(FltSem);
+ const int Precision = APFloat::semanticsPrecision(FltSem);
+
+ Constant *MaxExp = ConstantInt::getSigned(ExpVT, MaxExpVal);
+ Constant *MinExp = ConstantInt::getSigned(ExpVT, MinExpVal);
+
+ Constant *DoubleMaxExp = ConstantInt::getSigned(ExpVT, 2 * MaxExpVal);
+
+ const APFloat One = APFloat::getOne(FltSem);
+ APFloat ScaleUpK = scalbn(One, MaxExpVal, APFloat::rmNearestTiesToEven);
+
+ // Offset by precision to avoid denormal range.
+ APFloat ScaleDownK =
+ scalbn(One, MinExpVal + Precision, APFloat::rmNearestTiesToEven);
+
+ // TODO: Should really introduce control flow and use a block for the >
+ // MaxExp, < MinExp cases
+
+ // First, handle exponents Exp > MaxExp and scale down.
+ Value *NGtMaxExp = B.CreateICmpSGT(N, MaxExp);
+
+ Value *DecN0 = B.CreateNSWSub(N, MaxExp);
+ Constant *ClampMaxVal = ConstantInt::get(ExpVT, 3 * MaxExpVal);
+ Value *ClampN_Big = B.CreateBinaryIntrinsic(Intrinsic::smin, N, ClampMaxVal);
+ Value *DecN1 = B.CreateNSWSub(ClampN_Big, DoubleMaxExp);
+
+ Value *ScaleUpTwice = B.CreateICmpUGT(N, DoubleMaxExp);
+
+ Constant *ScaleUpVal = ConstantFP::get(Ctx, ScaleUpK);
+ Value *ScaleUp0 = B.CreateFMul(X, ScaleUpVal);
+ Value *ScaleUp1 = B.CreateFMul(ScaleUp0, ScaleUpVal);
+
+ Value *SelectN_Big = B.CreateSelect(ScaleUpTwice, DecN1, DecN0);
+ Value *SelectX_Big = B.CreateSelect(ScaleUpTwice, ScaleUp1, ScaleUp0);
+
+ // Now handle exponents Exp < MinExp
+ Value *NLtMinExp = B.CreateICmpSLT(N, MinExp);
+
+ Constant *Increment0 = ConstantInt::get(ExpVT, -(MinExpVal + Precision));
+ Constant *Increment1 = ConstantInt::get(ExpVT, -2 * (MinExpVal + Precision));
+
+ Value *IncN0 =
+ B.CreateAdd(N, Increment0, "", /*HasNUW=*/true, /*HasNSW=*/true);
+
+ Constant *ClampMinVal =
+ ConstantInt::getSigned(ExpVT, 3 * MinExpVal + 2 * Precision);
+ Value *ClampN_Small =
+ B.CreateBinaryIntrinsic(Intrinsic::smax, N, ClampMinVal);
+ Value *IncN1 = B.CreateNSWAdd(ClampN_Small, Increment1);
+
+ Constant *ScaleDownVal = ConstantFP::get(Ctx, ScaleDownK);
+ Value *ScaleDown0 = B.CreateFMul(X, ScaleDownVal);
+ Value *ScaleDown1 = B.CreateFMul(ScaleDown0, ScaleDownVal);
+
+ Value *ScaleDownTwice = B.CreateICmpULT(
+ N, ConstantInt::getSigned(ExpVT, 2 * MinExpVal + Precision));
+
+ Value *SelectN_Small = B.CreateSelect(ScaleDownTwice, IncN1, IncN0);
+ Value *SelectX_Small = B.CreateSelect(ScaleDownTwice, ScaleDown1, ScaleDown0);
+
+ // Now combine the two out of range exponent handling cases with the base
+ // case.
+ Value *NewX = B.CreateSelect(NGtMaxExp, SelectX_Big,
+ B.CreateSelect(NLtMinExp, SelectX_Small, X));
+
+ Value *NewN = B.CreateSelect(NGtMaxExp, SelectN_Big,
+ B.CreateSelect(NLtMinExp, SelectN_Small, N));
+
+ Value *BiasedN = B.CreateNSWAdd(NewN, MaxExp);
+
+ Constant *ExponentShiftAmt = ConstantInt::get(AsIntVT, Precision - 1);
+ Value *CastExpToValTy = B.CreateZExtOrTrunc(BiasedN, AsIntVT);
+
+ Value *AsInt = B.CreateShl(CastExpToValTy, ExponentShiftAmt, "",
+ /*HasNUW=*/true, /*HasNSW=*/true);
+ Value *AsFP = B.CreateBitCast(AsInt, VT);
+ Value *Result = B.CreateFMul(NewX, AsFP);
+
+ II->replaceAllUsesWith(Result);
+ if (auto *RI = dyn_cast<Instruction>(Result))
+ RI->takeName(II);
+ II->eraseFromParent();
+}
+
+static void expandFrexp(IntrinsicInst *II) {
+ LLVM_DEBUG(dbgs() << "Expanding instruction: " << *II << '\n');
+
+ IRBuilder<> B(II);
+ B.SetCurrentDebugLocation(II->getDebugLoc());
+ LLVMContext &Ctx = II->getContext();
+
+ Value *Val = II->getArgOperand(0);
+ auto *RetTy = cast<StructType>(II->getType());
+ Type *VT = Val->getType();
+ Type *ExpVT = RetTy->getElementType(1);
+ Type *AsIntVT = B.getIntNTy(VT->getScalarSizeInBits());
+
+ const fltSemantics &FltSem = VT->getFltSemantics();
+ const APFloat::ExponentType MinExpVal = APFloat::semanticsMinExponent(FltSem);
+ const unsigned Precision = APFloat::semanticsPrecision(FltSem);
+ const unsigned BitSize = VT->getScalarSizeInBits();
+
+ // TODO: Could introduce control flow and skip over the denormal handling.
+
+ // scale_up = fmul value, scalbn(1.0, precision + 1)
+ // extracted_exp = (bitcast value to uint) >> precision - 1
+ // biased_exp = extracted_exp + min_exp
+ // extracted_fract = (bitcast value to uint) & (fract_mask | sign_mask)
+ //
+ // is_denormal = val < smallest_normalized
+ // computed_fract = is_denormal ? scale_up : extracted_fract
+ // computed_exp = is_denormal ? biased_exp + (-precision - 1) : biased_exp
+ //
+ // result_0 = (!isfinite(val) || iszero(val)) ? val : computed_fract
+ // result_1 = (!isfinite(val) || iszero(val)) ? 0 : computed_exp
+
+ Constant *NegSmallestNormalizedInt = ConstantInt::get(
+ AsIntVT, APFloat::getSmallestNormalized(FltSem, true).bitcastToAPInt());
+
+ Constant *SmallestNormalizedInt = ConstantInt::get(
+ AsIntVT, APFloat::getSmallestNormalized(FltSem, false).bitcastToAPInt());
+
+ // Masks out the exponent bits.
+ Constant *ExpMask =
+ ConstantInt::get(AsIntVT, APFloat::getInf(FltSem).bitcastToAPInt());
+
+ // Mask out the exponent part of the value.
+ //
+ // e.g, for f32 FractSignMaskVal = 0x807fffff
+ APInt FractSignMaskVal = APInt::getBitsSet(BitSize, 0, Precision - 1);
+ FractSignMaskVal.setBit(BitSize - 1); // Set the sign bit
+
+ APInt SignMaskVal = APInt::getSignedMaxValue(BitSize);
+ Constant *SignMask = ConstantInt::get(AsIntVT, SignMaskVal);
+
+ Constant *FractSignMask = ConstantInt::get(AsIntVT, FractSignMaskVal);
+
+ const APFloat One(FltSem, "1.0");
----------------
arsenm wrote:
There's a new getOne constructor you should use instead
https://github.com/llvm/llvm-project/pull/208556
More information about the llvm-commits
mailing list