[llvm] InstCombine: Add baseline checks for ldexp(x, c ? x : 0) combine (PR #97354)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 1 14:40:29 PDT 2024
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/97354
InstCombine: Add baseline checks for ldexp(x, c ? x : 0) combine
InstCombine: Try to fold ldexp with select of power operand
This makes it more likely a constant value can fold into the source
operand.
>From d8ef055901f80ed746ff459a3ad53ee5452f8dea Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 11 Nov 2022 21:52:08 -0800
Subject: [PATCH 1/2] InstCombine: Add baseline checks for ldexp(x, c ? x : 0)
combine
---
llvm/test/Transforms/InstCombine/ldexp.ll | 128 +++++++++++++++++++++-
1 file changed, 127 insertions(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/InstCombine/ldexp.ll b/llvm/test/Transforms/InstCombine/ldexp.ll
index 3ede7d913b525..7d0d33487845b 100644
--- a/llvm/test/Transforms/InstCombine/ldexp.ll
+++ b/llvm/test/Transforms/InstCombine/ldexp.ll
@@ -870,7 +870,7 @@ define float @ldexp_2_flags(float %x) {
define float @ldexp_metadata(float %x) {
; CHECK-LABEL: define float @ldexp_metadata
; CHECK-SAME: (float [[X:%.*]]) {
-; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo !2
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo [[META2:![0-9]+]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%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) {
ret float %fadd
}
+define float @ldexp_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_nnan_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_nnan_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: [[LDEXP:%.*]] = call nnan float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call nnan float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_flags_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_flags_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: [[LDEXP:%.*]] = call ninf nsz float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call nsz ninf float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_swap(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 0, i32 [[Y]]
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 0, i32 %y
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_1(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_1
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 1
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 1
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_multi_use(i1 %cond, float %x, i32 %y, ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_multi_use
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: store i32 [[SELECT]], ptr addrspace(1) [[PTR]], align 4
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ store i32 %select, ptr addrspace(1) %ptr
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_swap_multi_use(i1 %cond, float %x, i32 %y, ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap_multi_use
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 0, i32 [[Y]]
+; CHECK-NEXT: store i32 [[SELECT]], ptr addrspace(1) [[PTR]], align 4
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 0, i32 %y
+ store i32 %select, ptr addrspace(1) %ptr
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_strictfp(i1 %cond, float %x, i32 %y) #0 {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_strictfp
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X]], i32 [[SELECT]], metadata !"round.dynamic", metadata !"fpexcept.strict")
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 %select, metadata !"round.dynamic", metadata !"fpexcept.strict")
+ ret float %ldexp
+}
+
+define <2 x float> @ldexp_v2f32_mask_select_0(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0
+; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> [[Y]], <2 x i32> zeroinitializer
+; CHECK-NEXT: [[LDEXP:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[SELECT]])
+; CHECK-NEXT: ret <2 x float> [[LDEXP]]
+;
+ %select = select <2 x i1> %cond, <2 x i32> %y, <2 x i32> zeroinitializer
+ %ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
+ ret <2 x float> %ldexp
+}
+
+define <2 x float> @ldexp_v2f32_mask_select_0_swap(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0_swap
+; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> zeroinitializer, <2 x i32> [[Y]]
+; CHECK-NEXT: [[LDEXP:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[SELECT]])
+; CHECK-NEXT: ret <2 x float> [[LDEXP]]
+;
+ %select = select <2 x i1> %cond, <2 x i32> zeroinitializer, <2 x i32> %y
+ %ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
+ ret <2 x float> %ldexp
+}
+
+attributes #0 = { strictfp }
+
!0 = !{i32 -127, i32 0}
!1 = !{i32 0, i32 127}
!2 = !{}
>From d76862603d60a4cdc5c49f15f75b2d522f000ce5 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 26 May 2020 21:05:47 -0400
Subject: [PATCH 2/2] InstCombine: Try to fold ldexp with select of power
operand
This makes it more likely a constant value can fold into the source
operand.
---
llvm/include/llvm/IR/IRBuilder.h | 7 ++++++
.../InstCombine/InstCombineCalls.cpp | 25 +++++++++++++++++++
llvm/test/Transforms/InstCombine/ldexp.ll | 24 +++++++++---------
3 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index f81cfc0dce972..d4b7f21b60a4b 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -1023,6 +1023,13 @@ class IRBuilderBase {
Name);
}
+ /// Create call to the ldexp intrinsic.
+ Value *CreateLdexp(Value *Src, Value *Exp, const Twine &Name = "") {
+ assert(!IsFPConstrained && "TODO: Support strictfp");
+ return CreateIntrinsic(Intrinsic::ldexp, {Src->getType(), Exp->getType()},
+ {Src, Exp}, nullptr, Name);
+ }
+
/// Create a call to the arithmetic_fence intrinsic.
CallInst *CreateArithmeticFence(Value *Val, Type *DstType,
const Twine &Name = "") {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 9291e6e67ef71..b6642b8bd6a39 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -2641,6 +2641,31 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
return BinaryOperator::CreateFMulFMF(Src, Select, II);
}
+ // ldexp(x, c ? exp : 0) -> c ? ldexp(x, exp) : x
+ // ldexp(x, c ? 0 : exp) -> c ? x : ldexp(x, exp)
+ ///
+ // TODO: If we cared, should insert a canonicalize for x
+ Value *SelectCond, *SelectLHS, *SelectRHS;
+ if (match(II->getArgOperand(1),
+ m_OneUse(m_Select(m_Value(SelectCond), m_Value(SelectLHS),
+ m_Value(SelectRHS))))) {
+ Value *NewLdexp = nullptr;
+ Value *Select = nullptr;
+ if (match(SelectRHS, m_ZeroInt())) {
+ NewLdexp = Builder.CreateLdexp(Src, SelectLHS);
+ Select = Builder.CreateSelect(SelectCond, NewLdexp, Src);
+ } else if (match(SelectLHS, m_ZeroInt())) {
+ NewLdexp = Builder.CreateLdexp(Src, SelectRHS);
+ Select = Builder.CreateSelect(SelectCond, Src, NewLdexp);
+ }
+
+ if (NewLdexp) {
+ Select->takeName(II);
+ cast<Instruction>(NewLdexp)->copyIRFlags(II);
+ return replaceInstUsesWith(*II, Select);
+ }
+ }
+
break;
}
case Intrinsic::ptrauth_auth:
diff --git a/llvm/test/Transforms/InstCombine/ldexp.ll b/llvm/test/Transforms/InstCombine/ldexp.ll
index 7d0d33487845b..12d160ed4f788 100644
--- a/llvm/test/Transforms/InstCombine/ldexp.ll
+++ b/llvm/test/Transforms/InstCombine/ldexp.ll
@@ -892,8 +892,8 @@ define float @ldexp_8_contractable(float %x, float %y) {
define float @ldexp_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
; CHECK-LABEL: define float @ldexp_f32_mask_select_0
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
-; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%select = select i1 %cond, i32 %y, i32 0
@@ -904,8 +904,8 @@ define float @ldexp_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
define float @ldexp_nnan_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
; CHECK-LABEL: define float @ldexp_nnan_f32_mask_select_0
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
-; CHECK-NEXT: [[LDEXP:%.*]] = call nnan float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%select = select i1 %cond, i32 %y, i32 0
@@ -916,8 +916,8 @@ define float @ldexp_nnan_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
define float @ldexp_flags_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
; CHECK-LABEL: define float @ldexp_flags_f32_mask_select_0
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
-; CHECK-NEXT: [[LDEXP:%.*]] = call ninf nsz float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call ninf nsz float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%select = select i1 %cond, i32 %y, i32 0
@@ -928,8 +928,8 @@ define float @ldexp_flags_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
define float @ldexp_f32_mask_select_0_swap(i1 %cond, float %x, i32 %y) {
; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap
; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 0, i32 [[Y]]
-; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[X]], float [[TMP1]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%select = select i1 %cond, i32 0, i32 %y
@@ -992,8 +992,8 @@ define float @ldexp_f32_mask_select_0_strictfp(i1 %cond, float %x, i32 %y) #0 {
define <2 x float> @ldexp_v2f32_mask_select_0(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0
; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> [[Y]], <2 x i32> zeroinitializer
-; CHECK-NEXT: [[LDEXP:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[TMP1]], <2 x float> [[X]]
; CHECK-NEXT: ret <2 x float> [[LDEXP]]
;
%select = select <2 x i1> %cond, <2 x i32> %y, <2 x i32> zeroinitializer
@@ -1004,8 +1004,8 @@ define <2 x float> @ldexp_v2f32_mask_select_0(<2 x i1> %cond, <2 x float> %x, <2
define <2 x float> @ldexp_v2f32_mask_select_0_swap(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0_swap
; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
-; CHECK-NEXT: [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> zeroinitializer, <2 x i32> [[Y]]
-; CHECK-NEXT: [[LDEXP:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[SELECT]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[X]], <2 x float> [[TMP1]]
; CHECK-NEXT: ret <2 x float> [[LDEXP]]
;
%select = select <2 x i1> %cond, <2 x i32> zeroinitializer, <2 x i32> %y
More information about the llvm-commits
mailing list