Skip to content

Commit d3c544a

Browse files
committed
[WebAssembly] Fix assembler parsing of br_table.
Summary: We use `variable_ops` in the tablegen defs to denote the list of branch targets in `br_table`, but unlike other uses of `variable_ops` (e.g. call) the these branch targets need to actually be encoded in the instruction. The existing tables for `variable_ops` cause not operands to be accepted by the assembly matcher. Following the example of ARM: https://github.com/llvm-mirror/llvm/blob/2cc0a7da876c1d8c32775b0119e1e15aaa759b9e/lib/Target/ARM/ARMInstrInfo.td#L550-L555 we introduce a new operand type to capture this list, and we use the same {} syntax as ARM as well to differentiate them from regular integer operands. Also removed definition and use of TSFlags in tablegen defs, since `br_table` now has a non-variable_ops immediate operand, so the previous logic of only the variable_ops arguments being labels didn't make sense anymore. Reviewers: dschuff, aheejin, sunfish Subscribers: javed.absar, sbc100, jgravelle-google, kristof.beyls, llvm-commits Differential Revision: https://reviews.llvm.org/D55401 llvm-svn: 349405
1 parent 8c9d772 commit d3c544a

File tree

8 files changed

+105
-62
lines changed

8 files changed

+105
-62
lines changed

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace {
3838
/// WebAssemblyOperand - Instances of this class represent the operands in a
3939
/// parsed WASM machine instruction.
4040
struct WebAssemblyOperand : public MCParsedAsmOperand {
41-
enum KindTy { Token, Integer, Float, Symbol } Kind;
41+
enum KindTy { Token, Integer, Float, Symbol, BrList } Kind;
4242

4343
SMLoc StartLoc, EndLoc;
4444

@@ -58,11 +58,16 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
5858
const MCExpr *Exp;
5959
};
6060

61+
struct BrLOp {
62+
std::vector<unsigned> List;
63+
};
64+
6165
union {
6266
struct TokOp Tok;
6367
struct IntOp Int;
6468
struct FltOp Flt;
6569
struct SymOp Sym;
70+
struct BrLOp BrL;
6671
};
6772

6873
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T)
@@ -73,13 +78,21 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
7378
: Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {}
7479
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp S)
7580
: Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {}
81+
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End)
82+
: Kind(K), StartLoc(Start), EndLoc(End), BrL() {}
83+
84+
~WebAssemblyOperand() {
85+
if (isBrList())
86+
BrL.~BrLOp();
87+
}
7688

7789
bool isToken() const override { return Kind == Token; }
7890
bool isImm() const override {
7991
return Kind == Integer || Kind == Float || Kind == Symbol;
8092
}
8193
bool isMem() const override { return false; }
8294
bool isReg() const override { return false; }
95+
bool isBrList() const { return Kind == BrList; }
8396

8497
unsigned getReg() const override {
8598
llvm_unreachable("Assembly inspects a register operand");
@@ -111,6 +124,12 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
111124
llvm_unreachable("Should be immediate or symbol!");
112125
}
113126

127+
void addBrListOperands(MCInst &Inst, unsigned N) const {
128+
assert(N == 1 && isBrList() && "Invalid BrList!");
129+
for (auto Br : BrL.List)
130+
Inst.addOperand(MCOperand::createImm(Br));
131+
}
132+
114133
void print(raw_ostream &OS) const override {
115134
switch (Kind) {
116135
case Token:
@@ -125,6 +144,9 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
125144
case Symbol:
126145
OS << "Sym:" << Sym.Exp;
127146
break;
147+
case BrList:
148+
OS << "BrList:" << BrL.List.size();
149+
break;
128150
}
129151
}
130152
};
@@ -340,6 +362,21 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
340362
Parser.Lex();
341363
break;
342364
}
365+
case AsmToken::LCurly: {
366+
Parser.Lex();
367+
auto Op = make_unique<WebAssemblyOperand>(
368+
WebAssemblyOperand::BrList, Tok.getLoc(), Tok.getEndLoc());
369+
if (!Lexer.is(AsmToken::RCurly))
370+
for (;;) {
371+
Op->BrL.List.push_back(Lexer.getTok().getIntVal());
372+
expect(AsmToken::Integer, "integer");
373+
if (!isNext(AsmToken::Comma))
374+
break;
375+
}
376+
expect(AsmToken::RCurly, "}");
377+
Operands.push_back(std::move(Op));
378+
break;
379+
}
343380
default:
344381
return error("Unexpected token in operand: ", Tok);
345382
}

llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,19 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
134134
unsigned NumFixedOperands = Desc.NumOperands;
135135
SmallSet<uint64_t, 8> Printed;
136136
for (unsigned i = 0, e = MI->getNumOperands(); i < e; ++i) {
137-
if (!(i < NumFixedOperands
138-
? (Desc.OpInfo[i].OperandType ==
139-
WebAssembly::OPERAND_BASIC_BLOCK)
140-
: (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsLabel)))
141-
continue;
137+
// See if this operand denotes a basic block target.
138+
if (i < NumFixedOperands) {
139+
// A non-variable_ops operand, check its type.
140+
if (Desc.OpInfo[i].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
141+
continue;
142+
} else {
143+
// A variable_ops operand, which currently can be immediates (used in
144+
// br_table) which are basic block targets, or for call instructions
145+
// when using -wasm-keep-registers (in which case they are registers,
146+
// and should not be processed).
147+
if (!MI->getOperand(i).isImm())
148+
continue;
149+
}
142150
uint64_t Depth = MI->getOperand(i).getImm();
143151
if (!Printed.insert(Depth).second)
144152
continue;
@@ -194,9 +202,6 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
194202
raw_ostream &O) {
195203
const MCOperand &Op = MI->getOperand(OpNo);
196204
if (Op.isReg()) {
197-
assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() ||
198-
MII.get(MI->getOpcode()).TSFlags == 0) &&
199-
"WebAssembly variable_ops register ops don't use TSFlags");
200205
unsigned WAReg = Op.getReg();
201206
if (int(WAReg) >= 0)
202207
printRegName(O, WAReg);
@@ -210,23 +215,9 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
210215
if (OpNo < MII.get(MI->getOpcode()).getNumDefs())
211216
O << '=';
212217
} else if (Op.isImm()) {
213-
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
214-
assert((OpNo < Desc.getNumOperands() ||
215-
(Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) &&
216-
"WebAssemblyII::VariableOpIsImmediate should be set for "
217-
"variable_ops immediate ops");
218-
(void)Desc;
219-
// TODO: (MII.get(MI->getOpcode()).TSFlags &
220-
// WebAssemblyII::VariableOpImmediateIsLabel)
221-
// can tell us whether this is an immediate referencing a label in the
222-
// control flow stack, and it may be nice to pretty-print.
223218
O << Op.getImm();
224219
} else if (Op.isFPImm()) {
225220
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
226-
assert(OpNo < Desc.getNumOperands() &&
227-
"Unexpected floating-point immediate as a non-fixed operand");
228-
assert(Desc.TSFlags == 0 &&
229-
"WebAssembly variable_ops floating point ops don't use TSFlags");
230221
const MCOperandInfo &Info = Desc.OpInfo[OpNo];
231222
if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
232223
// TODO: MC converts all floating point immediate operands to double.
@@ -237,16 +228,22 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
237228
O << ::toString(APFloat(Op.getFPImm()));
238229
}
239230
} else {
240-
assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() ||
241-
(MII.get(MI->getOpcode()).TSFlags &
242-
WebAssemblyII::VariableOpIsImmediate)) &&
243-
"WebAssemblyII::VariableOpIsImmediate should be set for "
244-
"variable_ops expr ops");
245231
assert(Op.isExpr() && "unknown operand kind in printOperand");
246232
Op.getExpr()->print(O, &MAI);
247233
}
248234
}
249235

