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,13 @@ : 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 +92,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 +124,12 @@ 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 +144,9 @@ case Symbol: OS << "Sym:" << Sym.Exp; break; + case BrList: + OS << "BrList:" << BrL.List.size(); + break; } } }; @@ -340,6 +362,21 @@ 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 @@ -134,10 +134,10 @@ unsigned NumFixedOperands = Desc.NumOperands; SmallSet Printed; for (unsigned i = 0, e = MI->getNumOperands(); i < e; ++i) { - if (!(i < NumFixedOperands - ? (Desc.OpInfo[i].OperandType == - WebAssembly::OPERAND_BASIC_BLOCK) - : (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsLabel))) + if (i < NumFixedOperands + ? Desc.OpInfo[i].OperandType != WebAssembly::OPERAND_BASIC_BLOCK + // This just needed to support -wasm-keep-registers + : !MI->getOperand(i).isImm()) continue; uint64_t Depth = MI->getOperand(i).getImm(); if (!Printed.insert(Depth).second) @@ -194,9 +194,6 @@ raw_ostream &O) { const MCOperand &Op = MI->getOperand(OpNo); if (Op.isReg()) { - assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() || - MII.get(MI->getOpcode()).TSFlags == 0) && - "WebAssembly variable_ops register ops don't use TSFlags"); unsigned WAReg = Op.getReg(); if (int(WAReg) >= 0) printRegName(O, WAReg); @@ -210,23 +207,9 @@ if (OpNo < MII.get(MI->getOpcode()).getNumDefs()) O << '='; } else if (Op.isImm()) { - const MCInstrDesc &Desc = MII.get(MI->getOpcode()); - assert((OpNo < Desc.getNumOperands() || - (Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) && - "WebAssemblyII::VariableOpIsImmediate should be set for " - "variable_ops immediate ops"); - (void)Desc; - // TODO: (MII.get(MI->getOpcode()).TSFlags & - // WebAssemblyII::VariableOpImmediateIsLabel) - // can tell us whether this is an immediate referencing a label in the - // control flow stack, and it may be nice to pretty-print. O << Op.getImm(); } else if (Op.isFPImm()) { const MCInstrDesc &Desc = MII.get(MI->getOpcode()); - assert(OpNo < Desc.getNumOperands() && - "Unexpected floating-point immediate as a non-fixed operand"); - assert(Desc.TSFlags == 0 && - "WebAssembly variable_ops floating point ops don't use TSFlags"); const MCOperandInfo &Info = Desc.OpInfo[OpNo]; if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { // TODO: MC converts all floating point immediate operands to double. @@ -237,16 +220,22 @@ O << ::toString(APFloat(Op.getFPImm())); } } else { - assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() || - (MII.get(MI->getOpcode()).TSFlags & - WebAssemblyII::VariableOpIsImmediate)) && - "WebAssemblyII::VariableOpIsImmediate should be set for " - "variable_ops expr ops"); assert(Op.isExpr() && "unknown operand kind in printOperand"); Op.getExpr()->print(O, &MAI); } } +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 @@ -89,8 +89,6 @@ } else if (MO.isImm()) { if (i < Desc.getNumOperands()) { - assert(Desc.TSFlags == 0 && - "WebAssembly non-variable_ops don't use TSFlags"); const MCOperandInfo &Info = Desc.OpInfo[i]; LLVM_DEBUG(dbgs() << "Encoding immediate: type=" << int(Info.OperandType) << "\n"); @@ -125,16 +123,10 @@ encodeULEB128(uint64_t(MO.getImm()), OS); } } else { - assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate | - WebAssemblyII::VariableOpImmediateIsLabel)); encodeULEB128(uint64_t(MO.getImm()), OS); } } else if (MO.isFPImm()) { - assert(i < Desc.getNumOperands() && - "Unexpected floating-point immediate as a non-fixed operand"); - assert(Desc.TSFlags == 0 && - "WebAssembly variable_ops floating point ops don't use TSFlags"); const MCOperandInfo &Info = Desc.OpInfo[i]; if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { // TODO: MC converts all floating point immediate operands to double. Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -83,14 +83,6 @@ } // end namespace WebAssembly namespace WebAssemblyII { -enum { - // For variadic instructions, this flag indicates whether an operand - // in the variable_ops range is an immediate value. - VariableOpIsImmediate = (1 << 0), - // For immediate values in the variable_ops range, this flag indicates - // whether the value represents a control-flow label. - VariableOpImmediateIsLabel = (1 << 1) -}; /// Target Operand Flag enum. enum TOF { Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -33,11 +33,17 @@ 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. -// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates. -// Set TSFlags{1} to 1 to indicate that the immediates represent labels. // FIXME: this can't inherit from I<> since there is no way to inherit from a // multiclass and still have the let statements. let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { @@ -45,29 +51,19 @@ def BR_TABLE_I32 : NI<(outs), (ins I32:$index, variable_ops), [(WebAssemblybr_table I32:$index)], "false", "br_table \t$index", 0x0e> { - let TSFlags{0} = 1; - let TSFlags{1} = 1; } let BaseName = "BR_TABLE_I32" in -def BR_TABLE_I32_S : NI<(outs), (ins variable_ops), - [], "true", - "br_table \t", 0x0e> { - let TSFlags{0} = 1; - let TSFlags{1} = 1; +def BR_TABLE_I32_S : NI<(outs), (ins brlist:$brl), [], "true", + "br_table \t$brl", 0x0e> { } let isCodeGenOnly = 1 in def BR_TABLE_I64 : NI<(outs), (ins I64:$index, variable_ops), [(WebAssemblybr_table I64:$index)], "false", "br_table \t$index"> { - let TSFlags{0} = 1; - let TSFlags{1} = 1; } let BaseName = "BR_TABLE_I64" in -def BR_TABLE_I64_S : NI<(outs), (ins variable_ops), - [], "true", - "br_table \t"> { - let TSFlags{0} = 1; - let TSFlags{1} = 1; +def BR_TABLE_I64_S : NI<(outs), (ins brlist:$brl), [], "true", + "br_table \t$brl"> { } } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 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