[llvm] add `frexp`/`ldexp` expansion in `ExpandIrInsts` (PR #208556)
Folkert de Vries via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 14 02:46:00 PDT 2026
https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/208556
>From f2448a7990efb1876c3a2c03dfd31a4fb22ed912 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Thu, 9 Jul 2026 13:43:48 +0200
Subject: [PATCH 1/5] don't expand frexp on SPIRV
it has an actual intrinsic for it
---
llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index 64c61d9f6a0c8..ee45db2ec3ac2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -35,6 +35,10 @@ SPIRVTargetLowering::SPIRVTargetLowering(const TargetMachine &TM,
// consider 128-bit OpTypeInt as valid either.
setMaxAtomicSizeInBitsSupported(64);
setMinCmpXchgSizeInBits(8);
+
+ // So that the expansion in ExpandIrInsts does not run.
+ for (MVT VT : {MVT::f16, MVT::f32, MVT::f64})
+ setOperationAction(ISD::FFREXP, VT, Legal);
}
// Returns true of the types logically match, as defined in
>From 0154556851a44ce195ee83fd49f8c9bab61e4b59 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Thu, 9 Jul 2026 13:44:56 +0200
Subject: [PATCH 2/5] Copy the LegalizeDAG versions to ExpandIRInsts for a
better diff
---
llvm/lib/CodeGen/ExpandIRInsts.cpp | 232 +++++++++++++++++++++++++++++
1 file changed, 232 insertions(+)
diff --git a/llvm/lib/CodeGen/ExpandIRInsts.cpp b/llvm/lib/CodeGen/ExpandIRInsts.cpp
index 9bc65f39e6e21..cc5aad7faacfc 100644
--- a/llvm/lib/CodeGen/ExpandIRInsts.cpp
+++ b/llvm/lib/CodeGen/ExpandIRInsts.cpp
@@ -546,6 +546,238 @@ static bool expandFRem(BinaryOperator &I, std::optional<SimplifyQuery> &SQ) {
return true;
}
+
+SDValue SelectionDAGLegalize::expandLdexp(SDNode *Node) const {
+ SDLoc dl(Node);
+ EVT VT = Node->getValueType(0);
+ SDValue X = Node->getOperand(0);
+ SDValue N = Node->getOperand(1);
+ EVT ExpVT = N.getValueType();
+ EVT AsIntVT = VT.changeTypeToInteger();
+ if (AsIntVT == EVT()) // TODO: How to handle f80?
+ return SDValue();
+
+ if (Node->getOpcode() == ISD::STRICT_FLDEXP) // TODO
+ return SDValue();
+
+ SDNodeFlags NSW;
+ NSW.setNoSignedWrap(true);
+ SDNodeFlags NUW_NSW;
+ NUW_NSW.setNoUnsignedWrap(true);
+ NUW_NSW.setNoSignedWrap(true);
+
+ EVT SetCCVT =
+ TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), ExpVT);
+ 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);
+
+ const SDValue MaxExp = DAG.getSignedConstant(MaxExpVal, dl, ExpVT);
+ const SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT);
+
+ const SDValue DoubleMaxExp = DAG.getSignedConstant(2 * MaxExpVal, dl, ExpVT);
+
+ const APFloat One(FltSem, "1.0");
+ 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.
+ SDValue NGtMaxExp = DAG.getSetCC(dl, SetCCVT, N, MaxExp, ISD::SETGT);
+
+ SDValue DecN0 = DAG.getNode(ISD::SUB, dl, ExpVT, N, MaxExp, NSW);
+ SDValue ClampMaxVal = DAG.getConstant(3 * MaxExpVal, dl, ExpVT);
+ SDValue ClampN_Big = DAG.getNode(ISD::SMIN, dl, ExpVT, N, ClampMaxVal);
+ SDValue DecN1 =
+ DAG.getNode(ISD::SUB, dl, ExpVT, ClampN_Big, DoubleMaxExp, NSW);
+
+ SDValue ScaleUpTwice =
+ DAG.getSetCC(dl, SetCCVT, N, DoubleMaxExp, ISD::SETUGT);
+
+ const SDValue ScaleUpVal = DAG.getConstantFP(ScaleUpK, dl, VT);
+ SDValue ScaleUp0 = DAG.getNode(ISD::FMUL, dl, VT, X, ScaleUpVal);
+ SDValue ScaleUp1 = DAG.getNode(ISD::FMUL, dl, VT, ScaleUp0, ScaleUpVal);
+
+ SDValue SelectN_Big =
+ DAG.getNode(ISD::SELECT, dl, ExpVT, ScaleUpTwice, DecN1, DecN0);
+ SDValue SelectX_Big =
+ DAG.getNode(ISD::SELECT, dl, VT, ScaleUpTwice, ScaleUp1, ScaleUp0);
+
+ // Now handle exponents Exp < MinExp
+ SDValue NLtMinExp = DAG.getSetCC(dl, SetCCVT, N, MinExp, ISD::SETLT);
+
+ SDValue Increment0 = DAG.getConstant(-(MinExpVal + Precision), dl, ExpVT);
+ SDValue Increment1 = DAG.getConstant(-2 * (MinExpVal + Precision), dl, ExpVT);
+
+ SDValue IncN0 = DAG.getNode(ISD::ADD, dl, ExpVT, N, Increment0, NUW_NSW);
+
+ SDValue ClampMinVal =
+ DAG.getSignedConstant(3 * MinExpVal + 2 * Precision, dl, ExpVT);
+ SDValue ClampN_Small = DAG.getNode(ISD::SMAX, dl, ExpVT, N, ClampMinVal);
+ SDValue IncN1 =
+ DAG.getNode(ISD::ADD, dl, ExpVT, ClampN_Small, Increment1, NSW);
+
+ const SDValue ScaleDownVal = DAG.getConstantFP(ScaleDownK, dl, VT);
+ SDValue ScaleDown0 = DAG.getNode(ISD::FMUL, dl, VT, X, ScaleDownVal);
+ SDValue ScaleDown1 = DAG.getNode(ISD::FMUL, dl, VT, ScaleDown0, ScaleDownVal);
+
+ SDValue ScaleDownTwice = DAG.getSetCC(
+ dl, SetCCVT, N,
+ DAG.getSignedConstant(2 * MinExpVal + Precision, dl, ExpVT), ISD::SETULT);
+
+ SDValue SelectN_Small =
+ DAG.getNode(ISD::SELECT, dl, ExpVT, ScaleDownTwice, IncN1, IncN0);
+ SDValue SelectX_Small =
+ DAG.getNode(ISD::SELECT, dl, VT, ScaleDownTwice, ScaleDown1, ScaleDown0);
+
+ // Now combine the two out of range exponent handling cases with the base
+ // case.
+ SDValue NewX = DAG.getNode(
+ ISD::SELECT, dl, VT, NGtMaxExp, SelectX_Big,
+ DAG.getNode(ISD::SELECT, dl, VT, NLtMinExp, SelectX_Small, X));
+
+ SDValue NewN = DAG.getNode(
+ ISD::SELECT, dl, ExpVT, NGtMaxExp, SelectN_Big,
+ DAG.getNode(ISD::SELECT, dl, ExpVT, NLtMinExp, SelectN_Small, N));
+
+ SDValue BiasedN = DAG.getNode(ISD::ADD, dl, ExpVT, NewN, MaxExp, NSW);
+
+ SDValue ExponentShiftAmt =
+ DAG.getShiftAmountConstant(Precision - 1, ExpVT, dl);
+ SDValue CastExpToValTy = DAG.getZExtOrTrunc(BiasedN, dl, AsIntVT);
+
+ SDValue AsInt = DAG.getNode(ISD::SHL, dl, AsIntVT, CastExpToValTy,
+ ExponentShiftAmt, NUW_NSW);
+ SDValue AsFP = DAG.getNode(ISD::BITCAST, dl, VT, AsInt);
+ return DAG.getNode(ISD::FMUL, dl, VT, NewX, AsFP);
+}
+
+SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const {
+ SDLoc dl(Node);
+ SDValue Val = Node->getOperand(0);
+ EVT VT = Val.getValueType();
+ EVT ExpVT = Node->getValueType(1);
+ EVT AsIntVT = VT.changeTypeToInteger();
+ if (AsIntVT == EVT()) // TODO: How to handle f80?
+ return SDValue();
+
+ 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
+
+ SDValue NegSmallestNormalizedInt = DAG.getConstant(
+ APFloat::getSmallestNormalized(FltSem, true).bitcastToAPInt(), dl,
+ AsIntVT);
+
+ SDValue SmallestNormalizedInt = DAG.getConstant(
+ APFloat::getSmallestNormalized(FltSem, false).bitcastToAPInt(), dl,
+ AsIntVT);
+
+ // Masks out the exponent bits.
+ SDValue ExpMask =
+ DAG.getConstant(APFloat::getInf(FltSem).bitcastToAPInt(), dl, AsIntVT);
+
+ // 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);
+ SDValue SignMask = DAG.getConstant(SignMaskVal, dl, AsIntVT);
+
+ SDValue FractSignMask = DAG.getConstant(FractSignMaskVal, dl, AsIntVT);
+
+ const APFloat One(FltSem, "1.0");
+ // Scale a possible denormal input.
+ // e.g., for f64, 0x1p+54
+ APFloat ScaleUpKVal =
+ scalbn(One, Precision + 1, APFloat::rmNearestTiesToEven);
+
+ SDValue ScaleUpK = DAG.getConstantFP(ScaleUpKVal, dl, VT);
+ SDValue ScaleUp = DAG.getNode(ISD::FMUL, dl, VT, Val, ScaleUpK);
+
+ EVT SetCCVT =
+ TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+
+ SDValue AsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, Val);
+
+ SDValue Abs = DAG.getNode(ISD::AND, dl, AsIntVT, AsInt, SignMask);
+
+ SDValue AddNegSmallestNormal =
+ DAG.getNode(ISD::ADD, dl, AsIntVT, Abs, NegSmallestNormalizedInt);
+ SDValue DenormOrZero = DAG.getSetCC(dl, SetCCVT, AddNegSmallestNormal,
+ NegSmallestNormalizedInt, ISD::SETULE);
+
+ SDValue IsDenormal =
+ DAG.getSetCC(dl, SetCCVT, Abs, SmallestNormalizedInt, ISD::SETULT);
+
+ SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT);
+ SDValue Zero = DAG.getConstant(0, dl, ExpVT);
+
+ SDValue ScaledAsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, ScaleUp);
+ SDValue ScaledSelect =
+ DAG.getNode(ISD::SELECT, dl, AsIntVT, IsDenormal, ScaledAsInt, AsInt);
+
+ SDValue ExpMaskScaled =
+ DAG.getNode(ISD::AND, dl, AsIntVT, ScaledAsInt, ExpMask);
+
+ SDValue ScaledValue =
+ DAG.getNode(ISD::SELECT, dl, AsIntVT, IsDenormal, ExpMaskScaled, Abs);
+
+ // Extract the exponent bits.
+ SDValue ExponentShiftAmt =
+ DAG.getShiftAmountConstant(Precision - 1, AsIntVT, dl);
+ SDValue ShiftedExp =
+ DAG.getNode(ISD::SRL, dl, AsIntVT, ScaledValue, ExponentShiftAmt);
+ SDValue Exp = DAG.getSExtOrTrunc(ShiftedExp, dl, ExpVT);
+
+ SDValue NormalBiasedExp = DAG.getNode(ISD::ADD, dl, ExpVT, Exp, MinExp);
+ SDValue DenormalOffset = DAG.getConstant(-Precision - 1, dl, ExpVT);
+ SDValue DenormalExpBias =
+ DAG.getNode(ISD::SELECT, dl, ExpVT, IsDenormal, DenormalOffset, Zero);
+
+ SDValue MaskedFractAsInt =
+ DAG.getNode(ISD::AND, dl, AsIntVT, ScaledSelect, FractSignMask);
+ const APFloat Half(FltSem, "0.5");
+ SDValue FPHalf = DAG.getConstant(Half.bitcastToAPInt(), dl, AsIntVT);
+ SDValue Or = DAG.getNode(ISD::OR, dl, AsIntVT, MaskedFractAsInt, FPHalf);
+ SDValue MaskedFract = DAG.getNode(ISD::BITCAST, dl, VT, Or);
+
+ SDValue ComputedExp =
+ DAG.getNode(ISD::ADD, dl, ExpVT, NormalBiasedExp, DenormalExpBias);
+
+ SDValue Result0 =
+ DAG.getNode(ISD::SELECT, dl, VT, DenormOrZero, Val, MaskedFract);
+
+ SDValue Result1 =
+ DAG.getNode(ISD::SELECT, dl, ExpVT, DenormOrZero, Zero, ComputedExp);
+
+ return DAG.getMergeValues({Result0, Result1}, dl);
+}
+
// clang-format off: preserve formatting of the following example
/// Generate code to convert a fp number to integer, replacing FPToS(U)I with
>From 3f5997ae9334c2fd0a7b6caaa372786bdfbd3127 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Wed, 8 Jul 2026 20:40:41 +0200
Subject: [PATCH 3/5] raw conversion to ExpandIRInsts
---
llvm/lib/CodeGen/ExpandIRInsts.cpp | 323 ++++++++++++++++-------------
1 file changed, 176 insertions(+), 147 deletions(-)
diff --git a/llvm/lib/CodeGen/ExpandIRInsts.cpp b/llvm/lib/CodeGen/ExpandIRInsts.cpp
index cc5aad7faacfc..64c7a96133d57 100644
--- a/llvm/lib/CodeGen/ExpandIRInsts.cpp
+++ b/llvm/lib/CodeGen/ExpandIRInsts.cpp
@@ -28,6 +28,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/ExpandIRInsts.h"
+#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/GlobalsModRef.h"
@@ -35,6 +36,7 @@
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/RuntimeLibcallUtil.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
@@ -547,37 +549,29 @@ static bool expandFRem(BinaryOperator &I, std::optional<SimplifyQuery> &SQ) {
return true;
}
-SDValue SelectionDAGLegalize::expandLdexp(SDNode *Node) const {
- SDLoc dl(Node);
- EVT VT = Node->getValueType(0);
- SDValue X = Node->getOperand(0);
- SDValue N = Node->getOperand(1);
- EVT ExpVT = N.getValueType();
- EVT AsIntVT = VT.changeTypeToInteger();
- if (AsIntVT == EVT()) // TODO: How to handle f80?
- return SDValue();
-
- if (Node->getOpcode() == ISD::STRICT_FLDEXP) // TODO
- return SDValue();
-
- SDNodeFlags NSW;
- NSW.setNoSignedWrap(true);
- SDNodeFlags NUW_NSW;
- NUW_NSW.setNoUnsignedWrap(true);
- NUW_NSW.setNoSignedWrap(true);
-
- EVT SetCCVT =
- TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), ExpVT);
- const fltSemantics &FltSem = VT.getFltSemantics();
+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);
- const SDValue MaxExp = DAG.getSignedConstant(MaxExpVal, dl, ExpVT);
- const SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT);
+ Constant *MaxExp = ConstantInt::getSigned(ExpVT, MaxExpVal);
+ Constant *MinExp = ConstantInt::getSigned(ExpVT, MinExpVal);
- const SDValue DoubleMaxExp = DAG.getSignedConstant(2 * MaxExpVal, dl, ExpVT);
+ Constant *DoubleMaxExp = ConstantInt::getSigned(ExpVT, 2 * MaxExpVal);
const APFloat One(FltSem, "1.0");
APFloat ScaleUpK = scalbn(One, MaxExpVal, APFloat::rmNearestTiesToEven);
@@ -590,88 +584,88 @@ SDValue SelectionDAGLegalize::expandLdexp(SDNode *Node) const {
// MaxExp, < MinExp cases
// First, handle exponents Exp > MaxExp and scale down.
- SDValue NGtMaxExp = DAG.getSetCC(dl, SetCCVT, N, MaxExp, ISD::SETGT);
+ Value *NGtMaxExp = B.CreateICmpSGT(N, MaxExp);
- SDValue DecN0 = DAG.getNode(ISD::SUB, dl, ExpVT, N, MaxExp, NSW);
- SDValue ClampMaxVal = DAG.getConstant(3 * MaxExpVal, dl, ExpVT);
- SDValue ClampN_Big = DAG.getNode(ISD::SMIN, dl, ExpVT, N, ClampMaxVal);
- SDValue DecN1 =
- DAG.getNode(ISD::SUB, dl, ExpVT, ClampN_Big, DoubleMaxExp, NSW);
+ 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);
- SDValue ScaleUpTwice =
- DAG.getSetCC(dl, SetCCVT, N, DoubleMaxExp, ISD::SETUGT);
+ Value *ScaleUpTwice = B.CreateICmpUGT(N, DoubleMaxExp);
- const SDValue ScaleUpVal = DAG.getConstantFP(ScaleUpK, dl, VT);
- SDValue ScaleUp0 = DAG.getNode(ISD::FMUL, dl, VT, X, ScaleUpVal);
- SDValue ScaleUp1 = DAG.getNode(ISD::FMUL, dl, VT, ScaleUp0, ScaleUpVal);
+ Constant *ScaleUpVal = ConstantFP::get(Ctx, ScaleUpK);
+ Value *ScaleUp0 = B.CreateFMul(X, ScaleUpVal);
+ Value *ScaleUp1 = B.CreateFMul(ScaleUp0, ScaleUpVal);
- SDValue SelectN_Big =
- DAG.getNode(ISD::SELECT, dl, ExpVT, ScaleUpTwice, DecN1, DecN0);
- SDValue SelectX_Big =
- DAG.getNode(ISD::SELECT, dl, VT, ScaleUpTwice, ScaleUp1, ScaleUp0);
+ Value *SelectN_Big = B.CreateSelect(ScaleUpTwice, DecN1, DecN0);
+ Value *SelectX_Big = B.CreateSelect(ScaleUpTwice, ScaleUp1, ScaleUp0);
// Now handle exponents Exp < MinExp
- SDValue NLtMinExp = DAG.getSetCC(dl, SetCCVT, N, MinExp, ISD::SETLT);
+ Value *NLtMinExp = B.CreateICmpSLT(N, MinExp);
- SDValue Increment0 = DAG.getConstant(-(MinExpVal + Precision), dl, ExpVT);
- SDValue Increment1 = DAG.getConstant(-2 * (MinExpVal + Precision), dl, ExpVT);
+ Constant *Increment0 = ConstantInt::get(ExpVT, -(MinExpVal + Precision));
+ Constant *Increment1 = ConstantInt::get(ExpVT, -2 * (MinExpVal + Precision));
- SDValue IncN0 = DAG.getNode(ISD::ADD, dl, ExpVT, N, Increment0, NUW_NSW);
+ Value *IncN0 =
+ B.CreateAdd(N, Increment0, "", /*HasNUW=*/true, /*HasNSW=*/true);
- SDValue ClampMinVal =
- DAG.getSignedConstant(3 * MinExpVal + 2 * Precision, dl, ExpVT);
- SDValue ClampN_Small = DAG.getNode(ISD::SMAX, dl, ExpVT, N, ClampMinVal);
- SDValue IncN1 =
- DAG.getNode(ISD::ADD, dl, ExpVT, ClampN_Small, Increment1, NSW);
+ 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);
- const SDValue ScaleDownVal = DAG.getConstantFP(ScaleDownK, dl, VT);
- SDValue ScaleDown0 = DAG.getNode(ISD::FMUL, dl, VT, X, ScaleDownVal);
- SDValue ScaleDown1 = DAG.getNode(ISD::FMUL, dl, VT, ScaleDown0, ScaleDownVal);
+ Constant *ScaleDownVal = ConstantFP::get(Ctx, ScaleDownK);
+ Value *ScaleDown0 = B.CreateFMul(X, ScaleDownVal);
+ Value *ScaleDown1 = B.CreateFMul(ScaleDown0, ScaleDownVal);
- SDValue ScaleDownTwice = DAG.getSetCC(
- dl, SetCCVT, N,
- DAG.getSignedConstant(2 * MinExpVal + Precision, dl, ExpVT), ISD::SETULT);
+ Value *ScaleDownTwice = B.CreateICmpULT(
+ N, ConstantInt::getSigned(ExpVT, 2 * MinExpVal + Precision));
- SDValue SelectN_Small =
- DAG.getNode(ISD::SELECT, dl, ExpVT, ScaleDownTwice, IncN1, IncN0);
- SDValue SelectX_Small =
- DAG.getNode(ISD::SELECT, dl, VT, ScaleDownTwice, ScaleDown1, ScaleDown0);
+ 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.
- SDValue NewX = DAG.getNode(
- ISD::SELECT, dl, VT, NGtMaxExp, SelectX_Big,
- DAG.getNode(ISD::SELECT, dl, VT, NLtMinExp, SelectX_Small, X));
+ Value *NewX = B.CreateSelect(NGtMaxExp, SelectX_Big,
+ B.CreateSelect(NLtMinExp, SelectX_Small, X));
- SDValue NewN = DAG.getNode(
- ISD::SELECT, dl, ExpVT, NGtMaxExp, SelectN_Big,
- DAG.getNode(ISD::SELECT, dl, ExpVT, NLtMinExp, SelectN_Small, N));
+ Value *NewN = B.CreateSelect(NGtMaxExp, SelectN_Big,
+ B.CreateSelect(NLtMinExp, SelectN_Small, N));
- SDValue BiasedN = DAG.getNode(ISD::ADD, dl, ExpVT, NewN, MaxExp, NSW);
+ Value *BiasedN = B.CreateNSWAdd(NewN, MaxExp);
- SDValue ExponentShiftAmt =
- DAG.getShiftAmountConstant(Precision - 1, ExpVT, dl);
- SDValue CastExpToValTy = DAG.getZExtOrTrunc(BiasedN, dl, AsIntVT);
+ Constant *ExponentShiftAmt = ConstantInt::get(AsIntVT, Precision - 1);
+ Value *CastExpToValTy = B.CreateZExtOrTrunc(BiasedN, AsIntVT);
- SDValue AsInt = DAG.getNode(ISD::SHL, dl, AsIntVT, CastExpToValTy,
- ExponentShiftAmt, NUW_NSW);
- SDValue AsFP = DAG.getNode(ISD::BITCAST, dl, VT, AsInt);
- return DAG.getNode(ISD::FMUL, dl, VT, NewX, AsFP);
+ 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();
}
-SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const {
- SDLoc dl(Node);
- SDValue Val = Node->getOperand(0);
- EVT VT = Val.getValueType();
- EVT ExpVT = Node->getValueType(1);
- EVT AsIntVT = VT.changeTypeToInteger();
- if (AsIntVT == EVT()) // TODO: How to handle f80?
- return SDValue();
+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 fltSemantics &FltSem = VT->getFltSemantics();
const APFloat::ExponentType MinExpVal = APFloat::semanticsMinExponent(FltSem);
const unsigned Precision = APFloat::semanticsPrecision(FltSem);
- const unsigned BitSize = VT.getScalarSizeInBits();
+ const unsigned BitSize = VT->getScalarSizeInBits();
// TODO: Could introduce control flow and skip over the denormal handling.
@@ -687,17 +681,15 @@ SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const {
// result_0 = (!isfinite(val) || iszero(val)) ? val : computed_fract
// result_1 = (!isfinite(val) || iszero(val)) ? 0 : computed_exp
- SDValue NegSmallestNormalizedInt = DAG.getConstant(
- APFloat::getSmallestNormalized(FltSem, true).bitcastToAPInt(), dl,
- AsIntVT);
+ Constant *NegSmallestNormalizedInt = ConstantInt::get(
+ AsIntVT, APFloat::getSmallestNormalized(FltSem, true).bitcastToAPInt());
- SDValue SmallestNormalizedInt = DAG.getConstant(
- APFloat::getSmallestNormalized(FltSem, false).bitcastToAPInt(), dl,
- AsIntVT);
+ Constant *SmallestNormalizedInt = ConstantInt::get(
+ AsIntVT, APFloat::getSmallestNormalized(FltSem, false).bitcastToAPInt());
// Masks out the exponent bits.
- SDValue ExpMask =
- DAG.getConstant(APFloat::getInf(FltSem).bitcastToAPInt(), dl, AsIntVT);
+ Constant *ExpMask =
+ ConstantInt::get(AsIntVT, APFloat::getInf(FltSem).bitcastToAPInt());
// Mask out the exponent part of the value.
//
@@ -706,9 +698,9 @@ SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const {
FractSignMaskVal.setBit(BitSize - 1); // Set the sign bit
APInt SignMaskVal = APInt::getSignedMaxValue(BitSize);
- SDValue SignMask = DAG.getConstant(SignMaskVal, dl, AsIntVT);
+ Constant *SignMask = ConstantInt::get(AsIntVT, SignMaskVal);
- SDValue FractSignMask = DAG.getConstant(FractSignMaskVal, dl, AsIntVT);
+ Constant *FractSignMask = ConstantInt::get(AsIntVT, FractSignMaskVal);
const APFloat One(FltSem, "1.0");
// Scale a possible denormal input.
@@ -716,68 +708,60 @@ SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const {
APFloat ScaleUpKVal =
scalbn(One, Precision + 1, APFloat::rmNearestTiesToEven);
- SDValue ScaleUpK = DAG.getConstantFP(ScaleUpKVal, dl, VT);
- SDValue ScaleUp = DAG.getNode(ISD::FMUL, dl, VT, Val, ScaleUpK);
+ Constant *ScaleUpK = ConstantFP::get(Ctx, ScaleUpKVal);
+ Value *ScaleUp = B.CreateFMul(Val, ScaleUpK);
- EVT SetCCVT =
- TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+ Value *AsInt = B.CreateBitCast(Val, AsIntVT);
- SDValue AsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, Val);
+ Value *Abs = B.CreateAnd(AsInt, SignMask);
- SDValue Abs = DAG.getNode(ISD::AND, dl, AsIntVT, AsInt, SignMask);
+ Value *AddNegSmallestNormal = B.CreateAdd(Abs, NegSmallestNormalizedInt);
+ Value *DenormOrZero =
+ B.CreateICmpULE(AddNegSmallestNormal, NegSmallestNormalizedInt);
- SDValue AddNegSmallestNormal =
- DAG.getNode(ISD::ADD, dl, AsIntVT, Abs, NegSmallestNormalizedInt);
- SDValue DenormOrZero = DAG.getSetCC(dl, SetCCVT, AddNegSmallestNormal,
- NegSmallestNormalizedInt, ISD::SETULE);
+ Value *IsDenormal = B.CreateICmpULT(Abs, SmallestNormalizedInt);
- SDValue IsDenormal =
- DAG.getSetCC(dl, SetCCVT, Abs, SmallestNormalizedInt, ISD::SETULT);
+ Constant *MinExp = ConstantInt::getSigned(ExpVT, MinExpVal);
+ Constant *Zero = ConstantInt::get(ExpVT, 0);
- SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT);
- SDValue Zero = DAG.getConstant(0, dl, ExpVT);
+ Value *ScaledAsInt = B.CreateBitCast(ScaleUp, AsIntVT);
+ Value *ScaledSelect = B.CreateSelect(IsDenormal, ScaledAsInt, AsInt);
- SDValue ScaledAsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, ScaleUp);
- SDValue ScaledSelect =
- DAG.getNode(ISD::SELECT, dl, AsIntVT, IsDenormal, ScaledAsInt, AsInt);
+ Value *ExpMaskScaled = B.CreateAnd(ScaledAsInt, ExpMask);
- SDValue ExpMaskScaled =
- DAG.getNode(ISD::AND, dl, AsIntVT, ScaledAsInt, ExpMask);
-
- SDValue ScaledValue =
- DAG.getNode(ISD::SELECT, dl, AsIntVT, IsDenormal, ExpMaskScaled, Abs);
+ Value *ScaledValue = B.CreateSelect(IsDenormal, ExpMaskScaled, Abs);
// Extract the exponent bits.
- SDValue ExponentShiftAmt =
- DAG.getShiftAmountConstant(Precision - 1, AsIntVT, dl);
- SDValue ShiftedExp =
- DAG.getNode(ISD::SRL, dl, AsIntVT, ScaledValue, ExponentShiftAmt);
- SDValue Exp = DAG.getSExtOrTrunc(ShiftedExp, dl, ExpVT);
-
- SDValue NormalBiasedExp = DAG.getNode(ISD::ADD, dl, ExpVT, Exp, MinExp);
- SDValue DenormalOffset = DAG.getConstant(-Precision - 1, dl, ExpVT);
- SDValue DenormalExpBias =
- DAG.getNode(ISD::SELECT, dl, ExpVT, IsDenormal, DenormalOffset, Zero);
-
- SDValue MaskedFractAsInt =
- DAG.getNode(ISD::AND, dl, AsIntVT, ScaledSelect, FractSignMask);
+ Constant *ExponentShiftAmt = ConstantInt::get(AsIntVT, Precision - 1);
+ Value *ShiftedExp = B.CreateLShr(ScaledValue, ExponentShiftAmt);
+ Value *Exp = B.CreateSExtOrTrunc(ShiftedExp, ExpVT);
+
+ Value *NormalBiasedExp = B.CreateAdd(Exp, MinExp);
+ Constant *DenormalOffset = ConstantInt::get(ExpVT, -Precision - 1);
+ Value *DenormalExpBias = B.CreateSelect(IsDenormal, DenormalOffset, Zero);
+
+ Value *MaskedFractAsInt = B.CreateAnd(ScaledSelect, FractSignMask);
const APFloat Half(FltSem, "0.5");
- SDValue FPHalf = DAG.getConstant(Half.bitcastToAPInt(), dl, AsIntVT);
- SDValue Or = DAG.getNode(ISD::OR, dl, AsIntVT, MaskedFractAsInt, FPHalf);
- SDValue MaskedFract = DAG.getNode(ISD::BITCAST, dl, VT, Or);
+ Constant *FPHalf = ConstantInt::get(AsIntVT, Half.bitcastToAPInt());
+ Value *Or = B.CreateOr(MaskedFractAsInt, FPHalf);
+ Value *MaskedFract = B.CreateBitCast(Or, VT);
- SDValue ComputedExp =
- DAG.getNode(ISD::ADD, dl, ExpVT, NormalBiasedExp, DenormalExpBias);
+ Value *ComputedExp = B.CreateAdd(NormalBiasedExp, DenormalExpBias);
- SDValue Result0 =
- DAG.getNode(ISD::SELECT, dl, VT, DenormOrZero, Val, MaskedFract);
+ Value *Result0 = B.CreateSelect(DenormOrZero, Val, MaskedFract);
- SDValue Result1 =
- DAG.getNode(ISD::SELECT, dl, ExpVT, DenormOrZero, Zero, ComputedExp);
+ Value *Result1 = B.CreateSelect(DenormOrZero, Zero, ComputedExp);
- return DAG.getMergeValues({Result0, Result1}, dl);
-}
+ // Combine the two results into the { fp, int } aggregate the intrinsic
+ // returns (the SelectionDAG expansion returns a merge_values instead).
+ Value *Res = PoisonValue::get(RetTy);
+ Res = B.CreateInsertValue(Res, Result0, {0});
+ Res = B.CreateInsertValue(Res, Result1, {1});
+ II->replaceAllUsesWith(Res);
+ Res->takeName(II);
+ II->eraseFromParent();
+}
// clang-format off: preserve formatting of the following example
/// Generate code to convert a fp number to integer, replacing FPToS(U)I with
@@ -1508,7 +1492,22 @@ static bool runImpl(Function &F, const TargetLowering &TLI,
MaxLegalDivRemBitWidth >= IntegerType::MAX_INT_BITS;
bool DisableFrem = !FRemExpander::shouldExpandAnyFremType(TLI);
- if (DisableExpandLargeFp && DisableFrem && DisableExpandLargeDivRem)
+ static constexpr std::array ExpandableTypes{MVT::f32, MVT::f64, MVT::f128};
+ auto NeedsExpand = [&](EVT VT, unsigned ISDOp, RTLIB::Libcall LC) {
+ // For f16/bf16 LC is UNKNOWN_LIBCALL, they are handled via promotion.
+ return LC != RTLIB::UNKNOWN_LIBCALL &&
+ TLI.getOperationAction(ISDOp, VT) == TargetLowering::Expand &&
+ Libcalls.getLibcallImpl(LC) == RTLIB::Unsupported;
+ };
+ bool DisableLdexp = none_of(ExpandableTypes, [&](MVT VT) {
+ return NeedsExpand(VT, ISD::FLDEXP, RTLIB::getLDEXP(VT));
+ });
+ bool DisableFrexp = none_of(ExpandableTypes, [&](MVT VT) {
+ return NeedsExpand(VT, ISD::FFREXP, RTLIB::getFREXP(VT));
+ });
+
+ if (DisableExpandLargeFp && DisableFrem && DisableExpandLargeDivRem &&
+ DisableLdexp && DisableFrexp)
return false;
auto ShouldHandleInst = [&](Instruction &I) {
@@ -1543,13 +1542,32 @@ static bool runImpl(Function &F, const TargetLowering &TLI,
MaxLegalDivRemBitWidth;
case Instruction::Call: {
auto *II = dyn_cast<IntrinsicInst>(&I);
- if (II && (II->getIntrinsicID() == Intrinsic::fptoui_sat ||
- II->getIntrinsicID() == Intrinsic::fptosi_sat)) {
+ if (!II)
+ return false;
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::fptoui_sat:
+ case Intrinsic::fptosi_sat:
return !DisableExpandLargeFp &&
cast<IntegerType>(Ty->getScalarType())->getIntegerBitWidth() >
MaxLegalFpConvertBitWidth;
+ case Intrinsic::ldexp: {
+ // the IEEE check skips fp80/ppcf128/vectors.
+ Type *FpTy = II->getArgOperand(0)->getType();
+ if (DisableLdexp || !FpTy->isIEEELikeFPTy())
+ return false;
+ EVT VT = EVT::getEVT(FpTy);
+ return NeedsExpand(VT, ISD::FLDEXP, RTLIB::getLDEXP(VT));
+ }
+ case Intrinsic::frexp: {
+ Type *FpTy = II->getArgOperand(0)->getType();
+ if (DisableFrexp || !FpTy->isIEEELikeFPTy())
+ return false;
+ EVT VT = EVT::getEVT(FpTy);
+ return NeedsExpand(VT, ISD::FFREXP, RTLIB::getFREXP(VT));
+ }
+ default:
+ return false;
}
- return false;
}
}
@@ -1617,10 +1635,21 @@ static bool runImpl(Function &F, const TargetLowering &TLI,
}
case Instruction::Call: {
auto *II = cast<IntrinsicInst>(I);
- assert(II->getIntrinsicID() == Intrinsic::fptoui_sat ||
- II->getIntrinsicID() == Intrinsic::fptosi_sat);
- expandFPToI(I, /*IsSaturating=*/true,
- /*IsSigned=*/II->getIntrinsicID() == Intrinsic::fptosi_sat);
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::ldexp:
+ expandLdexp(II);
+ break;
+ case Intrinsic::frexp:
+ expandFrexp(II);
+ break;
+ case Intrinsic::fptoui_sat:
+ case Intrinsic::fptosi_sat:
+ expandFPToI(I, /*IsSaturating=*/true,
+ /*IsSigned=*/II->getIntrinsicID() == Intrinsic::fptosi_sat);
+ break;
+ default:
+ llvm_unreachable("unexpected intrinsic in ExpandIRInsts worklist");
+ }
break;
}
}
>From 0b9233512bf4f74677fc5c6e637311fe21599736 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Wed, 8 Jul 2026 20:53:32 +0200
Subject: [PATCH 4/5] add f128 tests
---
llvm/test/CodeGen/AArch64/ldexp.ll | 195 ++++++++++
llvm/test/CodeGen/AArch64/llvm.frexp.ll | 144 +++++++
llvm/test/CodeGen/ARM/ldexp.ll | 142 +++++++
llvm/test/CodeGen/X86/ldexp.ll | 487 ++++++++++++++++++++++++
llvm/test/CodeGen/X86/llvm.frexp.ll | 268 +++++++++++++
5 files changed, 1236 insertions(+)
diff --git a/llvm/test/CodeGen/AArch64/ldexp.ll b/llvm/test/CodeGen/AArch64/ldexp.ll
index a18d61aa2553b..4d7ec81b073cd 100644
--- a/llvm/test/CodeGen/AArch64/ldexp.ll
+++ b/llvm/test/CodeGen/AArch64/ldexp.ll
@@ -364,3 +364,198 @@ entry:
%0 = tail call fast bfloat @llvm.ldexp.bf16.i32(bfloat %val, i32 %a)
ret bfloat %0
}
+
+define fp128 @testExpf128(fp128 %val, i32 %a) {
+; SVELINUX-LABEL: testExpf128:
+; SVELINUX: // %bb.0: // %entry
+; SVELINUX-NEXT: b ldexpl
+;
+; GISEL-LABEL: testExpf128:
+; GISEL: // %bb.0: // %entry
+; GISEL-NEXT: b ldexpl
+;
+; SVEWINDOWS-LABEL: testExpf128:
+; SVEWINDOWS: .seh_proc testExpf128
+; SVEWINDOWS-NEXT: // %bb.0: // %entry
+; SVEWINDOWS-NEXT: sub sp, sp, #128
+; SVEWINDOWS-NEXT: .seh_stackalloc 128
+; SVEWINDOWS-NEXT: stp x19, x20, [sp, #80] // 16-byte Folded Spill
+; SVEWINDOWS-NEXT: .seh_save_regp x19, 80
+; SVEWINDOWS-NEXT: stp x21, x22, [sp, #96] // 16-byte Folded Spill
+; SVEWINDOWS-NEXT: .seh_save_regp x21, 96
+; SVEWINDOWS-NEXT: str x30, [sp, #112] // 8-byte Spill
+; SVEWINDOWS-NEXT: .seh_save_reg x30, 112
+; SVEWINDOWS-NEXT: .seh_endprologue
+; SVEWINDOWS-NEXT: mov w8, #49149 // =0xbffd
+; SVEWINDOWS-NEXT: mov w10, #-32766 // =0xffff8002
+; SVEWINDOWS-NEXT: mov w9, #-16383 // =0xffffc001
+; SVEWINDOWS-NEXT: cmp w0, w8
+; SVEWINDOWS-NEXT: mov w19, w0
+; SVEWINDOWS-NEXT: add w20, w0, w9
+; SVEWINDOWS-NEXT: csel w8, w0, w8, lt
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: add w21, w8, w10
+; SVEWINDOWS-NEXT: adrp x8, "__xmm at 7ffe0000000000000000000000000000"
+; SVEWINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 7ffe0000000000000000000000000000"]
+; SVEWINDOWS-NEXT: str q1, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: ldr q1, [sp, #16] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: mov w8, #32766 // =0x7ffe
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: csel w20, w21, w20, hi
+; SVEWINDOWS-NEXT: b.ls .LBB9_2
+; SVEWINDOWS-NEXT: // %bb.1: // %entry
+; SVEWINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_2: // %entry
+; SVEWINDOWS-NEXT: mov w8, #-48920 // =0xffff40e8
+; SVEWINDOWS-NEXT: mov w10, #32538 // =0x7f1a
+; SVEWINDOWS-NEXT: ldr q0, [sp, #48] // 16-byte Reload
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: mov w9, #16269 // =0x3f8d
+; SVEWINDOWS-NEXT: csel w8, w19, w8, gt
+; SVEWINDOWS-NEXT: add w21, w19, w9
+; SVEWINDOWS-NEXT: add w22, w8, w10
+; SVEWINDOWS-NEXT: adrp x8, "__xmm at 00720000000000000000000000000000"
+; SVEWINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 00720000000000000000000000000000"]
+; SVEWINDOWS-NEXT: str q1, [sp] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: mov w8, #-32651 // =0xffff8075
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: csel w8, w22, w21, lo
+; SVEWINDOWS-NEXT: b.hs .LBB9_4
+; SVEWINDOWS-NEXT: // %bb.3: // %entry
+; SVEWINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_4: // %entry
+; SVEWINDOWS-NEXT: mov w9, #-16382 // =0xffffc002
+; SVEWINDOWS-NEXT: cmp w19, w9
+; SVEWINDOWS-NEXT: b.ge .LBB9_6
+; SVEWINDOWS-NEXT: // %bb.5: // %entry
+; SVEWINDOWS-NEXT: ldr q0, [sp, #16] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_6: // %entry
+; SVEWINDOWS-NEXT: csel w8, w8, w19, lt
+; SVEWINDOWS-NEXT: cmp w19, #4, lsl #12 // =16384
+; SVEWINDOWS-NEXT: b.lt .LBB9_8
+; SVEWINDOWS-NEXT: // %bb.7: // %entry
+; SVEWINDOWS-NEXT: ldr q0, [sp, #32] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_8: // %entry
+; SVEWINDOWS-NEXT: csel w8, w20, w8, ge
+; SVEWINDOWS-NEXT: mov w9, #16383 // =0x3fff
+; SVEWINDOWS-NEXT: add w8, w8, w9
+; SVEWINDOWS-NEXT: lsl x8, x8, #48
+; SVEWINDOWS-NEXT: stp xzr, x8, [sp, #64]
+; SVEWINDOWS-NEXT: ldp q0, q1, [sp, #48] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_startepilogue
+; SVEWINDOWS-NEXT: ldr x30, [sp, #112] // 8-byte Reload
+; SVEWINDOWS-NEXT: .seh_save_reg x30, 112
+; SVEWINDOWS-NEXT: ldp x21, x22, [sp, #96] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_save_regp x21, 96
+; SVEWINDOWS-NEXT: ldp x19, x20, [sp, #80] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_save_regp x19, 80
+; SVEWINDOWS-NEXT: add sp, sp, #128
+; SVEWINDOWS-NEXT: .seh_stackalloc 128
+; SVEWINDOWS-NEXT: .seh_endepilogue
+; SVEWINDOWS-NEXT: b __multf3
+; SVEWINDOWS-NEXT: .seh_endfunclet
+; SVEWINDOWS-NEXT: .seh_endproc
+;
+; WINDOWS-LABEL: testExpf128:
+; WINDOWS: .seh_proc testExpf128
+; WINDOWS-NEXT: // %bb.0: // %entry
+; WINDOWS-NEXT: sub sp, sp, #128
+; WINDOWS-NEXT: .seh_stackalloc 128
+; WINDOWS-NEXT: stp x19, x20, [sp, #80] // 16-byte Folded Spill
+; WINDOWS-NEXT: .seh_save_regp x19, 80
+; WINDOWS-NEXT: stp x21, x22, [sp, #96] // 16-byte Folded Spill
+; WINDOWS-NEXT: .seh_save_regp x21, 96
+; WINDOWS-NEXT: str x30, [sp, #112] // 8-byte Spill
+; WINDOWS-NEXT: .seh_save_reg x30, 112
+; WINDOWS-NEXT: .seh_endprologue
+; WINDOWS-NEXT: mov w8, #49149 // =0xbffd
+; WINDOWS-NEXT: mov w10, #-32766 // =0xffff8002
+; WINDOWS-NEXT: mov w9, #-16383 // =0xffffc001
+; WINDOWS-NEXT: cmp w0, w8
+; WINDOWS-NEXT: mov w19, w0
+; WINDOWS-NEXT: add w20, w0, w9
+; WINDOWS-NEXT: csel w8, w0, w8, lt
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: add w21, w8, w10
+; WINDOWS-NEXT: adrp x8, "__xmm at 7ffe0000000000000000000000000000"
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 7ffe0000000000000000000000000000"]
+; WINDOWS-NEXT: str q1, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp, #16] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: mov w8, #32766 // =0x7ffe
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: csel w20, w21, w20, hi
+; WINDOWS-NEXT: b.ls .LBB9_2
+; WINDOWS-NEXT: // %bb.1: // %entry
+; WINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_2: // %entry
+; WINDOWS-NEXT: mov w8, #-48920 // =0xffff40e8
+; WINDOWS-NEXT: mov w10, #32538 // =0x7f1a
+; WINDOWS-NEXT: ldr q0, [sp, #48] // 16-byte Reload
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: mov w9, #16269 // =0x3f8d
+; WINDOWS-NEXT: csel w8, w19, w8, gt
+; WINDOWS-NEXT: add w21, w19, w9
+; WINDOWS-NEXT: add w22, w8, w10
+; WINDOWS-NEXT: adrp x8, "__xmm at 00720000000000000000000000000000"
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 00720000000000000000000000000000"]
+; WINDOWS-NEXT: str q1, [sp] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: mov w8, #-32651 // =0xffff8075
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: csel w8, w22, w21, lo
+; WINDOWS-NEXT: b.hs .LBB9_4
+; WINDOWS-NEXT: // %bb.3: // %entry
+; WINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_4: // %entry
+; WINDOWS-NEXT: mov w9, #-16382 // =0xffffc002
+; WINDOWS-NEXT: cmp w19, w9
+; WINDOWS-NEXT: b.ge .LBB9_6
+; WINDOWS-NEXT: // %bb.5: // %entry
+; WINDOWS-NEXT: ldr q0, [sp, #16] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_6: // %entry
+; WINDOWS-NEXT: csel w8, w8, w19, lt
+; WINDOWS-NEXT: cmp w19, #4, lsl #12 // =16384
+; WINDOWS-NEXT: b.lt .LBB9_8
+; WINDOWS-NEXT: // %bb.7: // %entry
+; WINDOWS-NEXT: ldr q0, [sp, #32] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_8: // %entry
+; WINDOWS-NEXT: csel w8, w20, w8, ge
+; WINDOWS-NEXT: mov w9, #16383 // =0x3fff
+; WINDOWS-NEXT: add w8, w8, w9
+; WINDOWS-NEXT: lsl x8, x8, #48
+; WINDOWS-NEXT: stp xzr, x8, [sp, #64]
+; WINDOWS-NEXT: ldp q0, q1, [sp, #48] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_startepilogue
+; WINDOWS-NEXT: ldr x30, [sp, #112] // 8-byte Reload
+; WINDOWS-NEXT: .seh_save_reg x30, 112
+; WINDOWS-NEXT: ldp x21, x22, [sp, #96] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_save_regp x21, 96
+; WINDOWS-NEXT: ldp x19, x20, [sp, #80] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_save_regp x19, 80
+; WINDOWS-NEXT: add sp, sp, #128
+; WINDOWS-NEXT: .seh_stackalloc 128
+; WINDOWS-NEXT: .seh_endepilogue
+; WINDOWS-NEXT: b __multf3
+; WINDOWS-NEXT: .seh_endfunclet
+; WINDOWS-NEXT: .seh_endproc
+entry:
+ %ldexp = call fp128 @llvm.ldexp.f128.i32(fp128 %val, i32 %a)
+ ret fp128 %ldexp
+}
diff --git a/llvm/test/CodeGen/AArch64/llvm.frexp.ll b/llvm/test/CodeGen/AArch64/llvm.frexp.ll
index 2729b9bc233fa..0ce2a4f85e042 100644
--- a/llvm/test/CodeGen/AArch64/llvm.frexp.ll
+++ b/llvm/test/CodeGen/AArch64/llvm.frexp.ll
@@ -1181,4 +1181,148 @@ define <2 x i32> @test_frexp_v2f64_v2i32_only_use_exp(<2 x double> %a) nounwind
ret <2 x i32> %result.1
}
+define { fp128, i32 } @test_frexp_f128_i32(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT: add x0, sp, #12
+; CHECK-NEXT: bl frexpl
+; CHECK-NEXT: ldr w0, [sp, #12]
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT: ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32:
+; WINDOWS: // %bb.0:
+; WINDOWS-NEXT: sub sp, sp, #80
+; WINDOWS-NEXT: adrp x8, "__xmm at 40710000000000000000000000000000"
+; WINDOWS-NEXT: str x30, [sp, #64] // 8-byte Spill
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 40710000000000000000000000000000"]
+; WINDOWS-NEXT: str q0, [sp] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; WINDOWS-NEXT: mov x10, #-9223090561878065152 // =0x8001000000000000
+; WINDOWS-NEXT: mov w15, #-114 // =0xffffff8e
+; WINDOWS-NEXT: stp q1, q0, [sp, #32]
+; WINDOWS-NEXT: ldp x9, x8, [sp, #32]
+; WINDOWS-NEXT: ldp x13, x12, [sp, #48]
+; WINDOWS-NEXT: ubfx x11, x8, #48, #15
+; WINDOWS-NEXT: and x14, x8, #0x7fffffffffffffff
+; WINDOWS-NEXT: cmp x11, #0
+; WINDOWS-NEXT: add x11, x14, x10
+; WINDOWS-NEXT: csel x8, x12, x8, eq
+; WINDOWS-NEXT: and x12, x12, #0x7fff000000000000
+; WINDOWS-NEXT: csel x13, x13, x9, eq
+; WINDOWS-NEXT: csel x12, x12, x14, eq
+; WINDOWS-NEXT: csel w14, w15, wzr, eq
+; WINDOWS-NEXT: and x8, x8, #0x8000ffffffffffff
+; WINDOWS-NEXT: cmp x9, #1
+; WINDOWS-NEXT: add x9, x14, x12, lsr #48
+; WINDOWS-NEXT: orr x8, x8, #0x3ffe000000000000
+; WINDOWS-NEXT: mov w12, #-16382 // =0xffffc002
+; WINDOWS-NEXT: sbcs xzr, x11, x10
+; WINDOWS-NEXT: stp x13, x8, [sp, #16]
+; WINDOWS-NEXT: add w8, w9, w12
+; WINDOWS-NEXT: b.lo .LBB24_2
+; WINDOWS-NEXT: // %bb.1:
+; WINDOWS-NEXT: ldr q1, [sp, #16]
+; WINDOWS-NEXT: .LBB24_2:
+; WINDOWS-NEXT: ldr x30, [sp, #64] // 8-byte Reload
+; WINDOWS-NEXT: csel w0, wzr, w8, lo
+; WINDOWS-NEXT: mov v0.16b, v1.16b
+; WINDOWS-NEXT: add sp, sp, #80
+; WINDOWS-NEXT: ret
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ ret { fp128, i32 } %result
+}
+
+define fp128 @test_frexp_f128_i32_only_use_fract(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_fract:
+; CHECK: // %bb.0:
+; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT: add x0, sp, #12
+; CHECK-NEXT: bl frexpl
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT: ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32_only_use_fract:
+; WINDOWS: // %bb.0:
+; WINDOWS-NEXT: sub sp, sp, #80
+; WINDOWS-NEXT: adrp x8, "__xmm at 40710000000000000000000000000000"
+; WINDOWS-NEXT: str x30, [sp, #64] // 8-byte Spill
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 40710000000000000000000000000000"]
+; WINDOWS-NEXT: str q0, [sp] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; WINDOWS-NEXT: mov x13, #-9223090561878065152 // =0x8001000000000000
+; WINDOWS-NEXT: stp q1, q0, [sp, #32]
+; WINDOWS-NEXT: ldp x9, x8, [sp, #32]
+; WINDOWS-NEXT: ldp x14, x12, [sp, #48]
+; WINDOWS-NEXT: ubfx x10, x8, #48, #15
+; WINDOWS-NEXT: and x11, x8, #0x7fffffffffffffff
+; WINDOWS-NEXT: cmp x10, #0
+; WINDOWS-NEXT: add x10, x11, x13
+; WINDOWS-NEXT: csel x8, x12, x8, eq
+; WINDOWS-NEXT: csel x11, x14, x9, eq
+; WINDOWS-NEXT: cmp x9, #1
+; WINDOWS-NEXT: and x8, x8, #0x8000ffffffffffff
+; WINDOWS-NEXT: sbcs xzr, x10, x13
+; WINDOWS-NEXT: orr x8, x8, #0x3ffe000000000000
+; WINDOWS-NEXT: stp x11, x8, [sp, #16]
+; WINDOWS-NEXT: b.lo .LBB25_2
+; WINDOWS-NEXT: // %bb.1:
+; WINDOWS-NEXT: ldr q1, [sp, #16]
+; WINDOWS-NEXT: .LBB25_2:
+; WINDOWS-NEXT: ldr x30, [sp, #64] // 8-byte Reload
+; WINDOWS-NEXT: mov v0.16b, v1.16b
+; WINDOWS-NEXT: add sp, sp, #80
+; WINDOWS-NEXT: ret
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ %result.0 = extractvalue { fp128, i32 } %result, 0
+ ret fp128 %result.0
+}
+
+define i32 @test_frexp_f128_i32_only_use_exp(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_exp:
+; CHECK: // %bb.0:
+; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT: add x0, sp, #12
+; CHECK-NEXT: bl frexpl
+; CHECK-NEXT: ldr w0, [sp, #12]
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT: ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32_only_use_exp:
+; WINDOWS: // %bb.0:
+; WINDOWS-NEXT: sub sp, sp, #64
+; WINDOWS-NEXT: adrp x8, "__xmm at 40710000000000000000000000000000"
+; WINDOWS-NEXT: str x30, [sp, #48] // 8-byte Spill
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 40710000000000000000000000000000"]
+; WINDOWS-NEXT: str q0, [sp] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; WINDOWS-NEXT: mov x12, #-9223090561878065152 // =0x8001000000000000
+; WINDOWS-NEXT: stp q1, q0, [sp, #16]
+; WINDOWS-NEXT: ldp x9, x8, [sp, #16]
+; WINDOWS-NEXT: ldp x11, x30, [sp, #40] // 8-byte Folded Reload
+; WINDOWS-NEXT: ubfx x10, x8, #48, #15
+; WINDOWS-NEXT: and x8, x8, #0x7fffffffffffffff
+; WINDOWS-NEXT: and x11, x11, #0x7fff000000000000
+; WINDOWS-NEXT: cmp x10, #0
+; WINDOWS-NEXT: mov w10, #-114 // =0xffffff8e
+; WINDOWS-NEXT: csel x11, x11, x8, eq
+; WINDOWS-NEXT: csel w10, w10, wzr, eq
+; WINDOWS-NEXT: add x8, x8, x12
+; WINDOWS-NEXT: add x10, x10, x11, lsr #48
+; WINDOWS-NEXT: mov w11, #-16382 // =0xffffc002
+; WINDOWS-NEXT: cmp x9, #1
+; WINDOWS-NEXT: sbcs xzr, x8, x12
+; WINDOWS-NEXT: add w9, w10, w11
+; WINDOWS-NEXT: csel w0, wzr, w9, lo
+; WINDOWS-NEXT: add sp, sp, #64
+; WINDOWS-NEXT: ret
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ %result.0 = extractvalue { fp128, i32 } %result, 1
+ ret i32 %result.0
+}
+
attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
diff --git a/llvm/test/CodeGen/ARM/ldexp.ll b/llvm/test/CodeGen/ARM/ldexp.ll
index cdf91eb902e05..31e351b48dcf0 100644
--- a/llvm/test/CodeGen/ARM/ldexp.ll
+++ b/llvm/test/CodeGen/ARM/ldexp.ll
@@ -56,3 +56,145 @@ entry:
}
declare half @llvm.ldexp.f16.i32(half, i32) memory(none)
+
+define fp128 @testExpf128(fp128 %val, i32 %a) {
+; LINUX-LABEL: testExpf128:
+; LINUX: @ %bb.0: @ %entry
+; LINUX-NEXT: push {r11, lr}
+; LINUX-NEXT: sub sp, sp, #8
+; LINUX-NEXT: ldr r12, [sp, #16]
+; LINUX-NEXT: str r12, [sp]
+; LINUX-NEXT: bl ldexpl
+; LINUX-NEXT: add sp, sp, #8
+; LINUX-NEXT: pop {r11, pc}
+;
+; WINDOWS-LABEL: testExpf128:
+; WINDOWS: @ %bb.0: @ %entry
+; WINDOWS-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, r11, lr}
+; WINDOWS-NEXT: .seh_save_regs_w {r4-r11, lr}
+; WINDOWS-NEXT: sub sp, #68
+; WINDOWS-NEXT: .seh_stackalloc 68
+; WINDOWS-NEXT: .seh_endprologue
+; WINDOWS-NEXT: movw r8, #0
+; WINDOWS-NEXT: movs r5, #0
+; WINDOWS-NEXT: movt r8, #32766
+; WINDOWS-NEXT: strd r5, r5, [sp]
+; WINDOWS-NEXT: strd r5, r8, [sp, #8]
+; WINDOWS-NEXT: mov r4, r0
+; WINDOWS-NEXT: str r0, [sp, #28] @ 4-byte Spill
+; WINDOWS-NEXT: mov r7, r1
+; WINDOWS-NEXT: str r1, [sp, #32] @ 4-byte Spill
+; WINDOWS-NEXT: mov r6, r2
+; WINDOWS-NEXT: str r2, [sp, #24] @ 4-byte Spill
+; WINDOWS-NEXT: mov r9, r3
+; WINDOWS-NEXT: str r3, [sp, #20] @ 4-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: str r0, [sp, #64] @ 4-byte Spill
+; WINDOWS-NEXT: strd r2, r1, [sp, #56] @ 8-byte Folded Spill
+; WINDOWS-NEXT: str r3, [sp, #44] @ 4-byte Spill
+; WINDOWS-NEXT: strd r5, r5, [sp]
+; WINDOWS-NEXT: strd r5, r8, [sp, #8]
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: strd r1, r0, [sp, #48] @ 8-byte Folded Spill
+; WINDOWS-NEXT: mov.w r10, #7471104
+; WINDOWS-NEXT: strd r3, r2, [sp, #36] @ 8-byte Folded Spill
+; WINDOWS-NEXT: mov r0, r4
+; WINDOWS-NEXT: mov r1, r7
+; WINDOWS-NEXT: mov r2, r6
+; WINDOWS-NEXT: mov r3, r9
+; WINDOWS-NEXT: strd r5, r5, [sp]
+; WINDOWS-NEXT: strd r5, r10, [sp, #8]
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: mov r11, r0
+; WINDOWS-NEXT: mov r4, r1
+; WINDOWS-NEXT: mov r8, r2
+; WINDOWS-NEXT: mov r9, r3
+; WINDOWS-NEXT: strd r5, r5, [sp]
+; WINDOWS-NEXT: strd r5, r10, [sp, #8]
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr.w r10, [sp, #104]
+; WINDOWS-NEXT: movw r6, #16616
+; WINDOWS-NEXT: movw r7, #32885
+; WINDOWS-NEXT: movt r6, #65535
+; WINDOWS-NEXT: cmp r10, r6
+; WINDOWS-NEXT: movt r7, #65535
+; WINDOWS-NEXT: str r5, [sp, #8]
+; WINDOWS-NEXT: movw r12, #32766
+; WINDOWS-NEXT: strd r5, r5, [sp]
+; WINDOWS-NEXT: it gt
+; WINDOWS-NEXT: movgt r6, r10
+; WINDOWS-NEXT: cmp r10, r7
+; WINDOWS-NEXT: movw r7, #16269
+; WINDOWS-NEXT: add r7, r10
+; WINDOWS-NEXT: itttt hs
+; WINDOWS-NEXT: movhs r1, r4
+; WINDOWS-NEXT: movhs r0, r11
+; WINDOWS-NEXT: movhs r2, r8
+; WINDOWS-NEXT: movhs r3, r9
+; WINDOWS-NEXT: movw r4, #32538
+; WINDOWS-NEXT: it lo
+; WINDOWS-NEXT: addlo r7, r6, r4
+; WINDOWS-NEXT: movw r6, #49154
+; WINDOWS-NEXT: movw lr, #16383
+; WINDOWS-NEXT: movt r6, #65535
+; WINDOWS-NEXT: cmp r10, r6
+; WINDOWS-NEXT: ldr r6, [sp, #20] @ 4-byte Reload
+; WINDOWS-NEXT: it ge
+; WINDOWS-NEXT: movge r3, r6
+; WINDOWS-NEXT: ldr r6, [sp, #24] @ 4-byte Reload
+; WINDOWS-NEXT: it ge
+; WINDOWS-NEXT: movge r2, r6
+; WINDOWS-NEXT: ldr r6, [sp, #28] @ 4-byte Reload
+; WINDOWS-NEXT: it ge
+; WINDOWS-NEXT: movge r0, r6
+; WINDOWS-NEXT: ldr r6, [sp, #32] @ 4-byte Reload
+; WINDOWS-NEXT: itt ge
+; WINDOWS-NEXT: movge r1, r6
+; WINDOWS-NEXT: movge r7, r10
+; WINDOWS-NEXT: movw r6, #49149
+; WINDOWS-NEXT: cmp r10, r6
+; WINDOWS-NEXT: it lt
+; WINDOWS-NEXT: movlt r6, r10
+; WINDOWS-NEXT: ldr.w r8, [sp, #36] @ 4-byte Reload
+; WINDOWS-NEXT: cmp r10, r12
+; WINDOWS-NEXT: ldr r5, [sp, #44] @ 4-byte Reload
+; WINDOWS-NEXT: it ls
+; WINDOWS-NEXT: movls r8, r5
+; WINDOWS-NEXT: ldr.w r9, [sp, #40] @ 4-byte Reload
+; WINDOWS-NEXT: ldr r5, [sp, #56] @ 4-byte Reload
+; WINDOWS-NEXT: it ls
+; WINDOWS-NEXT: movls r9, r5
+; WINDOWS-NEXT: ldr.w r11, [sp, #48] @ 4-byte Reload
+; WINDOWS-NEXT: ldr r4, [sp, #60] @ 4-byte Reload
+; WINDOWS-NEXT: it ls
+; WINDOWS-NEXT: movls r11, r4
+; WINDOWS-NEXT: ldr r4, [sp, #64] @ 4-byte Reload
+; WINDOWS-NEXT: ldr r5, [sp, #52] @ 4-byte Reload
+; WINDOWS-NEXT: it ls
+; WINDOWS-NEXT: movls r5, r4
+; WINDOWS-NEXT: sub.w r4, r10, lr
+; WINDOWS-NEXT: it hi
+; WINDOWS-NEXT: subhi.w r4, r6, r12
+; WINDOWS-NEXT: cmp.w r10, #16384
+; WINDOWS-NEXT: it lt
+; WINDOWS-NEXT: movlt r4, r7
+; WINDOWS-NEXT: add.w r7, r4, lr
+; WINDOWS-NEXT: lsl.w r7, r7, #16
+; WINDOWS-NEXT: str r7, [sp, #12]
+; WINDOWS-NEXT: itttt ge
+; WINDOWS-NEXT: movge r0, r5
+; WINDOWS-NEXT: movge r1, r11
+; WINDOWS-NEXT: movge r2, r9
+; WINDOWS-NEXT: movge r3, r8
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: .seh_startepilogue
+; WINDOWS-NEXT: add sp, #68
+; WINDOWS-NEXT: .seh_stackalloc 68
+; WINDOWS-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+; WINDOWS-NEXT: .seh_save_regs_w {r4-r11, lr}
+; WINDOWS-NEXT: .seh_endepilogue
+; WINDOWS-NEXT: .seh_endproc
+entry:
+ %0 = tail call fp128 @llvm.ldexp.f128.i32(fp128 %val, i32 %a)
+ ret fp128 %0
+}
diff --git a/llvm/test/CodeGen/X86/ldexp.ll b/llvm/test/CodeGen/X86/ldexp.ll
index b6f2793c4b1f7..1f023d4f60003 100644
--- a/llvm/test/CodeGen/X86/ldexp.ll
+++ b/llvm/test/CodeGen/X86/ldexp.ll
@@ -592,5 +592,492 @@ define <4 x double> @ldexp_v4f64(<4 x double> %val, <4 x i32> %exp) nounwind {
ret <4 x double> %1
}
+define fp128 @testExpf128(fp128 %val, i32 %a) {
+; SVELINUX-LABEL: testExpf128:
+; SVELINUX: // %bb.0: // %entry
+; SVELINUX-NEXT: b ldexpl
+;
+; GISEL-LABEL: testExpf128:
+; GISEL: // %bb.0: // %entry
+; GISEL-NEXT: b ldexpl
+;
+; SVEWINDOWS-LABEL: testExpf128:
+; SVEWINDOWS: .seh_proc testExpf128
+; SVEWINDOWS-NEXT: // %bb.0: // %entry
+; SVEWINDOWS-NEXT: sub sp, sp, #128
+; SVEWINDOWS-NEXT: .seh_stackalloc 128
+; SVEWINDOWS-NEXT: stp x19, x20, [sp, #80] // 16-byte Folded Spill
+; SVEWINDOWS-NEXT: .seh_save_regp x19, 80
+; SVEWINDOWS-NEXT: stp x21, x22, [sp, #96] // 16-byte Folded Spill
+; SVEWINDOWS-NEXT: .seh_save_regp x21, 96
+; SVEWINDOWS-NEXT: str x30, [sp, #112] // 8-byte Spill
+; SVEWINDOWS-NEXT: .seh_save_reg x30, 112
+; SVEWINDOWS-NEXT: .seh_endprologue
+; SVEWINDOWS-NEXT: mov w8, #49149 // =0xbffd
+; SVEWINDOWS-NEXT: mov w10, #-32766 // =0xffff8002
+; SVEWINDOWS-NEXT: mov w9, #-16383 // =0xffffc001
+; SVEWINDOWS-NEXT: cmp w0, w8
+; SVEWINDOWS-NEXT: mov w19, w0
+; SVEWINDOWS-NEXT: add w20, w0, w9
+; SVEWINDOWS-NEXT: csel w8, w0, w8, lt
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: add w21, w8, w10
+; SVEWINDOWS-NEXT: adrp x8, "__xmm at 7ffe0000000000000000000000000000"
+; SVEWINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 7ffe0000000000000000000000000000"]
+; SVEWINDOWS-NEXT: str q1, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: ldr q1, [sp, #16] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: mov w8, #32766 // =0x7ffe
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: csel w20, w21, w20, hi
+; SVEWINDOWS-NEXT: b.ls .LBB9_2
+; SVEWINDOWS-NEXT: // %bb.1: // %entry
+; SVEWINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_2: // %entry
+; SVEWINDOWS-NEXT: mov w8, #-48920 // =0xffff40e8
+; SVEWINDOWS-NEXT: mov w10, #32538 // =0x7f1a
+; SVEWINDOWS-NEXT: ldr q0, [sp, #48] // 16-byte Reload
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: mov w9, #16269 // =0x3f8d
+; SVEWINDOWS-NEXT: csel w8, w19, w8, gt
+; SVEWINDOWS-NEXT: add w21, w19, w9
+; SVEWINDOWS-NEXT: add w22, w8, w10
+; SVEWINDOWS-NEXT: adrp x8, "__xmm at 00720000000000000000000000000000"
+; SVEWINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 00720000000000000000000000000000"]
+; SVEWINDOWS-NEXT: str q1, [sp] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: bl __multf3
+; SVEWINDOWS-NEXT: mov w8, #-32651 // =0xffff8075
+; SVEWINDOWS-NEXT: cmp w19, w8
+; SVEWINDOWS-NEXT: csel w8, w22, w21, lo
+; SVEWINDOWS-NEXT: b.hs .LBB9_4
+; SVEWINDOWS-NEXT: // %bb.3: // %entry
+; SVEWINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_4: // %entry
+; SVEWINDOWS-NEXT: mov w9, #-16382 // =0xffffc002
+; SVEWINDOWS-NEXT: cmp w19, w9
+; SVEWINDOWS-NEXT: b.ge .LBB9_6
+; SVEWINDOWS-NEXT: // %bb.5: // %entry
+; SVEWINDOWS-NEXT: ldr q0, [sp, #16] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_6: // %entry
+; SVEWINDOWS-NEXT: csel w8, w8, w19, lt
+; SVEWINDOWS-NEXT: cmp w19, #4, lsl #12 // =16384
+; SVEWINDOWS-NEXT: b.lt .LBB9_8
+; SVEWINDOWS-NEXT: // %bb.7: // %entry
+; SVEWINDOWS-NEXT: ldr q0, [sp, #32] // 16-byte Reload
+; SVEWINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; SVEWINDOWS-NEXT: .LBB9_8: // %entry
+; SVEWINDOWS-NEXT: csel w8, w20, w8, ge
+; SVEWINDOWS-NEXT: mov w9, #16383 // =0x3fff
+; SVEWINDOWS-NEXT: add w8, w8, w9
+; SVEWINDOWS-NEXT: lsl x8, x8, #48
+; SVEWINDOWS-NEXT: stp xzr, x8, [sp, #64]
+; SVEWINDOWS-NEXT: ldp q0, q1, [sp, #48] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_startepilogue
+; SVEWINDOWS-NEXT: ldr x30, [sp, #112] // 8-byte Reload
+; SVEWINDOWS-NEXT: .seh_save_reg x30, 112
+; SVEWINDOWS-NEXT: ldp x21, x22, [sp, #96] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_save_regp x21, 96
+; SVEWINDOWS-NEXT: ldp x19, x20, [sp, #80] // 16-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_save_regp x19, 80
+; SVEWINDOWS-NEXT: add sp, sp, #128
+; SVEWINDOWS-NEXT: .seh_stackalloc 128
+; SVEWINDOWS-NEXT: .seh_endepilogue
+; SVEWINDOWS-NEXT: b __multf3
+; SVEWINDOWS-NEXT: .seh_endfunclet
+; SVEWINDOWS-NEXT: .seh_endproc
+;
+; WINDOWS-LABEL: testExpf128:
+; WINDOWS: .seh_proc testExpf128
+; WINDOWS-NEXT: // %bb.0: // %entry
+; WINDOWS-NEXT: sub sp, sp, #128
+; WINDOWS-NEXT: .seh_stackalloc 128
+; WINDOWS-NEXT: stp x19, x20, [sp, #80] // 16-byte Folded Spill
+; WINDOWS-NEXT: .seh_save_regp x19, 80
+; WINDOWS-NEXT: stp x21, x22, [sp, #96] // 16-byte Folded Spill
+; WINDOWS-NEXT: .seh_save_regp x21, 96
+; WINDOWS-NEXT: str x30, [sp, #112] // 8-byte Spill
+; WINDOWS-NEXT: .seh_save_reg x30, 112
+; WINDOWS-NEXT: .seh_endprologue
+; WINDOWS-NEXT: mov w8, #49149 // =0xbffd
+; WINDOWS-NEXT: mov w10, #-32766 // =0xffff8002
+; WINDOWS-NEXT: mov w9, #-16383 // =0xffffc001
+; WINDOWS-NEXT: cmp w0, w8
+; WINDOWS-NEXT: mov w19, w0
+; WINDOWS-NEXT: add w20, w0, w9
+; WINDOWS-NEXT: csel w8, w0, w8, lt
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: add w21, w8, w10
+; WINDOWS-NEXT: adrp x8, "__xmm at 7ffe0000000000000000000000000000"
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 7ffe0000000000000000000000000000"]
+; WINDOWS-NEXT: str q1, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp, #16] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: mov w8, #32766 // =0x7ffe
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: csel w20, w21, w20, hi
+; WINDOWS-NEXT: b.ls .LBB9_2
+; WINDOWS-NEXT: // %bb.1: // %entry
+; WINDOWS-NEXT: str q0, [sp, #32] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_2: // %entry
+; WINDOWS-NEXT: mov w8, #-48920 // =0xffff40e8
+; WINDOWS-NEXT: mov w10, #32538 // =0x7f1a
+; WINDOWS-NEXT: ldr q0, [sp, #48] // 16-byte Reload
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: mov w9, #16269 // =0x3f8d
+; WINDOWS-NEXT: csel w8, w19, w8, gt
+; WINDOWS-NEXT: add w21, w19, w9
+; WINDOWS-NEXT: add w22, w8, w10
+; WINDOWS-NEXT: adrp x8, "__xmm at 00720000000000000000000000000000"
+; WINDOWS-NEXT: ldr q1, [x8, :lo12:"__xmm at 00720000000000000000000000000000"]
+; WINDOWS-NEXT: str q1, [sp] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: ldr q1, [sp] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: bl __multf3
+; WINDOWS-NEXT: mov w8, #-32651 // =0xffff8075
+; WINDOWS-NEXT: cmp w19, w8
+; WINDOWS-NEXT: csel w8, w22, w21, lo
+; WINDOWS-NEXT: b.hs .LBB9_4
+; WINDOWS-NEXT: // %bb.3: // %entry
+; WINDOWS-NEXT: str q0, [sp, #16] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_4: // %entry
+; WINDOWS-NEXT: mov w9, #-16382 // =0xffffc002
+; WINDOWS-NEXT: cmp w19, w9
+; WINDOWS-NEXT: b.ge .LBB9_6
+; WINDOWS-NEXT: // %bb.5: // %entry
+; WINDOWS-NEXT: ldr q0, [sp, #16] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_6: // %entry
+; WINDOWS-NEXT: csel w8, w8, w19, lt
+; WINDOWS-NEXT: cmp w19, #4, lsl #12 // =16384
+; WINDOWS-NEXT: b.lt .LBB9_8
+; WINDOWS-NEXT: // %bb.7: // %entry
+; WINDOWS-NEXT: ldr q0, [sp, #32] // 16-byte Reload
+; WINDOWS-NEXT: str q0, [sp, #48] // 16-byte Spill
+; WINDOWS-NEXT: .LBB9_8: // %entry
+; WINDOWS-NEXT: csel w8, w20, w8, ge
+; WINDOWS-NEXT: mov w9, #16383 // =0x3fff
+; WINDOWS-NEXT: add w8, w8, w9
+; WINDOWS-NEXT: lsl x8, x8, #48
+; WINDOWS-NEXT: stp xzr, x8, [sp, #64]
+; WINDOWS-NEXT: ldp q0, q1, [sp, #48] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_startepilogue
+; WINDOWS-NEXT: ldr x30, [sp, #112] // 8-byte Reload
+; WINDOWS-NEXT: .seh_save_reg x30, 112
+; WINDOWS-NEXT: ldp x21, x22, [sp, #96] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_save_regp x21, 96
+; WINDOWS-NEXT: ldp x19, x20, [sp, #80] // 16-byte Folded Reload
+; WINDOWS-NEXT: .seh_save_regp x19, 80
+; WINDOWS-NEXT: add sp, sp, #128
+; WINDOWS-NEXT: .seh_stackalloc 128
+; WINDOWS-NEXT: .seh_endepilogue
+; WINDOWS-NEXT: b __multf3
+; WINDOWS-NEXT: .seh_endfunclet
+; WINDOWS-NEXT: .seh_endproc
+; X64-LABEL: testExpf128:
+; X64: # %bb.0: # %entry
+; X64-NEXT: jmp ldexpl at PLT # TAILCALL
+;
+; WIN64-LABEL: testExpf128:
+; WIN64: # %bb.0: # %entry
+; WIN64-NEXT: pushq %r14
+; WIN64-NEXT: .seh_pushreg %r14
+; WIN64-NEXT: pushq %rsi
+; WIN64-NEXT: .seh_pushreg %rsi
+; WIN64-NEXT: pushq %rdi
+; WIN64-NEXT: .seh_pushreg %rdi
+; WIN64-NEXT: pushq %rbp
+; WIN64-NEXT: .seh_pushreg %rbp
+; WIN64-NEXT: pushq %rbx
+; WIN64-NEXT: .seh_pushreg %rbx
+; WIN64-NEXT: subq $352, %rsp # imm = 0x160
+; WIN64-NEXT: .seh_stackalloc 352
+; WIN64-NEXT: movaps %xmm9, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; WIN64-NEXT: .seh_savexmm %xmm9, 336
+; WIN64-NEXT: movaps %xmm8, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; WIN64-NEXT: .seh_savexmm %xmm8, 320
+; WIN64-NEXT: movaps %xmm7, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; WIN64-NEXT: .seh_savexmm %xmm7, 304
+; WIN64-NEXT: movaps %xmm6, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; WIN64-NEXT: .seh_savexmm %xmm6, 288
+; WIN64-NEXT: .seh_endprologue
+; WIN64-NEXT: movl %r8d, %edi
+; WIN64-NEXT: movq %rcx, %rsi
+; WIN64-NEXT: movaps (%rdx), %xmm7
+; WIN64-NEXT: leal -16383(%rdi), %ebp
+; WIN64-NEXT: cmpl $49149, %r8d # imm = 0xBFFD
+; WIN64-NEXT: movl $49149, %ebx # imm = 0xBFFD
+; WIN64-NEXT: cmovll %r8d, %ebx
+; WIN64-NEXT: addl $-32766, %ebx # imm = 0x8002
+; WIN64-NEXT: movaps {{.*#+}} xmm8 = [5.94865747678615882542879663314003565E+4931]
+; WIN64-NEXT: movaps %xmm8, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps %xmm7, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-NEXT: callq __multf3
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm6
+; WIN64-NEXT: movaps %xmm8, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps %xmm6, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-NEXT: callq __multf3
+; WIN64-NEXT: cmpl $32767, %edi # imm = 0x7FFF
+; WIN64-NEXT: cmovbl %ebp, %ebx
+; WIN64-NEXT: jb .LBB8_2
+; WIN64-NEXT: # %bb.1:
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm6
+; WIN64-NEXT: .LBB8_2: # %entry
+; WIN64-NEXT: leal 16269(%rdi), %ebp
+; WIN64-NEXT: cmpl $-48919, %edi # imm = 0xFFFF40E9
+; WIN64-NEXT: movl $-48920, %r14d # imm = 0xFFFF40E8
+; WIN64-NEXT: cmovgel %edi, %r14d
+; WIN64-NEXT: addl $32538, %r14d # imm = 0x7F1A
+; WIN64-NEXT: movaps {{.*#+}} xmm9 = [3.49140751761019862105509484749900357E-4898]
+; WIN64-NEXT: movaps %xmm9, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps %xmm7, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-NEXT: callq __multf3
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm8
+; WIN64-NEXT: movaps %xmm9, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps %xmm8, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-NEXT: callq __multf3
+; WIN64-NEXT: cmpl $-32651, %edi # imm = 0x8075
+; WIN64-NEXT: cmovael %ebp, %r14d
+; WIN64-NEXT: jae .LBB8_4
+; WIN64-NEXT: # %bb.3:
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm8
+; WIN64-NEXT: .LBB8_4: # %entry
+; WIN64-NEXT: cmpl $-16382, %edi # imm = 0xC002
+; WIN64-NEXT: jl .LBB8_6
+; WIN64-NEXT: # %bb.5: # %entry
+; WIN64-NEXT: movaps %xmm7, %xmm8
+; WIN64-NEXT: .LBB8_6: # %entry
+; WIN64-NEXT: cmovgel %edi, %r14d
+; WIN64-NEXT: cmpl $16384, %edi # imm = 0x4000
+; WIN64-NEXT: jge .LBB8_8
+; WIN64-NEXT: # %bb.7: # %entry
+; WIN64-NEXT: movaps %xmm8, %xmm6
+; WIN64-NEXT: .LBB8_8: # %entry
+; WIN64-NEXT: cmovgel %ebx, %r14d
+; WIN64-NEXT: addl $16383, %r14d # imm = 0x3FFF
+; WIN64-NEXT: shlq $48, %r14
+; WIN64-NEXT: movq %r14, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movq $0, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-NEXT: movaps %xmm6, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-NEXT: leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-NEXT: callq __multf3
+; WIN64-NEXT: movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-NEXT: movaps %xmm0, (%rsi)
+; WIN64-NEXT: movq %rsi, %rax
+; WIN64-NEXT: movaps {{[-0-9]+}}(%r{{[sb]}}p), %xmm6 # 16-byte Reload
+; WIN64-NEXT: movaps {{[-0-9]+}}(%r{{[sb]}}p), %xmm7 # 16-byte Reload
+; WIN64-NEXT: movaps {{[-0-9]+}}(%r{{[sb]}}p), %xmm8 # 16-byte Reload
+; WIN64-NEXT: movaps {{[-0-9]+}}(%r{{[sb]}}p), %xmm9 # 16-byte Reload
+; WIN64-NEXT: .seh_startepilogue
+; WIN64-NEXT: addq $352, %rsp # imm = 0x160
+; WIN64-NEXT: popq %rbx
+; WIN64-NEXT: popq %rbp
+; WIN64-NEXT: popq %rdi
+; WIN64-NEXT: popq %rsi
+; WIN64-NEXT: popq %r14
+; WIN64-NEXT: .seh_endepilogue
+; WIN64-NEXT: retq
+; WIN64-NEXT: .seh_endproc
+;
+; WIN32-LABEL: testExpf128:
+; WIN32: # %bb.0: # %entry
+; WIN32-NEXT: pushl %ebp
+; WIN32-NEXT: movl %esp, %ebp
+; WIN32-NEXT: pushl %ebx
+; WIN32-NEXT: pushl %edi
+; WIN32-NEXT: pushl %esi
+; WIN32-NEXT: andl $-16, %esp
+; WIN32-NEXT: subl $176, %esp
+; WIN32-NEXT: movl 40(%ebp), %edi
+; WIN32-NEXT: cmpl $49149, %edi # imm = 0xBFFD
+; WIN32-NEXT: jl LBB8_2
+; WIN32-NEXT: # %bb.1: # %entry
+; WIN32-NEXT: movl $49149, %edi # imm = 0xBFFD
+; WIN32-NEXT: LBB8_2: # %entry
+; WIN32-NEXT: movl 36(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 32(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 28(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 24(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl $2147352576, {{[0-9]+}}(%esp) # imm = 0x7FFE0000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %esi
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %edx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %ecx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ebx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %esi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $2147352576, {{[0-9]+}}(%esp) # imm = 0x7FFE0000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl 40(%ebp), %eax
+; WIN32-NEXT: cmpl $32767, %eax # imm = 0x7FFF
+; WIN32-NEXT: jae LBB8_3
+; WIN32-NEXT: # %bb.4: # %entry
+; WIN32-NEXT: leal -16383(%eax), %edi
+; WIN32-NEXT: jmp LBB8_5
+; WIN32-NEXT: LBB8_3:
+; WIN32-NEXT: addl $-32766, %edi # imm = 0x8002
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %esi
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: LBB8_5: # %entry
+; WIN32-NEXT: movl %ebx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: cmpl $-48919, %eax # imm = 0xFFFF40E9
+; WIN32-NEXT: jge LBB8_7
+; WIN32-NEXT: # %bb.6: # %entry
+; WIN32-NEXT: movl $-48920, %eax # imm = 0xFFFF40E8
+; WIN32-NEXT: LBB8_7: # %entry
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl 36(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 32(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 28(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl 24(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl $7471104, {{[0-9]+}}(%esp) # imm = 0x720000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %esi
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %ecx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %edx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ebx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %esi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $7471104, {{[0-9]+}}(%esp) # imm = 0x720000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl 40(%ebp), %eax
+; WIN32-NEXT: cmpl $-32651, %eax # imm = 0x8075
+; WIN32-NEXT: jb LBB8_8
+; WIN32-NEXT: # %bb.9: # %entry
+; WIN32-NEXT: leal 16269(%eax), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: jmp LBB8_10
+; WIN32-NEXT: LBB8_8:
+; WIN32-NEXT: addl $32538, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Spill
+; WIN32-NEXT: # imm = 0x7F1A
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %esi
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT: LBB8_10: # %entry
+; WIN32-NEXT: cmpl $-16382, %eax # imm = 0xC002
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %edx # 4-byte Reload
+; WIN32-NEXT: jl LBB8_12
+; WIN32-NEXT: # %bb.11: # %entry
+; WIN32-NEXT: movl 36(%ebp), %esi
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl 32(%ebp), %esi
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl 28(%ebp), %ebx
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: LBB8_12: # %entry
+; WIN32-NEXT: cmpl $16384, %eax # imm = 0x4000
+; WIN32-NEXT: jge LBB8_14
+; WIN32-NEXT: # %bb.13: # %entry
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %ebx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %edx # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %edi # 4-byte Reload
+; WIN32-NEXT: LBB8_14: # %entry
+; WIN32-NEXT: shll $16, %edi
+; WIN32-NEXT: addl $1073676288, %edi # imm = 0x3FFF0000
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl %edi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %edx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ecx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %edi
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %esi
+; WIN32-NEXT: movl 8(%ebp), %eax
+; WIN32-NEXT: movl %esi, 12(%eax)
+; WIN32-NEXT: movl %edx, 8(%eax)
+; WIN32-NEXT: movl %ecx, 4(%eax)
+; WIN32-NEXT: movl %edi, (%eax)
+; WIN32-NEXT: leal -12(%ebp), %esp
+; WIN32-NEXT: popl %esi
+; WIN32-NEXT: popl %edi
+; WIN32-NEXT: popl %ebx
+; WIN32-NEXT: popl %ebp
+; WIN32-NEXT: retl
+entry:
+ %ldexp = call fp128 @llvm.ldexp.f128.i32(fp128 %val, i32 %a)
+ ret fp128 %ldexp
+}
+
attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #1 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
diff --git a/llvm/test/CodeGen/X86/llvm.frexp.ll b/llvm/test/CodeGen/X86/llvm.frexp.ll
index 3410fd5169c9a..55454f25f2e96 100644
--- a/llvm/test/CodeGen/X86/llvm.frexp.ll
+++ b/llvm/test/CodeGen/X86/llvm.frexp.ll
@@ -590,6 +590,274 @@ define { float, i32 } @pr160981() {
ret { float, i32 } %ret
}
+define { fp128, i32 } @test_frexp_f128_i32(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32:
+; X64: # %bb.0:
+; X64-NEXT: pushq %rax
+; X64-NEXT: leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT: callq frexpl at PLT
+; X64-NEXT: movl {{[0-9]+}}(%rsp), %eax
+; X64-NEXT: popq %rcx
+; X64-NEXT: retq
+;
+; WIN32-LABEL: test_frexp_f128_i32:
+; WIN32: # %bb.0:
+; WIN32-NEXT: pushl %ebp
+; WIN32-NEXT: movl %esp, %ebp
+; WIN32-NEXT: pushl %ebx
+; WIN32-NEXT: pushl %edi
+; WIN32-NEXT: pushl %esi
+; WIN32-NEXT: andl $-16, %esp
+; WIN32-NEXT: subl $96, %esp
+; WIN32-NEXT: movl 24(%ebp), %edx
+; WIN32-NEXT: movl 28(%ebp), %eax
+; WIN32-NEXT: movl 32(%ebp), %ecx
+; WIN32-NEXT: movl 36(%ebp), %esi
+; WIN32-NEXT: movl %esi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ecx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %eax, %ebx
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %edx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl $1081147392, {{[0-9]+}}(%esp) # imm = 0x40710000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT: movl %esi, %edi
+; WIN32-NEXT: andl $2147483647, %edi # imm = 0x7FFFFFFF
+; WIN32-NEXT: cmpl $65536, %edi # imm = 0x10000
+; WIN32-NEXT: jb LBB14_1
+; WIN32-NEXT: # %bb.2:
+; WIN32-NEXT: movl %edi, %ecx
+; WIN32-NEXT: jmp LBB14_3
+; WIN32-NEXT: LBB14_1:
+; WIN32-NEXT: movl %edx, %ecx
+; WIN32-NEXT: andl $2147418112, %ecx # imm = 0x7FFF0000
+; WIN32-NEXT: LBB14_3:
+; WIN32-NEXT: shrl $16, %ecx
+; WIN32-NEXT: cmpl $65536, %edi # imm = 0x10000
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: jb LBB14_4
+; WIN32-NEXT: # %bb.5:
+; WIN32-NEXT: movl 32(%ebp), %eax
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %ebx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %esi, %edx
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: jmp LBB14_6
+; WIN32-NEXT: LBB14_4:
+; WIN32-NEXT: addl $-114, %ecx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl 32(%ebp), %eax
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: LBB14_6:
+; WIN32-NEXT: addl $-2147418112, %edi # imm = 0x80010000
+; WIN32-NEXT: cmpl $1, %esi
+; WIN32-NEXT: movl %ebx, %esi
+; WIN32-NEXT: sbbl $0, %esi
+; WIN32-NEXT: movl %eax, %esi
+; WIN32-NEXT: sbbl $0, %esi
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: sbbl $-2147418112, %edi # imm = 0x80010000
+; WIN32-NEXT: movl $0, %edi
+; WIN32-NEXT: jb LBB14_8
+; WIN32-NEXT: # %bb.7:
+; WIN32-NEXT: andl $-2147418113, %edx # imm = 0x8000FFFF
+; WIN32-NEXT: orl $1073610752, %edx # imm = 0x3FFE0000
+; WIN32-NEXT: addl $-16382, %ecx # imm = 0xC002
+; WIN32-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %esi # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ebx # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload
+; WIN32-NEXT: movl %ecx, %edi
+; WIN32-NEXT: LBB14_8:
+; WIN32-NEXT: movl 8(%ebp), %edx
+; WIN32-NEXT: movl %eax, 8(%edx)
+; WIN32-NEXT: movl %ebx, 4(%edx)
+; WIN32-NEXT: movl %esi, (%edx)
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload
+; WIN32-NEXT: movl %ecx, 12(%edx)
+; WIN32-NEXT: movl %edi, 16(%edx)
+; WIN32-NEXT: movl %edx, %eax
+; WIN32-NEXT: leal -12(%ebp), %esp
+; WIN32-NEXT: popl %esi
+; WIN32-NEXT: popl %edi
+; WIN32-NEXT: popl %ebx
+; WIN32-NEXT: popl %ebp
+; WIN32-NEXT: retl
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ ret { fp128, i32 } %result
+}
+
+define fp128 @test_frexp_f128_i32_only_use_fract(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32_only_use_fract:
+; X64: # %bb.0:
+; X64-NEXT: pushq %rax
+; X64-NEXT: leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT: callq frexpl at PLT
+; X64-NEXT: popq %rax
+; X64-NEXT: retq
+;
+; WIN32-LABEL: test_frexp_f128_i32_only_use_fract:
+; WIN32: # %bb.0:
+; WIN32-NEXT: pushl %ebp
+; WIN32-NEXT: movl %esp, %ebp
+; WIN32-NEXT: pushl %ebx
+; WIN32-NEXT: pushl %edi
+; WIN32-NEXT: pushl %esi
+; WIN32-NEXT: andl $-16, %esp
+; WIN32-NEXT: subl $96, %esp
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: movl 28(%ebp), %eax
+; WIN32-NEXT: movl 32(%ebp), %edi
+; WIN32-NEXT: movl 36(%ebp), %ebx
+; WIN32-NEXT: movl %ebx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %edi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %esi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl $1081147392, {{[0-9]+}}(%esp) # imm = 0x40710000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: movl %ebx, %ecx
+; WIN32-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF
+; WIN32-NEXT: leal -2147418112(%ecx), %eax
+; WIN32-NEXT: cmpl $65536, %ecx # imm = 0x10000
+; WIN32-NEXT: jb LBB15_1
+; WIN32-NEXT: # %bb.2:
+; WIN32-NEXT: movl %edi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl 28(%ebp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl %ebx, %edx
+; WIN32-NEXT: jmp LBB15_3
+; WIN32-NEXT: LBB15_1:
+; WIN32-NEXT: movl %ebx, %edx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill
+; WIN32-NEXT: LBB15_3:
+; WIN32-NEXT: cmpl $1, %esi
+; WIN32-NEXT: movl 28(%ebp), %ecx
+; WIN32-NEXT: movl %ecx, %esi
+; WIN32-NEXT: sbbl $0, %esi
+; WIN32-NEXT: movl %edi, %esi
+; WIN32-NEXT: sbbl $0, %esi
+; WIN32-NEXT: movl 24(%ebp), %esi
+; WIN32-NEXT: sbbl $-2147418112, %eax # imm = 0x80010000
+; WIN32-NEXT: jb LBB15_5
+; WIN32-NEXT: # %bb.4:
+; WIN32-NEXT: andl $-2147418113, %ebx # imm = 0x8000FFFF
+; WIN32-NEXT: orl $1073610752, %ebx # imm = 0x3FFE0000
+; WIN32-NEXT: movl %ebx, %edx
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %esi # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload
+; WIN32-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %edi # 4-byte Reload
+; WIN32-NEXT: LBB15_5:
+; WIN32-NEXT: movl 8(%ebp), %eax
+; WIN32-NEXT: movl %edi, 8(%eax)
+; WIN32-NEXT: movl %ecx, 4(%eax)
+; WIN32-NEXT: movl %esi, (%eax)
+; WIN32-NEXT: movl %edx, 12(%eax)
+; WIN32-NEXT: leal -12(%ebp), %esp
+; WIN32-NEXT: popl %esi
+; WIN32-NEXT: popl %edi
+; WIN32-NEXT: popl %ebx
+; WIN32-NEXT: popl %ebp
+; WIN32-NEXT: retl
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ %result.0 = extractvalue { fp128, i32 } %result, 0
+ ret fp128 %result.0
+}
+
+define i32 @test_frexp_f128_i32_only_use_exp(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32_only_use_exp:
+; X64: # %bb.0:
+; X64-NEXT: pushq %rax
+; X64-NEXT: leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT: callq frexpl at PLT
+; X64-NEXT: movl {{[0-9]+}}(%rsp), %eax
+; X64-NEXT: popq %rcx
+; X64-NEXT: retq
+;
+; WIN32-LABEL: test_frexp_f128_i32_only_use_exp:
+; WIN32: # %bb.0:
+; WIN32-NEXT: pushl %ebp
+; WIN32-NEXT: movl %esp, %ebp
+; WIN32-NEXT: pushl %ebx
+; WIN32-NEXT: pushl %edi
+; WIN32-NEXT: pushl %esi
+; WIN32-NEXT: andl $-16, %esp
+; WIN32-NEXT: subl $80, %esp
+; WIN32-NEXT: movl 8(%ebp), %eax
+; WIN32-NEXT: movl 12(%ebp), %ebx
+; WIN32-NEXT: movl 16(%ebp), %edi
+; WIN32-NEXT: movl 20(%ebp), %esi
+; WIN32-NEXT: movl %esi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %edi, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %ebx, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; WIN32-NEXT: leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT: movl %eax, (%esp)
+; WIN32-NEXT: movl $1081147392, {{[0-9]+}}(%esp) # imm = 0x40710000
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: movl $0, {{[0-9]+}}(%esp)
+; WIN32-NEXT: calll ___multf3
+; WIN32-NEXT: andl $2147483647, %esi # imm = 0x7FFFFFFF
+; WIN32-NEXT: cmpl $65536, %esi # imm = 0x10000
+; WIN32-NEXT: jb LBB16_1
+; WIN32-NEXT: # %bb.2:
+; WIN32-NEXT: movl %esi, %ecx
+; WIN32-NEXT: jmp LBB16_3
+; WIN32-NEXT: LBB16_1:
+; WIN32-NEXT: movl $2147418112, %ecx # imm = 0x7FFF0000
+; WIN32-NEXT: andl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT: LBB16_3:
+; WIN32-NEXT: shrl $16, %ecx
+; WIN32-NEXT: cmpl $65536, %esi # imm = 0x10000
+; WIN32-NEXT: jae LBB16_5
+; WIN32-NEXT: # %bb.4:
+; WIN32-NEXT: addl $-114, %ecx
+; WIN32-NEXT: LBB16_5:
+; WIN32-NEXT: addl $-2147418112, %esi # imm = 0x80010000
+; WIN32-NEXT: xorl %eax, %eax
+; WIN32-NEXT: cmpl $1, 8(%ebp)
+; WIN32-NEXT: sbbl $0, %ebx
+; WIN32-NEXT: sbbl $0, %edi
+; WIN32-NEXT: sbbl $-2147418112, %esi # imm = 0x80010000
+; WIN32-NEXT: jb LBB16_7
+; WIN32-NEXT: # %bb.6:
+; WIN32-NEXT: addl $-16382, %ecx # imm = 0xC002
+; WIN32-NEXT: movl %ecx, %eax
+; WIN32-NEXT: LBB16_7:
+; WIN32-NEXT: leal -12(%ebp), %esp
+; WIN32-NEXT: popl %esi
+; WIN32-NEXT: popl %edi
+; WIN32-NEXT: popl %ebx
+; WIN32-NEXT: popl %ebp
+; WIN32-NEXT: retl
+ %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+ %result.0 = extractvalue { fp128, i32 } %result, 1
+ ret i32 %result.0
+}
+
; FIXME: Widen vector result
; define { <2 x double>, <2 x i32> } @test_frexp_v2f64_v2i32(<2 x double> %a) nounwind {
; %result = call { <2 x double>, <2 x i32> } @llvm.frexp.v2f64.v2i32(<2 x double> %a)
>From 64ba326be659da48385b931844873b9889105a29 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <flokkievids at gmail.com>
Date: Tue, 14 Jul 2026 11:45:50 +0200
Subject: [PATCH 5/5] Update llvm/lib/CodeGen/ExpandIRInsts.cpp
Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
llvm/lib/CodeGen/ExpandIRInsts.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/ExpandIRInsts.cpp b/llvm/lib/CodeGen/ExpandIRInsts.cpp
index 64c7a96133d57..2a4f71fcbca57 100644
--- a/llvm/lib/CodeGen/ExpandIRInsts.cpp
+++ b/llvm/lib/CodeGen/ExpandIRInsts.cpp
@@ -573,7 +573,7 @@ static void expandLdexp(IntrinsicInst *II) {
Constant *DoubleMaxExp = ConstantInt::getSigned(ExpVT, 2 * MaxExpVal);
- const APFloat One(FltSem, "1.0");
+ const APFloat One = APFloat::getOne(FltSem);
APFloat ScaleUpK = scalbn(One, MaxExpVal, APFloat::rmNearestTiesToEven);
// Offset by precision to avoid denormal range.
More information about the llvm-commits
mailing list