https://github.com/harrisonGPU updated https://github.com/llvm/llvm-project/pull/227380
>From 1e5ebb6546e7a04133dcc07ab7d2541b2a7a550a Mon Sep 17 00:00:00 2001 From: Harrison Hao <[email protected]> Date: Tue, 29 Sep 2026 23:42:45 +0800 Subject: [PATCH 1/2] [InstCombine] Fold exp2(uitofp x) to ldexp(1.0, x) with ninf `exp2(uitofp iN x) -> ldexp(1.0, zext x)` currently requires N to be narrower than `int`, because `ldexp` takes a signed `int` exponent. When N equals the width of `int`, the fold is still correct under `ninf`. Every LLVM FP type has $E_{\max} \le 16383 < 2^{15} \le 2^{N-1}$, so: - $x < 2^{N-1}$: signed and unsigned $x$ agree, so $\operatorname{ldexp}(1.0, x) = 2^{x} = \operatorname{exp2}(x)$. - $x \ge 2^{N-1}$: $\operatorname{exp2}(x) \ge 2^{2^{N-1}} > 2^{E_{\max}}$ overflows to $+\infty$, which is poison under `ninf`. This only applies to the intrinsic, since the `exp2` libcall may set `errno` on overflow. This also enables `pow(2.0, uitofp x) -> ldexp` via the existing `pow(2^n, x) -> exp2(n * x)` fold. --- .../lib/Transforms/Utils/SimplifyLibCalls.cpp | 51 +++++++++++------- llvm/test/Transforms/InstCombine/exp2-1.ll | 20 +++---- .../Transforms/InstCombine/exp2-to-ldexp.ll | 53 ++++++++----------- .../Transforms/InstCombine/pow-to-ldexp.ll | 8 +-- .../test/Transforms/InstCombine/pow_fp_int.ll | 7 ++- .../Transforms/InstCombine/pow_fp_int16.ll | 7 ++- 6 files changed, 74 insertions(+), 72 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp index f6235b5b98fcb..e7ec4e33567d9 100644 --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -2633,28 +2633,43 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) { // exp2(sitofp(x)) -> ldexp(1.0, sext(x)) if sizeof(x) <= IntSize // exp2(uitofp(x)) -> ldexp(1.0, zext(x)) if sizeof(x) < IntSize + // exp2(uitofp(x)) -> ldexp(1.0, umin(x, C)) if sizeof(x) == IntSize Value *Op = CI->getArgOperand(0); - if ((isa<SIToFPInst>(Op) || isa<UIToFPInst>(Op)) && - (UseIntrinsic || - hasFloatFn(M, TLI, Ty, LibFunc_ldexp, LibFunc_ldexpf, LibFunc_ldexpl))) { - if (Value *Exp = getIntToFPVal(Op, B, TLI->getIntSize())) { - Constant *One = ConstantFP::get(Ty, 1.0); + if (!isa<SIToFPInst, UIToFPInst>(Op) || + (!UseIntrinsic && + !hasFloatFn(M, TLI, Ty, LibFunc_ldexp, LibFunc_ldexpf, LibFunc_ldexpl))) + return Ret; - if (UseIntrinsic) { - return copyFlags(*CI, B.CreateIntrinsic(Intrinsic::ldexp, - {Ty, Exp->getType()}, - {One, Exp}, CI)); - } + unsigned IntSize = TLI->getIntSize(); + Value *Exp = getIntToFPVal(Op, B, IntSize); + Value *UIntOp = nullptr; + // A uitofp source as wide as int may exceed INT_MAX, so clamp it for ldexp. + if (!Exp && UseIntrinsic && match(Op, m_UIToFP(m_Value(UIntOp))) && + UIntOp->getType()->getScalarSizeInBits() == IntSize) { + const fltSemantics &Sem = Ty->getScalarType()->getFltSemantics(); + // exp2(x) and ldexp(1.0, Clamp) both overflow for every x >= Clamp. + int Clamp = llvm::ilogb(APFloat::getLargest(Sem)) + 1; + assert(isIntN(IntSize, Clamp) && "Emax + 1 must fit in int"); + Exp = UIntOp; + if (!CI->hasNoInfs()) + Exp = B.CreateBinaryIntrinsic(Intrinsic::umin, UIntOp, + ConstantInt::get(UIntOp->getType(), Clamp)); + } + + if (!Exp) + return Ret; - IRBuilderBase::FastMathFlagGuard Guard(B); - B.setFastMathFlags(CI->getFastMathFlags()); - return copyFlags(*CI, emitBinaryFloatFnCall( - One, Exp, TLI, LibFunc_ldexp, LibFunc_ldexpf, - LibFunc_ldexpl, B, AttributeList())); - } - } + Constant *One = ConstantFP::get(Ty, 1.0); + if (UseIntrinsic) + return copyFlags(*CI, + B.CreateIntrinsic(Intrinsic::ldexp, {Ty, Exp->getType()}, + {One, Exp}, CI)); - return Ret; + IRBuilderBase::FastMathFlagGuard Guard(B); + B.setFastMathFlags(CI->getFastMathFlags()); + return copyFlags(*CI, emitBinaryFloatFnCall(One, Exp, TLI, LibFunc_ldexp, + LibFunc_ldexpf, LibFunc_ldexpl, B, + AttributeList())); } Value *LibCallSimplifier::optimizeFMinFMax(CallInst *CI, IRBuilderBase &B, diff --git a/llvm/test/Transforms/InstCombine/exp2-1.ll b/llvm/test/Transforms/InstCombine/exp2-1.ll index cd2deaad91e2f..72260afb37343 100644 --- a/llvm/test/Transforms/InstCombine/exp2-1.ll +++ b/llvm/test/Transforms/InstCombine/exp2-1.ll @@ -306,8 +306,7 @@ define float @test_readonly_exp2f_f32_of_uitofp_flags(i32 %x) { define float @uitofp_i32_scalar_intrinsic_with_ninf(i32 %x) { ; LDEXP32-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( -; LDEXP32-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; LDEXP32-NEXT: [[R:%.*]] = tail call ninf float @llvm.exp2.f32(float [[U]]) +; LDEXP32-NEXT: [[R:%.*]] = tail call ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X:%.*]]) ; LDEXP32-NEXT: ret float [[R]] ; ; LDEXP16-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( @@ -316,13 +315,11 @@ define float @uitofp_i32_scalar_intrinsic_with_ninf(i32 %x) { ; LDEXP16-NEXT: ret float [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( -; NOLDEXPF-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; NOLDEXPF-NEXT: [[R:%.*]] = tail call ninf float @llvm.exp2.f32(float [[U]]) +; NOLDEXPF-NEXT: [[R:%.*]] = tail call ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X:%.*]]) ; NOLDEXPF-NEXT: ret float [[R]] ; ; NOLDEXP-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( -; NOLDEXP-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; NOLDEXP-NEXT: [[R:%.*]] = tail call ninf float @llvm.exp2.f32(float [[U]]) +; NOLDEXP-NEXT: [[R:%.*]] = tail call ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X:%.*]]) ; NOLDEXP-NEXT: ret float [[R]] ; %u = uitofp i32 %x to float @@ -363,8 +360,7 @@ define float @uitofp_i16_scalar_intrinsic_with_ninf(i16 %x) { ; LDEXP32-NEXT: ret float [[R]] ; ; LDEXP16-LABEL: @uitofp_i16_scalar_intrinsic_with_ninf( -; LDEXP16-NEXT: [[U:%.*]] = uitofp i16 [[X:%.*]] to float -; LDEXP16-NEXT: [[R:%.*]] = tail call ninf float @llvm.exp2.f32(float [[U]]) +; LDEXP16-NEXT: [[R:%.*]] = tail call ninf float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[X:%.*]]) ; LDEXP16-NEXT: ret float [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i16_scalar_intrinsic_with_ninf( @@ -389,8 +385,8 @@ define float @uitofp_i16_scalar_intrinsic(i16 %x) { ; LDEXP32-NEXT: ret float [[R]] ; ; LDEXP16-LABEL: @uitofp_i16_scalar_intrinsic( -; LDEXP16-NEXT: [[U:%.*]] = uitofp i16 [[X:%.*]] to float -; LDEXP16-NEXT: [[R:%.*]] = tail call float @llvm.exp2.f32(float [[U]]) +; LDEXP16-NEXT: [[TMP1:%.*]] = call i16 @llvm.umin.i16(i16 [[X:%.*]], i16 128) +; LDEXP16-NEXT: [[R:%.*]] = tail call float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP1]]) ; LDEXP16-NEXT: ret float [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i16_scalar_intrinsic( @@ -415,8 +411,8 @@ define fp128 @uitofp_i16_scalar_intrinsic_fp128(i16 %x) { ; LDEXP32-NEXT: ret fp128 [[R]] ; ; LDEXP16-LABEL: @uitofp_i16_scalar_intrinsic_fp128( -; LDEXP16-NEXT: [[U:%.*]] = uitofp i16 [[X:%.*]] to fp128 -; LDEXP16-NEXT: [[R:%.*]] = tail call fp128 @llvm.exp2.f128(fp128 [[U]]) +; LDEXP16-NEXT: [[TMP1:%.*]] = call i16 @llvm.umin.i16(i16 [[X:%.*]], i16 16384) +; LDEXP16-NEXT: [[R:%.*]] = tail call fp128 @llvm.ldexp.f128.i16(fp128 1.000000e+00, i16 [[TMP1]]) ; LDEXP16-NEXT: ret fp128 [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i16_scalar_intrinsic_fp128( diff --git a/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll b/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll index 651286d6aaf52..6d4aebdf587a7 100644 --- a/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll +++ b/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll @@ -103,8 +103,7 @@ define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(<vscale x 4 x i8> %x) { define float @exp2_f32_uitofp_i32_ninf(i32 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i32_ninf( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call ninf float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i32 %x to float @@ -115,8 +114,7 @@ define float @exp2_f32_uitofp_i32_ninf(i32 %x) { define float @exp2_f32_uitofp_i32_flags(i32 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i32_flags( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call nnan ninf float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call nnan ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i32 %x to float @@ -127,8 +125,7 @@ define float @exp2_f32_uitofp_i32_flags(i32 %x) { define <2 x float> @exp2_v2f32_uitofp_v2i32_ninf(<2 x i32> %x) { ; CHECK-LABEL: define <2 x float> @exp2_v2f32_uitofp_v2i32_ninf( ; CHECK-SAME: <2 x i32> [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp <2 x i32> [[X]] to <2 x float> -; CHECK-NEXT: [[EXP2:%.*]] = call ninf <2 x float> @llvm.exp2.v2f32(<2 x float> [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> splat (float 1.000000e+00), <2 x i32> [[X]]) ; CHECK-NEXT: ret <2 x float> [[EXP2]] ; %itofp = uitofp <2 x i32> %x to <2 x float> @@ -139,8 +136,7 @@ define <2 x float> @exp2_v2f32_uitofp_v2i32_ninf(<2 x i32> %x) { define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32_ninf(<vscale x 4 x i32> %x) { ; CHECK-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32_ninf( ; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp <vscale x 4 x i32> [[X]] to <vscale x 4 x float> -; CHECK-NEXT: [[EXP2:%.*]] = call ninf <vscale x 4 x float> @llvm.exp2.nxv4f32(<vscale x 4 x float> [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf <vscale x 4 x float> @llvm.ldexp.nxv4f32.nxv4i32(<vscale x 4 x float> splat (float 1.000000e+00), <vscale x 4 x i32> [[X]]) ; CHECK-NEXT: ret <vscale x 4 x float> [[EXP2]] ; %itofp = uitofp <vscale x 4 x i32> %x to <vscale x 4 x float> @@ -151,8 +147,7 @@ define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32_ninf(<vscale x 4 x i32> %x) define half @exp2_f16_uitofp_i32_ninf(i32 %x) { ; CHECK-LABEL: define half @exp2_f16_uitofp_i32_ninf( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to half -; CHECK-NEXT: [[EXP2:%.*]] = call ninf half @llvm.exp2.f16(half [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf half @llvm.ldexp.f16.i32(half 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: ret half [[EXP2]] ; %itofp = uitofp i32 %x to half @@ -163,8 +158,7 @@ define half @exp2_f16_uitofp_i32_ninf(i32 %x) { define double @exp2_f64_uitofp_i32_ninf(i32 %x) { ; CHECK-LABEL: define double @exp2_f64_uitofp_i32_ninf( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to double -; CHECK-NEXT: [[EXP2:%.*]] = call ninf double @llvm.exp2.f64(double [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: ret double [[EXP2]] ; %itofp = uitofp i32 %x to double @@ -175,8 +169,7 @@ define double @exp2_f64_uitofp_i32_ninf(i32 %x) { define fp128 @exp2_fp128_uitofp_i32_ninf(i32 %x) { ; CHECK-LABEL: define fp128 @exp2_fp128_uitofp_i32_ninf( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to fp128 -; CHECK-NEXT: [[EXP2:%.*]] = call ninf fp128 @llvm.exp2.f128(fp128 [[ITOFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = call ninf fp128 @llvm.ldexp.f128.i32(fp128 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: ret fp128 [[EXP2]] ; %itofp = uitofp i32 %x to fp128 @@ -201,8 +194,8 @@ define float @exp2_f32_uitofp_i64_ninf(i64 %x) { define float @exp2_f32_uitofp_i32(i32 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 128) +; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i32 %x to float @@ -213,8 +206,8 @@ define float @exp2_f32_uitofp_i32(i32 %x) { define float @exp2_f32_uitofp_i32_nnan(i32 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i32_nnan( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call nnan float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 128) +; CHECK-NEXT: [[EXP2:%.*]] = call nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i32 %x to float @@ -225,8 +218,8 @@ define float @exp2_f32_uitofp_i32_nnan(i32 %x) { define <2 x float> @exp2_v2f32_uitofp_v2i32(<2 x i32> %x) { ; CHECK-LABEL: define <2 x float> @exp2_v2f32_uitofp_v2i32( ; CHECK-SAME: <2 x i32> [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp <2 x i32> [[X]] to <2 x float> -; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.umin.v2i32(<2 x i32> [[X]], <2 x i32> splat (i32 128)) +; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> splat (float 1.000000e+00), <2 x i32> [[TMP1]]) ; CHECK-NEXT: ret <2 x float> [[EXP2]] ; %itofp = uitofp <2 x i32> %x to <2 x float> @@ -237,8 +230,8 @@ define <2 x float> @exp2_v2f32_uitofp_v2i32(<2 x i32> %x) { define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32(<vscale x 4 x i32> %x) { ; CHECK-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32( ; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp <vscale x 4 x i32> [[X]] to <vscale x 4 x float> -; CHECK-NEXT: [[EXP2:%.*]] = call <vscale x 4 x float> @llvm.exp2.nxv4f32(<vscale x 4 x float> [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call <vscale x 4 x i32> @llvm.umin.nxv4i32(<vscale x 4 x i32> [[X]], <vscale x 4 x i32> splat (i32 128)) +; CHECK-NEXT: [[EXP2:%.*]] = call <vscale x 4 x float> @llvm.ldexp.nxv4f32.nxv4i32(<vscale x 4 x float> splat (float 1.000000e+00), <vscale x 4 x i32> [[TMP1]]) ; CHECK-NEXT: ret <vscale x 4 x float> [[EXP2]] ; %itofp = uitofp <vscale x 4 x i32> %x to <vscale x 4 x float> @@ -249,8 +242,8 @@ define <vscale x 4 x float> @exp2_nxv4f32_uitofp_i32(<vscale x 4 x i32> %x) { define half @exp2_f16_uitofp_i32(i32 %x) { ; CHECK-LABEL: define half @exp2_f16_uitofp_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to half -; CHECK-NEXT: [[EXP2:%.*]] = call half @llvm.exp2.f16(half [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 16) +; CHECK-NEXT: [[EXP2:%.*]] = call half @llvm.ldexp.f16.i32(half 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret half [[EXP2]] ; %itofp = uitofp i32 %x to half @@ -261,8 +254,8 @@ define half @exp2_f16_uitofp_i32(i32 %x) { define double @exp2_f64_uitofp_i32(i32 %x) { ; CHECK-LABEL: define double @exp2_f64_uitofp_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to double -; CHECK-NEXT: [[EXP2:%.*]] = call double @llvm.exp2.f64(double [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 1024) +; CHECK-NEXT: [[EXP2:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret double [[EXP2]] ; %itofp = uitofp i32 %x to double @@ -273,8 +266,8 @@ define double @exp2_f64_uitofp_i32(i32 %x) { define fp128 @exp2_fp128_uitofp_i32(i32 %x) { ; CHECK-LABEL: define fp128 @exp2_fp128_uitofp_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to fp128 -; CHECK-NEXT: [[EXP2:%.*]] = call fp128 @llvm.exp2.f128(fp128 [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 16384) +; CHECK-NEXT: [[EXP2:%.*]] = call fp128 @llvm.ldexp.f128.i32(fp128 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret fp128 [[EXP2]] ; %itofp = uitofp i32 %x to fp128 @@ -285,8 +278,8 @@ define fp128 @exp2_fp128_uitofp_i32(i32 %x) { define ppc_fp128 @exp2_ppcf128_uitofp_i32(i32 %x) { ; CHECK-LABEL: define ppc_fp128 @exp2_ppcf128_uitofp_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to ppc_fp128 -; CHECK-NEXT: [[EXP2:%.*]] = call ppc_fp128 @llvm.exp2.ppcf128(ppc_fp128 [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 1024) +; CHECK-NEXT: [[EXP2:%.*]] = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret ppc_fp128 [[EXP2]] ; %itofp = uitofp i32 %x to ppc_fp128 diff --git a/llvm/test/Transforms/InstCombine/pow-to-ldexp.ll b/llvm/test/Transforms/InstCombine/pow-to-ldexp.ll index d2b1f6e0a4337..24ec96e2ed216 100644 --- a/llvm/test/Transforms/InstCombine/pow-to-ldexp.ll +++ b/llvm/test/Transforms/InstCombine/pow-to-ldexp.ll @@ -29,8 +29,8 @@ define float @pow_sitofp_f32_const_base_2__flags(i32 %x) { define float @pow_uitofp_f32_const_base_2(i32 %x) { ; LDEXP-EXP2-LABEL: define float @pow_uitofp_f32_const_base_2( ; LDEXP-EXP2-SAME: i32 [[X:%.*]]) { -; LDEXP-EXP2-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; LDEXP-EXP2-NEXT: [[EXP2:%.*]] = tail call float @llvm.exp2.f32(float [[ITOFP]]) +; LDEXP-EXP2-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 128) +; LDEXP-EXP2-NEXT: [[EXP2:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; LDEXP-EXP2-NEXT: ret float [[EXP2]] ; ; LDEXP-NOEXP2-LABEL: define float @pow_uitofp_f32_const_base_2( @@ -41,8 +41,8 @@ define float @pow_uitofp_f32_const_base_2(i32 %x) { ; ; NOLDEXP-LABEL: define float @pow_uitofp_f32_const_base_2( ; NOLDEXP-SAME: i32 [[X:%.*]]) { -; NOLDEXP-NEXT: [[ITOFP:%.*]] = uitofp i32 [[X]] to float -; NOLDEXP-NEXT: [[EXP2:%.*]] = tail call float @llvm.exp2.f32(float [[ITOFP]]) +; NOLDEXP-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 128) +; NOLDEXP-NEXT: [[EXP2:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; NOLDEXP-NEXT: ret float [[EXP2]] ; %itofp = uitofp i32 %x to float diff --git a/llvm/test/Transforms/InstCombine/pow_fp_int.ll b/llvm/test/Transforms/InstCombine/pow_fp_int.ll index 13c2d3ca8070c..94025fd6c8c80 100644 --- a/llvm/test/Transforms/InstCombine/pow_fp_int.ll +++ b/llvm/test/Transforms/InstCombine/pow_fp_int.ll @@ -259,8 +259,7 @@ define double @pow_uitofp_const_base_fast_i32(i32 %x) { define double @pow_uitofp_const_base_2_fast_i32(i32 %x) { ; CHECK-LABEL: define double @pow_uitofp_const_base_2_fast_i32( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[SUBFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = tail call fast float @llvm.exp2.f32(float [[SUBFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = tail call fast float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[X]]) ; CHECK-NEXT: [[RES:%.*]] = fpext float [[EXP2]] to double ; CHECK-NEXT: ret double [[RES]] ; @@ -401,8 +400,8 @@ define double @pow_sitofp_const_base_power_of_2_no_fast(i32 %x) { define double @pow_uitofp_const_base_2_no_fast(i32 %x) { ; CHECK-LABEL: define double @pow_uitofp_const_base_2_no_fast( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[SUBFP:%.*]] = uitofp i32 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = tail call float @llvm.exp2.f32(float [[SUBFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 128) +; CHECK-NEXT: [[EXP2:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: [[RES:%.*]] = fpext float [[EXP2]] to double ; CHECK-NEXT: ret double [[RES]] ; diff --git a/llvm/test/Transforms/InstCombine/pow_fp_int16.ll b/llvm/test/Transforms/InstCombine/pow_fp_int16.ll index db836258daa1c..c768b1fa4d62b 100644 --- a/llvm/test/Transforms/InstCombine/pow_fp_int16.ll +++ b/llvm/test/Transforms/InstCombine/pow_fp_int16.ll @@ -256,8 +256,7 @@ define double @pow_uitofp_const_base_fast_i16(i16 %x) { define double @pow_uitofp_const_base_2_fast_i16(i16 %x) { ; CHECK-LABEL: define double @pow_uitofp_const_base_2_fast_i16( ; CHECK-SAME: i16 [[X:%.*]]) { -; CHECK-NEXT: [[SUBFP:%.*]] = uitofp i16 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = tail call fast float @llvm.exp2.f32(float [[SUBFP]]) +; CHECK-NEXT: [[EXP2:%.*]] = tail call fast float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[X]]) ; CHECK-NEXT: [[RES:%.*]] = fpext float [[EXP2]] to double ; CHECK-NEXT: ret double [[RES]] ; @@ -367,8 +366,8 @@ define double @pow_sitofp_const_base_power_of_2_no_fast(i16 %x) { define double @pow_uitofp_const_base_2_no_fast(i16 %x) { ; CHECK-LABEL: define double @pow_uitofp_const_base_2_no_fast( ; CHECK-SAME: i16 [[X:%.*]]) { -; CHECK-NEXT: [[SUBFP:%.*]] = uitofp i16 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = tail call float @llvm.exp2.f32(float [[SUBFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.umin.i16(i16 [[X]], i16 128) +; CHECK-NEXT: [[EXP2:%.*]] = tail call float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP1]]) ; CHECK-NEXT: [[RES:%.*]] = fpext float [[EXP2]] to double ; CHECK-NEXT: ret double [[RES]] ; >From a68a8f9222cb235274fbcb69364326d522fe6acc Mon Sep 17 00:00:00 2001 From: Harrison Hao <[email protected]> Date: Thu, 1 Oct 2026 01:43:25 +0800 Subject: [PATCH 2/2] Update for comments --- .../lib/Transforms/Utils/SimplifyLibCalls.cpp | 7 ++++--- llvm/test/Transforms/InstCombine/exp2-1.ll | 21 ++++++++++--------- .../Transforms/InstCombine/exp2-to-ldexp.ll | 14 +++++++------ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp index e7ec4e33567d9..f02632891ab9b 100644 --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -2633,7 +2633,7 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) { // exp2(sitofp(x)) -> ldexp(1.0, sext(x)) if sizeof(x) <= IntSize // exp2(uitofp(x)) -> ldexp(1.0, zext(x)) if sizeof(x) < IntSize - // exp2(uitofp(x)) -> ldexp(1.0, umin(x, C)) if sizeof(x) == IntSize + // exp2(uitofp(x)) -> ldexp(1.0, trunc(umin(x, C))) if sizeof(x) >= IntSize Value *Op = CI->getArgOperand(0); if (!isa<SIToFPInst, UIToFPInst>(Op) || (!UseIntrinsic && @@ -2643,9 +2643,9 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) { unsigned IntSize = TLI->getIntSize(); Value *Exp = getIntToFPVal(Op, B, IntSize); Value *UIntOp = nullptr; - // A uitofp source as wide as int may exceed INT_MAX, so clamp it for ldexp. + // A uitofp source at least as wide as int may exceed INT_MAX, so clamp it. if (!Exp && UseIntrinsic && match(Op, m_UIToFP(m_Value(UIntOp))) && - UIntOp->getType()->getScalarSizeInBits() == IntSize) { + UIntOp->getType()->getScalarSizeInBits() >= IntSize) { const fltSemantics &Sem = Ty->getScalarType()->getFltSemantics(); // exp2(x) and ldexp(1.0, Clamp) both overflow for every x >= Clamp. int Clamp = llvm::ilogb(APFloat::getLargest(Sem)) + 1; @@ -2654,6 +2654,7 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) { if (!CI->hasNoInfs()) Exp = B.CreateBinaryIntrinsic(Intrinsic::umin, UIntOp, ConstantInt::get(UIntOp->getType(), Clamp)); + Exp = B.CreateTrunc(Exp, UIntOp->getType()->getWithNewBitWidth(IntSize)); } if (!Exp) diff --git a/llvm/test/Transforms/InstCombine/exp2-1.ll b/llvm/test/Transforms/InstCombine/exp2-1.ll index 72260afb37343..4826fd302f793 100644 --- a/llvm/test/Transforms/InstCombine/exp2-1.ll +++ b/llvm/test/Transforms/InstCombine/exp2-1.ll @@ -310,8 +310,8 @@ define float @uitofp_i32_scalar_intrinsic_with_ninf(i32 %x) { ; LDEXP32-NEXT: ret float [[R]] ; ; LDEXP16-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( -; LDEXP16-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; LDEXP16-NEXT: [[R:%.*]] = tail call ninf float @llvm.exp2.f32(float [[U]]) +; LDEXP16-NEXT: [[TMP1:%.*]] = trunc i32 [[X:%.*]] to i16 +; LDEXP16-NEXT: [[R:%.*]] = tail call ninf float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP1]]) ; LDEXP16-NEXT: ret float [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i32_scalar_intrinsic_with_ninf( @@ -329,23 +329,24 @@ define float @uitofp_i32_scalar_intrinsic_with_ninf(i32 %x) { define float @uitofp_i32_scalar_intrinsic(i32 %x) { ; LDEXP32-LABEL: @uitofp_i32_scalar_intrinsic( -; LDEXP32-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; LDEXP32-NEXT: [[R:%.*]] = tail call float @llvm.exp2.f32(float [[U]]) +; LDEXP32-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X:%.*]], i32 128) +; LDEXP32-NEXT: [[R:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; LDEXP32-NEXT: ret float [[R]] ; ; LDEXP16-LABEL: @uitofp_i32_scalar_intrinsic( -; LDEXP16-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; LDEXP16-NEXT: [[R:%.*]] = tail call float @llvm.exp2.f32(float [[U]]) +; LDEXP16-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X:%.*]], i32 128) +; LDEXP16-NEXT: [[TMP2:%.*]] = trunc nuw nsw i32 [[TMP1]] to i16 +; LDEXP16-NEXT: [[R:%.*]] = tail call float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP2]]) ; LDEXP16-NEXT: ret float [[R]] ; ; NOLDEXPF-LABEL: @uitofp_i32_scalar_intrinsic( -; NOLDEXPF-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; NOLDEXPF-NEXT: [[R:%.*]] = tail call float @llvm.exp2.f32(float [[U]]) +; NOLDEXPF-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X:%.*]], i32 128) +; NOLDEXPF-NEXT: [[R:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; NOLDEXPF-NEXT: ret float [[R]] ; ; NOLDEXP-LABEL: @uitofp_i32_scalar_intrinsic( -; NOLDEXP-NEXT: [[U:%.*]] = uitofp i32 [[X:%.*]] to float -; NOLDEXP-NEXT: [[R:%.*]] = tail call float @llvm.exp2.f32(float [[U]]) +; NOLDEXP-NEXT: [[TMP1:%.*]] = call i32 @llvm.umin.i32(i32 [[X:%.*]], i32 128) +; NOLDEXP-NEXT: [[R:%.*]] = tail call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; NOLDEXP-NEXT: ret float [[R]] ; %u = uitofp i32 %x to float diff --git a/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll b/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll index 6d4aebdf587a7..d0f93692141b9 100644 --- a/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll +++ b/llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll @@ -180,8 +180,8 @@ define fp128 @exp2_fp128_uitofp_i32_ninf(i32 %x) { define float @exp2_f32_uitofp_i64_ninf(i64 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i64_ninf( ; CHECK-SAME: i64 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i64 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call ninf float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[X]] to i32 +; CHECK-NEXT: [[EXP2:%.*]] = call ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i64 %x to float @@ -290,8 +290,9 @@ define ppc_fp128 @exp2_ppcf128_uitofp_i32(i32 %x) { define float @exp2_f32_uitofp_i64(i64 %x) { ; CHECK-LABEL: define float @exp2_f32_uitofp_i64( ; CHECK-SAME: i64 [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp i64 [[X]] to float -; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.exp2.f32(float [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.umin.i64(i64 [[X]], i64 128) +; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw nsw i64 [[TMP1]] to i32 +; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP2]]) ; CHECK-NEXT: ret float [[EXP2]] ; %itofp = uitofp i64 %x to float @@ -302,8 +303,9 @@ define float @exp2_f32_uitofp_i64(i64 %x) { define <2 x float> @exp2_v2f32_uitofp_v2i64(<2 x i64> %x) { ; CHECK-LABEL: define <2 x float> @exp2_v2f32_uitofp_v2i64( ; CHECK-SAME: <2 x i64> [[X:%.*]]) { -; CHECK-NEXT: [[ITOFP:%.*]] = uitofp <2 x i64> [[X]] to <2 x float> -; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[ITOFP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i64> @llvm.umin.v2i64(<2 x i64> [[X]], <2 x i64> splat (i64 128)) +; CHECK-NEXT: [[TMP2:%.*]] = trunc nuw nsw <2 x i64> [[TMP1]] to <2 x i32> +; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> splat (float 1.000000e+00), <2 x i32> [[TMP2]]) ; CHECK-NEXT: ret <2 x float> [[EXP2]] ; %itofp = uitofp <2 x i64> %x to <2 x float> _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
