Skip to content

Commit a7be375

Browse files
committed
Revert "[WebAssembly] Added default stack-only instruction mode for MC."
This reverts commit 917a99b71ce21c975be7bfbf66f4040f965d9f3c. llvm-svn: 339630
1 parent 2997a30 commit a7be375

File tree

92 files changed

+426
-852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+426
-852
lines changed

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

Lines changed: 202 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,47 @@ using namespace llvm;
3434

3535
namespace {
3636

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+
3754
/// WebAssemblyOperand - Instances of this class represent the operands in a
3855
/// parsed WASM machine instruction.
3956
struct WebAssemblyOperand : public MCParsedAsmOperand {
40-
enum KindTy { Token, Integer, Float, Symbol } Kind;
57+
enum KindTy { Token, Local, Stack, Integer, Float, Symbol } Kind;
4158

4259
SMLoc StartLoc, EndLoc;
4360

4461
struct TokOp {
4562
StringRef Tok;
4663
};
4764

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+
4878
struct IntOp {
4979
int64_t Val;
5080
};
@@ -59,13 +89,16 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
5989

6090
union {
6191
struct TokOp Tok;
92+
struct RegOp Reg;
6293
struct IntOp Int;
6394
struct FltOp Flt;
6495
struct SymOp Sym;
6596
};
6697

6798
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T)
6899
: 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) {}
69102
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, IntOp I)
70103
: Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}
71104
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, FltOp F)
@@ -77,12 +110,14 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
77110
bool isImm() const override { return Kind == Integer ||
78111
Kind == Float ||
79112
Kind == Symbol; }
113+
bool isReg() const override { return Kind == Local || Kind == Stack; }
80114
bool isMem() const override { return false; }
81-
bool isReg() const override { return false; }
82115

83116
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);
86121
}
87122

88123
StringRef getToken() const {
@@ -93,9 +128,19 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
93128
SMLoc getStartLoc() const override { return StartLoc; }
94129
SMLoc getEndLoc() const override { return EndLoc; }
95130

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));
99144
}
100145

101146
void addImmOperands(MCInst &Inst, unsigned N) const {
@@ -115,6 +160,12 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
115160
case Token:
116161
OS << "Tok:" << Tok.Tok;
117162
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;
118169
case Integer:
119170
OS << "Int:" << Int.Val;
120171
break;
@@ -131,6 +182,11 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
131182
class WebAssemblyAsmParser final : public MCTargetAsmParser {
132183
MCAsmParser &Parser;
133184
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;
134190
MCSymbol *LastLabel;
135191

136192
public:
@@ -180,6 +236,68 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
180236
.Default(MVT::INVALID_SIMPLE_VALUE_TYPE);
181237
}
182238

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+
183301
void ParseSingleInteger(bool IsNegative, OperandVector &Operands) {
184302
auto &Int = Lexer.getTok();
185303
int64_t Val = Int.getIntVal();
@@ -192,26 +310,36 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
192310

193311
bool ParseOperandStartingWithInteger(bool IsNegative,
194312
OperandVector &Operands,
195-
StringRef InstName) {
313+
StringRef InstType) {
196314
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)) {
206336
ParseSingleInteger(false, Operands);
207337
} else {
208338
// Alignment not specified.
209339
// 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.
212340
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}));
215343
}
216344
}
217345
return false;
@@ -232,6 +360,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
232360
while (Lexer.isNot(AsmToken::EndOfStatement)) {
233361
auto &Tok = Lexer.getTok();
234362
switch (Tok.getKind()) {
363+
case AsmToken::Dollar: {
364+
Parser.Lex();
365+
if (ParseReg(Operands, NamePair.first)) return true;
366+
break;
367+
}
235368
case AsmToken::Identifier: {
236369
auto &Id = Lexer.getTok();
237370
const MCExpr *Val;
@@ -247,11 +380,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
247380
Parser.Lex();
248381
if (Lexer.isNot(AsmToken::Integer))
249382
return Error("Expected integer instead got: ", Lexer.getTok());
250-
if (ParseOperandStartingWithInteger(true, Operands, NamePair.second))
383+
if (ParseOperandStartingWithInteger(true, Operands, NamePair.first))
251384
return true;
252385
break;
253386
case AsmToken::Integer:
254-
if (ParseOperandStartingWithInteger(false, Operands, NamePair.second))
387+
if (ParseOperandStartingWithInteger(false, Operands, NamePair.first))
255388
return true;
256389
break;
257390
case AsmToken::Real: {
@@ -272,6 +405,35 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
272405
}
273406
}
274407
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+
}
275437
// Block instructions require a signature index, but these are missing in
276438
// assembly, so we add a dummy one explicitly (since we have no control
277439
// over signature tables here, we assume these will be regenerated when
@@ -281,6 +443,17 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
281443
WebAssemblyOperand::Integer, NameLoc,
282444
NameLoc, WebAssemblyOperand::IntOp{-1}));
283445
}
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+
}
284457
return false;
285458
}
286459

@@ -304,6 +477,11 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
304477
IsNext(AsmToken::At) &&
305478
Lexer.is(AsmToken::Identifier)))
306479
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+
}
307485
Parser.Lex();
308486
//Out.EmitSymbolAttribute(??, MCSA_ELF_TypeFunction);
309487
} else if (DirectiveID.getString() == ".param" ||
@@ -316,6 +494,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
316494
while (Lexer.is(AsmToken::Identifier)) {
317495
auto RegType = ParseRegType(Lexer.getTok().getString());
318496
if (RegType == MVT::INVALID_SIMPLE_VALUE_TYPE) return true;
497+
LocalTypes.push_back(RegType);
319498
if (DirectiveID.getString() == ".param") {
320499
Params.push_back(RegType);
321500
} else {

0 commit comments

Comments
 (0)