[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