]> granicus.if.org Git - llvm/commitdiff
[CodeGen] Omit range checks from jump tables when lowering switches with unreachable...
authorAyonam Ray <ayonam@rrlogic.co.in>
Wed, 6 Mar 2019 10:01:02 +0000 (10:01 +0000)
committerAyonam Ray <ayonam@rrlogic.co.in>
Wed, 6 Mar 2019 10:01:02 +0000 (10:01 +0000)
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.

Differential Revision: https://reviews.llvm.org/D52002
Reviewers: Hans Wennborg, Eli Freidman, Roman Lebedev

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@355490 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]
test/CodeGen/X86/pr38743.ll
test/CodeGen/X86/switch-jump-table.ll

index 33921a2c56d95224d4e3b1bcb6c16e6b1840fa11..52b039eb44fb503b8b50d0e7cdbe2ec8be73b9db 100644 (file)
@@ -2388,24 +2388,31 @@ 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);
+    SwitchBB->removeSuccessor(JT.Default, true);
+  }
 }
 
 /// Create a LOAD_STACK_GUARD node, and let it carry the target specific global
@@ -9790,10 +9797,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,
@@ -10599,38 +10609,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 e84214dbf5edf16fe8f0c1fd36c07ab77e1fc966..09dd7afd9253a73f528e6d5b4499acaab1275d86 100644 (file)
@@ -277,11 +277,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..ce11c14
--- /dev/null
@@ -0,0 +1,63 @@
+; 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:        ldrb {{w[0-9]+}}, [{{x[0-9]+}}, {{x[0-9]+}}]
+; CHECK:        add {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, lsl #2
+; 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)
index ac5d48ef5f55c35cfb3a817e92e7ada6245f5bf4..97fe277405ebeb44adf9914332e47b494fd1f59b 100644 (file)
@@ -18,41 +18,43 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture r
 
 define void @pr38743() #1 align 2 {
 ; CHECK-LABEL: pr38743:
