]> granicus.if.org Git - llvm/commitdiff
Omit range checks from jump tables when lowering switches with unreachable
authorAyonam Ray <ayonam@rrlogic.co.in>
Tue, 1 Jan 2019 06:37:50 +0000 (06:37 +0000)
committerAyonam Ray <ayonam@rrlogic.co.in>
Tue, 1 Jan 2019 06:37:50 +0000 (06:37 +0000)
default

During the lowering of a switch that would result in the generation of a jump
table, a range check is performed before indexing into the jump table, for the
switch value being outside the jump table range and a conditional branch is
inserted to jump to the default block. In case the default block is
unreachable, this conditional jump can be omitted. This patch implements
omitting this conditional branch for unreachable defaults.

Review Reference: D52002

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@350186 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
test/CodeGen/AArch64/switch-unreachable-default.ll [new file with mode: 0644]

index d64b96b387424d68ffdab3aba22258509d08038f..2cca7e077e42de71e758950a757e8cfeeeb7011e 100644 (file)
@@ -2189,24 +2189,30 @@ void SelectionDAGBuilder::visitJumpTableHeader(JumpTable &JT,
                                     JumpTableReg, SwitchOp);
   JT.Reg = JumpTableReg;
 
-  // Emit the range check for the jump table, and branch to the default block
-  // for the switch statement if the value being switched on exceeds the largest
-  // case in the switch.
-  SDValue CMP = DAG.getSetCC(
-      dl, TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(),
-                                 Sub.getValueType()),
-      Sub, DAG.getConstant(JTH.Last - JTH.First, dl, VT), ISD::SETUGT);
-
-  SDValue BrCond = DAG.getNode(ISD::BRCOND, dl,
-                               MVT::Other, CopyTo, CMP,
-                               DAG.getBasicBlock(JT.Default));
-
-  // Avoid emitting unnecessary branches to the next block.
-  if (JT.MBB != NextBlock(SwitchBB))
-    BrCond = DAG.getNode(ISD::BR, dl, MVT::Other, BrCond,
-                         DAG.getBasicBlock(JT.MBB));
-
-  DAG.setRoot(BrCond);
+  if (!JTH.OmitRangeCheck) {
+    // Emit the range check for the jump table, and branch to the default block
+    // for the switch statement if the value being switched on exceeds the
+    // largest case in the switch.
+    SDValue CMP = DAG.getSetCC(
+        dl, TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(),
+                                   Sub.getValueType()),
+        Sub, DAG.getConstant(JTH.Last - JTH.First, dl, VT), ISD::SETUGT);
+
+    SDValue BrCond = DAG.getNode(ISD::BRCOND, dl,
+                                 MVT::Other, CopyTo, CMP,
+                                 DAG.getBasicBlock(JT.Default));
+
+    // Avoid emitting unnecessary branches to the next block.
+    if (JT.MBB != NextBlock(SwitchBB))
+      BrCond = DAG.getNode(ISD::BR, dl, MVT::Other, BrCond,
+                           DAG.getBasicBlock(JT.MBB));
+
+    DAG.setRoot(BrCond);
+  } else {
+    SDValue BrCond = DAG.getNode(ISD::BR, dl, MVT::Other, CopyTo,
+                                 DAG.getBasicBlock(JT.MBB));
+    DAG.setRoot(BrCond);
+  }
 }
 
 /// Create a LOAD_STACK_GUARD node, and let it carry the target specific global
