Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -12,6 +12,7 @@ add_llvm_target(WebAssemblyCodeGen Relooper.cpp WebAssemblyAsmPrinter.cpp + WebAssemblyCFGStackify.cpp WebAssemblyFastISel.cpp WebAssemblyFrameLowering.cpp WebAssemblyISelDAGToDAG.cpp Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -26,6 +26,8 @@ FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, CodeGenOpt::Level OptLevel); +FunctionPass *createWebAssemblyCFGStackify(); + } // end namespace llvm #endif Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -73,6 +73,7 @@ void EmitGlobalVariable(const GlobalVariable *GV) override; + void EmitJumpTableInfo() override; void EmitConstantPool() override; void EmitFunctionEntryLabel() override; void EmitFunctionBodyStart() override; @@ -213,6 +214,10 @@ "WebAssembly disables constant pools"); } +void WebAssemblyAsmPrinter::EmitJumpTableInfo() { + // Nothing to do; jump tables are incorporated into the instruction stream. +} + void WebAssemblyAsmPrinter::EmitFunctionEntryLabel() { SmallString<128> Str; raw_svector_ostream OS(Str); @@ -293,6 +298,9 @@ case MachineOperand::MO_GlobalAddress: { OS << ' ' << toSymbol(MO.getGlobal()->getName()); } break; + case MachineOperand::MO_MachineBasicBlock: { + OS << ' ' << toSymbol(MO.getMBB()->getSymbol()->getName()); + } break; } OS << ')'; } Index: lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -0,0 +1,183 @@ +//===-- WebAssemblyCFGStackify.cpp - CFG Stackification -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements a CFG stacking pass. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssembly.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssemblySubtarget.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "wasm-cfg-stackify" + +namespace { +class WebAssemblyCFGStackify final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyCFGStackify() : MachineFunctionPass(ID) {} + +private: + const char *getPassName() const override { + return "WebAssembly CFG Stackify"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char WebAssemblyCFGStackify::ID = 0; +FunctionPass *llvm::createWebAssemblyCFGStackify() { + return new WebAssemblyCFGStackify(); +} + +static bool IsNext(MachineBasicBlock *M, MachineBasicBlock *N) { + return next(MachineFunction::iterator(M)) == MachineFunction::iterator(N); +} + +namespace { +struct RPOStackEntry { + MachineBasicBlock *MBB; + SmallVector Succs; + + RPOStackEntry(MachineBasicBlock *MBB, const MachineLoopInfo &MLI) : MBB(MBB) { + // 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. + // This code attempts to order the successors strategically. + // + // FIXME: Right now this is some hackish code that attempts to sort the + // successors to make loops contiguous and to prefer to keep blocks in + // existing layout order when possible. + unsigned Depth = MLI.getLoopDepth(MBB); + for (auto Succ : MBB->successors()) + if (MLI.getLoopDepth(Succ) != Depth && IsNext(MBB, Succ)) + Succs.push_back(Succ); + for (auto Succ : MBB->successors()) + if (MLI.getLoopDepth(Succ) != Depth && !IsNext(MBB, Succ)) + Succs.push_back(Succ); + for (auto Succ : MBB->successors()) + if (MLI.getLoopDepth(Succ) == Depth && IsNext(MBB, Succ)) + Succs.push_back(Succ); + for (auto Succ : MBB->successors()) + if (MLI.getLoopDepth(Succ) == Depth && !IsNext(MBB, Succ)) + Succs.push_back(Succ); + } +}; +} + +// Sort the blocks in RPO, taking special care to make sure that loops are +// contiguous even in the case of split backedges. +static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI) { + SmallPtrSet Visited; + SmallVector Stack; + + MachineBasicBlock *Entry = MF.begin(); + Stack.push_back(RPOStackEntry(Entry, MLI)); + Visited.insert(Entry); + + do { + top: + RPOStackEntry &Entry = Stack.back(); + SmallVectorImpl &Succs = Entry.Succs; + if (!Succs.empty()) { + MachineBasicBlock *Succ = Succs.pop_back_val(); + if (Visited.insert(Succ).second) + Stack.push_back(RPOStackEntry(Succ, MLI)); + goto top; + } + 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->back().isBranch()) + MBB->updateTerminator(); + Stack.pop_back(); + } while (!Stack.empty()); +} + +static void PlaceBlockMarkers(MachineBasicBlock *MBB, MachineBasicBlock *Succ, + MachineFunction &MF, + const MachineLoopInfo &MLI, + const WebAssemblyInstrInfo *TII) { + if (Succ->getNumber() > MBB->getNumber()) { + // Place the BLOCK for a forward branch. + MachineLoop *Loop = MLI.getLoopFor(Succ); + MachineBasicBlock *Header = Loop ? Loop->getHeader() : &MF.front(); + MachineBasicBlock::iterator I = Header->begin(); + if (I->getOpcode() == WebAssembly::LOOP) + ++I; + int SuccNumber = Succ->getNumber(); + // Position the BLOCK in nesting order. + for (; I->getOpcode() == WebAssembly::BLOCK; ++I) { + int N = I->getOperand(0).getMBB()->getNumber(); + if (N < SuccNumber) + break; + // If there's already a BLOCK for Succ, we don't need another. + if (N == SuccNumber) + return; + } + + BuildMI(*Header, I, DebugLoc(), TII->get(WebAssembly::BLOCK)).addMBB(Succ); + } +} + +static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI, + const WebAssemblyInstrInfo *TII) { + for (auto &MBB : MF) { + // Place the LOOP for a loop. + if (MachineLoop *Loop = MLI.getLoopFor(&MBB)) { + MachineBasicBlock *Top = Loop->getTopBlock(); + assert(Loop->getHeader() == Top); + if (Top == &MBB) + BuildMI(*Top, Top->begin(), DebugLoc(), TII->get(WebAssembly::LOOP)) + .addMBB(Loop->getBottomBlock()); + } + + // Check for forward branches and switches to handle. + for (auto &Term : MBB.terminators()) + for (auto &MO : Term.operands()) + if (MO.isMBB()) + PlaceBlockMarkers(&MBB, MO.getMBB(), MF, MLI, TII); + } +} + +bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "********** CFG Stackifying **********\n" + "********** Function: " + << MF.getName() << '\n'); + + const MachineLoopInfo &MLI = getAnalysis(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + + // Sort the blocks in RPO, with contiguous loops. + SortBlocks(MF, MLI); + + // Now that we've sorted the blocks in RPO, renumber them. + MF.RenumberBlocks(); + + // Place the BLOCK and LOOP markers to indicate the beginnings of scopes. + PlaceMarkers(MF, MLI, TII); + + return true; +} Index: lib/Target/WebAssembly/WebAssemblyISD.def =================================================================== --- lib/Target/WebAssembly/WebAssemblyISD.def +++ lib/Target/WebAssembly/WebAssemblyISD.def @@ -19,5 +19,7 @@ HANDLE_NODETYPE(RETURN) HANDLE_NODETYPE(ARGUMENT) HANDLE_NODETYPE(Wrapper) +HANDLE_NODETYPE(BRIF) +HANDLE_NODETYPE(SWITCH) // add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -69,6 +69,8 @@ // Custom lowering hooks. SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -20,6 +20,7 @@ #include "WebAssemblyTargetObjectFile.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/IR/DiagnosticInfo.h" @@ -114,6 +115,7 @@ computeRegisterProperties(Subtarget->getRegisterInfo()); setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); + setOperationAction(ISD::JumpTable, MVTPtr, Custom); for (auto T : {MVT::f32, MVT::f64}) { // Don't expand the floating-point types to constant pools. @@ -153,6 +155,14 @@ setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); + // Expand these forms; we pattern-match the forms that we can handle in isel. + for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) + for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) + setOperationAction(Op, T, Expand); + + // We have custom switch handling. + setOperationAction(ISD::BR_JT, MVT::Other, Custom); + // WebAssembly doesn't have: // - Floating-point extending loads. // - Floating-point truncating stores. @@ -365,6 +375,10 @@ return SDValue(); case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG); + case ISD::JumpTable: + return LowerJumpTable(Op, DAG); + case ISD::BR_JT: + return LowerBR_JT(Op, DAG); } } @@ -382,6 +396,43 @@ DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT)); } +SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, + SelectionDAG &DAG) const { + // There's no need for a Wrapper node because we always incorporate a jump + // table operand into a SWITCH instruction, rather than ever materializing + // it in a register. + const JumpTableSDNode *JT = cast(Op); + return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), + JT->getTargetFlags()); +} + +SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op.getOperand(0); + const auto *JT = cast(Op.getOperand(1)); + SDValue Index = Op.getOperand(2); + assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); + + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Index); + + MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); + const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; + + // FIXME: Pick something arbitrary for a default case for now. We really + // want to sniff out the guard and put in the real default case (and + // delete the guard). + Ops.push_back(DAG.getBasicBlock(MBBs[0])); + + // Add an operand for each case. + for (auto MBB : MBBs) + Ops.push_back(DAG.getBasicBlock(MBB)); + + return DAG.getNode(WebAssemblyISD::SWITCH, DL, MVT::Other, Ops); +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -25,6 +25,25 @@ * switch: switch statement with fallthrough */ +let isBranch = 1, isTerminator = 1, hasCtrlDep = 1 in { +def BRIF : I<(outs), (ins bb_op:$dst, Int32:$a), + [(brcond Int32:$a, bb:$dst)]>; +let isBarrier = 1 in { +def BR : I<(outs), (ins bb_op:$dst), + [(br bb:$dst)]>; +} // isBarrier = 1 +} // isBranch = 1, isTerminator = 1, hasCtrlDep = 1 + +let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { +def SWITCH_I32 : I<(outs), (ins Int32:$index, variable_ops), + [(WebAssemblyswitch Int32:$index)]>, Requires<[HasAddr32]>; +def SWITCH_I64 : I<(outs), (ins Int64:$index, variable_ops), + [(WebAssemblyswitch Int64:$index)]>, Requires<[HasAddr64]>; +} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 + +def BLOCK : I<(outs), (ins bb_op:$dst), []>; +def LOOP : I<(outs), (ins bb_op:$dst), []>; + multiclass RETURN { def RETURN_#vt : I<(outs), (ins vt:$val), [(WebAssemblyreturn vt:$val)]>; } @@ -35,3 +54,16 @@ defm : RETURN; def RETURN_VOID : I<(outs), (ins), [(WebAssemblyreturn)]>; } // isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 + +def SELECT_I32 : I<(outs Int32:$dst), (ins Int32:$a, Int32:$t, Int32:$f), + [(set Int32:$dst, (select Int32:$a, Int32:$t, Int32:$f))]>; +def SELECT_I64 : I<(outs Int64:$dst), (ins Int64:$a, Int64:$t, Int64:$f), + [(set Int64:$dst, (select Int64:$a, Int64:$t, Int64:$f))]>; +def SELECT_F32 : I<(outs Float32:$dst), + (ins Int32:$a, Float32:$lhs, Float32:$rhs), + [(set Float32:$dst, + (select Int32:$a, Float32:$lhs, Float32:$rhs))]>; +def SELECT_F64 : I<(outs Float64:$dst), + (ins Int32:$a, Float64:$lhs, Float64:$rhs), + [(set Float64:$dst, + (select Int32:$a, Float64:$lhs, Float64:$rhs))]>; Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.h @@ -37,6 +37,18 @@ void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, DebugLoc DL, unsigned DestReg, unsigned SrcReg, bool KillSrc) const override; + + bool AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify = false) const override; + unsigned RemoveBranch(MachineBasicBlock &MBB) const override; + unsigned InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + DebugLoc DL) const override; + bool + ReverseBranchCondition(SmallVectorImpl &Cond) const override; }; } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -37,3 +37,89 @@ BuildMI(MBB, I, DL, get(WebAssembly::COPY), DestReg) .addReg(SrcReg, KillSrc ? RegState::Kill : 0); } + +// Branch analysis. +bool WebAssemblyInstrInfo::AnalyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + bool HaveCond = false; + for (MachineInstr &MI : iterator_range( + MBB.getFirstInstrTerminator(), MBB.instr_end())) { + switch (MI.getOpcode()) { + default: + // Unhandled instruction; bail out. + return true; + case WebAssembly::BRIF: + if (HaveCond) + return true; + Cond.push_back(MI.getOperand(1)); + TBB = MI.getOperand(0).getMBB(); + HaveCond = true; + break; + case WebAssembly::BR: + if (!HaveCond) + TBB = MI.getOperand(0).getMBB(); + else + FBB = MI.getOperand(0).getMBB(); + break; + } + if (MI.isBarrier()) + break; + } + + return false; +} + +unsigned WebAssemblyInstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { + MachineBasicBlock::instr_iterator I = MBB.instr_end(); + unsigned Count = 0; + + while (I != MBB.instr_begin()) { + --I; + if (I->isDebugValue()) + continue; + if (!I->isTerminator()) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.instr_end(); + ++Count; + } + + return Count; +} + +unsigned WebAssemblyInstrInfo::InsertBranch( + MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, + ArrayRef Cond, DebugLoc DL) const { + assert(Cond.size() <= 1); + + if (Cond.empty()) { + if (!TBB) + return 0; + + BuildMI(&MBB, DL, get(WebAssembly::BR)).addMBB(TBB); + return 1; + } + + BuildMI(&MBB, DL, get(WebAssembly::BRIF)) + .addMBB(TBB) + .addOperand(Cond[0]); + if (!FBB) + return 1; + + BuildMI(&MBB, DL, get(WebAssembly::BR)).addMBB(FBB); + return 2; +} + +bool WebAssemblyInstrInfo::ReverseBranchCondition( + SmallVectorImpl &Cond) const { + assert(Cond.size() == 1); + + // TODO: Add branch reversal here... And re-enable MachineBlockPlacementID + // when we do. + + return true; +} Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -30,6 +30,7 @@ SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>; def SDT_WebAssemblyCall0 : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; def SDT_WebAssemblyCall1 : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>; +def SDT_WebAssemblySwitch : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>; def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>; def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, @@ -51,6 +52,9 @@ def WebAssemblycall1 : SDNode<"WebAssemblyISD::CALL1", SDT_WebAssemblyCall1, [SDNPHasChain, SDNPVariadic]>; +def WebAssemblyswitch : SDNode<"WebAssemblyISD::SWITCH", + SDT_WebAssemblySwitch, + [SDNPHasChain, SDNPVariadic]>; def WebAssemblyargument : SDNode<"WebAssemblyISD::ARGUMENT", SDT_WebAssemblyArgument>; def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN", @@ -69,6 +73,8 @@ * set_local: set the current value of a local variable */ +def bb_op : Operand; +def tjumptable_op : Operand; def global : Operand; //===----------------------------------------------------------------------===// @@ -96,9 +102,11 @@ def Immediate_F64 : I<(outs Float64:$res), (ins f64imm:$imm), [(set Float64:$res, fpimm:$imm)]>; -// Special types of immediates. +// Special types of immediates. FIXME: Hard-coded as 32-bit for now. def GLOBAL : I<(outs Int32:$dst), (ins global:$addr), [(set Int32:$dst, (WebAssemblywrapper tglobaladdr:$addr))]>; +def JUMP_TABLE : I<(outs Int32:$dst), (ins tjumptable_op:$addr), + [(set Int32:$dst, (WebAssemblywrapper tjumptable:$addr))]>; //===----------------------------------------------------------------------===// // Additional sets of instructions. Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -159,8 +159,14 @@ disablePass(&PrologEpilogCodeInserterID); // Fails with: should be run after register allocation. disablePass(&MachineCopyPropagationID); + + // FIXME: Until we get ReverseBranchCondition support, MachineBlockPlacement + // can create ugly-looking control flow. + disablePass(&MachineBlockPlacementID); } void WebAssemblyPassConfig::addPreSched2() {} -void WebAssemblyPassConfig::addPreEmitPass() {} +void WebAssemblyPassConfig::addPreEmitPass() { + addPass(createWebAssemblyCFGStackify()); +} Index: test/CodeGen/WebAssembly/phi.ll =================================================================== --- test/CodeGen/WebAssembly/phi.ll +++ test/CodeGen/WebAssembly/phi.ll @@ -1,8 +1,5 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s -; This test depends on branching support, which is not yet checked in. -; XFAIL: * - ; Test that phis are lowered. target datalayout = "e-p:32:32-i64:64-n32:64-S128" @@ -29,7 +26,7 @@ ; Swap phis. ; CHECK-LABEL: test1 -; CHECK: BB0_1: +; CHECK: BB1_1: ; CHECK: (setlocal [[REG0:@.*]] [[REG1:@.*]]) ; CHECK: (setlocal [[REG1]] [[REG2:@.*]]) ; CHECK: (setlocal [[REG2]] [[REG0]]) Index: test/CodeGen/WebAssembly/switch.ll =================================================================== --- test/CodeGen/WebAssembly/switch.ll +++ test/CodeGen/WebAssembly/switch.ll @@ -0,0 +1,93 @@ +; RUN: llc < %s -asm-verbose=false | FileCheck %s + +; Test switch instructions. + +target datalayout = "e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +declare void @foo0() +declare void @foo1() +declare void @foo2() +declare void @foo3() +declare void @foo4() +declare void @foo5() + +; CHECK-LABEL: bar +; CHECK: (block $BB0_8) +; CHECK: (block $BB0_7) +; CHECK: (block $BB0_6) +; CHECK: (block $BB0_5) +; CHECK: (block $BB0_4) +; CHECK: (block $BB0_3) +; CHECK: (block $BB0_2) +; CHECK: (switch {{.*}} $BB0_2 $BB0_2 $BB0_2 $BB0_2 $BB0_2 $BB0_2 $BB0_2 $BB0_2 $BB0_3 $BB0_3 $BB0_3 $BB0_3 $BB0_3 $BB0_3 $BB0_3 $BB0_3 $BB0_4 $BB0_4 $BB0_4 $BB0_4 $BB0_4 $BB0_4 $BB0_5 $BB0_6 $BB0_7) +; CHECk: BB0_2: +; CHECK: (setlocal {{.*}} (global $foo0)) +; CHECK: BB0_3: +; CHECK: (setlocal {{.*}} (global $foo1)) +; CHECK: BB0_4: +; CHECK: (setlocal {{.*}} (global $foo2)) +; CHECK: BB0_5: +; CHECK: (setlocal {{.*}} (global $foo3)) +; CHECK: BB0_6: +; CHECK: (setlocal {{.*}} (global $foo4)) +; CHECK: BB0_7: +; CHECK: (setlocal {{.*}} (global $foo5)) +; CHECK: BB0_8: +; CHECK: (return) +define void @bar(i32 %n) { +entry: + switch i32 %n, label %sw.epilog [ + i32 0, label %sw.bb + i32 1, label %sw.bb + i32 2, label %sw.bb + i32 3, label %sw.bb + i32 4, label %sw.bb + i32 5, label %sw.bb + i32 6, label %sw.bb + i32 7, label %sw.bb.1 + i32 8, label %sw.bb.1 + i32 9, label %sw.bb.1 + i32 10, label %sw.bb.1 + i32 11, label %sw.bb.1 + i32 12, label %sw.bb.1 + i32 13, label %sw.bb.1 + i32 14, label %sw.bb.1 + i32 15, label %sw.bb.2 + i32 16, label %sw.bb.2 + i32 17, label %sw.bb.2 + i32 18, label %sw.bb.2 + i32 19, label %sw.bb.2 + i32 20, label %sw.bb.2 + i32 21, label %sw.bb.3 + i32 22, label %sw.bb.4 + i32 23, label %sw.bb.5 + ] + +sw.bb: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry + tail call void @foo0() + br label %sw.epilog + +sw.bb.1: ; preds = %entry, %entry, %entry, %entry, %entry, %entry, %entry, %entry + tail call void @foo1() + br label %sw.epilog + +sw.bb.2: ; preds = %entry, %entry, %entry, %entry, %entry, %entry + tail call void @foo2() + br label %sw.epilog + +sw.bb.3: ; preds = %entry + tail call void @foo3() + br label %sw.epilog + +sw.bb.4: ; preds = %entry + tail call void @foo4() + br label %sw.epilog + +sw.bb.5: ; preds = %entry + tail call void @foo5() + br label %sw.epilog + +sw.epilog: ; preds = %entry, %sw.bb.5, %sw.bb.4, %sw.bb.3, %sw.bb.2, %sw.bb.1, %sw.bb + ret void +}