Index: lib/CodeGen/CodeGenPrepare.cpp =================================================================== --- lib/CodeGen/CodeGenPrepare.cpp +++ lib/CodeGen/CodeGenPrepare.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/BranchProbabilityInfo.h" +#include "llvm/Analysis/CFG.h" #include "llvm/Analysis/InstructionSimplify.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ProfileSummaryInfo.h" @@ -224,6 +225,7 @@ unsigned CreatedInstCost); bool splitBranchCondition(Function &F); bool simplifyOffsetableRelocate(Instruction &I); + bool splitIndirectCriticalEdges(Function &F); }; } @@ -319,6 +321,8 @@ EverMadeChange |= MadeChange; } + EverMadeChange |= splitIndirectCriticalEdges(F); + SunkAddrs.clear(); if (!DisableBranchOpts) { @@ -437,6 +441,108 @@ return DestBB; } +// Split critical edges where the source of the edge is an indirectbr +// instruction. This isn't always possible, but we can handle the easy cases. +// This is useful because MI is unable to split such critical edges, +// which means it will not be able to sink instructions along those edges. +// This is especially painful for indirect branches with many successors, where +// we end up having to prepare all outgoing values in the origin block. +bool CodeGenPrepare::splitIndirectCriticalEdges(Function &F) { + DenseMap Branches; + bool Changed = false; + + // We are specifically looking for the jump table pattern, that is + // "goto *table[offset]" + for (auto &BB : F) { + auto *IBI = dyn_cast(BB.getTerminator()); + if (!IBI) + continue; + + // If there's only one possible successor, it can't be a critical edge, + // so don't bother. + if (IBI->getNumSuccessors() == 1) + continue; + + auto *LI = dyn_cast(IBI->getOperand(0)); + if (!LI) + continue; + + auto *OffsetGEP = dyn_cast(LI->getOperand(0)); + if (!OffsetGEP) + continue; + + GlobalVariable *GV = dyn_cast(OffsetGEP->getOperand(0)); + // Only handle internal GVs that have exactly one use (this GEP), + // and the GEP has only one use (this load). + // TODO: Extend this to cover more cases. + // For a single use, we don't have to worry about the pointer escaping, + // the table being written to, etc. since that would require another use. + // For multiple uses, we'll need to be more careful. + if (!GV || !GV->hasLocalLinkage() || !GV->hasOneUse() || + !OffsetGEP->hasOneUse()) + continue; + + Branches[GV] = IBI; + + DEBUG(dbgs() << "Found indirect branch " << *IBI << " with jump table " + << GV->getName() << "\n"); + } + + for (auto Pair : Branches) { + // Since we require GV to have local linkage, we expect the initializer + // we see in this module to be its real initializer. + auto *GV = Pair.first; + auto *IBI = Pair.second; + + assert(GV->hasDefinitiveInitializer() && + "Expected jump table to be initialized"); + + ConstantArray *JumpTable = dyn_cast(GV->getInitializer()); + if (!JumpTable) + continue; + + // Map original destination blocks to the newly created blocks. We can't + // update the jump table in-place, since it's a Constant. + DenseMap BlockRemap; + + for (unsigned Succ = 0, E = IBI->getNumSuccessors(); Succ != E; ++Succ) { + BasicBlock *OrigDestBB = IBI->getSuccessor(Succ); + BasicBlock *NewBB = SplitCriticalEdge(IBI, Succ); + if (!NewBB) + continue; + BlockRemap[OrigDestBB] = NewBB; + } + + // Construct a list of blockaddreses for the new blocks. If we did not + // split an edge targeting a specific entry (e.g., if the entry is null), + // just copy it as is. + SmallVector BlockList; + BlockList.reserve(JumpTable->getNumOperands()); + for (auto &Op : JumpTable->operands()) { + Constant *Curr = cast(Op); + BlockAddress *Addr = dyn_cast(Curr); + if (Addr) { + auto NewBlock = BlockRemap.find(cast(Addr->getOperand(1))); + if (NewBlock != BlockRemap.end()) { + Curr = BlockAddress::get(NewBlock->second); + } + } + BlockList.push_back(Curr); + } + + Constant *NewJT = ConstantArray::get(JumpTable->getType(), BlockList); + + if (NewJT != JumpTable) { + DEBUG(dbgs() << "Replacing Jump Table " << *JumpTable << " with " + << *NewJT << "\n"); + + GV->setInitializer(NewJT); + } + } + + return Changed; +} + /// Eliminate blocks that contain only PHI nodes, debug info directives, and an /// unconditional branch. Passes before isel (e.g. LSR/loopsimplify) often split /// edges in ways that are non-optimal for isel. Start by eliminating these Index: lib/Transforms/Utils/BreakCriticalEdges.cpp =================================================================== --- lib/Transforms/Utils/BreakCriticalEdges.cpp +++ lib/Transforms/Utils/BreakCriticalEdges.cpp @@ -131,9 +131,6 @@ if (!isCriticalEdge(TI, SuccNum, Options.MergeIdenticalEdges)) return nullptr; - assert(!isa(TI) && - "Cannot split critical edge from IndirectBrInst"); - BasicBlock *TIBB = TI->getParent(); BasicBlock *DestBB = TI->getSuccessor(SuccNum); Index: test/CodeGen/MSP430/indirectbr2.ll =================================================================== --- test/CodeGen/MSP430/indirectbr2.ll +++ test/CodeGen/MSP430/indirectbr2.ll @@ -5,7 +5,7 @@ entry: %tmp1 = getelementptr inbounds [5 x i8*], [5 x i8*]* @C.0.2070, i16 0, i16 %i ; [#uses=1] %gotovar.4.0 = load i8*, i8** %tmp1, align 4 ; [#uses=1] -; CHECK: br .LC.0.2070(r12) +; CHECK: br .LC.0.2070(r15) indirectbr i8* %gotovar.4.0, [label %L5, label %L4, label %L3, label %L2, label %L1] L5: ; preds = %bb2 Index: test/Transforms/CodeGenPrepare/computedgoto.ll =================================================================== --- test/Transforms/CodeGenPrepare/computedgoto.ll +++ test/Transforms/CodeGenPrepare/computedgoto.ll @@ -0,0 +1,298 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -codegenprepare -S < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @use(i32) local_unnamed_addr +declare void @useptr([2 x i8*]*) local_unnamed_addr + +; CHECK: @simple.targets = internal constant [2 x i8*] [i8* blockaddress(@simple, %indirectgoto.bb0_crit_edge), i8* blockaddress(@simple, %indirectgoto.bb1_crit_edge)], align 16 +@simple.targets = internal constant [2 x i8*] [i8* blockaddress(@simple, %bb0), i8* blockaddress(@simple, %bb1)], align 16 + +; CHECK: @external.targets = constant [2 x i8*] [i8* blockaddress(@external, %bb0), i8* blockaddress(@external, %bb1)], align 16 +@external.targets = constant [2 x i8*] [i8* blockaddress(@external, %bb0), i8* blockaddress(@external, %bb1)], align 16 + +; CHECK: @escape.targets = internal constant [2 x i8*] [i8* blockaddress(@escape, %bb0), i8* blockaddress(@escape, %bb1)], align 16 +@escape.targets = internal constant [2 x i8*] [i8* blockaddress(@escape, %bb0), i8* blockaddress(@escape, %bb1)], align 16 + +; CHECK: @multi.targets = internal constant [2 x i8*] [i8* blockaddress(@multi, %bb0), i8* blockaddress(@multi, %bb1)], align 16 +@multi.targets = internal constant [2 x i8*] [i8* blockaddress(@multi, %bb0), i8* blockaddress(@multi, %bb1)], align 16 + + +; Check that we break the critical edge when an internal jump table has only one use. +define void @simple(i32* nocapture readonly %p) { +; CHECK-LABEL: @simple( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1 +; CHECK-NEXT: [[INITVAL:%.*]] = load i32, i32* [[P]], align 4 +; CHECK-NEXT: [[INITOP:%.*]] = load i32, i32* [[INCDEC_PTR]], align 4 +; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB0:%.*]] +; CHECK-NEXT: i32 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb0: +; CHECK-NEXT: [[P_ADDR_0:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[PTR:%.*]], [[INDIRECTGOTO_BB0_CRIT_EDGE:%.*]] ] +; CHECK-NEXT: [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[INDIRECTGOTO_BB0_CRIT_EDGE]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_0]]) +; CHECK-NEXT: br label [[INDIRECTGOTO:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[P_ADDR_1:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[PTR]], [[INDIRECTGOTO_BB1_CRIT_EDGE:%.*]] ] +; CHECK-NEXT: [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 1, [[INDIRECTGOTO_BB1_CRIT_EDGE]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_1]]) +; CHECK-NEXT: br label [[INDIRECTGOTO]] +; CHECK: indirectgoto: +; CHECK-NEXT: [[P_ADDR_SINK:%.*]] = phi i32* [ [[P_ADDR_1]], [[BB1]] ], [ [[P_ADDR_0]], [[BB0]] ] +; CHECK-NEXT: [[PTR]] = getelementptr inbounds i32, i32* [[P_ADDR_SINK]], i64 1 +; CHECK-NEXT: [[NEWP:%.*]] = load i32, i32* [[P_ADDR_SINK]], align 4 +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[NEWP]] to i64 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @simple.targets, i64 0, i64 [[IDX]] +; CHECK-NEXT: [[NEWOP:%.*]] = load i8*, i8** [[ARRAYIDX]], align 8 +; CHECK-NEXT: indirectbr i8* [[NEWOP]], [label [[INDIRECTGOTO_BB0_CRIT_EDGE]], label %indirectgoto.bb1_crit_edge] +; CHECK: indirectgoto.bb1_crit_edge: +; CHECK-NEXT: br label [[BB1]] +; CHECK: indirectgoto.bb0_crit_edge: +; CHECK-NEXT: br label [[BB0]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + %incdec.ptr = getelementptr inbounds i32, i32* %p, i64 1 + %initval = load i32, i32* %p, align 4 + %initop = load i32, i32* %incdec.ptr, align 4 + switch i32 %initop, label %exit [ + i32 0, label %bb0 + i32 1, label %bb1 + ] + +bb0: + %p.addr.0 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %indirectgoto ] + tail call void @use(i32 %opcode.0) + br label %indirectgoto + +bb1: + %p.addr.1 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.1 = phi i32 [ %initval, %entry ], [ 1, %indirectgoto ] + tail call void @use(i32 %opcode.1) + br label %indirectgoto + +indirectgoto: + %p.addr.sink = phi i32* [ %p.addr.1, %bb1 ], [ %p.addr.0, %bb0 ] + %ptr = getelementptr inbounds i32, i32* %p.addr.sink, i64 1 + %newp = load i32, i32* %p.addr.sink, align 4 + %idx = sext i32 %newp to i64 + %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @simple.targets, i64 0, i64 %idx + %newop = load i8*, i8** %arrayidx, align 8 + indirectbr i8* %newop, [label %bb0, label %bb1] + +exit: + ret void +} + +; We should not break the critical edge if the jump table does not have internal +; linkage, since we can't guarantee the edges weren't "shuffle around" +define void @external(i32* nocapture readonly %p) { +; CHECK-LABEL: @external( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1 +; CHECK-NEXT: [[INITVAL:%.*]] = load i32, i32* [[P]], align 4 +; CHECK-NEXT: [[INITOP:%.*]] = load i32, i32* [[INCDEC_PTR]], align 4 +; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB0:%.*]] +; CHECK-NEXT: i32 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb0: +; CHECK-NEXT: [[P_ADDR_0:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[PTR:%.*]], [[INDIRECTGOTO:%.*]] ] +; CHECK-NEXT: [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[INDIRECTGOTO]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_0]]) +; CHECK-NEXT: br label [[INDIRECTGOTO]] +; CHECK: bb1: +; CHECK-NEXT: [[P_ADDR_1:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[PTR]], [[INDIRECTGOTO]] ] +; CHECK-NEXT: [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 1, [[INDIRECTGOTO]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_1]]) +; CHECK-NEXT: br label [[INDIRECTGOTO]] +; CHECK: indirectgoto: +; CHECK-NEXT: [[P_ADDR_SINK:%.*]] = phi i32* [ [[P_ADDR_1]], [[BB1]] ], [ [[P_ADDR_0]], [[BB0]] ] +; CHECK-NEXT: [[PTR]] = getelementptr inbounds i32, i32* [[P_ADDR_SINK]], i64 1 +; CHECK-NEXT: [[NEWP:%.*]] = load i32, i32* [[P_ADDR_SINK]], align 4 +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[NEWP]] to i64 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @external.targets, i64 0, i64 [[IDX]] +; CHECK-NEXT: [[NEWOP:%.*]] = load i8*, i8** [[ARRAYIDX]], align 8 +; CHECK-NEXT: indirectbr i8* [[NEWOP]], [label [[BB0]], label %bb1] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + %incdec.ptr = getelementptr inbounds i32, i32* %p, i64 1 + %initval = load i32, i32* %p, align 4 + %initop = load i32, i32* %incdec.ptr, align 4 + switch i32 %initop, label %exit [ + i32 0, label %bb0 + i32 1, label %bb1 + ] + +bb0: + %p.addr.0 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %indirectgoto ] + tail call void @use(i32 %opcode.0) + br label %indirectgoto + +bb1: + %p.addr.1 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.1 = phi i32 [ %initval, %entry ], [ 1, %indirectgoto ] + tail call void @use(i32 %opcode.1) + br label %indirectgoto + +indirectgoto: + %p.addr.sink = phi i32* [ %p.addr.1, %bb1 ], [ %p.addr.0, %bb0 ] + %ptr = getelementptr inbounds i32, i32* %p.addr.sink, i64 1 + %newp = load i32, i32* %p.addr.sink, align 4 + %idx = sext i32 %newp to i64 + %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @external.targets, i64 0, i64 %idx + %newop = load i8*, i8** %arrayidx, align 8 + indirectbr i8* %newop, [label %bb0, label %bb1] + +exit: + ret void +} + +; We should not break the critical edge if the the jump table has a use that may +; modify it. +define void @escape(i32* nocapture readonly %p) { +; CHECK-LABEL: @escape( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1 +; CHECK-NEXT: call void @useptr([2 x i8*]* @escape.targets) +; CHECK-NEXT: [[INITVAL:%.*]] = load i32, i32* [[P]], align 4 +; CHECK-NEXT: [[INITOP:%.*]] = load i32, i32* [[INCDEC_PTR]], align 4 +; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB0:%.*]] +; CHECK-NEXT: i32 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb0: +; CHECK-NEXT: [[P_ADDR_0:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[PTR:%.*]], [[INDIRECTGOTO:%.*]] ] +; CHECK-NEXT: [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[INDIRECTGOTO]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_0]]) +; CHECK-NEXT: br label [[INDIRECTGOTO]] +; CHECK: bb1: +; CHECK-NEXT: [[P_ADDR_1:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[PTR]], [[INDIRECTGOTO]] ] +; CHECK-NEXT: [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 1, [[INDIRECTGOTO]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_1]]) +; CHECK-NEXT: br label [[INDIRECTGOTO]] +; CHECK: indirectgoto: +; CHECK-NEXT: [[P_ADDR_SINK:%.*]] = phi i32* [ [[P_ADDR_1]], [[BB1]] ], [ [[P_ADDR_0]], [[BB0]] ] +; CHECK-NEXT: [[PTR]] = getelementptr inbounds i32, i32* [[P_ADDR_SINK]], i64 1 +; CHECK-NEXT: [[NEWP:%.*]] = load i32, i32* [[P_ADDR_SINK]], align 4 +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[NEWP]] to i64 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @escape.targets, i64 0, i64 [[IDX]] +; CHECK-NEXT: [[NEWOP:%.*]] = load i8*, i8** [[ARRAYIDX]], align 8 +; CHECK-NEXT: indirectbr i8* [[NEWOP]], [label [[BB0]], label %bb1] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + %incdec.ptr = getelementptr inbounds i32, i32* %p, i64 1 + call void @useptr([2 x i8*]* @escape.targets) + %initval = load i32, i32* %p, align 4 + %initop = load i32, i32* %incdec.ptr, align 4 + switch i32 %initop, label %exit [ + i32 0, label %bb0 + i32 1, label %bb1 + ] + +bb0: + %p.addr.0 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %indirectgoto ] + tail call void @use(i32 %opcode.0) + br label %indirectgoto + +bb1: + %p.addr.1 = phi i32* [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ] + %opcode.1 = phi i32 [ %initval, %entry ], [ 1, %indirectgoto ] + tail call void @use(i32 %opcode.1) + br label %indirectgoto + +indirectgoto: + %p.addr.sink = phi i32* [ %p.addr.1, %bb1 ], [ %p.addr.0, %bb0 ] + %ptr = getelementptr inbounds i32, i32* %p.addr.sink, i64 1 + %newp = load i32, i32* %p.addr.sink, align 4 + %idx = sext i32 %newp to i64 + %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @escape.targets, i64 0, i64 %idx + %newop = load i8*, i8** %arrayidx, align 8 + indirectbr i8* %newop, [label %bb0, label %bb1] + +exit: + ret void +} + + +; We should be able to break the critical edges here, but currently don't. +define void @multi(i32* nocapture readonly %p) { +; CHECK-LABEL: @multi( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1 +; CHECK-NEXT: call void @useptr([2 x i8*]* @escape.targets) +; CHECK-NEXT: [[INITVAL:%.*]] = load i32, i32* [[P]], align 4 +; CHECK-NEXT: [[INITOP:%.*]] = load i32, i32* [[INCDEC_PTR]], align 4 +; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB0:%.*]] +; CHECK-NEXT: i32 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb0: +; CHECK-NEXT: [[P_ADDR_0:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[NEXT0:%.*]], [[BB0]] ], [ [[NEXT1:%.*]], [[BB1]] ] +; CHECK-NEXT: [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_0]]) +; CHECK-NEXT: [[NEXT0]] = getelementptr inbounds i32, i32* [[P_ADDR_0]], i64 1 +; CHECK-NEXT: [[NEWP0:%.*]] = load i32, i32* [[P_ADDR_0]], align 4 +; CHECK-NEXT: [[IDX0:%.*]] = sext i32 [[NEWP0]] to i64 +; CHECK-NEXT: [[ARRAYIDX0:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @multi.targets, i64 0, i64 [[IDX0]] +; CHECK-NEXT: [[NEWOP0:%.*]] = load i8*, i8** [[ARRAYIDX0]], align 8 +; CHECK-NEXT: indirectbr i8* [[NEWOP0]], [label [[BB0]], label %bb1] +; CHECK: bb1: +; CHECK-NEXT: [[P_ADDR_1:%.*]] = phi i32* [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[NEXT0]], [[BB0]] ], [ [[NEXT1]], [[BB1]] ] +; CHECK-NEXT: [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ] +; CHECK-NEXT: tail call void @use(i32 [[OPCODE_1]]) +; CHECK-NEXT: [[NEXT1]] = getelementptr inbounds i32, i32* [[P_ADDR_1]], i64 1 +; CHECK-NEXT: [[NEWP1:%.*]] = load i32, i32* [[P_ADDR_1]], align 4 +; CHECK-NEXT: [[IDX1:%.*]] = sext i32 [[NEWP1]] to i64 +; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* @multi.targets, i64 0, i64 [[IDX1]] +; CHECK-NEXT: [[NEWOP1:%.*]] = load i8*, i8** [[ARRAYIDX1]], align 8 +; CHECK-NEXT: indirectbr i8* [[NEWOP1]], [label [[BB0]], label %bb1] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + %incdec.ptr = getelementptr inbounds i32, i32* %p, i64 1 + call void @useptr([2 x i8*]* @escape.targets) + %initval = load i32, i32* %p, align 4 + %initop = load i32, i32* %incdec.ptr, align 4 + switch i32 %initop, label %exit [ + i32 0, label %bb0 + i32 1, label %bb1 + ] + +bb0: + %p.addr.0 = phi i32* [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ] + %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ] + tail call void @use(i32 %opcode.0) + %next0 = getelementptr inbounds i32, i32* %p.addr.0, i64 1 + %newp0 = load i32, i32* %p.addr.0, align 4 + %idx0 = sext i32 %newp0 to i64 + %arrayidx0 = getelementptr inbounds [2 x i8*], [2 x i8*]* @multi.targets, i64 0, i64 %idx0 + %newop0 = load i8*, i8** %arrayidx0, align 8 + indirectbr i8* %newop0, [label %bb0, label %bb1] + +bb1: + %p.addr.1 = phi i32* [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ] + %opcode.1 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ] + tail call void @use(i32 %opcode.1) + %next1 = getelementptr inbounds i32, i32* %p.addr.1, i64 1 + %newp1 = load i32, i32* %p.addr.1, align 4 + %idx1 = sext i32 %newp1 to i64 + %arrayidx1 = getelementptr inbounds [2 x i8*], [2 x i8*]* @multi.targets, i64 0, i64 %idx1 + %newop1 = load i8*, i8** %arrayidx1, align 8 + indirectbr i8* %newop1, [label %bb0, label %bb1] + +exit: + ret void +}