-; CHECK:       # %bb.0: # %bb
-; CHECK-NEXT:    cmpl $3, %eax
-; CHECK-NEXT:    je .LBB0_4
-; CHECK-NEXT:  # %bb.1: # %bb
-; CHECK-NEXT:    cmpl $1, %eax
-; CHECK-NEXT:    je .LBB0_2
-; CHECK-NEXT:  # %bb.3: # %bb5
-; CHECK-NEXT:    movzwl .str.17+{{.*}}(%rip), %eax
-; CHECK-NEXT:    movw %ax, -{{[0-9]+}}(%rsp)
-; CHECK-NEXT:    movq {{.*}}(%rip), %rax
-; CHECK-NEXT:    jmp .LBB0_5
-; CHECK-NEXT:  .LBB0_4: # %bb8
-; CHECK-NEXT:    movq .str.18+{{.*}}(%rip), %rax
-; CHECK-NEXT:    movq %rax, -{{[0-9]+}}(%rsp)
-; CHECK-NEXT:    movq {{.*}}(%rip), %rax
-; CHECK-NEXT:    jmp .LBB0_5
-; CHECK-NEXT:  .LBB0_2: # %bb2
-; CHECK-NEXT:    movq .str.16+{{.*}}(%rip), %rax
-; CHECK-NEXT:    movq %rax, -{{[0-9]+}}(%rsp)
-; CHECK-NEXT:    movq {{.*}}(%rip), %rax
-; CHECK-NEXT:  .LBB0_5: # %bb12
-; CHECK-NEXT:    movq %rax, -{{[0-9]+}}(%rsp)
-; CHECK-NEXT:    movq -{{[0-9]+}}(%rsp), %rax
-; CHECK-NEXT:    movq %rax, (%rax)
-; CHECK-NEXT:    movb -{{[0-9]+}}(%rsp), %al
-; CHECK-NEXT:    movq -{{[0-9]+}}(%rsp), %rcx
-; CHECK-NEXT:    movzwl -{{[0-9]+}}(%rsp), %edx
-; CHECK-NEXT:    movl -{{[0-9]+}}(%rsp), %esi
-; CHECK-NEXT:    movb -{{[0-9]+}}(%rsp), %dil
-; CHECK-NEXT:    movb %al, (%rax)
-; CHECK-NEXT:    movq %rcx, 1(%rax)
-; CHECK-NEXT:    movw %dx, 9(%rax)
-; CHECK-NEXT:    movl %esi, 11(%rax)
-; CHECK-NEXT:    movb %dil, 15(%rax)
-; CHECK-NEXT:    retq
+; CHECK:       # %bb.0:                                # %bb
+; CHECK-NEXT:          xorl    %eax, %eax
+; CHECK-NEXT:          jmpq    *.LJTI0_0(,%rax,8)
+; CHECK-NEXT: .[[LABEL1:[A-Z_0-9]+]]:                                # %bb5
+; CHECK-NEXT:  movzwl  .str.17+{{.*}}(%rip), %eax
+; CHECK-NEXT:  movw    %ax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:  movq    .str.17(%rip), %rax
+; CHECK-NEXT:  jmp     .[[LABEL4:[A-Z_0-9]+]]
+; CHECK-NEXT: .[[LABEL2:[A-Z_0-9]+]]:                                # %bb2
+; CHECK-NEXT:  movq    .str.16+{{.*}}(%rip), %rax
+; CHECK-NEXT:  movq    %rax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:  movq    .str.16(%rip), %rax
+; CHECK-NEXT:  jmp     .[[LABEL4]]
+; CHECK-NEXT: .[[LABEL3:[A-Z_0-9]+]]:                                # %bb8
+; CHECK-NEXT:  movq    .str.18+{{.*}}(%rip), %rax
+; CHECK-NEXT:  movq    %rax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:  movq    .str.18(%rip), %rax
+; CHECK-NEXT: .[[LABEL4]]:                                # %bb12
+; CHECK-NEXT:  movq    %rax, -{{[0-9]+}}(%rsp)
+; CHECK-NEXT:  movq    -{{[0-9]+}}(%rsp), %rax
+; CHECK-NEXT:  movq    %rax, (%rax)
+; CHECK-NEXT:  movb    -{{[0-9]+}}(%rsp), %al
+; CHECK-NEXT:  movq    -{{[0-9]+}}(%rsp), %rcx
+; CHECK-NEXT:  movzwl  -{{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:  movl    -{{[0-9]+}}(%rsp), %esi
+; CHECK-NEXT:  movb    -{{[0-9]+}}(%rsp), %dil
+; CHECK-NEXT:  movb    %al, (%rax)
+; CHECK-NEXT:  movq    %rcx, {{[0-9]+}}(%rax)
+; CHECK-NEXT:  movw    %dx, {{[0-9]+}}(%rax)
+; CHECK-NEXT:  movl    %esi, {{[0-9]+}}(%rax)
+; CHECK-NEXT:  movb    %dil, {{[0-9]+}}(%rax)
+; CHECK-NEXT:  retq
+; CHECK-LABEL: .LJTI0_0:
+; CHECK:       .quad   .[[LABEL2]]
+; CHECK-NEXT:  .quad   .[[LABEL1]]
+; CHECK-NEXT:  .quad   .[[LABEL3]]
+; CHECK-NEXT:  .quad   .[[LABEL1]]
 bb:
   %tmp = alloca %0, align 16
   %tmp1 = bitcast %0* %tmp to i8*
index 1e1f7c5cef0ee5df669b899253ecf518cc16ef94..4c7937078e87f53a23d06cd2ea56f871af38b154 100644 (file)
@@ -2,14 +2,12 @@
 ; RUN: llc -mtriple=i686-pc-gnu-linux -print-machineinstrs=expand-isel-pseudos %s -o /dev/null 2>&1 | FileCheck %s -check-prefix=CHECK-JT-PROB
 
 
-; An unreachable default destination is replaced with the most popular case label.
+; An unreachable default destination is ignored and no compare and branch
+; is generated for the default values.
 
 define void @foo(i32 %x, i32* %to) {
 ; CHECK-LABEL: foo:
 ; CHECK: movl 4(%esp), [[REG:%e[a-z]{2}]]
-; CHECK: cmpl $3, [[REG]]
-; CHECK: ja .LBB0_6
-; CHECK-NEXT: # %bb.1:
 ; CHECK-NEXT: jmpl *.LJTI0_0(,[[REG]],4)
 ; CHECK: movl $4
 ; CHECK: retl
@@ -45,10 +43,12 @@ default:
 
 ; The jump table has four entries.
 ; CHECK-LABEL: .LJTI0_0:
+; CHECK-NEXT: .long  .LBB0_1
 ; CHECK-NEXT: .long  .LBB0_2
 ; CHECK-NEXT: .long  .LBB0_3
 ; CHECK-NEXT: .long  .LBB0_4
 ; CHECK-NEXT: .long  .LBB0_5
+; CHECK-NEXT: .long  .LBB0_5
 }
 
 ; Check if branch probabilities are correctly assigned to the jump table.