Skip to content

InstCombine: Emit ldexp intrinsic in exp2->ldexp combine #92039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2389,12 +2389,21 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) {
if ((isa<SIToFPInst>(Op) || isa<UIToFPInst>(Op)) &&
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);

// TODO: Emitting the intrinsic should not depend on whether the libcall
// is available.
if (CI->doesNotAccessMemory()) {
return copyFlags(*CI, B.CreateIntrinsic(Intrinsic::ldexp,
{Ty, Exp->getType()},
{One, Exp}, CI));
}

IRBuilderBase::FastMathFlagGuard Guard(B);
B.setFastMathFlags(CI->getFastMathFlags());
return copyFlags(
*CI, emitBinaryFloatFnCall(ConstantFP::get(Ty, 1.0), Exp, TLI,
LibFunc_ldexp, LibFunc_ldexpf,
LibFunc_ldexpl, B, AttributeList()));
return copyFlags(*CI, emitBinaryFloatFnCall(
One, Exp, TLI, LibFunc_ldexp, LibFunc_ldexpf,
LibFunc_ldexpl, B, AttributeList()));
}
}

Expand Down
123 changes: 109 additions & 14 deletions llvm/test/Transforms/InstCombine/exp2-1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ declare float @exp2f(float)
declare double @llvm.exp2.f64(double)
declare float @llvm.exp2.f32(float)
declare <2 x float> @llvm.exp2.v2f32(<2 x float>)
declare fp128 @exp2l(fp128)


