[llvm] 4d85285 - [SimplifyCFG] Fold switch over ucmp/scmp to icmp and br (#105636)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 22 07:57:12 PDT 2024


Author: Nikita Popov
Date: 2024-08-22T16:57:09+02:00
New Revision: 4d85285ff68d11fcb8c6b296799a11074e7ff7d7

URL: https://github.com/llvm/llvm-project/commit/4d85285ff68d11fcb8c6b296799a11074e7ff7d7
DIFF: https://github.com/llvm/llvm-project/commit/4d85285ff68d11fcb8c6b296799a11074e7ff7d7.diff

LOG: [SimplifyCFG] Fold switch over ucmp/scmp to icmp and br (#105636)

If we switch over ucmp/scmp and have two switch cases going to the same
destination, we can convert into icmp+br.

Fixes https://github.com/llvm/llvm-project/issues/105632.

Added: 
    

Modified: 
    llvm/lib/Transforms/Utils/SimplifyCFG.cpp
    llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 00efd3c0eb72ec..da4d57f808e9bf 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7131,6 +7131,119 @@ static bool simplifySwitchOfPowersOfTwo(SwitchInst *SI, IRBuilder<> &Builder,
   return true;
 }
 
+/// Fold switch over ucmp/scmp intrinsic to br if two of the switch arms have
+/// the same destination.
+static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder,
+                                         DomTreeUpdater *DTU) {
+  auto *Cmp = dyn_cast<CmpIntrinsic>(SI->getCondition());
+  if (!Cmp || !Cmp->hasOneUse())
+    return false;
+
+  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->defaultDestUndefined()) {
+    // 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;
+      }
+    }
+  } else {
+    return false;
+  }
+
+  // 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;
+  }
+  if (Cmp->isSigned())
+    Pred = ICmpInst::getSignedPredicate(Pred);
+
+  MDNode *NewWeights = nullptr;
+  if (HasWeights)
+    NewWeights = MDBuilder(SI->getContext())
+                     .createBranchWeights(SuccWeight, OtherSuccWeight);
+
+  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);
+  SI->eraseFromParent();
+  Cmp->eraseFromParent();
+  if (DTU && Unreachable)
+    DTU->applyUpdates({{DominatorTree::Delete, BB, Unreachable}});
+  return true;
+}
+
 bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
   BasicBlock *BB = SI->getParent();
 
@@ -7163,6 +7276,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
   if (eliminateDeadSwitchCases(SI, DTU, Options.AC, DL))
     return requestResimplify();
 
+  if (simplifySwitchOfCmpIntrinsic(SI, Builder, DTU))
+    return requestResimplify();
+
   if (trySwitchToSelect(SI, Builder, DTU, DL, TTI))
     return requestResimplify();
 

