Skip to content

Commit

Permalink
SimplifyLibCalls: Don't require ldexp to emit intrinsic in exp2 combi…
Browse files Browse the repository at this point in the history
…ne (llvm#92707)

When folding exp2(itofp(x)) to ldexp(1, x), don't require an ldexp
libcall to emit the intrinsic.

The intrinsic needs to be handled regardless of whether the system has a
libcall, and we have an inline implementation of ldexp already. This
fixes the instance in the exp2->ldexp fold. Another instance exists for
the pow(2) -> ldexp case

The LTO test change isn't ideal, since it's just moving the problem to
another instance where we're relying on implied libm behavior for an
intrinsic transform. Use exp10 since that's a harder case to solve in
the libcall house of cards we have.
  • Loading branch information
arsenm authored and Lukacma committed Jun 12, 2024
1 parent 03aa4e1 commit d4375c4
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 223 deletions.
13 changes: 9 additions & 4 deletions llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2376,7 +2376,13 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) {
hasFloatVersion(M, Name))
Ret = optimizeUnaryDoubleFP(CI, B, TLI, true);

const bool UseIntrinsic = CI->doesNotAccessMemory();
// If we have an llvm.exp2 intrinsic, emit the llvm.ldexp intrinsic. If we
// have the libcall, emit the libcall.
//
// TODO: In principle we should be able to just always use the intrinsic for
// any doesNotAccessMemory callsite.

