[llvm] [PredicateInfo][SCCP] Handle switch comprehensively (PR #165258)
Kunqiu Chen via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 27 09:08:34 PDT 2025
https://github.com/Camsyn updated https://github.com/llvm/llvm-project/pull/165258
>From 7bf89cc66ff9053af8930ddd08284fa36d4dd1dc Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Mon, 27 Oct 2025 17:19:39 +0800
Subject: [PATCH 1/3] Add new tests before commit
---
llvm/test/Transforms/NewGVN/edge.ll | 95 +++++++++++
llvm/test/Transforms/SCCP/switch.ll | 153 ++++++++++++++----
.../Transforms/Util/PredicateInfo/condprop.ll | 10 +-
.../Transforms/Util/PredicateInfo/edge.ll | 97 ++++++++++-
4 files changed, 319 insertions(+), 36 deletions(-)
diff --git a/llvm/test/Transforms/NewGVN/edge.ll b/llvm/test/Transforms/NewGVN/edge.ll
index 143e52cd139c5..4235f7fbfef14 100644
--- a/llvm/test/Transforms/NewGVN/edge.ll
+++ b/llvm/test/Transforms/NewGVN/edge.ll
@@ -262,3 +262,98 @@ return:
ret double %retval
}
+
+define i32 @switch_default_dest(i32 %x) {
+; CHECK-LABEL: define i32 @switch_default_dest(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: case0:
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: case1:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+case0:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %case1
+ ]
+
+case1:
+ br label %phi
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ 0, %case1 ], [ 1, %case0 ], [ %x, %default ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
+
+define i32 @switch_multicases_dest(i32 %x) {
+; CHECK-LABEL: define i32 @switch_multicases_dest(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [
+; CHECK-NEXT: i32 0, label [[CASE:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE]]
+; CHECK-NEXT: ]
+; CHECK: case:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+entry:
+ switch i32 %x, label %phi [
+ i32 0, label %case
+ i32 1, label %case
+ ]
+
+case:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %case ], [ 0, %entry ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
+
+define i32 @switch_multicases_dest2(i32 %x) {
+; CHECK-LABEL: define i32 @switch_multicases_dest2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[PHI]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+entry:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %phi
+ ]
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
diff --git a/llvm/test/Transforms/SCCP/switch.ll b/llvm/test/Transforms/SCCP/switch.ll
index fb81213ed12dc..c4365486e63b4 100644
--- a/llvm/test/Transforms/SCCP/switch.ll
+++ b/llvm/test/Transforms/SCCP/switch.ll
@@ -37,8 +37,8 @@ entry:
switch:
switch i32 -1, label %switch.default [
- i32 0, label %end
- i32 1, label %end
+ i32 0, label %end
+ i32 1, label %end
]
switch.default:
@@ -65,8 +65,8 @@ entry:
switch:
switch i32 0, label %switch.default [
- i32 0, label %end
- i32 1, label %end
+ i32 0, label %end
+ i32 1, label %end
]
switch.default:
@@ -102,11 +102,11 @@ entry:
switch:
%x = load i32, ptr %p, !range !{i32 0, i32 3}
switch i32 %x, label %switch.default [
- i32 0, label %switch.default
- i32 1, label %switch.0
- i32 2, label %switch.0
- i32 3, label %switch.1
- i32 4, label %switch.1
+ i32 0, label %switch.default
+ i32 1, label %switch.0
+ i32 2, label %switch.0
+ i32 3, label %switch.1
+ i32 4, label %switch.1
]
switch.default:
@@ -140,10 +140,10 @@ define i32 @test_local_range(ptr %p) {
;
%x = load i32, ptr %p, !range !{i32 0, i32 3}
switch i32 %x, label %switch.default [
- i32 0, label %switch.0
- i32 1, label %switch.1
- i32 2, label %switch.2
- i32 3, label %switch.3
+ i32 0, label %switch.0
+ i32 1, label %switch.1
+ i32 2, label %switch.2
+ i32 3, label %switch.3
]
switch.default:
@@ -182,12 +182,12 @@ define i32 @test_duplicate_successors(ptr %p) {
;
%x = load i32, ptr %p, !range !{i32 0, i32 3}
switch i32 %x, label %switch.default [
- i32 0, label %switch.0
- i32 1, label %switch.0
- i32 2, label %switch.1
- i32 3, label %switch.1
- i32 4, label %switch.2
- i32 5, label %switch.2
+ i32 0, label %switch.0
+ i32 1, label %switch.0
+ i32 2, label %switch.1
+ i32 3, label %switch.1
+ i32 4, label %switch.2
+ i32 5, label %switch.2
]
switch.default:
@@ -223,10 +223,10 @@ define internal i32 @test_ip_range(i32 %x) {
; CHECK-NEXT: ret i32 3
;
switch i32 %x, label %switch.default [
- i32 0, label %switch.0
- i32 1, label %switch.1
- i32 2, label %switch.2
- i32 3, label %switch.3
+ i32 0, label %switch.0
+ i32 1, label %switch.1
+ i32 2, label %switch.2
+ i32 3, label %switch.3
], !prof !{!"branch_weights", i32 1, i32 2, i32 3, i32 4, i32 5}
switch.default:
@@ -295,8 +295,8 @@ else.2:
switch:
%p = phi i32 [ 0, %then.1 ], [ 2, %else.1 ], [ undef, %else.2 ]
switch i32 %p, label %switch.default [
- i32 0, label %end.1
- i32 3, label %end.2
+ i32 0, label %end.1
+ i32 3, label %end.2
]
switch.default:
@@ -346,10 +346,10 @@ entry:
if.then:
switch i32 %x, label %sw.epilog [
- i32 0, label %sw.bb
- i32 1, label %sw.bb2
- i32 2, label %sw.bb4
- i32 3, label %sw.bb6
+ i32 0, label %sw.bb
+ i32 1, label %sw.bb2
+ i32 2, label %sw.bb4
+ i32 3, label %sw.bb6
]
sw.bb:
@@ -377,6 +377,101 @@ return:
ret i32 %retval.0
}
+define i1 @switch_default_dest(i32 %x) {
+; CHECK-LABEL: define i1 @switch_default_dest(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: case0:
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: case1:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 2, [[CASE1]] ], [ 3, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ]
+; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
+; CHECK-NEXT: ret i1 [[RET]]
+;
+case0:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %case1
+ ]
+
+case1:
+ br label %phi
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ 2, %case1 ], [ 3, %case0 ], [ %x, %default ]
+ %ret = icmp ult i32 %res, 2
+ ret i1 %ret
+}
+
+define i1 @switch_multicases_dest(i32 %x) {
+; CHECK-LABEL: define i1 @switch_multicases_dest(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [
+; CHECK-NEXT: i32 0, label [[CASE:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE]]
+; CHECK-NEXT: ]
+; CHECK: case:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
+; CHECK-NEXT: ret i1 [[RET]]
+;
+entry:
+ switch i32 %x, label %phi [
+ i32 0, label %case
+ i32 1, label %case
+ ]
+
+case:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %case ], [ 0, %entry ]
+ %ret = icmp ult i32 %res, 2
+ ret i1 %ret
+}
+
+define i1 @switch_multicases_dest2(i32 %x) {
+; CHECK-LABEL: define i1 @switch_multicases_dest2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[PHI]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
+; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
+; CHECK-NEXT: ret i1 [[RET]]
+;
+entry:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %phi
+ ]
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ]
+ %ret = icmp ult i32 %res, 2
+ ret i1 %ret
+}
+
declare void @llvm.assume(i1)
;.
diff --git a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
index 256d0d908ec1e..f8753eeb16b3d 100644
--- a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
+++ b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
@@ -157,11 +157,11 @@ define void @test4(i1 %b, i32 %x) {
br i1 %b, label %sw, label %case3
sw:
switch i32 %x, label %default [
- i32 0, label %case0
- i32 1, label %case1
- i32 2, label %case0
- i32 3, label %case3
- i32 4, label %default
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case0
+ i32 3, label %case3
+ i32 4, label %default
]
default:
call void @bar(i32 %x)
diff --git a/llvm/test/Transforms/Util/PredicateInfo/edge.ll b/llvm/test/Transforms/Util/PredicateInfo/edge.ll
index ef757f323921a..936a1434ffaf8 100644
--- a/llvm/test/Transforms/Util/PredicateInfo/edge.ll
+++ b/llvm/test/Transforms/Util/PredicateInfo/edge.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=print-predicateinfo < %s 2>&1 | FileCheck %s
+; RUN: opt -passes=print-predicateinfo -disable-output < %s 2>&1 | FileCheck %s
define i32 @f1(i32 %x) {
; CHECK-LABEL: @f1(
@@ -54,7 +54,7 @@ define i32 @f3(i32 %x) {
; CHECK-NEXT: bb0:
; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32
; CHECK-NEXT: switch i32 [[X]], label [[BB1:%.*]] [
-; CHECK-NEXT: i32 0, label [[BB2:%.*]]
+; CHECK-NEXT: i32 0, label [[BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB2]]
@@ -240,3 +240,96 @@ return:
ret double %retval
}
+
+
+define i32 @switch_default_dest(i32 %x) {
+; CHECK-LABEL: @switch_default_dest(
+; CHECK-NEXT: case0:
+; CHECK-NEXT: switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: case1:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+case0:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %case1
+ ]
+
+case1:
+ br label %phi
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ 0, %case1 ], [ 1, %case0 ], [ %x, %default ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
+
+define i32 @switch_multicases_dest(i32 %x) {
+; CHECK-LABEL: @switch_multicases_dest(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X:%.*]], label [[PHI:%.*]] [
+; CHECK-NEXT: i32 0, label [[CASE:%.*]]
+; CHECK-NEXT: i32 1, label [[CASE]]
+; CHECK-NEXT: ]
+; CHECK: case:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+entry:
+ switch i32 %x, label %phi [
+ i32 0, label %case
+ i32 1, label %case
+ ]
+
+case:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %case ], [ 0, %entry ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
+
+define i32 @switch_multicases_dest2(i32 %x) {
+; CHECK-LABEL: @switch_multicases_dest2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[PHI:%.*]]
+; CHECK-NEXT: i32 1, label [[PHI]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: br label [[PHI]]
+; CHECK: phi:
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
+; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
+; CHECK-NEXT: ret i32 [[FOO]]
+;
+entry:
+ switch i32 %x, label %default [
+ i32 0, label %phi
+ i32 1, label %phi
+ ]
+
+default:
+ br label %phi
+
+phi:
+ %res = phi i32 [ %x, %entry ], [ %x, %entry ], [ 0, %default ]
+ %foo = add i32 %res, %x
+ ret i32 %foo
+}
>From 3feaafda7564bc1b1610b55e78b9753acbc6b203 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Mon, 27 Oct 2025 17:10:05 +0800
Subject: [PATCH 2/3] [PredicateInfo] Handle switch comprehensively
Now we can handle default-dst and multi-cases dest of switch for
PredicateInfo, via using a constant range to model such scenario.
---
.../llvm/Transforms/Utils/PredicateInfo.h | 22 ++--
llvm/lib/Transforms/Utils/PredicateInfo.cpp | 123 +++++++++++++++---
llvm/lib/Transforms/Utils/SCCPSolver.cpp | 83 +++++++-----
3 files changed, 175 insertions(+), 53 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
index 3df3495f84470..df7e5f209ab5d 100644
--- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
+++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
@@ -102,6 +102,8 @@ class PredicateBase {
/// Fetch condition in the form of PredicateConstraint, if possible.
LLVM_ABI std::optional<PredicateConstraint> getConstraint() const;
+ /// Fetch condition in the form of a ConstantRange, if possible.
+ LLVM_ABI std::optional<ConstantRange> getRangeConstraint() const;
protected:
PredicateBase(PredicateType PT, Value *Op, Value *Condition)
@@ -157,18 +159,22 @@ class PredicateBranch : public PredicateWithEdge {
class PredicateSwitch : public PredicateWithEdge {
public:
- Value *CaseValue;
- // This is the switch instruction.
- SwitchInst *Switch;
+ using CaseValuesVec = SmallVector<ConstantInt *, 2>;
+ CaseValuesVec CaseValues;
+ bool IsDefault;
PredicateSwitch(Value *Op, BasicBlock *SwitchBB, BasicBlock *TargetBB,
- Value *CaseValue, SwitchInst *SI)
+ ArrayRef<ConstantInt *> CaseValues, SwitchInst *SI,
+ bool IsDefault)
: PredicateWithEdge(PT_Switch, Op, SwitchBB, TargetBB,
SI->getCondition()),
- CaseValue(CaseValue), Switch(SI) {}
+ CaseValues(CaseValues), IsDefault(IsDefault) {}
+ PredicateSwitch(Value *Op, BasicBlock *SwitchBB, BasicBlock *TargetBB,
+ CaseValuesVec &&CaseValues, SwitchInst *SI, bool IsDefault)
+ : PredicateWithEdge(PT_Switch, Op, SwitchBB, TargetBB,
+ SI->getCondition()),
+ CaseValues(CaseValues), IsDefault(IsDefault) {}
PredicateSwitch() = delete;
- static bool classof(const PredicateBase *PB) {
- return PB->Type == PT_Switch;
- }
+ static bool classof(const PredicateBase *PB) { return PB->Type == PT_Switch; }
};
/// Encapsulates PredicateInfo, including all data associated with memory
diff --git a/llvm/lib/Transforms/Utils/PredicateInfo.cpp b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
index a9ab3b3144829..da828fef583f1 100644
--- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp
+++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
@@ -14,8 +14,10 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
@@ -442,6 +444,7 @@ void PredicateInfoBuilder::processBranch(
}
}
}
+
// Process a block terminating switch, and place relevant operations to be
// renamed into OpsToRename.
void PredicateInfoBuilder::processSwitch(
@@ -450,21 +453,41 @@ void PredicateInfoBuilder::processSwitch(
Value *Op = SI->getCondition();
if ((!isa<Instruction>(Op) && !isa<Argument>(Op)) || Op->hasOneUse())
return;
+ using CaseValuesVec = PredicateSwitch::CaseValuesVec;
+
+ BasicBlock *DefaultDest = SI->getDefaultDest();
+ // Remember all cases for PT_Switch related to the default dest.
+ CaseValuesVec AllCases;
+ AllCases.reserve(SI->getNumCases());
- // Remember how many outgoing edges there are to every successor.
- SmallDenseMap<BasicBlock *, unsigned, 16> SwitchEdges;
- for (BasicBlock *TargetBlock : successors(BranchBB))
- ++SwitchEdges[TargetBlock];
+ // For each successor, remember all its related case values.
+ SmallDenseMap<BasicBlock *, CaseValuesVec, 16> SwitchEdges;
- // Now propagate info for each case value
for (auto C : SI->cases()) {
BasicBlock *TargetBlock = C.getCaseSuccessor();
- if (SwitchEdges.lookup(TargetBlock) == 1) {
- PredicateSwitch *PS = new (Allocator) PredicateSwitch(
- Op, SI->getParent(), TargetBlock, C.getCaseValue(), SI);
- addInfoFor(OpsToRename, Op, PS);
- }
+ /// TODO: Replace this if with an assertion if we can guarantee that
+ /// this function must be called after SimplifyCFG, as a canonical switch
+ /// should not have case dest being the default dest.
+ if (TargetBlock == DefaultDest)
+ continue;
+ // Only collect real case values
+ ConstantInt *CaseValue = C.getCaseValue();
+ AllCases.push_back(CaseValue);
+ SwitchEdges[TargetBlock].push_back(CaseValue);
}
+
+ // Now propagate info for each case successor
+ for (auto *CaseSucc : SwitchEdges.keys()) {
+ auto &CaseValues = SwitchEdges.at(CaseSucc);
+ PredicateSwitch *PS = new (Allocator) PredicateSwitch(
+ Op, SI->getParent(), CaseSucc, std::move(CaseValues), SI, false);
+ addInfoFor(OpsToRename, Op, PS);
+ }
+
+ // Finally, propagate info for the default case
+ PredicateSwitch *PS = new (Allocator) PredicateSwitch(
+ Op, SI->getParent(), DefaultDest, std::move(AllCases), SI, true);
+ addInfoFor(OpsToRename, Op, PS);
}
// Build predicate info for our function
@@ -500,8 +523,8 @@ void PredicateInfoBuilder::buildPredicateInfo() {
// Given the renaming stack, make all the operands currently on the stack real
// by inserting them into the IR. Return the last operation's value.
Value *PredicateInfoBuilder::materializeStack(unsigned int &Counter,
- ValueDFSStack &RenameStack,
- Value *OrigOp) {
+ ValueDFSStack &RenameStack,
+ Value *OrigOp) {
// Find the first thing we have to materialize
auto RevIter = RenameStack.rbegin();
for (; RevIter != RenameStack.rend(); ++RevIter)
@@ -601,7 +624,8 @@ void PredicateInfoBuilder::renameUses(SmallVectorImpl<Value *> &OpsToRename) {
// block, and handle it specially. We know that it goes last, and only
// dominate phi uses.
auto BlockEdge = getBlockEdge(PossibleCopy);
- if (!BlockEdge.second->getSinglePredecessor()) {
+ // We use unique predecessor to identify the mult-cases dest in switch
+ if (!BlockEdge.second->getUniquePredecessor()) {
VD.LocalNum = LN_Last;
auto *DomNode = DT.getNode(BlockEdge.first);
if (DomNode) {
@@ -759,8 +783,63 @@ std::optional<PredicateConstraint> PredicateBase::getConstraint() const {
// TODO: Make this an assertion once RenamedOp is fully accurate.
return std::nullopt;
}
+ const auto &PS = *cast<PredicateSwitch>(this);
+ unsigned NumCases = PS.CaseValues.size();
+ assert(NumCases != 0 && "PT_Switch with no cases is invalid");
+ // PT_Switch with >1 cases is too complex to derive a PredicateConstraint.
+ if (NumCases > 1)
+ return std::nullopt;
+ // If we have a single case, we can derive a predicate constraint.
+ return {
+ {PS.IsDefault ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, PS.CaseValues[0]}};
+ }
+ llvm_unreachable("Unknown predicate type");
+}
- return {{CmpInst::ICMP_EQ, cast<PredicateSwitch>(this)->CaseValue}};
+std::optional<ConstantRange> PredicateBase::getRangeConstraint() const {
+ switch (Type) {
+ case PT_Assume:
+ case PT_Branch: {
+ // For PT_Assume/PT_Branch, we derive the condition constant range from
+ // its predicate constraint.
+ const std::optional<PredicateConstraint> &Constraint = getConstraint();
+ if (!Constraint)
+ return std::nullopt;
+ CmpInst::Predicate Pred = Constraint->Predicate;
+ Value *OtherOp = Constraint->OtherOp;
+ const APInt *IntOp;
+ // If the other operand is not a constant integer, we can't derive a
+ // constant range.
+ if (!match(OtherOp, m_APInt(IntOp)))
+ return std::nullopt;
+ return {ConstantRange::makeExactICmpRegion(Pred, *IntOp)};
+ }
+ case PT_Switch:
+ // For PT_Switch, we directly derive the constant range from its case
+ // values.
+ if (Condition != RenamedOp) {
+ // TODO: Make this an assertion once RenamedOp is fully accurate.
+ return std::nullopt;
+ }
+
+ const auto &PS = *cast<PredicateSwitch>(this);
+ assert(!PS.CaseValues.empty() && "SwitchInfo with no cases is invalid");
+
+ unsigned BitWidth = PS.Condition->getType()->getScalarSizeInBits();
+
+ // For case values, CR = emptyset ∪ {case1, case2,..., caseN}
+ // For default, CR = fullset ∩ ~{case1} ∩ ~{case2} ∩ ... ∩ ~{caseN}
+ bool IsDefault = PS.IsDefault;
+ ConstantRange CR = IsDefault ? ConstantRange::getFull(BitWidth)
+ : ConstantRange::getEmpty(BitWidth);
+ for (ConstantInt *Case : PS.CaseValues) {
+ assert(Case && "CaseValue in switch should not be null");
+ CR = IsDefault
+ ? CR.intersectWith(ConstantRange(Case->getValue()).inverse())
+ : CR.unionWith(Case->getValue());
+ }
+
+ return {CR};
}
llvm_unreachable("Unknown predicate type");
}
@@ -818,8 +897,20 @@ class PredicateInfoAnnotatedWriter : public AssemblyAnnotationWriter {
PB->To->printAsOperand(OS);
OS << "]";
} else if (const auto *PS = dyn_cast<PredicateSwitch>(PI)) {
- OS << "; switch predicate info { CaseValue: " << *PS->CaseValue
- << " Edge: [";
+ OS << "; switch predicate info { ";
+ if (PS->IsDefault) {
+ OS << "Case: default";
+ } else if (PS->CaseValues.size() == 1) {
+ OS << "CaseValue: " << *PS->CaseValues[0];
+ } else {
+ auto CaseValues =
+ llvm::map_range(PS->CaseValues, [](ConstantInt *Case) {
+ return std::to_string(Case->getSExtValue());
+ });
+ OS << "CaseValues: " << *PS->Condition->getType() << " [ "
+ << join(CaseValues, ", ") << " ]";
+ }
+ OS << " Edge: [";
PS->From->printAsOperand(OS);
OS << ",";
PS->To->printAsOperand(OS);
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 4947d03a2dc66..3cde239c7af65 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -2021,33 +2021,13 @@ void SCCPInstVisitor::handleCallArguments(CallBase &CB) {
void SCCPInstVisitor::handlePredicate(Instruction *I, Value *CopyOf,
const PredicateBase *PI) {
+ const std::optional<ConstantRange> &RangeConstraint =
+ PI->getRangeConstraint();
ValueLatticeElement CopyOfVal = getValueState(CopyOf);
- const std::optional<PredicateConstraint> &Constraint = PI->getConstraint();
- if (!Constraint) {
- mergeInValue(ValueState[I], I, CopyOfVal);
- return;
- }
-
- CmpInst::Predicate Pred = Constraint->Predicate;
- Value *OtherOp = Constraint->OtherOp;
-
- // Wait until OtherOp is resolved.
- if (getValueState(OtherOp).isUnknown()) {
- addAdditionalUser(OtherOp, I);
- return;
- }
-
- ValueLatticeElement CondVal = getValueState(OtherOp);
- ValueLatticeElement &IV = ValueState[I];
- if (CondVal.isConstantRange() || CopyOfVal.isConstantRange()) {
- auto ImposedCR =
- ConstantRange::getFull(DL.getTypeSizeInBits(CopyOf->getType()));
-
- // Get the range imposed by the condition.
- if (CondVal.isConstantRange())
- ImposedCR = ConstantRange::makeAllowedICmpRegion(
- Pred, CondVal.getConstantRange());
+ auto MergeInValueWithImposedCR = [this, I, CopyOfVal,
+ CopyOf](ValueLatticeElement &IV,
+ ConstantRange ImposedCR) {
// Combine range info for the original value with the new range from the
// condition.
auto CopyOfCR = CopyOfVal.asConstantRange(CopyOf->getType(),
@@ -2067,18 +2047,63 @@ void SCCPInstVisitor::handlePredicate(Instruction *I, Value *CopyOf,
// unless we have conditions that are always true/false (e.g. icmp ule
// i32, %a, i32_max). For the latter overdefined/empty range will be
// inferred, but the branch will get folded accordingly anyways.
- addAdditionalUser(OtherOp, I);
mergeInValue(
IV, I, ValueLatticeElement::getRange(NewCR, /*MayIncludeUndef*/ false));
+ };
+
+ if (RangeConstraint) {
+ // If we can derive a constant range directly from the predicate info,
+ // simply merge it into the lattice value.
+ // In such case, the relevant operands must be constants, and thus we do not
+ // need addAdditionalUser for such operands.
+ MergeInValueWithImposedCR(ValueState[I], *RangeConstraint);
+ return;
+ }
+
+ // If we can't simply get the constant range directly from the predicate info,
+ // then fallback to PredicateConstraint and let SCCPSolver resolve the
+ // possible Imposed CR.
+
+ const std::optional<PredicateConstraint> &Constraint = PI->getConstraint();
+ if (!Constraint) {
+ mergeInValue(ValueState[I], I, CopyOfVal);
+ return;
+ }
+
+ CmpInst::Predicate Pred = Constraint->Predicate;
+ Value *OtherOp = Constraint->OtherOp;
+
+ // Wait until OtherOp is resolved.
+ if (getValueState(OtherOp).isUnknown()) {
+ addAdditionalUser(OtherOp, I);
return;
- } else if (Pred == CmpInst::ICMP_EQ &&
- (CondVal.isConstant() || CondVal.isNotConstant())) {
+ }
+
+ ValueLatticeElement CondVal = getValueState(OtherOp);
+ ValueLatticeElement &IV = ValueState[I];
+ if (CondVal.isConstantRange() || CopyOfVal.isConstantRange()) {
+ // Get the range imposed by the condition.
+ auto ImposedCR =
+ CondVal.isConstantRange()
+ ? ConstantRange::makeAllowedICmpRegion(Pred,
+ CondVal.getConstantRange())
+ : ConstantRange::getFull(DL.getTypeSizeInBits(CopyOf->getType()));
+
+ addAdditionalUser(OtherOp, I);
+ MergeInValueWithImposedCR(IV, ImposedCR);
+ return;
+ }
+
+ if (Pred == CmpInst::ICMP_EQ &&
+ (CondVal.isConstant() || CondVal.isNotConstant())) {
// For non-integer values or integer constant expressions, only
// propagate equal constants or not-constants.
addAdditionalUser(OtherOp, I);
mergeInValue(IV, I, CondVal);
return;
- } else if (Pred == CmpInst::ICMP_NE && CondVal.isConstant()) {
+ }
+
+ if (Pred == CmpInst::ICMP_NE && CondVal.isConstant()) {
// Propagate inequalities.
addAdditionalUser(OtherOp, I);
mergeInValue(IV, I, ValueLatticeElement::getNot(CondVal.getConstant()));
>From 706232542c17540d4b083f801de28c9b96f3a5d4 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Mon, 27 Oct 2025 21:48:30 +0800
Subject: [PATCH 3/3] Regenerate the tests after commit
---
llvm/test/Transforms/SCCP/switch.ll | 9 +++------
.../Transforms/Util/PredicateInfo/condprop.ll | 8 +++++---
llvm/test/Transforms/Util/PredicateInfo/edge.ll | 15 +++++++++------
3 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/llvm/test/Transforms/SCCP/switch.ll b/llvm/test/Transforms/SCCP/switch.ll
index c4365486e63b4..dfbacdb84e54c 100644
--- a/llvm/test/Transforms/SCCP/switch.ll
+++ b/llvm/test/Transforms/SCCP/switch.ll
@@ -391,8 +391,7 @@ define i1 @switch_default_dest(i32 %x) {
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 2, [[CASE1]] ], [ 3, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ]
-; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
-; CHECK-NEXT: ret i1 [[RET]]
+; CHECK-NEXT: ret i1 false
;
case0:
switch i32 %x, label %default [
@@ -424,8 +423,7 @@ define i1 @switch_multicases_dest(i32 %x) {
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
-; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
-; CHECK-NEXT: ret i1 [[RET]]
+; CHECK-NEXT: ret i1 true
;
entry:
switch i32 %x, label %phi [
@@ -454,8 +452,7 @@ define i1 @switch_multicases_dest2(i32 %x) {
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
-; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[RES]], 2
-; CHECK-NEXT: ret i1 [[RET]]
+; CHECK-NEXT: ret i1 true
;
entry:
switch i32 %x, label %default [
diff --git a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
index f8753eeb16b3d..2e51bdf6cfc35 100644
--- a/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
+++ b/llvm/test/Transforms/Util/PredicateInfo/condprop.ll
@@ -134,6 +134,8 @@ define void @test4(i1 %b, i32 %x) {
; CHECK-NEXT: br i1 [[B:%.*]], label [[SW:%.*]], label [[CASE3:%.*]]
; CHECK: sw:
; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32
+; CHECK: [[X_1:%.*]] = bitcast i32 [[X]] to i32
+; CHECK: [[X_2:%.*]] = bitcast i32 [[X]] to i32
; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i32 0, label [[CASE0:%.*]]
; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
@@ -142,13 +144,13 @@ define void @test4(i1 %b, i32 %x) {
; CHECK-NEXT: i32 4, label [[DEFAULT]]
; CHECK-NEXT: ]
; CHECK: default:
-; CHECK-NEXT: call void @bar(i32 [[X]])
+; CHECK-NEXT: call void @bar(i32 [[X_0]])
; CHECK-NEXT: ret void
; CHECK: case0:
-; CHECK-NEXT: call void @bar(i32 [[X]])
+; CHECK-NEXT: call void @bar(i32 [[X_1]])
; CHECK-NEXT: ret void
; CHECK: case1:
-; CHECK-NEXT: call void @bar(i32 [[X_0]])
+; CHECK-NEXT: call void @bar(i32 [[X_2]])
; CHECK-NEXT: ret void
; CHECK: case3:
; CHECK-NEXT: call void @bar(i32 [[X]])
diff --git a/llvm/test/Transforms/Util/PredicateInfo/edge.ll b/llvm/test/Transforms/Util/PredicateInfo/edge.ll
index 936a1434ffaf8..4f7e75f68ac15 100644
--- a/llvm/test/Transforms/Util/PredicateInfo/edge.ll
+++ b/llvm/test/Transforms/Util/PredicateInfo/edge.ll
@@ -245,7 +245,8 @@ return:
define i32 @switch_default_dest(i32 %x) {
; CHECK-LABEL: @switch_default_dest(
; CHECK-NEXT: case0:
-; CHECK-NEXT: switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i32 0, label [[PHI:%.*]]
; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
; CHECK-NEXT: ]
@@ -254,7 +255,7 @@ define i32 @switch_default_dest(i32 %x) {
; CHECK: default:
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
-; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X]], [[DEFAULT]] ]
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[CASE1]] ], [ 1, [[CASE0:%.*]] ], [ [[X_0]], [[DEFAULT]] ]
; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
; CHECK-NEXT: ret i32 [[FOO]]
;
@@ -279,14 +280,15 @@ phi:
define i32 @switch_multicases_dest(i32 %x) {
; CHECK-LABEL: @switch_multicases_dest(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i32 [[X:%.*]], label [[PHI:%.*]] [
+; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32
+; CHECK-NEXT: switch i32 [[X]], label [[PHI:%.*]] [
; CHECK-NEXT: i32 0, label [[CASE:%.*]]
; CHECK-NEXT: i32 1, label [[CASE]]
; CHECK-NEXT: ]
; CHECK: case:
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
-; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X_0]], [[CASE]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
; CHECK-NEXT: ret i32 [[FOO]]
;
@@ -308,14 +310,15 @@ phi:
define i32 @switch_multicases_dest2(i32 %x) {
; CHECK-LABEL: @switch_multicases_dest2(
; CHECK-NEXT: entry:
-; CHECK-NEXT: switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK: [[X_0:%.*]] = bitcast i32 [[X:%.*]] to i32
+; CHECK-NEXT: switch i32 [[X]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i32 0, label [[PHI:%.*]]
; CHECK-NEXT: i32 1, label [[PHI]]
; CHECK-NEXT: ]
; CHECK: default:
; CHECK-NEXT: br label [[PHI]]
; CHECK: phi:
-; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ [[X]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
+; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[X_0]], [[ENTRY:%.*]] ], [ [[X_0]], [[ENTRY]] ], [ 0, [[DEFAULT]] ]
; CHECK-NEXT: [[FOO:%.*]] = add i32 [[RES]], [[X]]
; CHECK-NEXT: ret i32 [[FOO]]
;
More information about the llvm-commits
mailing list