@@ -34,17 +34,47 @@ using namespace llvm;
34
34
35
35
namespace {
36
36
37
+ // We store register types as SimpleValueType to retain SIMD layout
38
+ // information, but must also be able to supply them as the (unnamed)
39
+ // register enum from WebAssemblyRegisterInfo.td/.inc.
40
+ static unsigned MVTToWasmReg (MVT::SimpleValueType Type) {
41
+ switch (Type) {
42
+ case MVT::i32: return WebAssembly::I32_0;
43
+ case MVT::i64: return WebAssembly::I64_0;
44
+ case MVT::f32: return WebAssembly::F32_0;
45
+ case MVT::f64: return WebAssembly::F64_0;
46
+ case MVT::v16i8: return WebAssembly::V128_0;
47
+ case MVT::v8i16: return WebAssembly::V128_0;
48
+ case MVT::v4i32: return WebAssembly::V128_0;
49
+ case MVT::v4f32: return WebAssembly::V128_0;
50
+ default : return MVT::INVALID_SIMPLE_VALUE_TYPE;
51
+ }
52
+ }
53
+
37
54
// / WebAssemblyOperand - Instances of this class represent the operands in a
38
55
// / parsed WASM machine instruction.
39
56
struct WebAssemblyOperand : public MCParsedAsmOperand {
40
- enum KindTy { Token, Integer, Float, Symbol } Kind;
57
+ enum KindTy { Token, Local, Stack, Integer, Float, Symbol } Kind;
41
58
42
59
SMLoc StartLoc, EndLoc;
43
60
44
61
struct TokOp {
45
62
StringRef Tok;
46
63
};
47
64
65
+ struct RegOp {
66
+ // This is a (virtual) local or stack register represented as 0..
67
+ unsigned RegNo;
68
+ // In most targets, the register number also encodes the type, but for
69
+ // wasm we have to track that seperately since we have an unbounded
70
+ // number of registers.
71
+ // This has the unfortunate side effect that we supply a different value
72
+ // to the table-gen matcher at different times in the process (when it
73
+ // calls getReg() or addRegOperands().
74
+ // TODO: While this works, it feels brittle. and would be nice to clean up.
75
+ MVT::SimpleValueType Type;
76
+ };
77
+
48
78
struct IntOp {
49
79
int64_t Val;
50
80
};
@@ -59,13 +89,16 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
59
89
60
90
union {
61
91
struct TokOp Tok;
92
+ struct RegOp Reg;
62
93
struct IntOp Int;
63
94
struct FltOp Flt;
64
95
struct SymOp Sym;
65
96
};
66
97
67
98
WebAssemblyOperand (KindTy K, SMLoc Start, SMLoc End, TokOp T)
68
99
: Kind(K), StartLoc(Start), EndLoc(End), Tok(T) {}
100
+ WebAssemblyOperand (KindTy K, SMLoc Start, SMLoc End, RegOp R)
101
+ : Kind(K), StartLoc(Start), EndLoc(End), Reg(R) {}
69
102
WebAssemblyOperand (KindTy K, SMLoc Start, SMLoc End, IntOp I)
70
103
: Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}
71
104
WebAssemblyOperand (KindTy K, SMLoc Start, SMLoc End, FltOp F)
@@ -77,12 +110,14 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
77
110
bool isImm () const override { return Kind == Integer ||
78
111
Kind == Float ||
79
112
Kind == Symbol; }
113
+ bool isReg () const override { return Kind == Local || Kind == Stack; }
80
114
bool isMem () const override { return false ; }
81
- bool isReg () const override { return false ; }
82
115
83
116
unsigned getReg () const override {
84
- llvm_unreachable (" Assembly inspects a register operand" );
85
- return 0 ;
117
+ assert (isReg ());
118
+ // This is called from the tablegen matcher (MatchInstructionImpl)
119
+ // where it expects to match the type of register, see RegOp above.
120
+ return MVTToWasmReg (Reg.Type );
86
121
}
87
122
88
123
StringRef getToken () const {
@@ -93,9 +128,19 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
93
128
SMLoc getStartLoc () const override { return StartLoc; }
94
129
SMLoc getEndLoc () const override { return EndLoc; }
95
130
96
- void addRegOperands (MCInst &, unsigned ) const {
97
- // Required by the assembly matcher.
98
- llvm_unreachable (" Assembly matcher creates register operands" );
131
+ void addRegOperands (MCInst &Inst, unsigned N) const {
132
+ assert (N == 1 && " Invalid number of operands!" );
133
+ assert (isReg () && " Not a register operand!" );
134
+ // This is called from the tablegen matcher (MatchInstructionImpl)
135
+ // where it expects to output the actual register index, see RegOp above.
136
+ unsigned R = Reg.RegNo ;
137
+ if (Kind == Stack) {
138
+ // A stack register is represented as a large negative number.
139
+ // See WebAssemblyRegNumbering::runOnMachineFunction and
140
+ // getWARegStackId for why this | is needed.
141
+ R |= INT32_MIN;
142
+ }
143
+ Inst.addOperand (MCOperand::createReg (R));
99
144
}
100
145
101
146
void addImmOperands (MCInst &Inst, unsigned N) const {
@@ -115,6 +160,12 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
115
160
case Token:
116
161
OS << " Tok:" << Tok.Tok ;
117
162
break ;
163
+ case Local:
164
+ OS << " Loc:" << Reg.RegNo << " :" << static_cast <int >(Reg.Type );
165
+ break ;
166
+ case Stack:
167
+ OS << " Stk:" << Reg.RegNo << " :" << static_cast <int >(Reg.Type );
168
+ break ;
118
169
case Integer:
119
170
OS << " Int:" << Int.Val ;
120
171
break ;
@@ -131,6 +182,11 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
131
182
class WebAssemblyAsmParser final : public MCTargetAsmParser {
132
183
MCAsmParser &Parser;
133
184
MCAsmLexer &Lexer;
185
+ // These are for the current function being parsed:
186
+ // These are vectors since register assignments are so far non-sparse.
187
+ // Replace by map if necessary.
188
+ std::vector<MVT::SimpleValueType> LocalTypes;
189
+ std::vector<MVT::SimpleValueType> StackTypes;
134
190
MCSymbol *LastLabel;
135
191
136
192
public:
@@ -180,6 +236,68 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
180
236
.Default (MVT::INVALID_SIMPLE_VALUE_TYPE);
181
237
}
182
238
239
+ MVT::SimpleValueType &GetType (
240
+ std::vector<MVT::SimpleValueType> &Types, size_t i) {
241
+ Types.resize (std::max (i + 1 , Types.size ()), MVT::INVALID_SIMPLE_VALUE_TYPE);
242
+ return Types[i];
243
+ }
244
+
245
+ bool ParseReg (OperandVector &Operands, StringRef TypePrefix) {
246
+ if (Lexer.is (AsmToken::Integer)) {
247
+ auto &Local = Lexer.getTok ();
248
+ // This is a reference to a local, turn it into a virtual register.
249
+ auto LocalNo = static_cast <unsigned >(Local.getIntVal ());
250
+ Operands.push_back (make_unique<WebAssemblyOperand>(
251
+ WebAssemblyOperand::Local, Local.getLoc (),
252
+ Local.getEndLoc (),
253
+ WebAssemblyOperand::RegOp{LocalNo,
254
+ GetType (LocalTypes, LocalNo)}));
255
+ Parser.Lex ();
256
+ } else if (Lexer.is (AsmToken::Identifier)) {
257
+ auto &StackRegTok = Lexer.getTok ();
258
+ // These are push/pop/drop pseudo stack registers, which we turn
259
+ // into virtual registers also. The stackify pass will later turn them
260
+ // back into implicit stack references if possible.
261
+ auto StackReg = StackRegTok.getString ();
262
+ auto StackOp = StackReg.take_while ([](char c) { return isalpha (c); });
263
+ auto Reg = StackReg.drop_front (StackOp.size ());
264
+ unsigned long long ParsedRegNo = 0 ;
265
+ if (!Reg.empty () && getAsUnsignedInteger (Reg, 10 , ParsedRegNo))
266
+ return Error (" Cannot parse stack register index: " , StackRegTok);
267
+ unsigned RegNo = static_cast <unsigned >(ParsedRegNo);
268
+ if (StackOp == " push" ) {
269
+ // This defines a result, record register type.
270
+ auto RegType = ParseRegType (TypePrefix);
271
+ GetType (StackTypes, RegNo) = RegType;
272
+ Operands.push_back (make_unique<WebAssemblyOperand>(
273
+ WebAssemblyOperand::Stack,
274
+ StackRegTok.getLoc (),
275
+ StackRegTok.getEndLoc (),
276
+ WebAssemblyOperand::RegOp{RegNo, RegType}));
277
+ } else if (StackOp == " pop" ) {
278
+ // This uses a previously defined stack value.
279
+ auto RegType = GetType (StackTypes, RegNo);
280
+ Operands.push_back (make_unique<WebAssemblyOperand>(
281
+ WebAssemblyOperand::Stack,
282
+ StackRegTok.getLoc (),
283
+ StackRegTok.getEndLoc (),
284
+ WebAssemblyOperand::RegOp{RegNo, RegType}));
285
+ } else if (StackOp == " drop" ) {
286
+ // This operand will be dropped, since it is part of an instruction
287
+ // whose result is void.
288
+ } else {
289
+ return Error (" Unknown stack register prefix: " , StackRegTok);
290
+ }
291
+ Parser.Lex ();
292
+ } else {
293
+ return Error (
294
+ " Expected identifier/integer following $, instead got: " ,
295
+ Lexer.getTok ());
296
+ }
297
+ IsNext (AsmToken::Equal);
298
+ return false ;
299
+ }
300
+
183
301
void ParseSingleInteger (bool IsNegative, OperandVector &Operands) {
184
302
auto &Int = Lexer.getTok ();
185
303
int64_t Val = Int.getIntVal ();
@@ -192,26 +310,36 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
192
310
193
311
bool ParseOperandStartingWithInteger (bool IsNegative,
194
312
OperandVector &Operands,
195
- StringRef InstName ) {
313
+ StringRef InstType ) {
196
314
ParseSingleInteger (IsNegative, Operands);
197
- // FIXME: there is probably a cleaner way to do this.
198
- auto IsLoadStore = InstName.startswith (" load" ) ||
199
- InstName.startswith (" store" ) ||
200
- InstName.startswith (" atomic_load" ) ||
201
- InstName.startswith (" atomic_store" );
202
- if (IsLoadStore) {
203
- // Parse load/store operands of the form: offset align
204
- auto &Offset = Lexer.getTok ();
205
- if (Offset.is (AsmToken::Integer)) {
315
+ if (Lexer.is (AsmToken::LParen)) {
316
+ // Parse load/store operands of the form: offset($reg)align
317
+ auto &LParen = Lexer.getTok ();
318
+ Operands.push_back (
319
+ make_unique<WebAssemblyOperand>(WebAssemblyOperand::Token,
320
+ LParen.getLoc (),
321
+ LParen.getEndLoc (),
322
+ WebAssemblyOperand::TokOp{
323
+ LParen.getString ()}));
324
+ Parser.Lex ();
325
+ if (Expect (AsmToken::Dollar, " register" )) return true ;
326
+ if (ParseReg (Operands, InstType)) return true ;
327
+ auto &RParen = Lexer.getTok ();
328
+ Operands.push_back (
329
+ make_unique<WebAssemblyOperand>(WebAssemblyOperand::Token,
330
+ RParen.getLoc (),
331
+ RParen.getEndLoc (),
332
+ WebAssemblyOperand::TokOp{
333
+ RParen.getString ()}));
334
+ if (Expect (AsmToken::RParen, " )" )) return true ;
335
+ if (Lexer.is (AsmToken::Integer)) {
206
336
ParseSingleInteger (false , Operands);
207
337
} else {
208
338
// Alignment not specified.
209
339
// FIXME: correctly derive a default from the instruction.
210
- // We can't just call WebAssembly::GetDefaultP2Align since we don't have
211
- // an opcode until after the assembly matcher.
212
340
Operands.push_back (make_unique<WebAssemblyOperand>(
213
- WebAssemblyOperand::Integer, Offset .getLoc (),
214
- Offset .getEndLoc (), WebAssemblyOperand::IntOp{0 }));
341
+ WebAssemblyOperand::Integer, RParen .getLoc (),
342
+ RParen .getEndLoc (), WebAssemblyOperand::IntOp{0 }));
215
343
}
216
344
}
217
345
return false ;
@@ -232,6 +360,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
232
360
while (Lexer.isNot (AsmToken::EndOfStatement)) {
233
361
auto &Tok = Lexer.getTok ();
234
362
switch (Tok.getKind ()) {
363
+ case AsmToken::Dollar: {
364
+ Parser.Lex ();
365
+ if (ParseReg (Operands, NamePair.first )) return true ;
366
+ break ;
367
+ }
235
368
case AsmToken::Identifier: {
236
369
auto &Id = Lexer.getTok ();
237
370
const MCExpr *Val;
@@ -247,11 +380,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
247
380
Parser.Lex ();
248
381
if (Lexer.isNot (AsmToken::Integer))
249
382
return Error (" Expected integer instead got: " , Lexer.getTok ());
250
- if (ParseOperandStartingWithInteger (true , Operands, NamePair.second ))
383
+ if (ParseOperandStartingWithInteger (true , Operands, NamePair.first ))
251
384
return true ;
252
385
break ;
253
386
case AsmToken::Integer:
254
- if (ParseOperandStartingWithInteger (false , Operands, NamePair.second ))
387
+ if (ParseOperandStartingWithInteger (false , Operands, NamePair.first ))
255
388
return true ;
256
389
break ;
257
390
case AsmToken::Real: {
@@ -272,6 +405,35 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
272
405
}
273
406
}
274
407
Parser.Lex ();
408
+ // Call instructions are vararg, but the tablegen matcher doesn't seem to
409
+ // support that, so for now we strip these extra operands.
410
+ // This is problematic if these arguments are not simple $pop stack
411
+ // registers, since e.g. a local register would get lost, so we check for
412
+ // this. This can be the case when using -disable-wasm-explicit-locals
413
+ // which currently s2wasm requires.
414
+ // TODO: Instead, we can move this code to MatchAndEmitInstruction below and
415
+ // actually generate get_local instructions on the fly.
416
+ // Or even better, improve the matcher to support vararg?
417
+ auto IsIndirect = NamePair.second == " call_indirect" ;
418
+ if (IsIndirect || NamePair.second == " call" ) {
419
+ // Figure out number of fixed operands from the instruction.
420
+ size_t CallOperands = 1 ; // The name token.
421
+ if (!IsIndirect) CallOperands++; // The function index.
422
+ if (!NamePair.first .empty ()) CallOperands++; // The result register.
423
+ if (Operands.size () > CallOperands) {
424
+ // Ensure operands we drop are all $pop.
425
+ for (size_t I = CallOperands; I < Operands.size (); I++) {
426
+ auto Operand =
427
+ reinterpret_cast <WebAssemblyOperand *>(Operands[I].get ());
428
+ if (Operand->Kind != WebAssemblyOperand::Stack)
429
+ Parser.Error (NameLoc,
430
+ " Call instruction has non-stack arguments, if this code was "
431
+ " generated with -disable-wasm-explicit-locals please remove it" );
432
+ }
433
+ // Drop unneeded operands.
434
+ Operands.resize (CallOperands);
435
+ }
436
+ }
275
437
// Block instructions require a signature index, but these are missing in
276
438
// assembly, so we add a dummy one explicitly (since we have no control
277
439
// over signature tables here, we assume these will be regenerated when
@@ -281,6 +443,17 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
281
443
WebAssemblyOperand::Integer, NameLoc,
282
444
NameLoc, WebAssemblyOperand::IntOp{-1 }));
283
445
}
446
+ // These don't specify the type, which has to derived from the local index.
447
+ if (NamePair.second == " get_local" || NamePair.second == " tee_local" ) {
448
+ if (Operands.size () >= 3 && Operands[1 ]->isReg () &&
449
+ Operands[2 ]->isImm ()) {
450
+ auto Op1 = reinterpret_cast <WebAssemblyOperand *>(Operands[1 ].get ());
451
+ auto Op2 = reinterpret_cast <WebAssemblyOperand *>(Operands[2 ].get ());
452
+ auto Type = GetType (LocalTypes, static_cast <size_t >(Op2->Int .Val ));
453
+ Op1->Reg .Type = Type;
454
+ GetType (StackTypes, Op1->Reg .RegNo ) = Type;
455
+ }
456
+ }
284
457
return false ;
285
458
}
286
459
@@ -304,6 +477,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
304
477
IsNext (AsmToken::At) &&
305
478
Lexer.is (AsmToken::Identifier)))
306
479
return Error (" Expected label,@type declaration, got: " , Lexer.getTok ());
480
+ if (Lexer.getTok ().getString () == " function" ) {
481
+ // Track locals from start of function.
482
+ LocalTypes.clear ();
483
+ StackTypes.clear ();
484
+ }
307
485
Parser.Lex ();
308
486
// Out.EmitSymbolAttribute(??, MCSA_ELF_TypeFunction);
309
487
} else if (DirectiveID.getString () == " .param" ||
@@ -316,6 +494,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
316
494
while (Lexer.is (AsmToken::Identifier)) {
317
495
auto RegType = ParseRegType (Lexer.getTok ().getString ());
318
496
if (RegType == MVT::INVALID_SIMPLE_VALUE_TYPE) return true ;
497
+ LocalTypes.push_back (RegType);
319
498
if (DirectiveID.getString () == " .param" ) {
320
499
Params.push_back (RegType);
321
500
} else {
0 commit comments