const bool UseIntrinsic = Callee->isIntrinsic();
// Bail out for vectors because the code below only expects scalars.
Type *Ty = CI->getType();
if (!UseIntrinsic && Ty->isVectorTy())
Expand All @@ -2386,12 +2392,11 @@ Value *LibCallSimplifier::optimizeExp2(CallInst *CI, IRBuilderBase &B) {
// exp2(uitofp(x)) -> ldexp(1.0, zext(x)) if sizeof(x) < IntSize
Value *Op = CI->getArgOperand(0);
if ((isa<SIToFPInst>(Op) || isa<UIToFPInst>(Op)) &&
hasFloatFn(M, TLI, Ty, LibFunc_ldexp, LibFunc_ldexpf, LibFunc_ldexpl)) {
(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);

// TODO: Emitting the intrinsic should not depend on whether the libcall
// is available.
if (UseIntrinsic) {
return copyFlags(*CI, B.CreateIntrinsic(Intrinsic::ldexp,
{Ty, Exp->getType()},
Expand Down
11 changes: 5 additions & 6 deletions llvm/test/LTO/X86/triple-init2.ll
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@
; RUN: llvm-lto2 run -r %t1,main,plx -o %t2 %t1
; RUN: llvm-nm %t2.1 | FileCheck %s

; We check that LTO will be aware of target triple and prevent exp2 to ldexpf
; We check that LTO will be aware of target triple and prevent pow to exp10
; transformation on Windows.
; CHECK: U exp2f
; CHECK: U powf

target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.11.0"

declare float @llvm.pow.f32(float, float)

define dso_local i32 @main(i32 %argc, ptr nocapture readnone %argv) local_unnamed_addr {
entry:
%conv = sitofp i32 %argc to float
%exp2 = tail call float @llvm.exp2.f32(float %conv)
%exp2 = tail call float @llvm.pow.f32(float 10.0, float %conv)
%conv1 = fptosi float %exp2 to i32
ret i32 %conv1
}

; Function Attrs: nounwind readnone speculatable
declare float @llvm.exp2.f32(float)

29 changes: 17 additions & 12 deletions llvm/test/Transforms/InstCombine/exp2-1.ll
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ define double @test_simplify9(i8 zeroext %x) {
; NOLDEXPF-NEXT: ret double [[RET]]
;
; NOLDEXP-LABEL: @test_simplify9(
; NOLDEXP-NEXT: [[CONV:%.*]] = uitofp i8 [[X:%.*]] to double
; NOLDEXP-NEXT: [[RET:%.*]] = call double @llvm.exp2.f64(double [[CONV]])
; NOLDEXP-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; NOLDEXP-NEXT: [[RET:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]])
; NOLDEXP-NEXT: ret double [[RET]]
;
%conv = uitofp i8 %x to double
Expand All @@ -263,13 +263,13 @@ define float @test_simplify10(i8 zeroext %x) {
; LDEXP16-NEXT: ret float [[RET]]
;
; NOLDEXPF-LABEL: @test_simplify10(
; NOLDEXPF-NEXT: [[CONV:%.*]] = uitofp i8 [[X:%.*]] to float
; NOLDEXPF-NEXT: [[RET:%.*]] = call float @llvm.exp2.f32(float [[CONV]])
; NOLDEXPF-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; NOLDEXPF-NEXT: [[RET:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; NOLDEXPF-NEXT: ret float [[RET]]
;
; NOLDEXP-LABEL: @test_simplify10(
; NOLDEXP-NEXT: [[CONV:%.*]] = uitofp i8 [[X:%.*]] to float
; NOLDEXP-NEXT: [[RET:%.*]] = call float @llvm.exp2.f32(float [[CONV]])
; NOLDEXP-NEXT: [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
; NOLDEXP-NEXT: [[RET:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; NOLDEXP-NEXT: ret float [[RET]]
;
%conv = uitofp i8 %x to float
Expand All @@ -289,13 +289,13 @@ define float @sitofp_scalar_intrinsic_with_FMF(i8 %x) {
; LDEXP16-NEXT: ret float [[R]]
;
; NOLDEXPF-LABEL: @sitofp_scalar_intrinsic_with_FMF(
; NOLDEXPF-NEXT: [[S:%.*]] = sitofp i8 [[X:%.*]] to float
; NOLDEXPF-NEXT: [[R:%.*]] = tail call nnan float @llvm.exp2.f32(float [[S]])
; NOLDEXPF-NEXT: [[TMP1:%.*]] = sext i8 [[X:%.*]] to i32
; NOLDEXPF-NEXT: [[R:%.*]] = tail call nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; NOLDEXPF-NEXT: ret float [[R]]
;
; NOLDEXP-LABEL: @sitofp_scalar_intrinsic_with_FMF(
; NOLDEXP-NEXT: [[S:%.*]] = sitofp i8 [[X:%.*]] to float
; NOLDEXP-NEXT: [[R:%.*]] = tail call nnan float @llvm.exp2.f32(float [[S]])
; NOLDEXP-NEXT: [[TMP1:%.*]] = sext i8 [[X:%.*]] to i32
; NOLDEXP-NEXT: [[R:%.*]] = tail call nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; NOLDEXP-NEXT: ret float [[R]]
;
%s = sitofp i8 %x to float
Expand All @@ -317,9 +317,14 @@ define <2 x float> @sitofp_vector_intrinsic_with_FMF(<2 x i8> %x) {
; LDEXP16-NEXT: [[R:%.*]] = call nnan <2 x float> @llvm.ldexp.v2f32.v2i16(<2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x i16> [[TMP1]])
; LDEXP16-NEXT: ret <2 x float> [[R]]
;
; NOLDEXPF-LABEL: @sitofp_vector_intrinsic_with_FMF(
; NOLDEXPF-NEXT: [[TMP1:%.*]] = sext <2 x i8> [[X:%.*]] to <2 x i32>
; NOLDEXPF-NEXT: [[R:%.*]] = call nnan <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x i32> [[TMP1]])
; NOLDEXPF-NEXT: ret <2 x float> [[R]]
;
; NOLDEXP-LABEL: @sitofp_vector_intrinsic_with_FMF(
; NOLDEXP-NEXT: [[S:%.*]] = sitofp <2 x i8> [[X:%.*]] to <2 x float>
; NOLDEXP-NEXT: [[R:%.*]] = call nnan <2 x float> @llvm.exp2.v2f32(<2 x float> [[S]])
; NOLDEXP-NEXT: [[TMP1:%.*]] = sext <2 x i8> [[X:%.*]] to <2 x i32>
; NOLDEXP-NEXT: [[R:%.*]] = call nnan <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x i32> [[TMP1]])
; NOLDEXP-NEXT: ret <2 x float> [[R]]
;
%s = sitofp <2 x i8> %x to <2 x float>
Expand Down
120 changes: 39 additions & 81 deletions llvm/test/Transforms/InstCombine/exp2-to-ldexp.ll
Original file line number Diff line number Diff line change
@@ -1,73 +1,49 @@
; 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
; RUN: opt -S -passes=instcombine %s | FileCheck %s
; RUN: opt -S -passes=instcombine -disable-builtin=ldexpf -disable-builtin=ldexp -disable-builtin=ldexpl %s | FileCheck %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]]
; CHECK-LABEL: define float @exp2_f32_sitofp_i8(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; CHECK-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]]
; CHECK-LABEL: define float @exp2_f32_sitofp_i8_flags(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call nnan ninf float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; CHECK-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) {
; LDEXP-LABEL: define <2 x float> @exp2_v2f32_sitofp_v2i8(
; LDEXP-SAME: <2 x i8> [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext <2 x i8> [[X]] to <2 x i32>
; LDEXP-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x i32> [[TMP1]])
; LDEXP-NEXT: ret <2 x float> [[EXP2]]
;
; NOLDEXP-LABEL: define <2 x float> @exp2_v2f32_sitofp_v2i8(
; NOLDEXP-SAME: <2 x i8> [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp <2 x i8> [[X]] to <2 x float>
; NOLDEXP-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[ITOFP]])
; NOLDEXP-NEXT: ret <2 x float> [[EXP2]]
; CHECK-LABEL: define <2 x float> @exp2_v2f32_sitofp_v2i8(
; CHECK-SAME: <2 x i8> [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext <2 x i8> [[X]] to <2 x i32>
; CHECK-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x i32> [[TMP1]])
; 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]]
; CHECK-LABEL: define float @exp2_f32_uitofp_i8(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[TMP1]])
; CHECK-NEXT: ret float [[EXP2]]
;
%itofp = uitofp i8 %x to float
%exp2 = call float @llvm.exp2.f32(float %itofp)
Expand All @@ -77,8 +53,8 @@ define float @exp2_f32_uitofp_i8(i8 %x) {
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: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call half @llvm.ldexp.f16.i32(half 0xH3C00, i32 [[TMP1]])
; CHECK-NEXT: ret half [[EXP2]]
;
%itofp = sitofp i8 %x to half
Expand All @@ -87,53 +63,35 @@ define half @exp2_f16_sitofp_i8(i8 %x) {
}

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]]
; CHECK-LABEL: define double @exp2_f64_sitofp_i8(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call double @llvm.ldexp.f64.i32(double 1.000000e+00, i32 [[TMP1]])
; CHECK-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]]
; CHECK-LABEL: define fp128 @exp2_fp128_sitofp_i8(
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: [[EXP2:%.*]] = call fp128 @llvm.ldexp.f128.i32(fp128 0xL00000000000000003FFF000000000000, i32 [[TMP1]])
; CHECK-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) {
; LDEXP-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(
; LDEXP-SAME: <vscale x 4 x i8> [[X:%.*]]) {
; LDEXP-NEXT: [[TMP1:%.*]] = sext <vscale x 4 x i8> [[X]] to <vscale x 4 x i32>
; LDEXP-NEXT: [[EXP2:%.*]] = call <vscale x 4 x float> @llvm.ldexp.nxv4f32.nxv4i32(<vscale x 4 x float> shufflevector (<vscale x 4 x float> insertelement (<vscale x 4 x float> poison, float 1.000000e+00, i64 0), <vscale x 4 x float> poison, <vscale x 4 x i32> zeroinitializer), <vscale x 4 x i32> [[TMP1]])
; LDEXP-NEXT: ret <vscale x 4 x float> [[EXP2]]
;
; NOLDEXP-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(
; NOLDEXP-SAME: <vscale x 4 x i8> [[X:%.*]]) {
; NOLDEXP-NEXT: [[ITOFP:%.*]] = sitofp <vscale x 4 x i8> [[X]] to <vscale x 4 x float>
; NOLDEXP-NEXT: [[EXP2:%.*]] = call <vscale x 4 x float> @llvm.exp2.nxv4f32(<vscale x 4 x float> [[ITOFP]])
; NOLDEXP-NEXT: ret <vscale x 4 x float> [[EXP2]]
; CHECK-LABEL: define <vscale x 4 x float> @exp2_nxv4f32_sitofp_i8(
; CHECK-SAME: <vscale x 4 x i8> [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext <vscale x 4 x i8> [[X]] to <vscale x 4 x i32>
; CHECK-NEXT: [[EXP2:%.*]] = call <vscale x 4 x float> @llvm.ldexp.nxv4f32.nxv4i32(<vscale x 4 x float> shufflevector (<vscale x 4 x float> insertelement (<vscale x 4 x float> poison, float 1.000000e+00, i64 0), <vscale x 4 x float> poison, <vscale x 4 x i32> zeroinitializer), <vscale x 4 x i32> [[TMP1]])
; 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)
Expand Down
Loading

0 comments on commit d4375c4

Please sign in to comment.