Skip to content

Commit 5b77ed4

Browse files
authored
InstCombine: Try to fold ldexp with select of power operand (#97354)
This makes it more likely a constant value can fold into the source operand.
1 parent 1a42255 commit 5b77ed4

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

llvm/include/llvm/IR/IRBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,14 @@ class IRBuilderBase {
10231023
Name);
10241024
}
10251025

1026+
/// Create call to the ldexp intrinsic.
1027+
Value *CreateLdexp(Value *Src, Value *Exp, Instruction *FMFSource = nullptr,
1028+
const Twine &Name = "") {
1029+
assert(!IsFPConstrained && "TODO: Support strictfp");
1030+
return CreateIntrinsic(Intrinsic::ldexp, {Src->getType(), Exp->getType()},
1031+
{Src, Exp}, FMFSource, Name);
1032+
}
1033+
10261034
/// Create a call to the arithmetic_fence intrinsic.
10271035
CallInst *CreateArithmeticFence(Value *Val, Type *DstType,
10281036
const Twine &Name = "") {

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,31 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
26412641
return BinaryOperator::CreateFMulFMF(Src, Select, II);
26422642
}
26432643

2644+
// ldexp(x, c ? exp : 0) -> c ? ldexp(x, exp) : x
2645+
// ldexp(x, c ? 0 : exp) -> c ? x : ldexp(x, exp)
2646+
///
2647+
// TODO: If we cared, should insert a canonicalize for x
2648+
Value *SelectCond, *SelectLHS, *SelectRHS;
2649+
if (match(II->getArgOperand(1),
2650+
m_OneUse(m_Select(m_Value(SelectCond), m_Value(SelectLHS),
2651+
m_Value(SelectRHS))))) {
2652+
Value *NewLdexp = nullptr;
2653+
Value *Select = nullptr;
2654+
if (match(SelectRHS, m_ZeroInt())) {
2655+
NewLdexp = Builder.CreateLdexp(Src, SelectLHS);
2656+
Select = Builder.CreateSelect(SelectCond, NewLdexp, Src);
2657+
} else if (match(SelectLHS, m_ZeroInt())) {
2658+
NewLdexp = Builder.CreateLdexp(Src, SelectRHS);
2659+
Select = Builder.CreateSelect(SelectCond, Src, NewLdexp);
2660+
}
2661+
2662+
if (NewLdexp) {
2663+
Select->takeName(II);
2664+
cast<Instruction>(NewLdexp)->copyFastMathFlags(II);
2665+
return replaceInstUsesWith(*II, Select);
2666+
}
2667+
}
2668+
26442669
break;
26452670
}
26462671
case Intrinsic::ptrauth_auth:

