[llvm] extend SimplifyCFG to lower s/ucmp + 3-arm switch (PR #222786)
Joe Isaacs via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 02:57:37 PDT 2026
https://github.com/joseph-isaacs updated https://github.com/llvm/llvm-project/pull/222786
>From 57d411a49e6e29af6f85110de819a04d3e7c6407 Mon Sep 17 00:00:00 2001
From: Joe Isaacs <joe.isaacs at live.co.uk>
Date: Thu, 10 Sep 2026 22:12:27 +0100
Subject: [PATCH] extend SimplifyCFG to lower s/ucmp + 3-arm switch
---
.../Transforms/Utils/SimplifyCFGOptions.h | 5 +
llvm/lib/Passes/PassBuilder.cpp | 2 +
llvm/lib/Passes/PassBuilderPipelines.cpp | 1 +
llvm/lib/Passes/PassRegistry.def | 1 +
.../lib/Transforms/Scalar/SimplifyCFGPass.cpp | 2 +
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 238 +++++++-----
llvm/test/Other/new-pm-print-pipeline.ll | 4 +-
.../Transforms/SimplifyCFG/switch-on-cmp.ll | 360 +++++++++++++-----
8 files changed, 433 insertions(+), 180 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
index 2d0f95741077f..ed59fe70d9876 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h
@@ -26,6 +26,7 @@ struct SimplifyCFGOptions {
bool ConvertSwitchRangeToICmp = false;
bool ConvertSwitchToArithmetic = false;
bool ConvertSwitchToLookupTable = false;
+ bool ConvertThreeWayCmpSwitch = false;
bool NeedCanonicalLoop = true;
bool HoistCommonInsts = false;
bool HoistLoadsStoresWithCondFaulting = false;
@@ -57,6 +58,10 @@ struct SimplifyCFGOptions {
ConvertSwitchToLookupTable = B;
return *this;
}
+ SimplifyCFGOptions &convertThreeWayCmpSwitch(bool B) {
+ ConvertThreeWayCmpSwitch = B;
+ return *this;
+ }
SimplifyCFGOptions &needCanonicalLoops(bool B) {
NeedCanonicalLoop = B;
return *this;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 725ce2d589a31..a5791ef51aafe 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1244,6 +1244,8 @@ Expected<SimplifyCFGOptions> parseSimplifyCFGOptions(StringRef Params) {
Result.convertSwitchToArithmetic(Enable);
} else if (ParamName == "switch-to-lookup") {
Result.convertSwitchToLookupTable(Enable);
+ } else if (ParamName == "three-way-cmp-switch") {
+ Result.convertThreeWayCmpSwitch(Enable);
} else if (ParamName == "keep-loops") {
Result.needCanonicalLoops(Enable);
} else if (ParamName == "hoist-common-insts") {
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 8e532b5980e74..10591d4e23bd4 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -2376,6 +2376,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
LateFPM.addPass(SimplifyCFGPass(SimplifyCFGOptions()
.convertSwitchRangeToICmp(true)
.convertSwitchToArithmetic(true)
+ .convertThreeWayCmpSwitch(true)
.hoistCommonInsts(true)
.speculateUnpredictables(true)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(LateFPM)));
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 43a15f4cf9ff3..540e92bae13f7 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -709,6 +709,7 @@ FUNCTION_PASS_WITH_PARAMS(
"simplify-cond-branch;no-forward-switch-cond;forward-switch-cond;"
"no-switch-range-to-icmp;switch-range-to-icmp;no-switch-to-arithmetic;"
"switch-to-arithmetic;no-switch-to-lookup;switch-to-lookup;"
+ "no-three-way-cmp-switch;three-way-cmp-switch;"
"no-keep-loops;keep-loops;no-hoist-common-insts;"
"hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;"
"hoist-loads-stores-with-cond-faulting;no-sink-common-insts;"
diff --git a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp
index 2ef04eb6a337f..a1bd6470d8908 100644
--- a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp
+++ b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp
@@ -363,6 +363,8 @@ void SimplifyCFGPass::printPipeline(
<< "switch-to-arithmetic;";
OS << (Options.ConvertSwitchToLookupTable ? "" : "no-")
<< "switch-to-lookup;";
+ OS << (Options.ConvertThreeWayCmpSwitch ? "" : "no-")
+ << "three-way-cmp-switch;";
OS << (Options.NeedCanonicalLoop ? "" : "no-") << "keep-loops;";
OS << (Options.HoistCommonInsts ? "" : "no-") << "hoist-common-insts;";
OS << (Options.HoistLoadsStoresWithCondFaulting ? "" : "no-")
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 6a2601487b178..81904a4ce632c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -8224,114 +8224,164 @@ static bool simplifySwitchOfPowersOfTwo(SwitchInst *SI, IRBuilder<> &Builder,
/// Fold switch over ucmp/scmp intrinsic to br if two of the switch arms have
/// the same destination.
+/// Folds a switch with 3 arms if given `AllowThreeArms`
static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder,
- DomTreeUpdater *DTU) {
+ DomTreeUpdater *DTU,
+ bool AllowThreeArms) {
auto *Cmp = dyn_cast<CmpIntrinsic>(SI->getCondition());
if (!Cmp || !Cmp->hasOneUse())
return false;
+ // Record each destination's comparison outcomes and aggregate edge weight.
+ // A mask lets one predicate describe merged outcomes, e.g. Less | Equal.
+ enum : unsigned { Less = 1, Equal = 2, Greater = 4, All = 7 };
+ struct Arm {
+ BasicBlock *Dest;
+ unsigned Mask;
+ uint64_t Weight;
+ };
+ SmallVector<Arm, 3> Arms;
SmallVector<uint32_t, 4> Weights;
- bool HasWeights = extractBranchWeights(getBranchWeightMDNode(*SI), Weights);
- if (!HasWeights)
- Weights.resize(4); // Avoid checking HasWeights everywhere.
-
- // Normalize to [us]cmp == Res ? Succ : OtherSucc.
- int64_t Res;
- BasicBlock *Succ, *OtherSucc;
- uint32_t SuccWeight = 0, OtherSuccWeight = 0;
- BasicBlock *Unreachable = nullptr;
-
- if (SI->getNumCases() == 2) {
- // Find which of 1, 0 or -1 is missing (handled by default dest).
- SmallSet<int64_t, 3> Missing;
- Missing.insert(1);
- Missing.insert(0);
- Missing.insert(-1);
-
- Succ = SI->getDefaultDest();
- SuccWeight = Weights[0];
- OtherSucc = nullptr;
- for (auto &Case : SI->cases()) {
- std::optional<int64_t> Val =
- Case.getCaseValue()->getValue().trySExtValue();
- if (!Val)
- return false;
- if (!Missing.erase(*Val))
- return false;
- if (OtherSucc && OtherSucc != Case.getCaseSuccessor())
- return false;
- OtherSucc = Case.getCaseSuccessor();
- OtherSuccWeight += Weights[Case.getSuccessorIndex()];
- }
-
- assert(Missing.size() == 1 && "Should have one case left");
- Res = *Missing.begin();
- } else if (SI->getNumCases() == 3 && SI->defaultDestUnreachable()) {
- // Normalize so that Succ is taken once and OtherSucc twice.
- Unreachable = SI->getDefaultDest();
- Succ = OtherSucc = nullptr;
- for (auto &Case : SI->cases()) {
- BasicBlock *NewSucc = Case.getCaseSuccessor();
- uint32_t Weight = Weights[Case.getSuccessorIndex()];
- if (!OtherSucc || OtherSucc == NewSucc) {
- OtherSucc = NewSucc;
- OtherSuccWeight += Weight;
- } else if (!Succ) {
- Succ = NewSucc;
- SuccWeight = Weight;
- } else if (Succ == NewSucc) {
- std::swap(Succ, OtherSucc);
- std::swap(SuccWeight, OtherSuccWeight);
- } else
- return false;
- }
- for (auto &Case : SI->cases()) {
- std::optional<int64_t> Val =
- Case.getCaseValue()->getValue().trySExtValue();
- if (!Val || (Val != 1 && Val != 0 && Val != -1))
- return false;
- if (Case.getCaseSuccessor() == Succ) {
- Res = *Val;
- break;
+ bool HasWeights =
+ extractBranchWeights(getValidBranchWeightMDNode(*SI), Weights);
+ bool IsExpected = HasWeights && hasBranchWeightOrigin(*SI);
+ auto AddArm = [&](BasicBlock *Dest, unsigned Mask, unsigned Index) {
+ uint64_t Weight = HasWeights ? Weights[Index] : 0;
+ for (Arm &A : Arms) {
+ if (A.Dest == Dest) {
+ A.Mask |= Mask;
+ A.Weight += Weight;
+ return;
}
}
- } else {
- return false;
+ Arms.push_back({Dest, Mask, Weight});
+ };
+
+ // Tracks which values are not checked any of the switch arms
+ unsigned MissingCmp = All;
+ for (auto &Case : SI->cases()) {
+ std::optional<int64_t> Val = Case.getCaseValue()->getValue().trySExtValue();
+ if (!Val || *Val < -1 || *Val > 1)
+ return false;
+ unsigned Mask = *Val == -1 ? Less : *Val == 0 ? Equal : Greater;
+ MissingCmp &= ~Mask; // Track the default case.
+ AddArm(Case.getCaseSuccessor(), Mask, Case.getSuccessorIndex());
}
- // Determine predicate for the missing case.
- ICmpInst::Predicate Pred;
- switch (Res) {
- case 1:
- Pred = ICmpInst::ICMP_UGT;
- break;
- case 0:
- Pred = ICmpInst::ICMP_EQ;
- break;
- case -1:
- Pred = ICmpInst::ICMP_ULT;
- break;
+ // Do not lower switches whose missing results lead to an unreachable default.
+ if (MissingCmp) {
+ if (!SI->defaultDestUnreachable()) {
+ AddArm(SI->getDefaultDest(), MissingCmp, 0);
+ } else {
+ // Unreachable is reachable, bail out.
+ return false;
+ }
}
- if (Cmp->isSigned())
- Pred = ICmpInst::getSignedPredicate(Pred);
- MDNode *NewWeights = nullptr;
+ if (Arms.empty() || (Arms.size() >= 3 && !AllowThreeArms) || Arms.size() > 3)
+ return false;
+
if (HasWeights)
- NewWeights = MDBuilder(SI->getContext())
- .createBranchWeights(SuccWeight, OtherSuccWeight);
+ stable_sort(Arms,
+ [](const Arm &A, const Arm &B) { return A.Weight > B.Weight; });
- BasicBlock *BB = SI->getParent();
- Builder.SetInsertPoint(SI->getIterator());
- Value *ICmp = Builder.CreateICmp(Pred, Cmp->getLHS(), Cmp->getRHS());
- Builder.CreateCondBr(ICmp, Succ, OtherSucc, NewWeights,
- SI->getMetadata(LLVMContext::MD_unpredictable));
- OtherSucc->removePredecessor(BB);
- if (Unreachable)
- Unreachable->removePredecessor(BB);
+ // Translate each non-final arm's set of results into one comparison of the
+ // original operands. Signedness affects ordering, but not equality.
+ auto GetPredicate = [&](unsigned Mask) {
+ ICmpInst::Predicate Pred;
+ switch (Mask) {
+ case Less:
+ Pred = ICmpInst::ICMP_ULT;
+ break;
+ case Equal:
+ Pred = ICmpInst::ICMP_EQ;
+ break;
+ case Greater:
+ Pred = ICmpInst::ICMP_UGT;
+ break;
+ case Less | Equal:
+ Pred = ICmpInst::ICMP_ULE;
+ break;
+ case Less | Greater:
+ Pred = ICmpInst::ICMP_NE;
+ break;
+ case Equal | Greater:
+ Pred = ICmpInst::ICMP_UGE;
+ break;
+ default:
+ llvm_unreachable("Unexpected comparison outcome mask");
+ }
+ return Cmp->isSigned() ? ICmpInst::getSignedPredicate(Pred) : Pred;
+ };
+
+ BasicBlock *SwitchBB = SI->getParent();
+ SmallMapVector<BasicBlock *, unsigned, 4> SwitchSucc;
+ for (BasicBlock *Dest : successors(SI))
+ ++SwitchSucc[Dest];
+ SmallVector<DominatorTree::UpdateType, 6> Updates;
+ MDNode *Unpredictable = SI->getMetadata(LLVMContext::MD_unpredictable);
+
+ // Each check tests one arm against the remaining outcomes. Fit 64-bit weight
+ // sums back into metadata operands without overflowing merged case weights.
+ auto CreateCheck = [&](const Arm &A, BasicBlock *FalseDest,
+ uint64_t FalseWeight) {
+ Value *Cond =
+ Builder.CreateICmp(GetPredicate(A.Mask), Cmp->getLHS(), Cmp->getRHS());
+ auto *Br =
+ Builder.CreateCondBr(Cond, A.Dest, FalseDest, nullptr, Unpredictable);
+ if (HasWeights)
+ setFittedBranchWeights(*Br, {A.Weight, FalseWeight}, IsExpected);
+ };
+
+ // Add CmpNext BB if needed.
+ BasicBlock *Next = nullptr;
+ Builder.SetInsertPoint(SI);
+ if (Arms.size() == 1) {
+ Builder.CreateBr(Arms[0].Dest);
+ } else if (Arms.size() == 2) {
+ CreateCheck(Arms[0], Arms[1].Dest, Arms[1].Weight);
+ } else {
+ assert(Arms.size() == 3 && "Only three comparison outcomes are possible");
+ Next = BasicBlock::Create(SwitchBB->getContext(), "cmp.next",
+ SwitchBB->getParent(), SwitchBB->getNextNode());
+ CreateCheck(Arms[0], Next, Arms[1].Weight + Arms[2].Weight);
+ Builder.SetInsertPoint(Next);
+ Builder.SetCurrentDebugLocation(SI->getDebugLoc());
+ CreateCheck(Arms[1], Arms[2].Dest, Arms[2].Weight);
+ Updates.push_back({DominatorTree::Insert, SwitchBB, Next});
+ }
+
+ // Retain one existing PHI entry per surviving destination, removing only
+ // duplicate or discarded edges (including an unused default).
+ for (const auto &Entry : SwitchSucc) {
+ BasicBlock *Dest = Entry.first;
+ unsigned Count = Entry.second;
+ bool Keep = any_of(Arms, [Dest](const Arm &A) { return A.Dest == Dest; });
+ for (unsigned I = Keep ? 1 : 0; I < Count; ++I)
+ Dest->removePredecessor(SwitchBB);
+
+ if (!Keep)
+ Updates.push_back({DominatorTree::Delete, SwitchBB, Dest});
+ }
+
+ // The first arm keeps its edge from SwitchBB. Only the second and third
+ // arms move to Next.
+ if (Next) {
+ for (unsigned I = 1; I < 3; ++I) {
+ BasicBlock *Dest = Arms[I].Dest;
+ for (PHINode &PN : Dest->phis()) {
+ int Index = PN.getBasicBlockIndex(SwitchBB);
+ assert(Index >= 0 && "Missing switch predecessor");
+ PN.setIncomingBlock(Index, Next);
+ }
+ Updates.push_back({DominatorTree::Delete, SwitchBB, Dest});
+ Updates.push_back({DominatorTree::Insert, Next, Dest});
+ }
+ }
SI->eraseFromParent();
Cmp->eraseFromParent();
- if (DTU && Unreachable)
- DTU->applyUpdates({{DominatorTree::Delete, BB, Unreachable}});
+ if (DTU)
+ DTU->applyUpdates(Updates);
return true;
}
@@ -8605,7 +8655,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
if (eliminateDeadSwitchCases(SI, DTU, Options.AC, DL))
return requestResimplify();
- if (simplifySwitchOfCmpIntrinsic(SI, Builder, DTU))
+ // Lower switches over ucmp/scmp to comparisons of their operands.
+ if (simplifySwitchOfCmpIntrinsic(SI, Builder, DTU,
+ Options.ConvertThreeWayCmpSwitch))
return requestResimplify();
if (trySwitchToSelect(SI, Builder, DTU, DL, TTI))
diff --git a/llvm/test/Other/new-pm-print-pipeline.ll b/llvm/test/Other/new-pm-print-pipeline.ll
index 2a1192b6b66bd..666c88fdc862f 100644
--- a/llvm/test/Other/new-pm-print-pipeline.ll
+++ b/llvm/test/Other/new-pm-print-pipeline.ll
@@ -46,8 +46,8 @@
; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(print<stack-lifetime><may>,print<stack-lifetime><must>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-17
; CHECK-17: function(print<stack-lifetime><may>,print<stack-lifetime><must>)
-; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(simplifycfg<bonus-inst-threshold=5;forward-switch-cond;switch-to-lookup;keep-loops;hoist-common-insts;hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>,simplifycfg<bonus-inst-threshold=7;no-forward-switch-cond;no-switch-to-lookup;no-keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;no-speculate-blocks;no-simplify-cond-branch;no-speculate-unpredictables>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-18
-; CHECK-18: function(simplifycfg<bonus-inst-threshold=5;forward-switch-cond;no-switch-range-to-icmp;no-switch-to-arithmetic;switch-to-lookup;keep-loops;hoist-common-insts;hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>,simplifycfg<bonus-inst-threshold=7;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-arithmetic;no-switch-to-lookup;no-keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;no-speculate-blocks;no-simplify-cond-branch;no-speculate-unpredictables>)
+; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(simplifycfg<bonus-inst-threshold=5;forward-switch-cond;switch-to-lookup;three-way-cmp-switch;keep-loops;hoist-common-insts;hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>,simplifycfg<bonus-inst-threshold=7;no-forward-switch-cond;no-switch-to-lookup;no-three-way-cmp-switch;no-keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;no-speculate-blocks;no-simplify-cond-branch;no-speculate-unpredictables>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-18
+; CHECK-18: function(simplifycfg<bonus-inst-threshold=5;forward-switch-cond;no-switch-range-to-icmp;no-switch-to-arithmetic;switch-to-lookup;three-way-cmp-switch;keep-loops;hoist-common-insts;hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>,simplifycfg<bonus-inst-threshold=7;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-arithmetic;no-switch-to-lookup;no-three-way-cmp-switch;no-keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;no-speculate-blocks;no-simplify-cond-branch;no-speculate-unpredictables>)
; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only>,loop-vectorize<interleave-forced-only;vectorize-forced-only>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-19
; CHECK-19: function(loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,loop-vectorize<interleave-forced-only;vectorize-forced-only;>)
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
index 6230a319495db..20e7000168489 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
@@ -1,11 +1,12 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -verify-dom-info -verify-each -S | FileCheck %s --check-prefixes=CHECK,EARLY
+; RUN: opt < %s -passes='simplifycfg<three-way-cmp-switch>' -simplifycfg-require-and-preserve-domtree=1 -verify-dom-info -verify-each -S | FileCheck %s --check-prefixes=CHECK,LATE
define void @ucmp_gt1(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt1(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -29,8 +30,8 @@ bb2:
define void @ucmp_gt2(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt2(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -54,8 +55,8 @@ bb2:
define void @ucmp_lt1(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_lt1(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp uge i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -79,8 +80,8 @@ bb2:
define void @ucmp_lt2(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_lt2(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp uge i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -104,8 +105,8 @@ bb2:
define void @ucmp_eq1(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_eq1(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -129,8 +130,8 @@ bb2:
define void @ucmp_eq2(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_eq2(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -154,8 +155,8 @@ bb2:
define void @scmp_gt1(i32 %a, i32 %b) {
; CHECK-LABEL: define void @scmp_gt1(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp sle i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -179,8 +180,8 @@ bb2:
define void @scmp_gt2(i32 %a, i32 %b) {
; CHECK-LABEL: define void @scmp_gt2(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp sle i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -235,8 +236,8 @@ define i32 @ucmp_gt_phi(i32 %a, i32 %b) {
; CHECK-LABEL: define i32 @ucmp_gt_phi(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP0]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP0]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -263,16 +264,7 @@ bb2:
define void @ucmp_gt_extra_case(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_extra_case(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
-; CHECK-NEXT: switch i8 [[RES]], label %[[BB1:.*]] [
-; CHECK-NEXT: i8 -1, label %[[BB2:.*]]
-; CHECK-NEXT: i8 0, label %[[BB2]]
-; CHECK-NEXT: i8 1, label %[[BB2]]
-; CHECK-NEXT: ]
-; CHECK: [[BB1]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
-; CHECK: [[BB2]]:
+; CHECK-NEXT: [[BB2:.*:]]
; CHECK-NEXT: ret void
;
%res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
@@ -319,21 +311,31 @@ bb2:
}
define void @ucmp_gt_not_same_succ(i32 %a, i32 %b) {
-; CHECK-LABEL: define void @ucmp_gt_not_same_succ(
-; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
-; CHECK-NEXT: switch i8 [[RES]], label %[[BB1:.*]] [
-; CHECK-NEXT: i8 -1, label %[[BB2:.*]]
-; CHECK-NEXT: i8 0, label %[[BB3:.*]]
-; CHECK-NEXT: ]
-; CHECK: [[BB1]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
-; CHECK: [[BB3]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
-; CHECK: [[BB2]]:
-; CHECK-NEXT: ret void
+; EARLY-LABEL: define void @ucmp_gt_not_same_succ(
+; EARLY-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; EARLY-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
+; EARLY-NEXT: switch i8 [[RES]], label %[[BB1:.*]] [
+; EARLY-NEXT: i8 -1, label %[[BB2:.*]]
+; EARLY-NEXT: i8 0, label %[[BB3:.*]]
+; EARLY-NEXT: ]
+; EARLY: [[BB1]]:
+; EARLY-NEXT: call void @foo()
+; EARLY-NEXT: br label %[[BB2]]
+; EARLY: [[BB3]]:
+; EARLY-NEXT: call void @foo()
+; EARLY-NEXT: br label %[[BB2]]
+; EARLY: [[BB2]]:
+; EARLY-NEXT: ret void
+;
+; LATE-LABEL: define void @ucmp_gt_not_same_succ(
+; LATE-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; LATE-NEXT: [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
+; LATE-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[CMP_NEXT:.*]]
+; LATE: [[CMP_NEXT]]:
+; LATE-NEXT: call void @foo()
+; LATE-NEXT: br label %[[BB2]]
+; LATE: [[BB2]]:
+; LATE-NEXT: ret void
;
%res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
switch i8 %res, label %bb1 [
@@ -356,8 +358,8 @@ bb2:
define void @ucmp_gt_unpredictable(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_unpredictable(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]], !unpredictable [[META0:![0-9]+]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]], !unpredictable [[META0:![0-9]+]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -381,8 +383,8 @@ bb2:
define void @ucmp_gt_weights(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_weights(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]], !prof [[PROF1:![0-9]+]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]], !prof [[PROF1:![0-9]+]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -406,8 +408,8 @@ bb2:
define void @ucmp_gt_unreachable(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_unreachable(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -464,8 +466,8 @@ unreachable:
define void @ucmp_eq_unreachable(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_eq_unreachable(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -499,8 +501,8 @@ define void @ucmp_gt_unreachable_multi_edge(i8 %x, i32 %a, i32 %b) {
; CHECK-NEXT: i8 1, label %[[BB1:.*]]
; CHECK-NEXT: ]
; CHECK: [[SW]]:
-; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP0]], label %[[BB1]], label %[[BB2:.*]]
+; CHECK-NEXT: [[TMP0:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP0]], label %[[BB2:.*]], label %[[BB1]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -571,24 +573,40 @@ unreachable:
}
define void @ucmp_gt_unreachable_no_two_equal_cases(i32 %a, i32 %b) {
-; CHECK-LABEL: define void @ucmp_gt_unreachable_no_two_equal_cases(
-; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
-; CHECK-NEXT: switch i8 [[RES]], label %[[UNREACHABLE:.*]] [
-; CHECK-NEXT: i8 -1, label %[[BB3:.*]]
-; CHECK-NEXT: i8 0, label %[[BB2:.*]]
-; CHECK-NEXT: i8 1, label %[[BB1:.*]]
-; CHECK-NEXT: ]
-; CHECK: [[BB1]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
-; CHECK: [[BB3]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
-; CHECK: [[BB2]]:
-; CHECK-NEXT: ret void
-; CHECK: [[UNREACHABLE]]:
-; CHECK-NEXT: unreachable
+; EARLY-LABEL: define void @ucmp_gt_unreachable_no_two_equal_cases(
+; EARLY-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; EARLY-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
+; EARLY-NEXT: switch i8 [[RES]], label %[[UNREACHABLE:.*]] [
+; EARLY-NEXT: i8 -1, label %[[BB3:.*]]
+; EARLY-NEXT: i8 0, label %[[BB2:.*]]
+; EARLY-NEXT: i8 1, label %[[BB1:.*]]
+; EARLY-NEXT: ]
+; EARLY: [[BB1]]:
+; EARLY-NEXT: call void @foo()
+; EARLY-NEXT: br label %[[BB2]]
+; EARLY: [[BB3]]:
+; EARLY-NEXT: call void @foo()
+; EARLY-NEXT: br label %[[BB2]]
+; EARLY: [[BB2]]:
+; EARLY-NEXT: ret void
+; EARLY: [[UNREACHABLE]]:
+; EARLY-NEXT: unreachable
+;
+; LATE-LABEL: define void @ucmp_gt_unreachable_no_two_equal_cases(
+; LATE-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; LATE-NEXT: [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
+; LATE-NEXT: br i1 [[TMP1]], label %[[BB3:.*]], label %[[CMP_NEXT:.*]]
+; LATE: [[CMP_NEXT]]:
+; LATE-NEXT: [[TMP2:%.*]] = icmp eq i32 [[A]], [[B]]
+; LATE-NEXT: br i1 [[TMP2]], label %[[BB2:.*]], label %[[BB1:.*]]
+; LATE: [[BB1]]:
+; LATE-NEXT: call void @foo()
+; LATE-NEXT: br label %[[BB2]]
+; LATE: [[BB3]]:
+; LATE-NEXT: call void @foo()
+; LATE-NEXT: br label %[[BB2]]
+; LATE: [[BB2]]:
+; LATE-NEXT: ret void
;
%res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
switch i8 %res, label %unreachable [
@@ -637,20 +655,13 @@ unreachable:
define void @ucmp_gt_unreachable_default_not_unreachable(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_unreachable_default_not_unreachable(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
-; CHECK-NEXT: switch i8 [[RES]], label %[[NOT_UNREACHABLE:.*]] [
-; CHECK-NEXT: i8 -1, label %[[BB2:.*]]
-; CHECK-NEXT: i8 0, label %[[BB2]]
-; CHECK-NEXT: i8 1, label %[[BB1:.*]]
-; CHECK-NEXT: ]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
; CHECK: [[BB2]]:
; CHECK-NEXT: ret void
-; CHECK: [[NOT_UNREACHABLE]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[BB2]]
;
%res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
switch i8 %res, label %not.unreachable [
@@ -674,8 +685,8 @@ not.unreachable:
define void @ucmp_gt_unreachable_weights(i32 %a, i32 %b) {
; CHECK-LABEL: define void @ucmp_gt_unreachable_weights(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
-; CHECK-NEXT: br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]], !prof [[PROF1]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ule i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]], !prof [[PROF1]]
; CHECK: [[BB1]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB2]]
@@ -700,9 +711,188 @@ unreachable:
unreachable
}
+define i64 @ucmp_three_destinations_phi_select(i8 %x, i8 %y) {
+; EARLY-LABEL: define i64 @ucmp_three_destinations_phi_select(
+; EARLY-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; EARLY-NEXT: [[START:.*]]:
+; EARLY-NEXT: [[C:%.*]] = call i8 @llvm.ucmp.i8.i8(i8 [[X]], i8 [[Y]])
+; EARLY-NEXT: switch i8 [[C]], label %[[UNR:.*]] [
+; EARLY-NEXT: i8 -1, label %[[LT:.*]]
+; EARLY-NEXT: i8 0, label %[[END:.*]]
+; EARLY-NEXT: i8 1, label %[[GT:.*]]
+; EARLY-NEXT: ]
+; EARLY: [[UNR]]:
+; EARLY-NEXT: unreachable
+; EARLY: [[LT]]:
+; EARLY-NEXT: [[Z:%.*]] = zext i8 [[X]] to i64
+; EARLY-NEXT: br label %[[END]]
+; EARLY: [[GT]]:
+; EARLY-NEXT: br label %[[END]]
+; EARLY: [[END]]:
+; EARLY-NEXT: [[R:%.*]] = phi i64 [ [[Z]], %[[LT]] ], [ 2, %[[GT]] ], [ 4, %[[START]] ]
+; EARLY-NEXT: ret i64 [[R]]
+;
+; LATE-LABEL: define i64 @ucmp_three_destinations_phi_select(
+; LATE-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; LATE-NEXT: [[START:.*:]]
+; LATE-NEXT: [[TMP0:%.*]] = icmp ult i8 [[X]], [[Y]]
+; LATE-NEXT: [[Z:%.*]] = zext i8 [[X]] to i64
+; LATE-NEXT: [[TMP1:%.*]] = icmp eq i8 [[X]], [[Y]]
+; LATE-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[TMP1]], i64 4, i64 2
+; LATE-NEXT: [[R:%.*]] = select i1 [[TMP0]], i64 [[Z]], i64 [[SPEC_SELECT]]
+; LATE-NEXT: ret i64 [[R]]
+;
+start:
+ %c = call i8 @llvm.ucmp.i8.i8(i8 %x, i8 %y)
+ switch i8 %c, label %unr [
+ i8 -1, label %lt
+ i8 0, label %end
+ i8 1, label %gt
+ ]
+
+unr:
+ unreachable
+
+lt:
+ %z = zext i8 %x to i64
+ br label %end
+
+gt:
+ br label %end
+
+end:
+ %r = phi i64 [ %z, %lt ], [ 2, %gt ], [ 4, %start ]
+ ret i64 %r
+}
+
+define i32 @ucmp_constant_selection_deferred(i32 %a, i32 %b) {
+; EARLY-LABEL: define i32 @ucmp_constant_selection_deferred(
+; EARLY-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; EARLY-NEXT: [[ENTRY:.*]]:
+; EARLY-NEXT: [[C:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
+; EARLY-NEXT: switch i8 [[C]], label %[[UNR:.*]] [
+; EARLY-NEXT: i8 -1, label %[[END:.*]]
+; EARLY-NEXT: i8 0, label %[[EQ:.*]]
+; EARLY-NEXT: i8 1, label %[[GT:.*]]
+; EARLY-NEXT: ]
+; EARLY: [[UNR]]:
+; EARLY-NEXT: unreachable
+; EARLY: [[EQ]]:
+; EARLY-NEXT: br label %[[END]]
+; EARLY: [[GT]]:
+; EARLY-NEXT: br label %[[END]]
+; EARLY: [[END]]:
+; EARLY-NEXT: [[R:%.*]] = phi i32 [ 9, %[[GT]] ], [ 17, %[[EQ]] ], [ 5, %[[ENTRY]] ]
+; EARLY-NEXT: ret i32 [[R]]
+;
+; LATE-LABEL: define i32 @ucmp_constant_selection_deferred(
+; LATE-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; LATE-NEXT: [[ENTRY:.*:]]
+; LATE-NEXT: [[TMP0:%.*]] = icmp ult i32 [[A]], [[B]]
+; LATE-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
+; LATE-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[TMP1]], i32 17, i32 9
+; LATE-NEXT: [[R:%.*]] = select i1 [[TMP0]], i32 5, i32 [[SPEC_SELECT]]
+; LATE-NEXT: ret i32 [[R]]
+;
+entry:
+ %c = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+ switch i8 %c, label %unr [
+ i8 -1, label %lt
+ i8 0, label %eq
+ i8 1, label %gt
+ ]
+
+unr:
+ unreachable
+
+lt:
+ br label %end
+
+eq:
+ br label %end
+
+gt:
+ br label %end
+
+end:
+ %r = phi i32 [ 5, %lt ], [ 17, %eq ], [ 9, %gt ]
+ ret i32 %r
+}
+
+define void @three_destinations_weighted_signed(i32 %a, i32 %b) {
+; EARLY-LABEL: define void @three_destinations_weighted_signed(
+; EARLY-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; EARLY-NEXT: [[CMP:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
+; EARLY-NEXT: switch i8 [[CMP]], label %[[DEAD:.*]] [
+; EARLY-NEXT: i8 0, label %[[EQUAL:.*]]
+; EARLY-NEXT: i8 1, label %[[GREATER:.*]]
+; EARLY-NEXT: i8 -1, label %[[LESS:.*]]
+; EARLY-NEXT: ], !prof [[PROF2:![0-9]+]]
+; EARLY: [[COMMON_RET:.*]]:
+; EARLY-NEXT: ret void
+; EARLY: [[EQUAL]]:
+; EARLY-NEXT: call void @equal()
+; EARLY-NEXT: br label %[[COMMON_RET]]
+; EARLY: [[GREATER]]:
+; EARLY-NEXT: call void @greater()
+; EARLY-NEXT: br label %[[COMMON_RET]]
+; EARLY: [[LESS]]:
+; EARLY-NEXT: call void @less()
+; EARLY-NEXT: br label %[[COMMON_RET]]
+; EARLY: [[DEAD]]:
+; EARLY-NEXT: unreachable
+;
+; LATE-LABEL: define void @three_destinations_weighted_signed(
+; LATE-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; LATE-NEXT: [[TMP1:%.*]] = icmp slt i32 [[A]], [[B]]
+; LATE-NEXT: br i1 [[TMP1]], label %[[LESS:.*]], label %[[CMP_NEXT:.*]], !prof [[PROF2:![0-9]+]]
+; LATE: [[CMP_NEXT]]:
+; LATE-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[A]], [[B]]
+; LATE-NEXT: br i1 [[TMP2]], label %[[GREATER:.*]], label %[[EQUAL:.*]], !prof [[PROF3:![0-9]+]]
+; LATE: [[COMMON_RET:.*]]:
+; LATE-NEXT: ret void
+; LATE: [[EQUAL]]:
+; LATE-NEXT: call void @equal()
+; LATE-NEXT: br label %[[COMMON_RET]]
+; LATE: [[GREATER]]:
+; LATE-NEXT: call void @greater()
+; LATE-NEXT: br label %[[COMMON_RET]]
+; LATE: [[LESS]]:
+; LATE-NEXT: call void @less()
+; LATE-NEXT: br label %[[COMMON_RET]]
+;
+ %cmp = call i8 @llvm.scmp.i8.i32(i32 %a, i32 %b)
+ switch i8 %cmp, label %dead [
+ i8 0, label %equal
+ i8 1, label %greater
+ i8 -1, label %less
+ ], !prof !{!"branch_weights", i32 0, i32 10, i32 30, i32 60}
+equal:
+ call void @equal()
+ ret void
+greater:
+ call void @greater()
+ ret void
+less:
+ call void @less()
+ ret void
+dead:
+ unreachable
+}
+
declare void @use(i8)
declare void @foo()
+declare void @equal()
+declare void @greater()
+declare void @less()
+
+;.
+; EARLY: [[META0]] = !{}
+; EARLY: [[PROF1]] = !{!"branch_weights", i32 30, i32 5}
+; EARLY: [[PROF2]] = !{!"branch_weights", i32 0, i32 10, i32 30, i32 60}
;.
-; CHECK: [[META0]] = !{}
-; CHECK: [[PROF1]] = !{!"branch_weights", i32 5, i32 30}
+; LATE: [[META0]] = !{}
+; LATE: [[PROF1]] = !{!"branch_weights", i32 30, i32 5}
+; LATE: [[PROF2]] = !{!"branch_weights", i32 60, i32 40}
+; LATE: [[PROF3]] = !{!"branch_weights", i32 30, i32 10}
;.
More information about the llvm-commits
mailing list