diff  --git a/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll b/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
index 1ce18533d156d0..6230a319495dba 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-on-cmp.ll
@@ -4,11 +4,8 @@
 define void @ucmp_gt1(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_gt1(
 ; 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:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -32,11 +29,8 @@ bb2:
 define void @ucmp_gt2(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_gt2(
 ; 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 0, label %[[BB2:.*]]
-; CHECK-NEXT:      i8 -1, label %[[BB2]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ugt i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -60,11 +54,8 @@ bb2:
 define void @ucmp_lt1(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_lt1(
 ; 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 %[[BB2:.*]] [
-; CHECK-NEXT:      i8 1, label %[[BB1:.*]]
-; CHECK-NEXT:      i8 0, label %[[BB1]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -88,11 +79,8 @@ bb2:
 define void @ucmp_lt2(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_lt2(
 ; 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 %[[BB2:.*]] [
-; CHECK-NEXT:      i8 0, label %[[BB1:.*]]
-; CHECK-NEXT:      i8 1, label %[[BB1]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB2:.*]], label %[[BB1:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -116,11 +104,8 @@ bb2:
 define void @ucmp_eq1(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_eq1(
 ; 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 1, label %[[BB2]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -144,11 +129,8 @@ bb2:
 define void @ucmp_eq2(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @ucmp_eq2(
 ; 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 -1, label %[[BB2]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -172,11 +154,8 @@ bb2:
 define void @scmp_gt1(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @scmp_gt1(
 ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT:    [[RES:%.*]] = call i8 @llvm.scmp.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:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -200,11 +179,8 @@ bb2:
 define void @scmp_gt2(i32 %a, i32 %b) {
 ; CHECK-LABEL: define void @scmp_gt2(
 ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
-; CHECK-NEXT:    [[RES:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
-; CHECK-NEXT:    switch i8 [[RES]], label %[[BB1:.*]] [
-; CHECK-NEXT:      i8 0, label %[[BB2:.*]]
-; CHECK-NEXT:      i8 -1, label %[[BB2]]
-; CHECK-NEXT:    ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
@@ -259,16 +235,13 @@ 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:    [[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:    ]
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ugt i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP0]], label %[[BB1:.*]], label %[[BB2:.*]]
 ; CHECK:       [[BB1]]:
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    br label %[[BB2]]
 ; CHECK:       [[BB2]]:
-; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 0, %[[BB1]] ], [ 1, %[[ENTRY]] ], [ 1, %[[ENTRY]] ]
+; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 0, %[[BB1]] ], [ 1, %[[ENTRY]] ]
 ; CHECK-NEXT:    ret i32 [[PHI]]
 ;
 entry:
@@ -380,5 +353,356 @@ bb2:
   ret void
 }
 
+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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %bb1 [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  ], !unpredictable !{}
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+}
+
+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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %bb1 [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  ], !prof !{!"branch_weights", i32 5, i32 10, i32 20}
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+}
+
+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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  unreachable
+}
+
+define void @ucmp_lt_unreachable(i32 %a, i32 %b) {
+; CHECK-LABEL: define void @ucmp_lt_unreachable(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb1
+  i8 0, label %bb2
+  i8 1, label %bb2
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb2
+  i8 0, label %bb1
+  i8 1, label %bb2
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  unreachable
+}
+
+define void @ucmp_gt_unreachable_multi_edge(i8 %x, i32 %a, i32 %b) {
+; CHECK-LABEL: define void @ucmp_gt_unreachable_multi_edge(
+; CHECK-SAME: i8 [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    switch i8 [[X]], label %[[UNREACHABLE:.*]] [
+; CHECK-NEXT:      i8 0, label %[[SW:.*]]
+; 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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+;
+entry:
+  switch i8 %x, label %unreachable [
+  i8 0, label %sw
+  i8 1, label %bb1
+  ]
+
+sw:
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  %phi = phi i32 [ 0, %entry ], [ 1, %sw ]
+  unreachable
+}
+
+define void @ucmp_gt_unreachable_wrong_case(i32 %a, i32 %b) {
+; CHECK-LABEL: define void @ucmp_gt_unreachable_wrong_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 %[[UNREACHABLE:.*]] [
+; CHECK-NEXT:      i8 -2, label %[[BB2:.*]]
+; 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:       [[BB2]]:
+; CHECK-NEXT:    ret void
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -2, label %bb2
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  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
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb3
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb3:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  unreachable
+}
+
+define void @ucmp_gt_unreachable_three_equal_cases(i32 %a, i32 %b) {
+; CHECK-LABEL: define void @ucmp_gt_unreachable_three_equal_cases(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:  [[BB1:.*:]]
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb1
+  i8 0, label %bb1
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  ret void
+
+unreachable:
+  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:       [[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 [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ]
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+not.unreachable:
+  call void @foo()
+  br label %bb2
+}
+
+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:       [[BB1]]:
+; CHECK-NEXT:    call void @foo()
+; CHECK-NEXT:    br label %[[BB2]]
+; CHECK:       [[BB2]]:
+; CHECK-NEXT:    ret void
+;
+  %res = call i8 @llvm.ucmp.i8.i32(i32 %a, i32 %b)
+  switch i8 %res, label %unreachable [
+  i8 -1, label %bb2
+  i8 0, label %bb2
+  i8 1, label %bb1
+  ], !prof !{!"branch_weights", i32 0, i32 10, i32 20, i32 5}
+
+bb1:
+  call void @foo()
+  br label %bb2
+
+bb2:
+  ret void
+
+unreachable:
+  unreachable
+}
+
 declare void @use(i8)
 declare void @foo()
+;.
+; CHECK: [[META0]] = !{}
+; CHECK: [[PROF1]] = !{!"branch_weights", i32 5, i32 30}
+;.


        


More information about the llvm-commits mailing list