From: Dan Gohman Date: Tue, 16 Feb 2016 16:22:41 +0000 (+0000) Subject: [WebAssembly] Switch from RPO sorting to topological sorting. X-Git-Tag: llvmorg-3.9.0-rc1~14018 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=442bfcec0077a9de7bd1647f3312c42226132bd2;p=platform%2Fupstream%2Fllvm.git [WebAssembly] Switch from RPO sorting to topological sorting. WebAssembly doesn't require full RPO; topological sorting is sufficient and can preserve more of the MachineBlockPlacement ordering. Unfortunately, this still depends a lot on heuristics, because while we use the MachineBlockPlacement ordering as a guide, we can't use it in places where it isn't topologically ordered. This area will require further attention. llvm-svn: 260978 --- diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 7581687..2eed234 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -10,10 +10,10 @@ /// \file /// \brief This file implements a CFG stacking pass. /// -/// This pass reorders the blocks in a function to put them into a reverse -/// post-order [0], with special care to keep the order as similar as possible -/// to the original order, and to keep loops contiguous even in the case of -/// split backedges. +/// This pass reorders the blocks in a function to put them into topological +/// order, ignoring loop backedges, and without any loop being interrupted +/// by a block not dominated by the loop header, with special care to keep the +/// order as similar as possible to the original order. /// /// Then, it inserts BLOCK and LOOP markers to mark the start of scopes, since /// scope boundaries serve as the labels for WebAssembly's control transfers. @@ -21,14 +21,13 @@ /// This is sufficient to convert arbitrary CFGs into a form that works on /// WebAssembly, provided that all loops are single-entry. /// -/// [0] https://en.wikipedia.org/wiki/Depth-first_search#Vertex_orderings -/// //===----------------------------------------------------------------------===// #include "WebAssembly.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "llvm/ADT/PriorityQueue.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/CodeGen/MachineDominators.h" @@ -102,59 +101,6 @@ static void EliminateMultipleEntryLoops(MachineFunction &MF, } } -namespace { -/// Post-order traversal stack entry. -struct POStackEntry { - MachineBasicBlock *MBB; - SmallVector Succs; - - POStackEntry(MachineBasicBlock *MBB, MachineFunction &MF, - const MachineLoopInfo &MLI); -}; -} // end anonymous namespace - -static bool LoopContains(const MachineLoop *Loop, - const MachineBasicBlock *MBB) { - return Loop ? Loop->contains(MBB) : true; -} - -POStackEntry::POStackEntry(MachineBasicBlock *MBB, MachineFunction &MF, - const MachineLoopInfo &MLI) - : MBB(MBB), Succs(MBB->successors()) { - // RPO is not a unique form, since at every basic block with multiple - // successors, the DFS has to pick which order to visit the successors in. - // Sort them strategically (see below). - MachineLoop *Loop = MLI.getLoopFor(MBB); - MachineFunction::iterator Next = next(MachineFunction::iterator(MBB)); - MachineBasicBlock *LayoutSucc = Next == MF.end() ? nullptr : &*Next; - std::stable_sort( - Succs.begin(), Succs.end(), - [=, &MLI](const MachineBasicBlock *A, const MachineBasicBlock *B) { - if (A == B) - return false; - - // Keep loops contiguous by preferring the block that's in the same - // loop. - bool LoopContainsA = LoopContains(Loop, A); - bool LoopContainsB = LoopContains(Loop, B); - if (LoopContainsA && !LoopContainsB) - return true; - if (!LoopContainsA && LoopContainsB) - return false; - - // Minimize perturbation by preferring the block which is the immediate - // layout successor. - if (A == LayoutSucc) - return true; - if (B == LayoutSucc) - return false; - - // TODO: More sophisticated orderings may be profitable here. - - return false; - }); -} - /// Return the "bottom" block of a loop. This differs from /// MachineLoop::getBottomBlock in that it works even if the loop is /// discontiguous. @@ -166,53 +112,166 @@ static MachineBasicBlock *LoopBottom(const MachineLoop *Loop) { return Bottom; } -/// Sort the blocks in RPO, taking special care to make sure that loops are -/// contiguous even in the case of split backedges. -/// -/// TODO: Determine whether RPO is actually worthwhile, or whether we should -/// move to just a stable-topological-sort-based approach that would preserve -/// more of the original order. -static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI) { - // Note that we do our own RPO rather than using - // "llvm/ADT/PostOrderIterator.h" because we want control over the order that - // successors are visited in (see above). Also, we can sort the blocks in the - // MachineFunction as we go. - SmallPtrSet Visited; - SmallVector Stack; - - MachineBasicBlock *EntryBlock = &*MF.begin(); - Visited.insert(EntryBlock); - Stack.push_back(POStackEntry(EntryBlock, MF, MLI)); - - for (;;) { - POStackEntry &Entry = Stack.back(); - SmallVectorImpl &Succs = Entry.Succs; - if (!Succs.empty()) { - MachineBasicBlock *Succ = Succs.pop_back_val(); - if (Visited.insert(Succ).second) - Stack.push_back(POStackEntry(Succ, MF, MLI)); - continue; - } +static void MaybeUpdateTerminator(MachineBasicBlock *MBB) { +#ifndef NDEBUG + bool AnyBarrier = false; +#endif + bool AllAnalyzable = true; + for (const MachineInstr &Term : MBB->terminators()) { +#ifndef NDEBUG + AnyBarrier |= Term.isBarrier(); +#endif + AllAnalyzable &= Term.isBranch() && !Term.isIndirectBranch(); + } + assert((AnyBarrier || AllAnalyzable) && + "AnalyzeBranch needs to analyze any block with a fallthrough"); + if (AllAnalyzable) + MBB->updateTerminator(); +} - // Put the block in its position in the MachineFunction. - MachineBasicBlock &MBB = *Entry.MBB; - MBB.moveBefore(&*MF.begin()); - - // Branch instructions may utilize a fallthrough, so update them if a - // fallthrough has been added or removed. - if (!MBB.empty() && MBB.back().isTerminator() && !MBB.back().isBranch() && - !MBB.back().isBarrier()) - report_fatal_error( - "Non-branch terminator with fallthrough cannot yet be rewritten"); - if (MBB.empty() || !MBB.back().isTerminator() || MBB.back().isBranch()) - MBB.updateTerminator(); - - Stack.pop_back(); - if (Stack.empty()) - break; +namespace { +/// Sort blocks by their number. +struct CompareBlockNumbers { + bool operator()(const MachineBasicBlock *A, + const MachineBasicBlock *B) const { + return A->getNumber() > B->getNumber(); + } +}; +/// Sort blocks by their number in the opposite order.. +struct CompareBlockNumbersBackwards { + bool operator()(const MachineBasicBlock *A, + const MachineBasicBlock *B) const { + return A->getNumber() < B->getNumber(); } +}; +/// Bookkeeping for a loop to help ensure that we don't mix blocks not dominated +/// by the loop header among the loop's blocks. +struct Entry { + const MachineLoop *Loop; + unsigned NumBlocksLeft; + + /// List of blocks not dominated by Loop's header that are deferred until + /// after all of Loop's blocks have been seen. + std::vector Deferred; + + explicit Entry(const MachineLoop *L) + : Loop(L), NumBlocksLeft(L->getNumBlocks()) {} +}; +} - // Now that we've sorted the blocks in RPO, renumber them. +/// Sort the blocks, taking special care to make sure that loops are not +/// interrupted by blocks not dominated by their header. +/// TODO: There are many opportunities for improving the heuristics here. +/// Explore them. +static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI, + const MachineDominatorTree &MDT) { + // Prepare for a topological sort: Record the number of predecessors each + // block has, ignoring loop backedges. + MF.RenumberBlocks(); + SmallVector NumPredsLeft(MF.getNumBlockIDs(), 0); + for (MachineBasicBlock &MBB : MF) { + unsigned N = MBB.pred_size(); + if (MachineLoop *L = MLI.getLoopFor(&MBB)) + if (L->getHeader() == &MBB) + for (const MachineBasicBlock *Pred : MBB.predecessors()) + if (L->contains(Pred)) + --N; + NumPredsLeft[MBB.getNumber()] = N; + } + + // Topological sort the CFG, with additional constraints: + // - Between a loop header and the last block in the loop, there can be + // no blocks not dominated by the loop header. + // - It's desirable to preserve the original block order when possible. + // We use two ready lists; Preferred and Ready. Preferred has recently + // processed sucessors, to help preserve block sequences from the original + // order. Ready has the remaining ready blocks. + PriorityQueue, + CompareBlockNumbers> + Preferred; + PriorityQueue, + CompareBlockNumbersBackwards> + Ready; + SmallVector Loops; + for (MachineBasicBlock *MBB = &MF.front();;) { + const MachineLoop *L = MLI.getLoopFor(MBB); + if (L) { + // If MBB is a loop header, add it to the active loop list. We can't put + // any blocks that it doesn't dominate until we see the end of the loop. + if (L->getHeader() == MBB) + Loops.push_back(Entry(L)); + // For each active loop the block is in, decrement the count. If MBB is + // the last block in an active loop, take it off the list and pick up any + // blocks deferred because the header didn't dominate them. + for (Entry &E : Loops) + if (E.Loop->contains(MBB) && --E.NumBlocksLeft == 0) + for (auto DeferredBlock : E.Deferred) + Ready.push(DeferredBlock); + while (!Loops.empty() && Loops.back().NumBlocksLeft == 0) + Loops.pop_back(); + } + // The main topological sort logic. + for (MachineBasicBlock *Succ : MBB->successors()) { + // Ignore backedges. + if (MachineLoop *SuccL = MLI.getLoopFor(Succ)) + if (SuccL->getHeader() == Succ && SuccL->contains(MBB)) + continue; + // Decrement the predecessor count. If it's now zero, it's ready. + if (--NumPredsLeft[Succ->getNumber()] == 0) + Preferred.push(Succ); + } + // Determine the block to follow MBB. First try to find a preferred block, + // to preserve the original block order when possible. + MachineBasicBlock *Next = nullptr; + while (!Preferred.empty()) { + Next = Preferred.top(); + Preferred.pop(); + // If X isn't dominated by the top active loop header, defer it until that + // loop is done. + if (!Loops.empty() && + !MDT.dominates(Loops.back().Loop->getHeader(), Next)) { + Loops.back().Deferred.push_back(Next); + Next = nullptr; + continue; + } + // If Next was originally ordered before MBB, and it isn't because it was + // loop-rotated above the header, it's not preferred. + if (Next->getNumber() < MBB->getNumber() && + (!L || !L->contains(Next) || + L->getHeader()->getNumber() < Next->getNumber())) { + Ready.push(Next); + Next = nullptr; + continue; + } + break; + } + // If we didn't find a suitable block in the Preferred list, check the + // general Ready list. + if (!Next) { + // If there are no more blocks to process, we're done. + if (Ready.empty()) { + MaybeUpdateTerminator(MBB); + break; + } + for (;;) { + Next = Ready.top(); + Ready.pop(); + // If Next isn't dominated by the top active loop header, defer it until + // that loop is done. + if (!Loops.empty() && + !MDT.dominates(Loops.back().Loop->getHeader(), Next)) { + Loops.back().Deferred.push_back(Next); + continue; + } + break; + } + } + // Move the next block into place and iterate. + Next->moveAfter(MBB); + MaybeUpdateTerminator(MBB); + MBB = Next; + } + assert(Loops.empty() && "Active loop list not finished"); MF.RenumberBlocks(); #ifndef NDEBUG @@ -342,8 +401,7 @@ static void PlaceBlockMarker(MachineBasicBlock &MBB, MachineFunction &MF, // Otherwise, insert the BLOCK as late in Header as we can, but before the // beginning of the local expression tree and any nested BLOCKs. InsertPos = Header->getFirstTerminator(); - while (InsertPos != Header->begin() && - IsChild(prev(InsertPos), MFI) && + while (InsertPos != Header->begin() && IsChild(prev(InsertPos), MFI) && prev(InsertPos)->getOpcode() != WebAssembly::LOOP && prev(InsertPos)->getOpcode() != WebAssembly::END_BLOCK && prev(InsertPos)->getOpcode() != WebAssembly::END_LOOP) @@ -405,7 +463,7 @@ static void PlaceLoopMarker( assert((!ScopeTops[AfterLoop->getNumber()] || ScopeTops[AfterLoop->getNumber()]->getNumber() < MBB.getNumber()) && - "With RPO we should visit the outer-most loop for a block first."); + "With block sorting the outermost loop for a block should be first."); if (!ScopeTops[AfterLoop->getNumber()]) ScopeTops[AfterLoop->getNumber()] = &MBB; } @@ -499,11 +557,11 @@ bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { WebAssemblyFunctionInfo &MFI = *MF.getInfo(); MF.getRegInfo().invalidateLiveness(); - // RPO sorting needs all loops to be single-entry. + // Sorting needs all loops to be single-entry. EliminateMultipleEntryLoops(MF, MLI); - // Sort the blocks in RPO, with contiguous loops. - SortBlocks(MF, MLI); + // Sort the blocks, with contiguous loops. + SortBlocks(MF, MLI, MDT); // Place the BLOCK and LOOP markers to indicate the beginnings of scopes. PlaceMarkers(MF, MLI, TII, MDT, MFI); diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify.ll index fdfe489..58d0994 100644 --- a/llvm/test/CodeGen/WebAssembly/cfg-stackify.ll +++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify.ll @@ -14,12 +14,16 @@ declare void @something() ; CHECK: loop ; CHECK-NOT: br ; CHECK: i32.add -; CHECK-NEXT: i32.ge_s +; CHECK-NEXT: block +; CHECK-NEXT: i32.lt_s ; CHECK-NEXT: br_if -; CHECK-NOT: br -; CHECK: call -; CHECK: br 0{{$}} -; CHECK: return{{$}} +; CHECK-NEXT: return +; CHECK-NEXT: .LBB0_3: +; CHECK-NEXT: end_block +; CHECK-NEXT: call +; CHECK-NEXT: br +; CHECK-NEXT: .LBB0_4: +; CHECK-NEXT: end_loop ; OPT-LABEL: test0: ; OPT: loop ; OPT-NOT: br @@ -55,12 +59,16 @@ back: ; CHECK: loop ; CHECK-NOT: br ; CHECK: i32.add -; CHECK-NEXT: i32.ge_s +; CHECK-NEXT: block +; CHECK-NEXT: i32.lt_s ; CHECK-NEXT: br_if -; CHECK-NOT: br -; CHECK: call -; CHECK: br 0{{$}} -; CHECK: return{{$}} +; CHECK-NEXT: return +; CHECK-NEXT: .LBB1_3: +; CHECK-NEXT: end_block +; CHECK-NEXT: call +; CHECK-NEXT: br +; CHECK-NEXT: .LBB1_4: +; CHECK-NEXT: end_loop ; OPT-LABEL: test1: ; OPT: loop ; OPT-NOT: br @@ -150,16 +158,19 @@ for.end: ; CHECK: i32.const $push{{[0-9]+}}=, 0{{$}} ; CHECK-NEXT: return $pop{{[0-9]+}}{{$}} ; OPT-LABEL: doublediamond: -; OPT: block{{$}} +; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT: br_if 0, ${{[^,]+}}{{$}} -; OPT: block{{$}} -; OPT: br_if 0, ${{[^,]+}}{{$}} -; OPT: br 1{{$}} -; OPT: .LBB3_4: -; OPT: .LBB3_5: -; OPT: i32.const $push{{[0-9]+}}=, 0{{$}} -; OPT-NEXT: return $pop{{[0-9]+}}{{$}} +; OPT-NEXT: block{{$}} +; OPT: br_if 0, ${{[^,]+}}{{$}} +; OPT: br_if 1, ${{[^,]+}}{{$}} +; OPT: br 2{{$}} +; OPT-NEXT: .LBB3_3: +; OPT-NEXT: end_block +; OPT: br 1{{$}} +; OPT-NEXT: .LBB3_4: +; OPT: .LBB3_5: +; OPT-NEXT: end_block +; OPT: return $pop{{[0-9]+}}{{$}} define i32 @doublediamond(i32 %a, i32 %b, i32* %p) { entry: %c = icmp eq i32 %a, 0 @@ -395,23 +406,23 @@ exit: ; CHECK: .LBB11_7: ; CHECK-NEXT: end_loop{{$}} ; OPT-LABEL: doublediamond_in_a_loop: -; OPT: .LBB11_1: -; OPT: loop{{$}} -; OPT: block{{$}} -; OPT: block{{$}} -; OPT: br_if 0, {{[^,]+}}{{$}} -; OPT: block{{$}} -; OPT: br_if 0, {{[^,]+}}{{$}} -; OPT: br 2{{$}} -; OPT: .LBB11_4: +; OPT: .LBB11_1: +; OPT: loop{{$}} +; OPT: block{{$}} +; OPT-NEXT: block{{$}} +; OPT-NEXT: block{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT: br 2{{$}} +; OPT-NEXT: .LBB11_4: ; OPT-NEXT: end_block{{$}} -; OPT: br 1{{$}} -; OPT: .LBB11_5: +; OPT: br 1{{$}} +; OPT: .LBB11_5: ; OPT-NEXT: end_block{{$}} -; OPT: .LBB11_6: +; OPT: .LBB11_6: ; OPT-NEXT: end_block{{$}} -; OPT: br 0{{$}} -; OPT: .LBB11_7: +; OPT: br 0{{$}} +; OPT: .LBB11_7: ; OPT-NEXT: end_loop{{$}} define i32 @doublediamond_in_a_loop(i32 %a, i32 %b, i32* %p) { entry: @@ -446,10 +457,28 @@ exit: ; CHECK-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: ; CHECK-NEXT: loop ; OPT-LABEL: test3: -; OPT: loop +; OPT: block +; OPT: br_if +; OPT-NEXT: return +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_block +; OPT-NEXT: loop +; OPT-NEXT: block +; OPT-NEXT: block ; OPT-NEXT: br_if ; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: ; OPT-NEXT: loop +; OPT: br_if +; OPT-NEXT: br +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_loop +; OPT-NEXT: end_block +; OPT-NEXT: unreachable +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_block +; OPT: br +; OPT-NEXT: .LBB{{[0-9]+}}_{{[0-9]+}}: +; OPT-NEXT: end_loop declare void @bar() define void @test3(i32 %w) { entry: @@ -483,7 +512,6 @@ if.end: ; CHECK-NEXT: .param i32{{$}} ; CHECK: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: block{{$}} ; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: block{{$}} ; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} @@ -492,8 +520,10 @@ if.end: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: return{{$}} ; CHECK-NEXT: .LBB13_4: -; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: end_block{{$}} +; CHECK-NEXT: block{{$}} ; CHECK: br_if 0, $pop{{[0-9]+}}{{$}} +; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return{{$}} ; CHECK-NEXT: .LBB13_7: ; CHECK-NEXT: end_block{{$}} @@ -686,6 +716,7 @@ second: ; CHECK: unreachable ; OPT-LABEL: test7: ; OPT: .LBB16_1: +; OPT-NEXT: block ; OPT-NEXT: loop{{$}} ; OPT-NOT: block ; OPT: block{{$}} @@ -693,13 +724,16 @@ second: ; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block ; OPT: br_if 1, {{[^,]+}}{{$}} -; OPT-NOT: block -; OPT: unreachable -; OPT-NEXT: .LBB16_4: -; OPT-NEXT: end_block{{$}} +; OPT: br 3{{$}} +; OPT-NEXT: .LBB16_3: +; OPT-NEXT: end_block ; OPT-NOT: block ; OPT: br_if 0, {{[^,]+}}{{$}} -; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: end_loop +; OPT-NOT: block +; OPT: unreachable +; OPT-NEXT: .LBB16_5: +; OPT-NEXT: end_block ; OPT-NOT: block ; OPT: unreachable define void @test7(i1 %tobool2, i1 %tobool9) { @@ -961,9 +995,9 @@ bb6: ; CHECK-NEXT: .LBB20_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if 2, {{[^,]+}}{{$}} -; CHECK-NOT: block ; CHECK: br_if 1, {{[^,]+}}{{$}} +; CHECK-NOT: block +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB20_6: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block @@ -1043,54 +1077,49 @@ bb8: ; CHECK-NOT: block ; CHECK: block{{$}} ; CHECK-NEXT: block{{$}} -; CHECK-NEXT: block{{$}} ; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if 2, {{[^,]+}}{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if 2, {{[^,]+}}{{$}} -; CHECK-NEXT: br 1{{$}} +; CHECK: br_if 1, {{[^,]+}}{{$}} +; CHECK-NEXT: br 3{{$}} ; CHECK-NEXT: .LBB21_4: ; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block -; CHECK: br_if 1, {{[^,]+}}{{$}} +; CHECK: br_if 0, {{[^,]+}}{{$}} ; CHECK-NOT: block -; CHECK: br_if 1, {{[^,]+}}{{$}} +; CHECK: br_if 2, {{[^,]+}}{{$}} ; CHECK-NEXT: .LBB21_6: ; CHECK-NEXT: end_block{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB21_7: -; CHECK-NEXT: end_block{{$}} ; CHECK-NOT: block ; CHECK: br 0{{$}} -; CHECK-NEXT: .LBB21_8: +; CHECK-NEXT: .LBB21_7: +; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: return{{$}} ; OPT-LABEL: test12: ; OPT: .LBB21_1: ; OPT-NEXT: loop{{$}} ; OPT-NOT: block ; OPT: block{{$}} ; OPT-NEXT: block{{$}} -; OPT-NEXT: block{{$}} ; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if 2, {{[^,]+}}{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if 2, {{[^,]+}}{{$}} -; OPT-NEXT: br 1{{$}} +; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT-NEXT: br 3{{$}} ; OPT-NEXT: .LBB21_4: ; OPT-NEXT: end_block{{$}} ; OPT-NOT: block -; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT-NOT: block -; OPT: br_if 1, {{[^,]+}}{{$}} +; OPT: br_if 2, {{[^,]+}}{{$}} ; OPT-NEXT: .LBB21_6: ; OPT-NEXT: end_block{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB21_7: -; OPT-NEXT: end_block{{$}} -; OPT-NOT: block ; OPT: br 0{{$}} -; OPT-NEXT: .LBB21_8: +; OPT-NEXT: .LBB21_7: +; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: return{{$}} define void @test12(i8* %arg) { bb: br label %bb1 @@ -1120,34 +1149,36 @@ bb7: ; CHECK-LABEL: test13: ; CHECK-NEXT: .local i32{{$}} +; CHECK-NEXT: block{{$}} +; CHECK-NEXT: block{{$}} +; CHECK: br_if 0, $pop0{{$}} ; CHECK: block{{$}} -; CHECK: br_if 0, $pop5{{$}} -; CHECK-NEXT: return{{$}} -; CHECK-NEXT: .LBB22_2: +; CHECK: br_if 0, $pop3{{$}} +; CHECK: .LBB22_3: ; CHECK-NEXT: end_block{{$}} -; CHECK: block{{$}} -; CHECK-NEXT: i32.const $push3=, 0{{$}} -; CHECK-NEXT: br_if 0, $pop3{{$}} -; CHECK: .LBB22_4: +; CHECK: br_if 1, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: br 1{{$}} +; CHECK-NEXT: .LBB22_4: ; CHECK-NEXT: end_block{{$}} -; CHECK: block{{$}} -; CHECK: br_if 0, $pop7{{$}} +; CHECK-NEXT: return{{$}} +; CHECK-NEXT: .LBB22_5: ; CHECK-NEXT: end_block{{$}} ; CHECK-NEXT: unreachable{{$}} ; OPT-LABEL: test13: ; OPT-NEXT: .local i32{{$}} +; OPT-NEXT: block{{$}} +; OPT-NEXT: block{{$}} +; OPT: br_if 0, $pop0{{$}} ; OPT: block{{$}} -; OPT: br_if 0, $pop5{{$}} -; OPT-NEXT: return{{$}} -; OPT-NEXT: .LBB22_2: -; OPT-NEXT: end_block{{$}} -; OPT: block{{$}} -; OPT-NEXT: i32.const $push3=, 0{{$}} -; OPT-NEXT: br_if 0, $pop3{{$}} -; OPT: .LBB22_4: +; OPT: br_if 0, $pop3{{$}} +; OPT: .LBB22_3: ; OPT-NEXT: end_block{{$}} -; OPT: block{{$}} -; OPT: br_if 0, $pop7{{$}} +; OPT: br_if 1, $pop{{[0-9]+}}{{$}} +; OPT-NEXT: br 1{{$}} +; OPT-NEXT: .LBB22_4: +; OPT-NEXT: end_block +; OPT-NEXT: return +; OPT-NEXT: .LBB22_5: ; OPT-NEXT: end_block{{$}} ; OPT-NEXT: unreachable{{$}} define void @test13() noinline optnone { @@ -1234,11 +1265,26 @@ bb50: ; CHECK-LABEL: test15: ; CHECK: block -; CHECK-NEXT: i32.const $push{{.*}}=, 1{{$}} -; CHECK-NEXT: br_if 0, $pop{{.*}}{{$}} -; CHECK-NEXT: .LBB24_1: ; CHECK-NEXT: block -; CHECK-NEXT: loop +; CHECK: br_if 0, $pop{{.*}}{{$}} +; CHECK-NEXT: .LBB24_1: +; CHECK-NEXT: block{{$}} +; CHECK-NEXT: loop{{$}} +; CHECK: br_if 1, $pop{{.*}}{{$}} +; CHECK: br_if 2, $pop{{.*}}{{$}} +; CHECK-NEXT: br 0{{$}} +; CHECK-NEXT: .LBB24_3: +; CHECK-NEXT: end_loop{{$}} +; CHECK: .LBB24_4: +; CHECK-NEXT: end_block{{$}} +; CHECK: br_if 1, $pop{{.*}}{{$}} +; CHECK: return{{$}} +; CHECK: .LBB24_6: +; CHECK-NEXT: end_block{{$}} +; CHECK: return{{$}} +; CHECK: .LBB24_7: +; CHECK-NEXT: end_block{{$}} +; CHECK-NEXT: return{{$}} ; OPT-LABEL: test15: ; OPT: block ; OPT-NEXT: i32.const $push