-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[InstCombine] Fold switch(zext/sext(X))
into switch(X)
#76988
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesThis patch folds
Full diff: https://github.com/llvm/llvm-project/pull/76988.diff 4 Files Affected:
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 61d891d65346bd..399153b442f936 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7009,6 +7009,37 @@ static bool simplifySwitchOfPowersOfTwo(SwitchInst *SI, IRBuilder<> &Builder,
return true;
}
+// Fold switch(zext/sext(X)) into switch(X) if possible.
+static bool simplifySwitchCondition(SwitchInst *SI) {
+ Value *X;
+ if (match(SI->getCondition(), m_ZExtOrSExt(m_Value(X)))) {
+ SwitchInst *NewSI = SwitchInst::Create(X, SI->getDefaultDest(),
+ SI->getNumCases(), SI->getParent());
+ SwitchInstProfUpdateWrapper SIW(*SI);
+ SwitchInstProfUpdateWrapper NewSIW(*NewSI);
+ NewSI->setDebugLoc(SI->getDebugLoc());
+ NewSIW.setSuccessorWeight(0, SIW.getSuccessorWeight(0));
+
+ Type *SrcTy = X->getType();
+ unsigned SrcBW = SrcTy->getScalarSizeInBits();
+ for (auto &Case : SI->cases()) {
+ const APInt &CaseVal = Case.getCaseValue()->getValue();
+ assert(
+ (isa<ZExtInst>(SI->getCondition()) ? CaseVal.isIntN(SrcBW)
+ : CaseVal.isSignedIntN(SrcBW)) &&
+ "Unreachable cases should be eliminated by eliminateDeadSwitchCases");
+ APInt TruncVal = CaseVal.trunc(SrcBW);
+ NewSIW.addCase(cast<ConstantInt>(ConstantInt::get(SrcTy, TruncVal)),
+ Case.getCaseSuccessor(),
+ SIW.getSuccessorWeight(Case.getSuccessorIndex()));
+ }
+ SI->eraseFromParent();
+ return true;
+ }
+
+ return false;
+}
+
bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
BasicBlock *BB = SI->getParent();
@@ -7041,6 +7072,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
if (eliminateDeadSwitchCases(SI, DTU, Options.AC, DL))
return requestResimplify();
+ if (simplifySwitchCondition(SI))
+ return requestResimplify();
+
if (trySwitchToSelect(SI, Builder, DTU, DL, TTI))
return requestResimplify();
diff --git a/llvm/test/Transforms/SimplifyCFG/suppress-zero-branch-weights.ll b/llvm/test/Transforms/SimplifyCFG/suppress-zero-branch-weights.ll
index fd30b6352f7d41..2355f3987b8924 100644
--- a/llvm/test/Transforms/SimplifyCFG/suppress-zero-branch-weights.ll
+++ b/llvm/test/Transforms/SimplifyCFG/suppress-zero-branch-weights.ll
@@ -11,11 +11,11 @@ define i1 @repeated_signbits(i8 %condition) {
; CHECK-LABEL: @repeated_signbits(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT: switch i32 [[SEXT]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i32 0, label [[COMMON_RET:%.*]]
-; CHECK-NEXT: i32 127, label [[COMMON_RET]]
-; CHECK-NEXT: i32 -128, label [[COMMON_RET]]
-; CHECK-NEXT: i32 -1, label [[COMMON_RET]]
+; CHECK-NEXT: switch i8 [[CONDITION]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i8 127, label [[COMMON_RET]]
+; CHECK-NEXT: i8 -128, label [[COMMON_RET]]
+; CHECK-NEXT: i8 -1, label [[COMMON_RET]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ false, [[DEFAULT]] ], [ true, [[ENTRY:%.*]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ]
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-masked-bits.ll b/llvm/test/Transforms/SimplifyCFG/switch-masked-bits.ll
index f66240e9f4f7d4..0a0523aeb08ede 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-masked-bits.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-masked-bits.ll
@@ -50,11 +50,11 @@ define i1 @repeated_signbits(i8 %condition) {
; CHECK-LABEL: @repeated_signbits(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT: switch i32 [[SEXT]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i32 0, label [[COMMON_RET:%.*]]
-; CHECK-NEXT: i32 127, label [[COMMON_RET]]
-; CHECK-NEXT: i32 -128, label [[COMMON_RET]]
-; CHECK-NEXT: i32 -1, label [[COMMON_RET]]
+; CHECK-NEXT: switch i8 [[CONDITION]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i8 127, label [[COMMON_RET]]
+; CHECK-NEXT: i8 -128, label [[COMMON_RET]]
+; CHECK-NEXT: i8 -1, label [[COMMON_RET]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ false, [[DEFAULT]] ], [ true, [[ENTRY:%.*]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ], [ true, [[ENTRY]] ]
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-simplify-cond.ll b/llvm/test/Transforms/SimplifyCFG/switch-simplify-cond.ll
new file mode 100644
index 00000000000000..b0172d83fdbf03
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-simplify-cond.ll
@@ -0,0 +1,159 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+
+define i1 @test_switch_with_zext(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_zext(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_EXT:%.*]] = zext i16 [[A]] to i32
+; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i16 37, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i16 38, label [[COMMON_RET]]
+; CHECK-NEXT: i16 39, label [[COMMON_RET]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ [[C]], [[SW_DEFAULT]] ], [ [[B]], [[ENTRY:%.*]] ], [ [[B]], [[ENTRY]] ], [ [[B]], [[ENTRY]] ]
+; CHECK-NEXT: ret i1 [[COMMON_RET_OP]]
+; CHECK: sw.default:
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %a.ext = zext i16 %a to i32
+ switch i32 %a.ext, label %sw.default [
+ i32 37, label %sw.bb
+ i32 38, label %sw.bb
+ i32 39, label %sw.bb
+ ]
+
+sw.bb:
+ ret i1 %b
+sw.default:
+ ret i1 %c
+}
+
+define i1 @test_switch_with_sext(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_sext(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_EXT:%.*]] = sext i16 [[A]] to i32
+; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i16 37, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i16 38, label [[COMMON_RET]]
+; CHECK-NEXT: i16 39, label [[COMMON_RET]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ [[C]], [[SW_DEFAULT]] ], [ [[B]], [[ENTRY:%.*]] ], [ [[B]], [[ENTRY]] ], [ [[B]], [[ENTRY]] ]
+; CHECK-NEXT: ret i1 [[COMMON_RET_OP]]
+; CHECK: sw.default:
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %a.ext = sext i16 %a to i32
+ switch i32 %a.ext, label %sw.default [
+ i32 37, label %sw.bb
+ i32 38, label %sw.bb
+ i32 39, label %sw.bb
+ ]
+
+sw.bb:
+ ret i1 %b
+sw.default:
+ ret i1 %c
+}
+
+define i1 @test_switch_with_zext_unreachable_case(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_zext_unreachable_case(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_EXT:%.*]] = zext i16 [[A]] to i32
+; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i16 37, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i16 38, label [[COMMON_RET]]
+; CHECK-NEXT: i16 39, label [[COMMON_RET]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ [[C]], [[SW_DEFAULT]] ], [ [[B]], [[ENTRY:%.*]] ], [ [[B]], [[ENTRY]] ], [ [[B]], [[ENTRY]] ]
+; CHECK-NEXT: ret i1 [[COMMON_RET_OP]]
+; CHECK: sw.default:
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %a.ext = zext i16 %a to i32
+ switch i32 %a.ext, label %sw.default [
+ i32 37, label %sw.bb
+ i32 38, label %sw.bb
+ i32 39, label %sw.bb
+ i32 65537, label %sw.bb
+ ]
+
+sw.bb:
+ ret i1 %b
+sw.default:
+ ret i1 %c
+}
+
+define i1 @test_switch_with_sext_unreachable_case(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_sext_unreachable_case(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_EXT:%.*]] = sext i16 [[A]] to i32
+; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i16 37, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i16 38, label [[COMMON_RET]]
+; CHECK-NEXT: i16 39, label [[COMMON_RET]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ [[C]], [[SW_DEFAULT]] ], [ [[B]], [[ENTRY:%.*]] ], [ [[B]], [[ENTRY]] ], [ [[B]], [[ENTRY]] ]
+; CHECK-NEXT: ret i1 [[COMMON_RET_OP]]
+; CHECK: sw.default:
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %a.ext = sext i16 %a to i32
+ switch i32 %a.ext, label %sw.default [
+ i32 37, label %sw.bb
+ i32 38, label %sw.bb
+ i32 39, label %sw.bb
+ i32 -65537, label %sw.bb
+ ]
+
+sw.bb:
+ ret i1 %b
+sw.default:
+ ret i1 %c
+}
+
+define i1 @test_switch_with_zext_prof_preserve(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_zext_prof_preserve(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A_EXT:%.*]] = zext i16 [[A]] to i32
+; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i16 37, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i16 38, label [[COMMON_RET]]
+; CHECK-NEXT: i16 39, label [[COMMON_RET]]
+; CHECK-NEXT: ], !prof [[PROF0:![0-9]+]]
+; CHECK: common.ret:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i1 [ [[C]], [[SW_DEFAULT]] ], [ [[B]], [[ENTRY:%.*]] ], [ [[B]], [[ENTRY]] ], [ [[B]], [[ENTRY]] ]
+; CHECK-NEXT: ret i1 [[COMMON_RET_OP]]
+; CHECK: sw.default:
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %a.ext = zext i16 %a to i32
+ switch i32 %a.ext, label %sw.default [
+ i32 37, label %sw.bb
+ i32 38, label %sw.bb
+ i32 39, label %sw.bb
+ ], !prof !0
+
+sw.bb:
+ ret i1 %b
+sw.default:
+ ret i1 %c
+}
+
+!0 = !{!"branch_weights", i32 8, i32 4, i32 2, i32 1}
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 8, i32 4, i32 2, i32 1}
+;.
|
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.
Why is this in SimplifyCFG? This seems like an extension of the existing switch width fold in InstCombine:
unsigned NewWidth = Known.getBitWidth() - std::max(LeadingKnownZeros, LeadingKnownOnes); |
IIRC, See the following case:
In this case, the edge |
@dtcxzyw But won't that edge get eliminated by SimplifyCFG anyway? |
Okay, I have got your point. |
b37b69c
to
502b13e
Compare
switch(zext/sext(X))
into switch(X)
switch(zext/sext(X))
into switch(X)
Done. |
Does |
@dtcxzyw The compile-time increases are probably due to increased code size: http://llvm-compile-time-tracker.com/compare.php?from=7954c57124b495fbdc73674d71f2e366e4afe522&to=502b13ed34e561d995ae1f724cf06d20008bd86f&stat=size-text As long as the code size increase itself is not a regression, this is fine. |
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 if you're happy with the diffs :)
502b13e
to
9108c5d
Compare
9108c5d
to
bd90b60
Compare
This patch folds `switch(zext/sext(X))` into `switch(X)`. The original motivation of this patch is to optimize a pattern found in cvc5. For example: ``` %bf.load.i = load i16, ptr %d_kind.i, align 8 %bf.clear.i = and i16 %bf.load.i, 1023 %bf.cast.i = zext nneg i16 %bf.clear.i to i32 switch i32 %bf.cast.i, label %if.else [ i32 335, label %if.then i32 303, label %if.then ] if.then: ; preds = %entry, %entry %d_children.i.i = getelementptr inbounds %"class.cvc5::internal::expr::NodeValue", ptr %0, i64 0, i32 3 %cmp.i.i.i.i.i = icmp eq i16 %bf.clear.i, 1023 %cond.i.i.i.i.i = select i1 %cmp.i.i.i.i.i, i32 -1, i32 %bf.cast.i ``` `%cmp.i.i.i.i.i` always evaluates to false because `%bf.clear.i` can only be 335 or 303. Folding `switch i32 %bf.cast.i` to `switch i16 %bf.clear.i` will help `CVP` to handle this case. See also llvm#76928 (comment). Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=7954c57124b495fbdc73674d71f2e366e4afe522&to=502b13ed34e561d995ae1f724cf06d20008bd86f&stat=instructions:u |stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang| |--|--|--|--|--|--|--| |+0.03%|+0.06%|+0.07%|+0.00%|-0.02%|-0.03%|+0.02%|
This patch folds
switch(zext/sext(X))
intoswitch(X)
.The original motivation of this patch is to optimize a pattern found in cvc5. For example:
%cmp.i.i.i.i.i
always evaluates to false because%bf.clear.i
can only be 335 or 303.Folding
switch i32 %bf.cast.i
toswitch i16 %bf.clear.i
will helpCVP
to handle this case.See also #76928 (comment).
Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=7954c57124b495fbdc73674d71f2e366e4afe522&to=502b13ed34e561d995ae1f724cf06d20008bd86f&stat=instructions:u