-
Notifications
You must be signed in to change notification settings - Fork 14.1k
[InstCombine] Fold frexp of select to select of frexp #121227
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
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
628eb07
to
27228cd
Compare
@llvm/pr-subscribers-vectorizers @llvm/pr-subscribers-llvm-transforms Author: Narayan (vortex73) ChangesThis patch implements an optimization to push select operations through
We transform it to:
Fixes #92542 Full diff: https://github.com/llvm/llvm-project/pull/121227.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 934156f04f7fdd..b4b31c25ef080d 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4043,6 +4043,52 @@ InstCombinerImpl::foldExtractOfOverflowIntrinsic(ExtractValueInst &EV) {
return nullptr;
}
+static Value *foldFrexpOfSelect(ExtractValueInst &EV, CallInst *FrexpCall,
+ SelectInst *SelectInst,
+ InstCombiner::BuilderTy &Builder) {
+ // Helper to fold frexp of select to select of frexp.
+ Value *Cond = SelectInst->getCondition();
+ Value *TrueVal = SelectInst->getTrueValue();
+ Value *FalseVal = SelectInst->getFalseValue();
+ ConstantFP *ConstOp = nullptr;
+ Value *VarOp = nullptr;
+ bool ConstIsTrue = false;
+
+ if (auto *TrueConst = dyn_cast<ConstantFP>(TrueVal)) {
+ ConstOp = TrueConst;
+ VarOp = FalseVal;
+ ConstIsTrue = true;
+ } else if (auto *FalseConst = dyn_cast<ConstantFP>(FalseVal)) {
+ ConstOp = FalseConst;
+ VarOp = TrueVal;
+ ConstIsTrue = false;
+ }
+
+ if (!ConstOp || !VarOp)
+ return nullptr;
+
+ CallInst *NewFrexp =
+ Builder.CreateCall(FrexpCall->getCalledFunction(), {VarOp}, "frexp");
+
+ Value *NewEV = Builder.CreateExtractValue(NewFrexp, 0, "mantissa");
+
+ APFloat ConstVal = ConstOp->getValueAPF();
+ int Exp = 0;
+ APFloat Mantissa = ConstVal;
+
+ if (ConstVal.isFiniteNonZero()) {
+ Mantissa = frexp(ConstVal, Exp, APFloat::rmNearestTiesToEven);
+ }
+
+ Constant *ConstantMantissa = ConstantFP::get(ConstOp->getType(), Mantissa);
+
+ Value *NewSel = Builder.CreateSelect(
+ Cond, ConstIsTrue ? ConstantMantissa : NewEV,
+ ConstIsTrue ? NewEV : ConstantMantissa, "select.frexp");
+
+ return NewSel;
+}
+
Instruction *InstCombinerImpl::visitExtractValueInst(ExtractValueInst &EV) {
Value *Agg = EV.getAggregateOperand();
@@ -4052,7 +4098,26 @@ Instruction *InstCombinerImpl::visitExtractValueInst(ExtractValueInst &EV) {
if (Value *V = simplifyExtractValueInst(Agg, EV.getIndices(),
SQ.getWithInstruction(&EV)))
return replaceInstUsesWith(EV, V);
-
+ if (EV.getNumIndices() == 1 && EV.getIndices()[0] == 0) {
+ if (auto *FrexpCall = dyn_cast<CallInst>(Agg)) {
+ if (Function *F = FrexpCall->getCalledFunction()) {
+ if (F->getIntrinsicID() == Intrinsic::frexp) {
+ if (auto *SelInst =
+ dyn_cast<SelectInst>(FrexpCall->getArgOperand(0))) {
+ if (isa<ConstantFP>(SelInst->getTrueValue()) ||
+ isa<ConstantFP>(SelInst->getFalseValue())) {
+ Builder.SetInsertPoint(&EV);
+
+ if (Value *Result =
+ foldFrexpOfSelect(EV, FrexpCall, SelInst, Builder)) {
+ return replaceInstUsesWith(EV, Result);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
if (InsertValueInst *IV = dyn_cast<InsertValueInst>(Agg)) {
// We're extracting from an insertvalue instruction, compare the indices
const unsigned *exti, *exte, *insi, *inse;
diff --git a/llvm/test/Transforms/InstCombine/select_frexp.ll b/llvm/test/Transforms/InstCombine/select_frexp.ll
new file mode 100644
index 00000000000000..652d4de27b7591
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select_frexp.ll
@@ -0,0 +1,130 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+declare { float, i32 } @llvm.frexp.f32.i32(float)
+declare void @use(float)
+
+; Basic test case - constant in true position
+define float @test_select_frexp_basic(float %x, i1 %cond) {
+; CHECK-LABEL: define float @test_select_frexp_basic(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { float, i32 } [[FREXP]], 0
+; CHECK-NEXT: [[SELECT_FREXP:%.*]] = select i1 [[COND]], float 5.000000e-01, float [[FREXP_0]]
+; CHECK-NEXT: ret float [[SELECT_FREXP]]
+;
+ %sel = select i1 %cond, float 1.000000e+00, float %x
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %sel)
+ %frexp.0 = extractvalue { float, i32 } %frexp, 0
+ ret float %frexp.0
+}
+
+; Test with constant in false position
+define float @test_select_frexp_const_false(float %x, i1 %cond) {
+; CHECK-LABEL: define float @test_select_frexp_const_false(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { float, i32 } [[FREXP]], 0
+; CHECK-NEXT: [[SELECT_FREXP:%.*]] = select i1 [[COND]], float [[FREXP_0]], float 5.000000e-01
+; CHECK-NEXT: ret float [[SELECT_FREXP]]
+;
+ %sel = select i1 %cond, float %x, float 1.000000e+00
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %sel)
+ %frexp.0 = extractvalue { float, i32 } %frexp, 0
+ ret float %frexp.0
+}
+
+; Multi-use test
+define float @test_select_frexp_multi_use(float %x, i1 %cond) {
+; CHECK-LABEL: define float @test_select_frexp_multi_use(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], float 1.000000e+00, float [[X]]
+; CHECK-NEXT: call void @use(float [[SEL]])
+; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[X]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { float, i32 } [[FREXP]], 0
+; CHECK-NEXT: [[SELECT_FREXP:%.*]] = select i1 [[COND]], float 5.000000e-01, float [[FREXP_0]]
+; CHECK-NEXT: ret float [[SELECT_FREXP]]
+;
+ %sel = select i1 %cond, float 1.000000e+00, float %x
+ call void @use(float %sel)
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %sel)
+ %frexp.0 = extractvalue { float, i32 } %frexp, 0
+ ret float %frexp.0
+}
+
+; Vector test - splat constant
+define <2 x float> @test_select_frexp_vec_splat(<2 x float> %x, <2 x i1> %cond) {
+; CHECK-LABEL: define <2 x float> @test_select_frexp_vec_splat(
+; CHECK-SAME: <2 x float> [[X:%.*]], <2 x i1> [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[COND]], <2 x float> splat (float 1.000000e+00), <2 x float> [[X]]
+; CHECK-NEXT: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> [[SEL]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0
+; CHECK-NEXT: ret <2 x float> [[FREXP_0]]
+;
+ %sel = select <2 x i1> %cond, <2 x float> <float 1.000000e+00, float 1.000000e+00>, <2 x float> %x
+ %frexp = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %sel)
+ %frexp.0 = extractvalue { <2 x float>, <2 x i32> } %frexp, 0
+ ret <2 x float> %frexp.0
+}
+
+; Vector test with poison
+define <2 x float> @test_select_frexp_vec_poison(<2 x float> %x, <2 x i1> %cond) {
+; CHECK-LABEL: define <2 x float> @test_select_frexp_vec_poison(
+; CHECK-SAME: <2 x float> [[X:%.*]], <2 x i1> [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[COND]], <2 x float> <float 1.000000e+00, float poison>, <2 x float> [[X]]
+; CHECK-NEXT: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> [[SEL]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0
+; CHECK-NEXT: ret <2 x float> [[FREXP_0]]
+;
+ %sel = select <2 x i1> %cond, <2 x float> <float 1.000000e+00, float poison>, <2 x float> %x
+ %frexp = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %sel)
+ %frexp.0 = extractvalue { <2 x float>, <2 x i32> } %frexp, 0
+ ret <2 x float> %frexp.0
+}
+
+; Vector test - non-splat (should not fold)
+define <2 x float> @test_select_frexp_vec_nonsplat(<2 x float> %x, <2 x i1> %cond) {
+; CHECK-LABEL: define <2 x float> @test_select_frexp_vec_nonsplat(
+; CHECK-SAME: <2 x float> [[X:%.*]], <2 x i1> [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[COND]], <2 x float> <float 1.000000e+00, float 2.000000e+00>, <2 x float> [[X]]
+; CHECK-NEXT: [[FREXP:%.*]] = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> [[SEL]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { <2 x float>, <2 x i32> } [[FREXP]], 0
+; CHECK-NEXT: ret <2 x float> [[FREXP_0]]
+;
+ %sel = select <2 x i1> %cond, <2 x float> <float 1.000000e+00, float 2.000000e+00>, <2 x float> %x
+ %frexp = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %sel)
+ %frexp.0 = extractvalue { <2 x float>, <2 x i32> } %frexp, 0
+ ret <2 x float> %frexp.0
+}
+
+; Negative test - both operands non-constant
+define float @test_select_frexp_no_const(float %x, float %y, i1 %cond) {
+; CHECK-LABEL: define float @test_select_frexp_no_const(
+; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], float [[X]], float [[Y]]
+; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[SEL]])
+; CHECK-NEXT: [[FREXP_0:%.*]] = extractvalue { float, i32 } [[FREXP]], 0
+; CHECK-NEXT: ret float [[FREXP_0]]
+;
+ %sel = select i1 %cond, float %x, float %y
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %sel)
+ %frexp.0 = extractvalue { float, i32 } %frexp, 0
+ ret float %frexp.0
+}
+
+; Negative test - extracting exp instead of mantissa
+define i32 @test_select_frexp_extract_exp(float %x, i1 %cond) {
+; CHECK-LABEL: define i32 @test_select_frexp_extract_exp(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], float 1.000000e+00, float [[X]]
+; CHECK-NEXT: [[FREXP:%.*]] = call { float, i32 } @llvm.frexp.f32.i32(float [[SEL]])
+; CHECK-NEXT: [[FREXP_1:%.*]] = extractvalue { float, i32 } [[FREXP]], 1
+; CHECK-NEXT: ret i32 [[FREXP_1]]
+;
+ %sel = select i1 %cond, float 1.000000e+00, float %x
+ %frexp = call { float, i32 } @llvm.frexp.f32.i32(float %sel)
+ %frexp.1 = extractvalue { float, i32 } %frexp, 1
+ ret i32 %frexp.1
+}
+
+declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float>)
|
@arsenm Thanks for the feedback! I've tried to incorporate the changes, please have a look and let me know if anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to support this in
llvm-project/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
Lines 3738 to 3747 in 0fa59c6
// Try to fold intrinsic into select operands. This is legal if: | |
// * The intrinsic is speculatable. | |
// * The select condition is not a vector, or the intrinsic does not | |
// perform cross-lane operations. | |
if (isSafeToSpeculativelyExecuteWithVariableReplaced(&CI) && | |
isNotCrossLaneOperation(II)) | |
for (Value *Op : II->args()) | |
if (auto *Sel = dyn_cast<SelectInst>(Op)) | |
if (Instruction *R = FoldOpIntoSelect(*II, Sel)) | |
return R; |
Hmm, would need to look into this. The original issue mentioned that it should be rooted in |
I don't see isTriviallyVectorizable used in InstCombine so I don't think that would help. Do you mean such that we would end up producing a select of struct? |
isTriviallyVectorizable is used by isNotCrossLaneOperation (but we can also add it to isNotCrossLaneOperation independently, it's just how all the other intrinsics are currently handled).
Yes, that's what I had in mind. We'd create the select of struct and then fold in the extractvalue if possible. Is the concern here that doing the transform if we can't then fold extractvalue is non-profitable? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. But the rebase looks incorrect :(
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/50/builds/9701 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/12810 Here is the relevant piece of the build log for the reference
|
This patch implements an optimization to push select operations through
frexp when one of the select operands is a constant. When we encounter:
We transform it to:
Fixes #92542