[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