llvm/test/Transforms/InstCombine/ldexp.ll

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ define float @ldexp_2_flags(float %x) {
870870
define float @ldexp_metadata(float %x) {
871871
; CHECK-LABEL: define float @ldexp_metadata
872872
; CHECK-SAME: (float [[X:%.*]]) {
873-
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo !2
873+
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo [[META2:![0-9]+]]
874874
; CHECK-NEXT: ret float [[LDEXP]]
875875
;
876876
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 2), !foo !2
@@ -889,6 +889,132 @@ define float @ldexp_8_contractable(float %x, float %y) {
889889
ret float %fadd
890890
}
891891

892+
define float @ldexp_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
893+
; CHECK-LABEL: define float @ldexp_f32_mask_select_0
894+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
895+
; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
896+
; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
897+
; CHECK-NEXT: ret float [[LDEXP]]
898+
;
899+
%select = select i1 %cond, i32 %y, i32 0
900+
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
901+
ret float %ldexp
902+
}
903+
904+
define float @ldexp_nnan_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
905+
; CHECK-LABEL: define float @ldexp_nnan_f32_mask_select_0
906+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
907+
; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
908+
; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
909+
; CHECK-NEXT: ret float [[LDEXP]]
910+
;
911+
%select = select i1 %cond, i32 %y, i32 0
912+
%ldexp = call nnan float @llvm.ldexp.f32.i32(float %x, i32 %select)
913+
ret float %ldexp
914+
}
915+
916+
define float @ldexp_flags_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
917+
; CHECK-LABEL: define float @ldexp_flags_f32_mask_select_0
918+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
919+
; CHECK-NEXT: [[TMP1:%.*]] = call ninf nsz float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
920+
; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
921+
; CHECK-NEXT: ret float [[LDEXP]]
922+
;
923+
%select = select i1 %cond, i32 %y, i32 0
924+
%ldexp = call nsz ninf float @llvm.ldexp.f32.i32(float %x, i32 %select)
925+
ret float %ldexp
926+
}
927+
928+
define float @ldexp_f32_mask_select_0_swap(i1 %cond, float %x, i32 %y) {
929+
; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap
930+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
931+
; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
932+
; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[X]], float [[TMP1]]
933+
; CHECK-NEXT: ret float [[LDEXP]]
934+
;
935+
%select = select i1 %cond, i32 0, i32 %y
936+
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
937+
ret float %ldexp
938+
}
939+
940+
define float @ldexp_f32_mask_select_1(i1 %cond, float %x, i32 %y) {
941+
; CHECK-LABEL: define float @ldexp_f32_mask_select_1
942+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
943+
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 1
944+
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
945+
; CHECK-NEXT: ret float [[LDEXP]]
946+
;
947+
%select = select i1 %cond, i32 %y, i32 1
948+
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
949+
ret float %ldexp
950+
}
951+
952+
define float @ldexp_f32_mask_select_0_multi_use(i1 %cond, float %x, i32 %y, ptr %ptr) {
953+
; CHECK-LABEL: define float @ldexp_f32_mask_select_0_multi_use
954+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr [[PTR:%.*]]) {
955+
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
956+
; CHECK-NEXT: store i32 [[SELECT]], ptr [[PTR]], align 4
957+
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
958+
; CHECK-NEXT: ret float [[LDEXP]]
959+
;
960+
%select = select i1 %cond, i32 %y, i32 0
961+
store i32 %select, ptr %ptr
962+
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
963+
ret float %ldexp
964+
}
965+
966+
define float @ldexp_f32_mask_select_0_swap_multi_use(i1 %cond, float %x, i32 %y, ptr %ptr) {
967+
; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap_multi_use
968+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr [[PTR:%.*]]) {
969+
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 0, i32 [[Y]]
970+
; CHECK-NEXT: store i32 [[SELECT]], ptr [[PTR]], align 4
971+
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
972+
; CHECK-NEXT: ret float [[LDEXP]]
973+
;
974+
%select = select i1 %cond, i32 0, i32 %y
975+
store i32 %select, ptr %ptr
976+
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
977+
ret float %ldexp
978+
}
979+
980+
define float @ldexp_f32_mask_select_0_strictfp(i1 %cond, float %x, i32 %y) #0 {
981+
; CHECK-LABEL: define float @ldexp_f32_mask_select_0_strictfp
982+
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR1:[0-9]+]] {
983+
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
984+
; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X]], i32 [[SELECT]], metadata !"round.dynamic", metadata !"fpexcept.strict")
985+
; CHECK-NEXT: ret float [[LDEXP]]
986+
;
987+
%select = select i1 %cond, i32 %y, i32 0
988+
%ldexp = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 %select, metadata !"round.dynamic", metadata !"fpexcept.strict")
989+
ret float %ldexp
990+
}
991+
992+
define <2 x float> @ldexp_v2f32_mask_select_0(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
993+
; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0
994+
; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
995+
; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
996+
; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[TMP1]], <2 x float> [[X]]
997+
; CHECK-NEXT: ret <2 x float> [[LDEXP]]
998+
;
999+
%select = select <2 x i1> %cond, <2 x i32> %y, <2 x i32> zeroinitializer
1000+
%ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
1001+
ret <2 x float> %ldexp
1002+
}
1003+
1004+
define <2 x float> @ldexp_v2f32_mask_select_0_swap(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
1005+
; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0_swap
1006+
; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
1007+
; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
1008+
; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[X]], <2 x float> [[TMP1]]
1009+
; CHECK-NEXT: ret <2 x float> [[LDEXP]]
1010+
;
1011+
%select = select <2 x i1> %cond, <2 x i32> zeroinitializer, <2 x i32> %y
1012+
%ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
1013+
ret <2 x float> %ldexp
1014+
}
1015+
1016+
attributes #0 = { strictfp }
1017+
8921018
!0 = !{i32 -127, i32 0}
8931019
!1 = !{i32 0, i32 127}
8941020
!2 = !{}

0 commit comments

Comments
 (0)