Line data Source code
1 : //==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 : ///
10 : /// \file
11 : /// This file is part of the WebAssembly Assembler.
12 : ///
13 : /// It contains code to translate a parsed .s file into MCInsts.
14 : ///
15 : //===----------------------------------------------------------------------===//
16 :
17 : #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18 : #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
19 : #include "WebAssembly.h"
20 : #include "llvm/MC/MCContext.h"
21 : #include "llvm/MC/MCInst.h"
22 : #include "llvm/MC/MCInstrInfo.h"
23 : #include "llvm/MC/MCParser/MCParsedAsmOperand.h"
24 : #include "llvm/MC/MCParser/MCTargetAsmParser.h"
25 : #include "llvm/MC/MCStreamer.h"
26 : #include "llvm/MC/MCSubtargetInfo.h"
27 : #include "llvm/MC/MCSymbol.h"
28 : #include "llvm/Support/Endian.h"
29 : #include "llvm/Support/TargetRegistry.h"
30 :
31 : using namespace llvm;
32 :
33 : #define DEBUG_TYPE "wasm-asm-parser"
34 :
35 : namespace {
36 :
37 : /// WebAssemblyOperand - Instances of this class represent the operands in a
38 : /// parsed WASM machine instruction.
39 : struct WebAssemblyOperand : public MCParsedAsmOperand {
40 : enum KindTy { Token, Integer, Float, Symbol } Kind;
41 :
42 : SMLoc StartLoc, EndLoc;
43 :
44 : struct TokOp {
45 : StringRef Tok;
46 : };
47 :
48 : struct IntOp {
49 : int64_t Val;
50 : };
51 :
52 : struct FltOp {
53 : double Val;
54 : };
55 :
56 : struct SymOp {
57 : const MCExpr *Exp;
58 : };
59 :
60 : union {
61 : struct TokOp Tok;
62 : struct IntOp Int;
63 : struct FltOp Flt;
64 : struct SymOp Sym;
65 : };
66 :
67 : WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T)
68 0 : : Kind(K), StartLoc(Start), EndLoc(End), Tok(T) {}
69 : WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, IntOp I)
70 204 : : Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}
71 : WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, FltOp F)
72 14 : : Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {}
73 : WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp S)
74 6 : : Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {}
75 :
76 189 : bool isToken() const override { return Kind == Token; }
77 0 : bool isImm() const override {
78 189 : return Kind == Integer || Kind == Float || Kind == Symbol;
79 : }
80 0 : bool isMem() const override { return false; }
81 0 : bool isReg() const override { return false; }
82 :
83 0 : unsigned getReg() const override {
84 0 : llvm_unreachable("Assembly inspects a register operand");
85 : return 0;
86 : }
87 :
88 0 : StringRef getToken() const {
89 : assert(isToken());
90 0 : return Tok.Tok;
91 : }
92 :
93 0 : SMLoc getStartLoc() const override { return StartLoc; }
94 0 : SMLoc getEndLoc() const override { return EndLoc; }
95 :
96 0 : void addRegOperands(MCInst &, unsigned) const {
97 : // Required by the assembly matcher.
98 0 : llvm_unreachable("Assembly matcher creates register operands");
99 : }
100 :
101 0 : void addImmOperands(MCInst &Inst, unsigned N) const {
102 : assert(N == 1 && "Invalid number of operands!");
103 0 : if (Kind == Integer)
104 0 : Inst.addOperand(MCOperand::createImm(Int.Val));
105 0 : else if (Kind == Float)
106 0 : Inst.addOperand(MCOperand::createFPImm(Flt.Val));
107 0 : else if (Kind == Symbol)
108 0 : Inst.addOperand(MCOperand::createExpr(Sym.Exp));
109 : else
110 0 : llvm_unreachable("Should be immediate or symbol!");
111 0 : }
112 :
113 0 : void print(raw_ostream &OS) const override {
114 0 : switch (Kind) {
115 0 : case Token:
116 0 : OS << "Tok:" << Tok.Tok;
117 0 : break;
118 0 : case Integer:
119 0 : OS << "Int:" << Int.Val;
120 0 : break;
121 0 : case Float:
122 0 : OS << "Flt:" << Flt.Val;
123 0 : break;
124 0 : case Symbol:
125 0 : OS << "Sym:" << Sym.Exp;
126 0 : break;
127 : }
128 0 : }
129 : };
130 :
131 : class WebAssemblyAsmParser final : public MCTargetAsmParser {
132 : MCAsmParser &Parser;
133 : MCAsmLexer &Lexer;
134 : MCSymbol *LastLabel;
135 :
136 : public:
137 2 : WebAssemblyAsmParser(const MCSubtargetInfo &sti, MCAsmParser &Parser,
138 : const MCInstrInfo &mii, const MCTargetOptions &Options)
139 2 : : MCTargetAsmParser(Options, sti, mii), Parser(Parser),
140 2 : Lexer(Parser.getLexer()), LastLabel(nullptr) {
141 2 : setAvailableFeatures(ComputeAvailableFeatures(sti.getFeatureBits()));
142 2 : }
143 :
144 : #define GET_ASSEMBLER_HEADER
145 : #include "WebAssemblyGenAsmMatcher.inc"
146 :
147 : // TODO: This is required to be implemented, but appears unused.
148 0 : bool ParseRegister(unsigned & /*RegNo*/, SMLoc & /*StartLoc*/,
149 : SMLoc & /*EndLoc*/) override {
150 0 : llvm_unreachable("ParseRegister is not implemented.");
151 : }
152 :
153 0 : bool Error(const StringRef &msg, const AsmToken &tok) {
154 0 : return Parser.Error(tok.getLoc(), msg + tok.getString());
155 : }
156 :
157 0 : bool IsNext(AsmToken::TokenKind Kind) {
158 0 : auto ok = Lexer.is(Kind);
159 76 : if (ok)
160 74 : Parser.Lex();
161 0 : return ok;
162 : }
163 :
164 67 : bool Expect(AsmToken::TokenKind Kind, const char *KindName) {
165 134 : if (!IsNext(Kind))
166 0 : return Error(std::string("Expected ") + KindName + ", instead got: ",
167 0 : Lexer.getTok());
168 : return false;
169 : }
170 :
171 0 : MVT::SimpleValueType ParseRegType(const StringRef &RegType) {
172 : // Derive type from .param .local decls, or the instruction itself.
173 0 : return StringSwitch<MVT::SimpleValueType>(RegType)
174 0 : .Case("i32", MVT::i32)
175 0 : .Case("i64", MVT::i64)
176 0 : .Case("f32", MVT::f32)
177 0 : .Case("f64", MVT::f64)
178 0 : .Case("i8x16", MVT::v16i8)
179 0 : .Case("i16x8", MVT::v8i16)
180 0 : .Case("i32x4", MVT::v4i32)
181 0 : .Case("i64x2", MVT::v2i64)
182 0 : .Case("f32x4", MVT::v4f32)
183 0 : .Case("f64x2", MVT::v2f64)
184 : // arbitrarily chosen vector type to associate with "v128"
185 : // FIXME: should these be EVTs to avoid this arbitrary hack? Do we want
186 : // to accept more specific SIMD register types?
187 0 : .Case("v128", MVT::v16i8)
188 0 : .Default(MVT::INVALID_SIMPLE_VALUE_TYPE);
189 : }
190 :
191 97 : void ParseSingleInteger(bool IsNegative, OperandVector &Operands) {
192 97 : auto &Int = Lexer.getTok();
193 : int64_t Val = Int.getIntVal();
194 97 : if (IsNegative)
195 1 : Val = -Val;
196 97 : Operands.push_back(make_unique<WebAssemblyOperand>(
197 97 : WebAssemblyOperand::Integer, Int.getLoc(), Int.getEndLoc(),
198 : WebAssemblyOperand::IntOp{Val}));
199 97 : Parser.Lex();
200 97 : }
201 :
202 97 : bool ParseOperandStartingWithInteger(bool IsNegative, OperandVector &Operands,
203 : StringRef InstName) {
204 97 : ParseSingleInteger(IsNegative, Operands);
205 : // FIXME: there is probably a cleaner way to do this.
206 : auto IsLoadStore = InstName.startswith("load") ||
207 : InstName.startswith("store") ||
208 : InstName.startswith("atomic_load") ||
209 : InstName.startswith("atomic_store");
210 : if (IsLoadStore) {
211 : // Parse load/store operands of the form: offset align
212 3 : auto &Offset = Lexer.getTok();
213 3 : if (Offset.is(AsmToken::Integer)) {
214 0 : ParseSingleInteger(false, Operands);
215 : } else {
216 : // Alignment not specified.
217 : // FIXME: correctly derive a default from the instruction.
218 : // We can't just call WebAssembly::GetDefaultP2Align since we don't have
219 : // an opcode until after the assembly matcher.
220 3 : Operands.push_back(make_unique<WebAssemblyOperand>(
221 6 : WebAssemblyOperand::Integer, Offset.getLoc(), Offset.getEndLoc(),
222 : WebAssemblyOperand::IntOp{0}));
223 : }
224 : }
225 97 : return false;
226 : }
227 :
228 177 : bool ParseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,
229 : SMLoc NameLoc, OperandVector &Operands) override {
230 : // Note: Name does NOT point into the sourcecode, but to a local, so
231 : // use NameLoc instead.
232 177 : Name = StringRef(NameLoc.getPointer(), Name.size());
233 : // WebAssembly has instructions with / in them, which AsmLexer parses
234 : // as seperate tokens, so if we find such tokens immediately adjacent (no
235 : // whitespace), expand the name to include them:
236 : for (;;) {
237 186 : auto &Sep = Lexer.getTok();
238 186 : if (Sep.getLoc().getPointer() != Name.end() ||
239 140 : Sep.getKind() != AsmToken::Slash) break;
240 : // Extend name with /
241 9 : Name = StringRef(Name.begin(), Name.size() + Sep.getString().size());
242 9 : Parser.Lex();
243 : // We must now find another identifier, or error.
244 9 : auto &Id = Lexer.getTok();
245 18 : if (Id.getKind() != AsmToken::Identifier ||
246 9 : Id.getLoc().getPointer() != Name.end())
247 0 : return Error("Incomplete instruction name: ", Id);
248 9 : Name = StringRef(Name.begin(), Name.size() + Id.getString().size());
249 9 : Parser.Lex();
250 9 : }
251 : // Now construct the name as first operand.
252 354 : Operands.push_back(make_unique<WebAssemblyOperand>(
253 354 : WebAssemblyOperand::Token, NameLoc, SMLoc::getFromPointer(Name.end()),
254 354 : WebAssemblyOperand::TokOp{Name}));
255 177 : auto NamePair = Name.split('.');
256 : // If no '.', there is no type prefix.
257 335 : auto BaseName = NamePair.second.empty() ? NamePair.first : NamePair.second;
258 568 : while (Lexer.isNot(AsmToken::EndOfStatement)) {
259 : auto &Tok = Lexer.getTok();
260 107 : switch (Tok.getKind()) {
261 3 : case AsmToken::Identifier: {
262 : auto &Id = Lexer.getTok();
263 : const MCExpr *Val;
264 3 : SMLoc End;
265 3 : if (Parser.parsePrimaryExpr(Val, End))
266 0 : return Error("Cannot parse symbol: ", Lexer.getTok());
267 3 : Operands.push_back(make_unique<WebAssemblyOperand>(
268 3 : WebAssemblyOperand::Symbol, Id.getLoc(), Id.getEndLoc(),
269 3 : WebAssemblyOperand::SymOp{Val}));
270 3 : break;
271 : }
272 1 : case AsmToken::Minus:
273 1 : Parser.Lex();
274 2 : if (Lexer.isNot(AsmToken::Integer))
275 0 : return Error("Expected integer instead got: ", Lexer.getTok());
276 1 : if (ParseOperandStartingWithInteger(true, Operands, BaseName))
277 : return true;
278 : break;
279 96 : case AsmToken::Integer:
280 96 : if (ParseOperandStartingWithInteger(false, Operands, BaseName))
281 : return true;
282 : break;
283 7 : case AsmToken::Real: {
284 : double Val;
285 7 : if (Tok.getString().getAsDouble(Val, false))
286 0 : return Error("Cannot parse real: ", Tok);
287 7 : Operands.push_back(make_unique<WebAssemblyOperand>(
288 7 : WebAssemblyOperand::Float, Tok.getLoc(), Tok.getEndLoc(),
289 7 : WebAssemblyOperand::FltOp{Val}));
290 7 : Parser.Lex();
291 7 : break;
292 : }
293 : default:
294 0 : return Error("Unexpected token in operand: ", Tok);
295 : }
296 214 : if (Lexer.isNot(AsmToken::EndOfStatement)) {
297 63 : if (Expect(AsmToken::Comma, ","))
298 : return true;
299 : }
300 : }
301 177 : Parser.Lex();
302 : // Block instructions require a signature index, but these are missing in
303 : // assembly, so we add a dummy one explicitly (since we have no control
304 : // over signature tables here, we assume these will be regenerated when
305 : // the wasm module is generated).
306 : if (BaseName == "block" || BaseName == "loop") {
307 4 : Operands.push_back(make_unique<WebAssemblyOperand>(
308 : WebAssemblyOperand::Integer, NameLoc, NameLoc,
309 : WebAssemblyOperand::IntOp{-1}));
310 : }
311 : return false;
312 : }
313 :
314 3 : void onLabelParsed(MCSymbol *Symbol) override { LastLabel = Symbol; }
315 :
316 4 : bool ParseDirective(AsmToken DirectiveID) override {
317 : assert(DirectiveID.getKind() == AsmToken::Identifier);
318 : auto &Out = getStreamer();
319 : auto &TOut =
320 : reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());
321 : // TODO: we're just parsing the subset of directives we're interested in,
322 : // and ignoring ones we don't recognise. We should ideally verify
323 : // all directives here.
324 : if (DirectiveID.getString() == ".type") {
325 : // This could be the start of a function, check if followed by
326 : // "label,@function"
327 4 : if (!(IsNext(AsmToken::Identifier) && IsNext(AsmToken::Comma) &&
328 2 : IsNext(AsmToken::At) && Lexer.is(AsmToken::Identifier)))
329 0 : return Error("Expected label,@type declaration, got: ", Lexer.getTok());
330 1 : Parser.Lex();
331 : // Out.EmitSymbolAttribute(??, MCSA_ELF_TypeFunction);
332 : } else if (DirectiveID.getString() == ".param" ||
333 : DirectiveID.getString() == ".local") {
334 : // Track the number of locals, needed for correct virtual register
335 : // assignment elsewhere.
336 : // Also output a directive to the streamer.
337 : std::vector<MVT> Params;
338 : std::vector<MVT> Locals;
339 12 : while (Lexer.is(AsmToken::Identifier)) {
340 6 : auto RegType = ParseRegType(Lexer.getTok().getString());
341 6 : if (RegType == MVT::INVALID_SIMPLE_VALUE_TYPE)
342 : return true;
343 : if (DirectiveID.getString() == ".param") {
344 2 : Params.push_back(RegType);
345 : } else {
346 4 : Locals.push_back(RegType);
347 : }
348 6 : Parser.Lex();
349 10 : if (!IsNext(AsmToken::Comma))
350 : break;
351 : }
352 : assert(LastLabel);
353 4 : TOut.emitParam(LastLabel, Params);
354 4 : TOut.emitLocal(Locals);
355 : } else {
356 : // For now, ignore anydirective we don't recognize:
357 2 : while (Lexer.isNot(AsmToken::EndOfStatement))
358 0 : Parser.Lex();
359 : }
360 4 : return Expect(AsmToken::EndOfStatement, "EOL");
361 : }
362 :
363 177 : bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/,
364 : OperandVector &Operands, MCStreamer &Out,
365 : uint64_t &ErrorInfo,
366 : bool MatchingInlineAsm) override {
367 : MCInst Inst;
368 : unsigned MatchResult =
369 177 : MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm);
370 177 : switch (MatchResult) {
371 177 : case Match_Success: {
372 177 : Out.EmitInstruction(Inst, getSTI());
373 177 : return false;
374 : }
375 0 : case Match_MissingFeature:
376 0 : return Parser.Error(
377 : IDLoc, "instruction requires a WASM feature not currently enabled");
378 0 : case Match_MnemonicFail:
379 0 : return Parser.Error(IDLoc, "invalid instruction");
380 0 : case Match_NearMisses:
381 0 : return Parser.Error(IDLoc, "ambiguous instruction");
382 0 : case Match_InvalidTiedOperand:
383 : case Match_InvalidOperand: {
384 0 : SMLoc ErrorLoc = IDLoc;
385 0 : if (ErrorInfo != ~0ULL) {
386 0 : if (ErrorInfo >= Operands.size())
387 0 : return Parser.Error(IDLoc, "too few operands for instruction");
388 0 : ErrorLoc = Operands[ErrorInfo]->getStartLoc();
389 0 : if (ErrorLoc == SMLoc())
390 : ErrorLoc = IDLoc;
391 : }
392 0 : return Parser.Error(ErrorLoc, "invalid operand for instruction");
393 : }
394 : }
395 0 : llvm_unreachable("Implement any new match types added!");
396 : }
397 : };
398 : } // end anonymous namespace
399 :
400 : // Force static initialization.
401 75416 : extern "C" void LLVMInitializeWebAssemblyAsmParser() {
402 75416 : RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32());
403 75416 : RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64());
404 75416 : }
405 :
406 : #define GET_REGISTER_MATCHER
407 : #define GET_MATCHER_IMPLEMENTATION
408 : #include "WebAssemblyGenAsmMatcher.inc"
|