236+
void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo,
237+
raw_ostream &O) {
238+
O << "{";
239+
for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
240+
if (I != OpNo)
241+
O << ", ";
242+
O << MI->getOperand(I).getImm();
243+
}
244+
O << "}";
245+
}
246+
250247
void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
251248
unsigned OpNo,
252249
raw_ostream &O) {

llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class WebAssemblyInstPrinter final : public MCInstPrinter {
4343

4444
// Used by tblegen code.
4545
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
46+
void printBrList(const MCInst *MI, unsigned OpNo, raw_ostream &O);
4647
void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo,
4748
raw_ostream &O);
4849
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo,

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
8989

9090
} else if (MO.isImm()) {
9191
if (i < Desc.getNumOperands()) {
92-
assert(Desc.TSFlags == 0 &&
93-
"WebAssembly non-variable_ops don't use TSFlags");
9492
const MCOperandInfo &Info = Desc.OpInfo[i];
9593
LLVM_DEBUG(dbgs() << "Encoding immediate: type="
9694
<< int(Info.OperandType) << "\n");
@@ -125,16 +123,10 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
125123
encodeULEB128(uint64_t(MO.getImm()), OS);
126124
}
127125
} else {
128-
assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate |
129-
WebAssemblyII::VariableOpImmediateIsLabel));
130126
encodeULEB128(uint64_t(MO.getImm()), OS);
131127
}
132128