@@ -9558,10 +9564,13 @@ bool SelectionDAGBuilder::buildJumpTable(const CaseClusterVector &Clusters,
                      ->createJumpTableIndex(Table);
 
   // Set up the jump table info.
+  bool UnreachableDefault =
+      isa<UnreachableInst>(SI->getDefaultDest()->getFirstNonPHIOrDbg());
+  bool OmitRangeCheck = UnreachableDefault;
   JumpTable JT(-1U, JTI, JumpTableMBB, nullptr);
   JumpTableHeader JTH(Clusters[First].Low->getValue(),
                       Clusters[Last].High->getValue(), SI->getCondition(),
-                      nullptr, false);
+                      nullptr, false, OmitRangeCheck);
   JTCases.emplace_back(std::move(JTH), std::move(JT));
 
   JTCluster = CaseCluster::jumpTable(Clusters[First].Low, Clusters[Last].High,
@@ -10298,6 +10307,7 @@ MachineBasicBlock *SelectionDAGBuilder::peelDominantCaseCluster(
     const SwitchInst &SI, CaseClusterVector &Clusters,
     BranchProbability &PeeledCaseProb) {
   MachineBasicBlock *SwitchMBB = FuncInfo.MBB;
+
   // Don't perform if there is only one cluster or optimizing for size.
   if (SwitchPeelThreshold > 100 || !FuncInfo.BPI || Clusters.size() < 2 ||
       TM.getOptLevel() == CodeGenOpt::None ||
@@ -10350,6 +10360,7 @@ void SelectionDAGBuilder::visitSwitch(const SwitchInst &SI) {
   // Extract cases from the switch.
   BranchProbabilityInfo *BPI = FuncInfo.BPI;
   CaseClusterVector Clusters;
+
   Clusters.reserve(SI.getNumCases());
   for (auto I : SI.cases()) {
     MachineBasicBlock *Succ = FuncInfo.MBBMap[I.getCaseSuccessor()];
@@ -10367,38 +10378,6 @@ void SelectionDAGBuilder::visitSwitch(const SwitchInst &SI) {
   // if there are many clusters.
   sortAndRangeify(Clusters);
 
-  if (TM.getOptLevel() != CodeGenOpt::None) {
-    // Replace an unreachable default with the most popular destination.
-    // FIXME: Exploit unreachable default more aggressively.
-    bool UnreachableDefault =
-        isa<UnreachableInst>(SI.getDefaultDest()->getFirstNonPHIOrDbg());
-    if (UnreachableDefault && !Clusters.empty()) {
-      DenseMap<const BasicBlock *, unsigned> Popularity;
-      unsigned MaxPop = 0;
-      const BasicBlock *MaxBB = nullptr;
-      for (auto I : SI.cases()) {
-        const BasicBlock *BB = I.getCaseSuccessor();
-        if (++Popularity[BB] > MaxPop) {
-          MaxPop = Popularity[BB];
-          MaxBB = BB;
-        }
-      }
-      // Set new default.
-      assert(MaxPop > 0 && MaxBB);
-      DefaultMBB = FuncInfo.MBBMap[MaxBB];
-
-      // Remove cases that were pointing to the destination that is now the
-      // default.
-      CaseClusterVector New;
-      New.reserve(Clusters.size());
-      for (CaseCluster &CC : Clusters) {
-        if (CC.MBB != DefaultMBB)
-          New.push_back(CC);
-      }
-      Clusters = std::move(New);
-    }
-  }
-
   // The branch probablity of the peeled case.
   BranchProbability PeeledCaseProb = BranchProbability::getZero();
   MachineBasicBlock *PeeledSwitchMBB =
index 5f9cdb69daf72d0d017ace630f7f6f7bed163951..d2fb8e28ff3444374d3c0465f791a5b595811e7e 100644 (file)
@@ -279,11 +279,12 @@ private:
     const Value *SValue;
     MachineBasicBlock *HeaderBB;
     bool Emitted;
+    bool OmitRangeCheck;
 
     JumpTableHeader(APInt F, APInt L, const Value *SV, MachineBasicBlock *H,
-                    bool E = false)
+                    bool E = false, bool ORC = false)
         : First(std::move(F)), Last(std::move(L)), SValue(SV), HeaderBB(H),
-          Emitted(E) {}
+          Emitted(E), OmitRangeCheck(ORC) {}
   };
   using JumpTableBlock = std::pair<JumpTableHeader, JumpTable>;
 
diff --git a/test/CodeGen/AArch64/switch-unreachable-default.ll b/test/CodeGen/AArch64/switch-unreachable-default.ll
new file mode 100644 (file)
index 0000000..7ddcd51
--- /dev/null
@@ -0,0 +1,62 @@
+; RUN: llc -O3 -o - %s | FileCheck %s
+
+; Test that the output in the presence of an unreachable default does not have
+; a compare and branch at the top of the switch to handle the default case.
+
+target triple = "aarch64-unknown-linux-gnu"
+
+; Function Attrs: nounwind
+define void @fn(i4) {
+  switch i4 %0, label %default [
+    i4 0, label %case_0
+    i4 1, label %case_1
+    i4 2, label %case_2
+    i4 3, label %case_3
+    i4 4, label %case_4
+    i4 5, label %case_5
+  ]
+
+; CHECK-LABEL: fn:
+; CHECK-NOT:    sub
+; CHECK-NOT:    cmp
+; CHECK-NOT:    b.hi
+; CHECK:        ldr {{x[0-9]+}}, [{{x[0-9]+}}, {{x[0-9]+}}, lsl #3]
+; CHECK:        br {{x[0-9]+}}
+
+default:
+  unreachable
+
+case_0:
+  tail call void @handle_case_00(i4 %0) #2
+  br label %return_label
+
+case_1:
+  tail call void @handle_case_01(i4 %0) #2
+  br label %return_label
+
+case_2:
+  tail call void @handle_case_02(i4 %0) #2
+  br label %return_label
+
+case_3:
+  tail call void @handle_case_03(i4 %0) #2
+  br label %return_label
+
+case_4:
+  tail call void @handle_case_04(i4 %0) #2
+  br label %return_label
+
+case_5:
+  tail call void @handle_case_05(i4 %0) #2
+  br label %return_label
+
+return_label:
+  ret void
+}
+
+declare  void @handle_case_00(i4)
+declare  void @handle_case_01(i4)
+declare  void @handle_case_02(i4)
+declare  void @handle_case_03(i4)
+declare  void @handle_case_04(i4)
+declare  void @handle_case_05(i4)