Index: lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp =================================================================== --- lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -38,7 +38,7 @@ /// WebAssemblyOperand - Instances of this class represent the operands in a /// parsed WASM machine instruction. struct WebAssemblyOperand : public MCParsedAsmOperand { - enum KindTy { Token, Integer, Float, Symbol } Kind; + enum KindTy { Token, Integer, Float, Symbol, BrList } Kind; SMLoc StartLoc, EndLoc; @@ -58,11 +58,16 @@ const MCExpr *Exp; }; + struct BrLOp { + std::vector List; + }; + union { struct TokOp Tok; struct IntOp Int; struct FltOp Flt; struct SymOp Sym; + struct BrLOp BrL; }; WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T) @@ -73,6 +78,12 @@ : Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {} WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp S) : Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {} + WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End) + : Kind(K), StartLoc(Start), EndLoc(End), BrL() {} + + ~WebAssemblyOperand() { + if (isBrList()) BrL.~BrLOp(); + } bool isToken() const override { return Kind == Token; } bool isImm() const override { @@ -80,6 +91,7 @@ } bool isMem() const override { return false; } bool isReg() const override { return false; } + bool isBrList() const { return Kind == BrList; } unsigned getReg() const override { llvm_unreachable("Assembly inspects a register operand"); @@ -111,6 +123,11 @@ llvm_unreachable("Should be immediate or symbol!"); } + void addBrListOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && isBrList() && "Invalid BrList!"); + for (auto Br : BrL.List) Inst.addOperand(MCOperand::createImm(Br)); + } + void print(raw_ostream &OS) const override { switch (Kind) { case Token: @@ -125,6 +142,9 @@ case Symbol: OS << "Sym:" << Sym.Exp; break; + case BrList: + OS << "BrList:" << BrL.List.size(); + break; } } }; @@ -331,6 +351,19 @@ Parser.Lex(); break; } + case AsmToken::LCurly: { + Parser.Lex(); + auto Op = make_unique( + WebAssemblyOperand::BrList, Tok.getLoc(), Tok.getEndLoc()); + if (!Lexer.is(AsmToken::RCurly)) for(;;) { + Op->BrL.List.push_back(Lexer.getTok().getIntVal()); + Expect(AsmToken::Integer, "integer"); + if (!IsNext(AsmToken::Comma)) break; + } + Expect(AsmToken::RCurly, "}"); + Operands.push_back(std::move(Op)); + break; + } default: return Error("Unexpected token in operand: ", Tok); } Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h @@ -43,6 +43,7 @@ // Used by tblegen code. void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printBrList(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo, Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -247,6 +247,17 @@ } } +void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + O << "{"; + for (unsigned i = OpNo, e = MI->getNumOperands(); i != e; ++i) { + if (i != OpNo) + O << ", "; + O << MI->getOperand(i).getImm(); + } + O << "}"; +} + void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) { Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -74,12 +74,17 @@ // For br_table instructions, encode the size of the table. In the MCInst, // there's an index operand (if not a stack instruction), one operand for // each table entry, and the default operand. + bool IsBrTable = false; if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S || - MI.getOpcode() == WebAssembly::BR_TABLE_I64_S) + MI.getOpcode() == WebAssembly::BR_TABLE_I64_S) { encodeULEB128(MI.getNumOperands() - 1, OS); + IsBrTable = true; + } if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 || - MI.getOpcode() == WebAssembly::BR_TABLE_I64) + MI.getOpcode() == WebAssembly::BR_TABLE_I64) { encodeULEB128(MI.getNumOperands() - 2, OS); + IsBrTable = true; + } const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) { @@ -89,7 +94,7 @@ } else if (MO.isImm()) { if (i < Desc.getNumOperands()) { - assert(Desc.TSFlags == 0 && + assert((Desc.TSFlags == 0 || IsBrTable) && "WebAssembly non-variable_ops don't use TSFlags"); const MCOperandInfo &Info = Desc.OpInfo[i]; LLVM_DEBUG(dbgs() << "Encoding immediate: type=" Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -33,6 +33,14 @@ def : Pat<(brcond (i32 (seteq I32:$cond, 0)), bb:$dst), (BR_UNLESS bb_op:$dst, I32:$cond)>; +// A list of branch targets enclosed in {} and separated by comma. +// Used by br_table only. +def BrListAsmOperand : AsmOperandClass { let Name = "BrList"; } +def brlist : Operand { + let ParserMatchClass = BrListAsmOperand; + let PrintMethod = "printBrList"; +} + // TODO: SelectionDAG's lowering insists on using a pointer as the index for // jump tables, so in practice we don't ever use BR_TABLE_I64 in wasm32 mode // currently. @@ -49,9 +57,9 @@ let TSFlags{1} = 1; } let BaseName = "BR_TABLE_I32" in -def BR_TABLE_I32_S : NI<(outs), (ins variable_ops), +def BR_TABLE_I32_S : NI<(outs), (ins brlist:$brl), [], "true", - "br_table \t", 0x0e> { + "br_table \t$brl", 0x0e> { let TSFlags{0} = 1; let TSFlags{1} = 1; } @@ -63,9 +71,9 @@ let TSFlags{1} = 1; } let BaseName = "BR_TABLE_I64" in -def BR_TABLE_I64_S : NI<(outs), (ins variable_ops), +def BR_TABLE_I64_S : NI<(outs), (ins brlist:$brl), [], "true", - "br_table \t"> { + "br_table \t$brl"> { let TSFlags{0} = 1; let TSFlags{1} = 1; } Index: test/CodeGen/WebAssembly/stack-insts.ll =================================================================== --- test/CodeGen/WebAssembly/stack-insts.ll +++ test/CodeGen/WebAssembly/stack-insts.ll @@ -8,8 +8,7 @@ ; Tests if br_table is printed correctly with a tab. ; CHECK-LABEL: test0: -; CHECK-NOT: br_table0, 1, 0, 1, 0 -; CHECK: br_table 0, 1, 0, 1, 0 +; CHECK: br_table {0, 1, 0, 1, 0} define void @test0(i32 %n) { entry: switch i32 %n, label %sw.epilog [ Index: test/MC/WebAssembly/basic-assembly.s =================================================================== --- test/MC/WebAssembly/basic-assembly.s +++ test/MC/WebAssembly/basic-assembly.s @@ -44,6 +44,20 @@ end_block # label0: get_local 4 get_local 5 + block + block + block + block + br_table {0, 1, 2} # 2 entries, default + end_block # first entry jumps here. + i32.const 1 + br 2 + end_block # second entry jumps here. + i32.const 2 + br 1 + end_block # default jumps here. + i32.const 3 + end_block # "switch" exit. f32x4.add # Test correct parsing of instructions with / and : in them: # TODO: enable once instruction has been added. @@ -98,6 +112,21 @@ # CHECK-NEXT: end_block # label0: # CHECK-NEXT: get_local 4 # CHECK-NEXT: get_local 5 +# CHECK-NEXT: block +# CHECK-NEXT: block +# CHECK-NEXT: block +# CHECK-NEXT: block +# CHECK-NEXT: br_table {0, 1, 2} # 1: down to label4 +# CHECK-NEXT: # 2: down to label3 +# CHECK-NEXT: end_block +# CHECK-NEXT: i32.const 1 +# CHECK-NEXT: br 2 +# CHECK-NEXT: end_block +# CHECK-NEXT: i32.const 2 +# CHECK-NEXT: br 1 +# CHECK-NEXT: end_block +# CHECK-NEXT: i32.const 3 +# CHECK-NEXT: end_block # CHECK-NEXT: f32x4.add # CHECK-NEXT: i32.trunc_s/f32 # CHECK-NEXT: try