133129
} else if (MO.isFPImm()) {
134-
assert(i < Desc.getNumOperands() &&
135-
"Unexpected floating-point immediate as a non-fixed operand");
136-
assert(Desc.TSFlags == 0 &&
137-
"WebAssembly variable_ops floating point ops don't use TSFlags");
138130
const MCOperandInfo &Info = Desc.OpInfo[i];
139131
if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
140132
// TODO: MC converts all floating point immediate operands to double.

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,6 @@ enum OperandType {
8383
} // end namespace WebAssembly
8484

8585
namespace WebAssemblyII {
86-
enum {
87-
// For variadic instructions, this flag indicates whether an operand
88-
// in the variable_ops range is an immediate value.
89-
VariableOpIsImmediate = (1 << 0),
90-
// For immediate values in the variable_ops range, this flag indicates
91-
// whether the value represents a control-flow label.
92-
VariableOpImmediateIsLabel = (1 << 1)
93-
};
9486

9587
/// Target Operand Flag enum.
9688
enum TOF {

llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,37 @@ def : Pat<(brcond (i32 (setne I32:$cond, 0)), bb:$dst),
3333
def : Pat<(brcond (i32 (seteq I32:$cond, 0)), bb:$dst),
3434
(BR_UNLESS bb_op:$dst, I32:$cond)>;
3535

36+
// A list of branch targets enclosed in {} and separated by comma.
37+
// Used by br_table only.
38+
def BrListAsmOperand : AsmOperandClass { let Name = "BrList"; }
39+
def brlist : Operand<i32> {
40+
let ParserMatchClass = BrListAsmOperand;
41+
let PrintMethod = "printBrList";
42+
}
43+
3644
// TODO: SelectionDAG's lowering insists on using a pointer as the index for
3745
// jump tables, so in practice we don't ever use BR_TABLE_I64 in wasm32 mode
3846
// currently.
39-
// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates.
40-
// Set TSFlags{1} to 1 to indicate that the immediates represent labels.
4147
// FIXME: this can't inherit from I<> since there is no way to inherit from a
4248
// multiclass and still have the let statements.
4349
let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
4450
let isCodeGenOnly = 1 in
4551
def BR_TABLE_I32 : NI<(outs), (ins I32:$index, variable_ops),
4652
[(WebAssemblybr_table I32:$index)], "false",
4753
"br_table \t$index", 0x0e> {
48-
let TSFlags{0} = 1;
49-
let TSFlags{1} = 1;
5054
}
5155
let BaseName = "BR_TABLE_I32" in
52-
def BR_TABLE_I32_S : NI<(outs), (ins variable_ops),
53-
[], "true",
54-
"br_table \t", 0x0e> {
55-
let TSFlags{0} = 1;
56-
let TSFlags{1} = 1;
56+
def BR_TABLE_I32_S : NI<(outs), (ins brlist:$brl), [], "true",
57+
"br_table \t$brl", 0x0e> {
5758
}
5859
let isCodeGenOnly = 1 in
5960
def BR_TABLE_I64 : NI<(outs), (ins I64:$index, variable_ops),
6061
[(WebAssemblybr_table I64:$index)], "false",
6162
"br_table \t$index"> {
62-
let TSFlags{0} = 1;
63-
let TSFlags{1} = 1;
6463
}
6564
let BaseName = "BR_TABLE_I64" in
66-
def BR_TABLE_I64_S : NI<(outs), (ins variable_ops),
67-
[], "true",
68-
"br_table \t"> {
69-
let TSFlags{0} = 1;
70-
let TSFlags{1} = 1;
65+
def BR_TABLE_I64_S : NI<(outs), (ins brlist:$brl), [], "true",
66+
"br_table \t$brl"> {
7167
}
7268
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
7369

llvm/test/CodeGen/WebAssembly/stack-insts.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ declare void @foo1()
88

99
; Tests if br_table is printed correctly with a tab.
1010
; CHECK-LABEL: test0:
11-
; CHECK-NOT: br_table0, 1, 0, 1, 0
12-
; CHECK: br_table 0, 1, 0, 1, 0
11+
; CHECK: br_table {0, 1, 0, 1, 0}
1312
define void @test0(i32 %n) {
1413
entry:
1514
switch i32 %n, label %sw.epilog [

llvm/test/MC/WebAssembly/basic-assembly.s

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ test0:
4545
end_block # label0:
4646
get_local 4
4747
get_local 5
48+
block
49+
block
50+
block
51+
block
52+
br_table {0, 1, 2} # 2 entries, default
53+
end_block # first entry jumps here.
54+
i32.const 1
55+
br 2
56+
end_block # second entry jumps here.
57+
i32.const 2
58+
br 1
59+
end_block # default jumps here.
60+
i32.const 3
61+
end_block # "switch" exit.
4862
f32x4.add
4963
# Test correct parsing of instructions with / and : in them:
5064
# TODO: enable once instruction has been added.
@@ -100,6 +114,21 @@ test0:
100114
# CHECK-NEXT: end_block # label0:
101115
# CHECK-NEXT: get_local 4
102116
# CHECK-NEXT: get_local 5
117+
# CHECK-NEXT: block
118+
# CHECK-NEXT: block
119+
# CHECK-NEXT: block
120+
# CHECK-NEXT: block
121+
# CHECK-NEXT: br_table {0, 1, 2} # 1: down to label4
122+
# CHECK-NEXT: # 2: down to label3
123+
# CHECK-NEXT: end_block # label5:
124+
# CHECK-NEXT: i32.const 1
125+
# CHECK-NEXT: br 2 # 2: down to label2
126+
# CHECK-NEXT: end_block # label4:
127+
# CHECK-NEXT: i32.const 2
128+
# CHECK-NEXT: br 1 # 1: down to label2
129+
# CHECK-NEXT: end_block # label3:
130+
# CHECK-NEXT: i32.const 3
131+
# CHECK-NEXT: end_block # label2:
103132
# CHECK-NEXT: f32x4.add
104133
# CHECK-NEXT: i32.trunc_s/f32
105134
# CHECK-NEXT: try

0 commit comments

Comments
 (0)