; Check exp2(sitofp(x)) -> ldexp(1.0, sext(x)).
Expand Down Expand Up @@ -227,18 +228,18 @@ define float @test_simplify8(i8 zeroext %x) {
define double @test_simplify9(i8 zeroext %x) {
; LDEXP32-LABEL: @test_simplify9(
; LDEXP32-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; LDEXP32-NEXT: [[LDEXP:%.*]] = call double @ldexp(double 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret double [[LDEXP]]
; LDEXP32-NEXT: [[RET:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret double [[RET]]
;
; LDEXP16-LABEL: @test_simplify9(
; LDEXP16-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i16
; LDEXP16-NEXT: [[LDEXP:%.*]] = call double @ldexp(double 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret double [[LDEXP]]
; LDEXP16-NEXT: [[RET:%.*]] = call double @llvm.ldexp.f64.i16(double 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret double [[RET]]
;
; NOLDEXPF-LABEL: @test_simplify9(
; NOLDEXPF-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; NOLDEXPF-NEXT: [[LDEXP:%.*]] = call double @ldexp(double 1.000000e+00, i32 [[TMP1]])
; NOLDEXPF-NEXT: ret double [[LDEXP]]
; NOLDEXPF-NEXT: [[RET:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]])
; NOLDEXPF-NEXT: ret double [[RET]]
;
; NOLDEXP-LABEL: @test_simplify9(
; NOLDEXP-NEXT: [[CONV:%.*]] = uitofp i8 [[X:%.*]] to double
Expand All @@ -253,13 +254,13 @@ define double @test_simplify9(i8 zeroext %x) {
define float @test_simplify10(i8 zeroext %x) {
; LDEXP32-LABEL: @test_simplify10(
; LDEXP32-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; LDEXP32-NEXT: [[LDEXPF:%.*]] = call float @ldexpf(float 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret float [[LDEXPF]]
; LDEXP32-NEXT: [[RET:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret float [[RET]]
;
; LDEXP16-LABEL: @test_simplify10(
; LDEXP16-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i16
; LDEXP16-NEXT: [[LDEXPF:%.*]] = call float @ldexpf(float 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret float [[LDEXPF]]
; LDEXP16-NEXT: [[RET:%.*]] = call float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret float [[RET]]
;
; NOLDEXPF-LABEL: @test_simplify10(
; NOLDEXPF-NEXT: [[CONV:%.*]] = uitofp i8 [[X:%.*]] to float
Expand All @@ -279,13 +280,13 @@ define float @test_simplify10(i8 zeroext %x) {
define float @sitofp_scalar_intrinsic_with_FMF(i8 %x) {
; LDEXP32-LABEL: @sitofp_scalar_intrinsic_with_FMF(
; LDEXP32-NEXT: [[TMP1:%.*]] = sext i8 [[X:%.*]] to i32
; LDEXP32-NEXT: [[LDEXPF:%.*]] = tail call nnan float @ldexpf(float 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret float [[LDEXPF]]
; LDEXP32-NEXT: [[R:%.*]] = tail call nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; LDEXP32-NEXT: ret float [[R]]
;
; LDEXP16-LABEL: @sitofp_scalar_intrinsic_with_FMF(
; LDEXP16-NEXT: [[TMP1:%.*]] = sext i8 [[X:%.*]] to i16
; LDEXP16-NEXT: [[LDEXPF:%.*]] = tail call nnan float @ldexpf(float 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret float [[LDEXPF]]
; LDEXP16-NEXT: [[R:%.*]] = tail call nnan float @llvm.ldexp.f32.i16(float 1.000000e+00, i16 [[TMP1]])
; LDEXP16-NEXT: ret float [[R]]
;
; NOLDEXPF-LABEL: @sitofp_scalar_intrinsic_with_FMF(
; NOLDEXPF-NEXT: [[S:%.*]] = sitofp i8 [[X:%.*]] to float
Expand Down Expand Up @@ -330,3 +331,97 @@ define <2 x float> @sitofp_vector_intrinsic_with_FMF(<2 x i8> %x) {
%r = call nnan <2 x float> @llvm.exp2.v2f32(<2 x float> %s)
ret <2 x float> %r
}

define double @test_readonly_exp2_f64_of_sitofp(i32 %x) {
; LDEXP32-LABEL: @test_readonly_exp2_f64_of_sitofp(
; LDEXP32-NEXT: [[LDEXP:%.*]] = call double @ldexp(double 1.000000e+00, i32 [[X:%.*]])
; LDEXP32-NEXT: ret double [[LDEXP]]
;
; LDEXP16-LABEL: @test_readonly_exp2_f64_of_sitofp(
; LDEXP16-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to double
; LDEXP16-NEXT: [[RET:%.*]] = call double @exp2(double [[CONV]]) #[[ATTR2:[0-9]+]]
; LDEXP16-NEXT: ret double [[RET]]
;
; NOLDEXPF-LABEL: @test_readonly_exp2_f64_of_sitofp(
; NOLDEXPF-NEXT: [[LDEXP:%.*]] = call double @ldexp(double 1.000000e+00, i32 [[X:%.*]])
; NOLDEXPF-NEXT: ret double [[LDEXP]]
;
; NOLDEXP-LABEL: @test_readonly_exp2_f64_of_sitofp(
; NOLDEXP-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to double
; NOLDEXP-NEXT: [[RET:%.*]] = call double @exp2(double [[CONV]]) #[[ATTR1:[0-9]+]]
; NOLDEXP-NEXT: ret double [[RET]]
;
%conv = sitofp i32 %x to double
%ret = call double @exp2(double %conv) readonly
ret double %ret
}

define float @test_readonly_exp2f_f32_of_sitofp(i32 %x) {
; LDEXP32-LABEL: @test_readonly_exp2f_f32_of_sitofp(
; LDEXP32-NEXT: [[LDEXPF:%.*]] = call float @ldexpf(float 1.000000e+00, i32 [[X:%.*]])
; LDEXP32-NEXT: ret float [[LDEXPF]]
;
; LDEXP16-LABEL: @test_readonly_exp2f_f32_of_sitofp(
; LDEXP16-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; LDEXP16-NEXT: [[RET:%.*]] = call float @exp2f(float [[CONV]]) #[[ATTR2]]
; LDEXP16-NEXT: ret float [[RET]]
;
; NOLDEXPF-LABEL: @test_readonly_exp2f_f32_of_sitofp(
; NOLDEXPF-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; NOLDEXPF-NEXT: [[RET:%.*]] = call float @exp2f(float [[CONV]]) #[[ATTR2:[0-9]+]]
; NOLDEXPF-NEXT: ret float [[RET]]
;
; NOLDEXP-LABEL: @test_readonly_exp2f_f32_of_sitofp(
; NOLDEXP-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; NOLDEXP-NEXT: [[RET:%.*]] = call float @exp2f(float [[CONV]]) #[[ATTR1]]
; NOLDEXP-NEXT: ret float [[RET]]
;
%conv = sitofp i32 %x to float
%ret = call float @exp2f(float %conv) readonly
ret float %ret
}

define fp128 @test_readonly_exp2l_fp128_of_sitofp(i32 %x) {
; LDEXP32-LABEL: @test_readonly_exp2l_fp128_of_sitofp(
; LDEXP32-NEXT: [[LDEXPL:%.*]] = call fp128 @ldexpl(fp128 0xL00000000000000003FFF000000000000, i32 [[X:%.*]])
; LDEXP32-NEXT: ret fp128 [[LDEXPL]]
;
; LDEXP16-LABEL: @test_readonly_exp2l_fp128_of_sitofp(
; LDEXP16-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to fp128
; LDEXP16-NEXT: [[RET:%.*]] = call fp128 @exp2l(fp128 [[CONV]]) #[[ATTR2]]
; LDEXP16-NEXT: ret fp128 [[RET]]
;
; NOLDEXP-LABEL: @test_readonly_exp2l_fp128_of_sitofp(
; NOLDEXP-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to fp128
; NOLDEXP-NEXT: [[RET:%.*]] = call fp128 @exp2l(fp128 [[CONV]]) #[[ATTR1]]
; NOLDEXP-NEXT: ret fp128 [[RET]]
;
%conv = sitofp i32 %x to fp128
%ret = call fp128 @exp2l(fp128 %conv) readonly
ret fp128 %ret
}

define float @test_readonly_exp2f_f32_of_sitofp_flags(i32 %x) {
; LDEXP32-LABEL: @test_readonly_exp2f_f32_of_sitofp_flags(
; LDEXP32-NEXT: [[LDEXPF:%.*]] = call nnan ninf float @ldexpf(float 1.000000e+00, i32 [[X:%.*]])
; LDEXP32-NEXT: ret float [[LDEXPF]]
;
; LDEXP16-LABEL: @test_readonly_exp2f_f32_of_sitofp_flags(
; LDEXP16-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; LDEXP16-NEXT: [[RET:%.*]] = call nnan ninf float @exp2f(float [[CONV]]) #[[ATTR2]]
; LDEXP16-NEXT: ret float [[RET]]
;
; NOLDEXPF-LABEL: @test_readonly_exp2f_f32_of_sitofp_flags(
; NOLDEXPF-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; NOLDEXPF-NEXT: [[RET:%.*]] = call nnan ninf float @exp2f(float [[CONV]]) #[[ATTR2]]
; NOLDEXPF-NEXT: ret float [[RET]]
;
; NOLDEXP-LABEL: @test_readonly_exp2f_f32_of_sitofp_flags(
; NOLDEXP-NEXT: [[CONV:%.*]] = sitofp i32 [[X:%.*]] to float
; NOLDEXP-NEXT: [[RET:%.*]] = call nnan ninf float @exp2f(float [[CONV]]) #[[ATTR1]]
; NOLDEXP-NEXT: ret float [[RET]]
;
%conv = sitofp i32 %x to float
%ret = call nnan ninf float @exp2f(float %conv) readonly
ret float %ret
}
150 changes: 150 additions & 0 deletions llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -S -passes=instcombine %s | FileCheck -check-prefixes=CHECK,LDEXP %s
; RUN: opt -S -passes=instcombine -disable-builtin=ldexpf -disable-builtin=ldexp -disable-builtin=ldexpl %s | FileCheck -check-prefixes=CHECK,NOLDEXP %s

define float @exp2_f32_sitofp_i8(i8 %x) {
; LDEXP-LABEL: define float @exp2_f32_sitofp_i8(
; LDEXP-SAME: i8 [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; LDEXP-NEXT: [[LDEXPF:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; LDEXP-NEXT: ret float [[LDEXPF]]
;
; NOLDEXP-LABEL: define float @exp2_f32_sitofp_i8(
; NOLDEXP-SAME: i8 [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp i8 [[X]] to float
; NOLDEXP-NEXT: [[EXP2:%.*]] = call float @llvm.exp2.f32(float [[ITOFP]])
; NOLDEXP-NEXT: ret float [[EXP2]]
;
%itofp = sitofp i8 %x to float
%exp2 = call float @llvm.exp2.f32(float %itofp)
ret float %exp2
}

define float @exp2_f32_sitofp_i8_flags(i8 %x) {
; LDEXP-LABEL: define float @exp2_f32_sitofp_i8_flags(
; LDEXP-SAME: i8 [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; LDEXP-NEXT: [[LDEXPF:%.*]] = call nnan ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; LDEXP-NEXT: ret float [[LDEXPF]]
;
; NOLDEXP-LABEL: define float @exp2_f32_sitofp_i8_flags(
; NOLDEXP-SAME: i8 [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp i8 [[X]] to float
; NOLDEXP-NEXT: [[EXP2:%.*]] = call nnan ninf float @llvm.exp2.f32(float [[ITOFP]])
; NOLDEXP-NEXT: ret float [[EXP2]]
;
%itofp = sitofp i8 %x to float
%exp2 = call nnan ninf float @llvm.exp2.f32(float %itofp)
ret float %exp2
}

define <2 x float> @exp2_v2f32_sitofp_v2i8(<2 x i8> %x) {
; CHECK-LABEL: define <2 x float> @exp2_v2f32_sitofp_v2i8(
; CHECK-SAME: <2 x i8> [[X:%.*]]) {
; CHECK-NEXT: [[ITOFP:%.*]] = sitofp <2 x i8> [[X]] to <2 x float>
; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[ITOFP]])
; CHECK-NEXT: ret <2 x float> [[EXP2]]
;
%itofp = sitofp <2 x i8> %x to <2 x float>
%exp2 = call <2 x float> @llvm.exp2.v2f32(<2 x float> %itofp)
ret <2 x float> %exp2
}

define float @exp2_f32_uitofp_i8(i8 %x) {
; LDEXP-LABEL: define float @exp2_f32_uitofp_i8(
; LDEXP-SAME: i8 [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i32
; LDEXP-NEXT: [[LDEXPF:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; LDEXP-NEXT: ret float [[LDEXPF]]
;
; NOLDEXP-LABEL: define float @exp2_f32_uitofp_i8(
; NOLDEXP-SAME: i8 [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = uitofp i8 [[X]] to float
; NOLDEXP-NEXT: [[EXP2:%.*]] = call float @llvm.exp2.f32(float [[ITOFP]])
; NOLDEXP-NEXT: ret float [[EXP2]]
;
%itofp = uitofp i8 %x to float
%exp2 = call float @llvm.exp2.f32(float %itofp)
ret float %exp2
}

define half @exp2_f16_sitofp_i8(i8 %x) {
; CHECK-LABEL: define half @exp2_f16_sitofp_i8(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[ITOFP:%.*]] = sitofp i8 [[X]] to half
; CHECK-NEXT: [[EXP2:%.*]] = call half @llvm.exp2.f16(half [[ITOFP]])
; CHECK-NEXT: ret half [[EXP2]]
;
%itofp = sitofp i8 %x to half
%exp2 = call half @llvm.exp2.f16(half %itofp)
ret half %exp2
}

define double @exp2_f64_sitofp_i8(i8 %x) {
; LDEXP-LABEL: define double @exp2_f64_sitofp_i8(
; LDEXP-SAME: i8 [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; LDEXP-NEXT: [[LDEXP:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]])
; LDEXP-NEXT: ret double [[LDEXP]]
;
; NOLDEXP-LABEL: define double @exp2_f64_sitofp_i8(
; NOLDEXP-SAME: i8 [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp i8 [[X]] to double
; NOLDEXP-NEXT: [[EXP2:%.*]] = call double @llvm.exp2.f64(double [[ITOFP]])
; NOLDEXP-NEXT: ret double [[EXP2]]
;
%itofp = sitofp i8 %x to double
%exp2 = call double @llvm.exp2.f64(double %itofp)
ret double %exp2
}

define fp128 @exp2_fp128_sitofp_i8(i8 %x) {
; LDEXP-LABEL: define fp128 @exp2_fp128_sitofp_i8(
; LDEXP-SAME: i8 [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; LDEXP-NEXT: [[LDEXPL:%.*]] = call fp128 @llvm.ldexp.f128.i32(fp128 0xL00000000000000003FFF000000000000, i32 [[TMP1]])
; LDEXP-NEXT: ret fp128 [[LDEXPL]]
;
; NOLDEXP-LABEL: define fp128 @exp2_fp128_sitofp_i8(
; NOLDEXP-SAME: i8 [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp i8 [[X]] to fp128
; NOLDEXP-NEXT: [[EXP2:%.*]] = call fp128 @llvm.exp2.f128(fp128 [[ITOFP]])
; NOLDEXP-NEXT: ret fp128 [[EXP2]]
;
%itofp = sitofp i8 %x to fp128
%exp2 = call fp128 @llvm.exp2.fp128(fp128 %itofp)
ret fp128 %exp2
}

define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(<vscale x 4 x i8> %x) {
; CHECK-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(
; CHECK-SAME: <vscale x 4 x i8> [[X:%.*]]) {
; CHECK-NEXT: [[ITOFP:%.*]] = sitofp <vscale x 4 x i8> [[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: ret <vscale x 4 x float> [[EXP2]]
;
%itofp = sitofp <vscale x 4 x i8> %x to <vscale x 4 x float>
%exp2 = call <vscale x 4 x float> @llvm.exp2.nxv4f32(<vscale x 4 x float> %itofp)
ret <vscale x 4 x float> %exp2
}

; FIXME: This asserts
; define bfloat @exp2_bf16_sitofp_i8(i8 %x) {
; %itofp = sitofp i8 %x to bfloat
; %exp2 = call bfloat @llvm.exp2.bf16(bfloat %itofp)
; ret bfloat %exp2
; }

; FIXME: This asserts
; define ppc_fp128 @exp2_ppc_fp128_sitofp_i8(i8 %x) {
; %itofp = sitofp i8 %x to ppc_fp128
; %exp2 = call ppc_fp128 @llvm.exp2.ppcf128(ppc_fp128 %itofp)
; ret ppc_fp128 %exp2
; }

; FIXME: This asserts
; define x86_fp80 @exp2_x86_fp80_sitofp_i8(i8 %x) {
; %itofp = sitofp i8 %x to x86_fp80
; %exp2 = call x86_fp80 @llvm.exp2.f80(x86_fp80 %itofp)
; ret x86_fp80 %exp2
; }
Loading