1//==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file is part of the WebAssembly Assembler.
11///
12/// It contains code to translate a parsed .s file into MCInsts.
13///
14//===----------------------------------------------------------------------===//
15
16#include "AsmParser/WebAssemblyAsmTypeCheck.h"
17#include "MCTargetDesc/WebAssemblyMCAsmInfo.h"
18#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"
20#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
21#include "TargetInfo/WebAssemblyTargetInfo.h"
22#include "llvm/MC/MCContext.h"
23#include "llvm/MC/MCExpr.h"
24#include "llvm/MC/MCInst.h"
25#include "llvm/MC/MCInstrInfo.h"
26#include "llvm/MC/MCParser/AsmLexer.h"
27#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
28#include "llvm/MC/MCParser/MCTargetAsmParser.h"
29#include "llvm/MC/MCRegisterInfo.h"
30#include "llvm/MC/MCSectionWasm.h"
31#include "llvm/MC/MCStreamer.h"
32#include "llvm/MC/MCSubtargetInfo.h"
33#include "llvm/MC/MCSymbol.h"
34#include "llvm/MC/MCSymbolWasm.h"
35#include "llvm/MC/TargetRegistry.h"
36#include "llvm/Support/Compiler.h"
37#include "llvm/Support/SourceMgr.h"
38
39using namespace llvm;
40
41#define DEBUG_TYPE "wasm-asm-parser"
42
43static const char *getSubtargetFeatureName(uint64_t Val);
44
45namespace {
46
47/// WebAssemblyOperand - Instances of this class represent the operands in a
48/// parsed Wasm machine instruction.
49struct WebAssemblyOperand : public MCParsedAsmOperand {
50 enum KindTy {
51 Token,
52 Integer,
53 Float,
54 Symbol,
55 BrList,
56 CatchList,
57 TypeList
58 } Kind;
59
60 SMLoc StartLoc, EndLoc;
61
62 struct TokOp {
63 StringRef Tok;
64 };
65
66 struct IntOp {
67 int64_t Val;
68 };
69
70 struct FltOp {
71 double Val;
72 };
73
74 struct SymOp {
75 const MCExpr *Exp;
76 };
77
78 struct BrLOp {
79 std::vector<unsigned> List;
80 };
81
82 struct CaLOpElem {
83 uint8_t Opcode;
84 const MCExpr *Tag;
85 unsigned Dest;
86 };
87
88 struct CaLOp {
89 std::vector<CaLOpElem> List;
90 };
91
92 struct TyLOp {
93 std::vector<uint8_t> List;
94 };
95
96 union {
97 struct TokOp Tok;
98 struct IntOp Int;
99 struct FltOp Flt;
100 struct SymOp Sym;
101 struct BrLOp BrL;
102 struct CaLOp CaL;
103 struct TyLOp TyL;
104 };
105
106 WebAssemblyOperand(SMLoc Start, SMLoc End, TokOp T)
107 : Kind(Token), StartLoc(Start), EndLoc(End), Tok(T) {}
108 WebAssemblyOperand(SMLoc Start, SMLoc End, IntOp I)
109 : Kind(Integer), StartLoc(Start), EndLoc(End), Int(I) {}
110 WebAssemblyOperand(SMLoc Start, SMLoc End, FltOp F)
111 : Kind(Float), StartLoc(Start), EndLoc(End), Flt(F) {}
112 WebAssemblyOperand(SMLoc Start, SMLoc End, SymOp S)
113 : Kind(Symbol), StartLoc(Start), EndLoc(End), Sym(S) {}
114 WebAssemblyOperand(SMLoc Start, SMLoc End, BrLOp B)
115 : Kind(BrList), StartLoc(Start), EndLoc(End), BrL(B) {}
116 WebAssemblyOperand(SMLoc Start, SMLoc End, CaLOp C)
117 : Kind(CatchList), StartLoc(Start), EndLoc(End), CaL(C) {}
118 WebAssemblyOperand(SMLoc Start, SMLoc End, TyLOp T)
119 : Kind(TypeList), StartLoc(Start), EndLoc(End), TyL(T) {}
120
121 ~WebAssemblyOperand() override {
122 if (isBrList())
123 BrL.~BrLOp();
124 if (isCatchList())
125 CaL.~CaLOp();
126 if (isTypeList())
127 TyL.~TyLOp();
128 }
129
130 bool isToken() const override { return Kind == Token; }
131 bool isImm() const override { return Kind == Integer || Kind == Symbol; }
132 bool isFPImm() const { return Kind == Float; }
133 bool isMem() const override { return false; }
134 bool isReg() const override { return false; }
135 bool isBrList() const { return Kind == BrList; }
136 bool isCatchList() const { return Kind == CatchList; }
137 bool isTypeList() const { return Kind == TypeList; }
138
139 MCRegister getReg() const override {
140 llvm_unreachable("Assembly inspects a register operand");
141 return 0;
142 }
143
144 StringRef getToken() const {
145 assert(isToken());
146 return Tok.Tok;
147 }
148
149 SMLoc getStartLoc() const override { return StartLoc; }
150 SMLoc getEndLoc() const override { return EndLoc; }
151
152 void addRegOperands(MCInst &, unsigned) const {
153 // Required by the assembly matcher.
154 llvm_unreachable("Assembly matcher creates register operands");
155 }
156
157 void addImmOperands(MCInst &Inst, unsigned N) const {
158 assert(N == 1 && "Invalid number of operands!");
159 if (Kind == Integer)
160 Inst.addOperand(Op: MCOperand::createImm(Val: Int.Val));
161 else if (Kind == Symbol)
162 Inst.addOperand(Op: MCOperand::createExpr(Val: Sym.Exp));
163 else
164 llvm_unreachable("Should be integer immediate or symbol!");
165 }
166
167 void addFPImmf32Operands(MCInst &Inst, unsigned N) const {
168 assert(N == 1 && "Invalid number of operands!");
169 if (Kind == Float)
170 Inst.addOperand(
171 Op: MCOperand::createSFPImm(Val: bit_cast<uint32_t>(from: float(Flt.Val))));
172 else
173 llvm_unreachable("Should be float immediate!");
174 }
175
176 void addFPImmf64Operands(MCInst &Inst, unsigned N) const {
177 assert(N == 1 && "Invalid number of operands!");
178 if (Kind == Float)
179 Inst.addOperand(Op: MCOperand::createDFPImm(Val: bit_cast<uint64_t>(from: Flt.Val)));
180 else
181 llvm_unreachable("Should be float immediate!");
182 }
183
184 void addBrListOperands(MCInst &Inst, unsigned N) const {
185 assert(N == 1 && isBrList() && "Invalid BrList!");
186 for (auto Br : BrL.List)
187 Inst.addOperand(Op: MCOperand::createImm(Val: Br));
188 }
189
190 void addCatchListOperands(MCInst &Inst, unsigned N) const {
191 assert(N == 1 && isCatchList() && "Invalid CatchList!");
192 Inst.addOperand(Op: MCOperand::createImm(Val: CaL.List.size()));
193 for (auto Ca : CaL.List) {
194 Inst.addOperand(Op: MCOperand::createImm(Val: Ca.Opcode));
195 if (Ca.Opcode == wasm::WASM_OPCODE_CATCH ||
196 Ca.Opcode == wasm::WASM_OPCODE_CATCH_REF)
197 Inst.addOperand(Op: MCOperand::createExpr(Val: Ca.Tag));
198 Inst.addOperand(Op: MCOperand::createImm(Val: Ca.Dest));
199 }
200 }
201
202 void addTypeListOperands(MCInst &Inst, unsigned N) const {
203 assert(N == 1 && isTypeList() && "Invalid TypeList!");
204 Inst.addOperand(Op: MCOperand::createImm(Val: TyL.List.size()));
205 for (auto Ty : TyL.List)
206 Inst.addOperand(Op: MCOperand::createImm(Val: Ty));
207 }
208
209 void print(raw_ostream &OS, const MCAsmInfo &MAI) const override {
210 switch (Kind) {
211 case Token:
212 OS << "Tok:" << Tok.Tok;
213 break;
214 case Integer:
215 OS << "Int:" << Int.Val;
216 break;
217 case Float:
218 OS << "Flt:" << Flt.Val;
219 break;
220 case Symbol:
221 OS << "Sym:" << Sym.Exp;
222 break;
223 case BrList:
224 OS << "BrList:" << BrL.List.size();
225 break;
226 case CatchList:
227 OS << "CaList:" << CaL.List.size();
228 break;
229 case TypeList:
230 OS << "TyList:" << TyL.List.size();
231 break;
232 }
233 }
234};
235
236// Perhaps this should go somewhere common.
237static wasm::WasmLimits defaultLimits() {
238 return {.Flags: wasm::WASM_LIMITS_FLAG_NONE, .Minimum: 0, .Maximum: 0, .PageSize: 0};
239}
240
241static MCSymbolWasm *getOrCreateFunctionTableSymbol(MCContext &Ctx,
242 const StringRef &Name,
243 bool Is64) {
244 auto *Sym = static_cast<MCSymbolWasm *>(Ctx.lookupSymbol(Name));
245 if (Sym) {
246 if (!Sym->isFunctionTable())
247 Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table");
248 } else {
249 Sym = static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name));
250 Sym->setFunctionTable(Is64);
251 // The default function table is synthesized by the linker.
252 }
253 return Sym;
254}
255
256class WebAssemblyAsmParser final : public MCTargetAsmParser {
257 MCAsmParser &Parser;
258 AsmLexer &Lexer;
259
260 // Order of labels, directives and instructions in a .s file have no
261 // syntactical enforcement. This class is a callback from the actual parser,
262 // and yet we have to be feeding data to the streamer in a very particular
263 // order to ensure a correct binary encoding that matches the regular backend
264 // (the streamer does not enforce this). This "state machine" enum helps
265 // guarantee that correct order.
266 enum ParserState {
267 FileStart,
268 FunctionLabel,
269 FunctionStart,
270 FunctionLocals,
271 Instructions,
272 EndFunction,
273 DataSection,
274 } CurrentState = FileStart;
275
276 // For ensuring blocks are properly nested.
277 enum NestingType {
278 Function,
279 Block,
280 Loop,
281 Try,
282 CatchAll,
283 TryTable,
284 If,
285 Else,
286 Undefined,
287 };
288 struct Nested {
289 NestingType NT;
290 wasm::WasmSignature Sig;
291 };
292 std::vector<Nested> NestingStack;
293
294 MCSymbolWasm *DefaultFunctionTable = nullptr;
295 MCSymbol *LastFunctionLabel = nullptr;
296
297 bool Is64;
298
299 WebAssemblyAsmTypeCheck TC;
300 // Don't type check if -no-type-check was set.
301 bool SkipTypeCheck;
302
303public:
304 WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
305 const MCInstrInfo &MII)
306 : MCTargetAsmParser(STI, MII), Parser(Parser), Lexer(Parser.getLexer()),
307 Is64(STI.getTargetTriple().isArch64Bit()), TC(Parser, MII, Is64),
308 SkipTypeCheck(Parser.getContext().getTargetOptions().MCNoTypeCheck) {
309 setAvailableFeatures(ComputeAvailableFeatures(FB: STI.getFeatureBits()));
310 // Don't type check if this is inline asm, since that is a naked sequence of
311 // instructions without a function/locals decl.
312 auto &SM = Parser.getSourceManager();
313 auto BufferName =
314 SM.getBufferInfo(i: SM.getMainFileID()).Buffer->getBufferIdentifier();
315 if (BufferName == "<inline asm>")
316 SkipTypeCheck = true;
317 }
318
319 void Initialize(MCAsmParser &Parser) override {
320 MCAsmParserExtension::Initialize(Parser);
321
322 DefaultFunctionTable = getOrCreateFunctionTableSymbol(
323 Ctx&: getContext(), Name: "__indirect_function_table", Is64);
324 if (!STI->checkFeatures(FS: "+call-indirect-overlong") &&
325 !STI->checkFeatures(FS: "+reference-types"))
326 DefaultFunctionTable->setOmitFromLinkingSection();
327 }
328
329#define GET_ASSEMBLER_HEADER
330#include "WebAssemblyGenAsmMatcher.inc"
331
332 // TODO: This is required to be implemented, but appears unused.
333 bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override {
334 llvm_unreachable("parseRegister is not implemented.");
335 }
336 ParseStatus tryParseRegister(MCRegister &Reg, SMLoc &StartLoc,
337 SMLoc &EndLoc) override {
338 llvm_unreachable("tryParseRegister is not implemented.");
339 }
340
341 bool error(const Twine &Msg, const AsmToken &Tok) {
342 return Parser.Error(L: Tok.getLoc(), Msg: Msg + Tok.getString());
343 }
344
345 bool error(const Twine &Msg, SMLoc Loc = SMLoc()) {
346 return Parser.Error(L: Loc.isValid() ? Loc : Lexer.getTok().getLoc(), Msg);
347 }
348
349 std::pair<StringRef, StringRef> nestingString(NestingType NT) {
350 switch (NT) {
351 case Function:
352 return {"function", "end_function"};
353 case Block:
354 return {"block", "end_block"};
355 case Loop:
356 return {"loop", "end_loop"};
357 case Try:
358 return {"try", "end_try/delegate"};
359 case CatchAll:
360 return {"catch_all", "end_try"};
361 case TryTable:
362 return {"try_table", "end_try_table"};
363 case If:
364 return {"if", "end_if"};
365 case Else:
366 return {"else", "end_if"};
367 default:
368 llvm_unreachable("unknown NestingType");
369 }
370 }
371
372 void push(NestingType NT, wasm::WasmSignature Sig = wasm::WasmSignature()) {
373 NestingStack.push_back(x: {.NT: NT, .Sig: Sig});
374 }
375
376 bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {
377 if (NestingStack.empty())
378 return error(Msg: Twine("End of block construct with no start: ") + Ins);
379 auto Top = NestingStack.back();
380 if (Top.NT != NT1 && Top.NT != NT2)
381 return error(Msg: Twine("Block construct type mismatch, expected: ") +
382 nestingString(NT: Top.NT).second + ", instead got: " + Ins);
383 TC.setLastSig(Top.Sig);
384 NestingStack.pop_back();
385 return false;
386 }
387
388 // Pop a NestingType and push a new NestingType with the same signature. Used
389 // for if-else and try-catch(_all).
390 bool popAndPushWithSameSignature(StringRef Ins, NestingType PopNT,
391 NestingType PushNT) {
392 if (NestingStack.empty())
393 return error(Msg: Twine("End of block construct with no start: ") + Ins);
394 auto Sig = NestingStack.back().Sig;
395 if (pop(Ins, NT1: PopNT))
396 return true;
397 push(NT: PushNT, Sig);
398 return false;
399 }
400
401 bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) {
402 auto Err = !NestingStack.empty();
403 while (!NestingStack.empty()) {
404 error(Msg: Twine("Unmatched block construct(s) at function end: ") +
405 nestingString(NT: NestingStack.back().NT).first,
406 Loc);
407 NestingStack.pop_back();
408 }
409 return Err;
410 }
411
412 bool isNext(AsmToken::TokenKind Kind) {
413 auto Ok = Lexer.is(K: Kind);
414 if (Ok)
415 Parser.Lex();
416 return Ok;
417 }
418
419 bool expect(AsmToken::TokenKind Kind, const char *KindName) {
420 if (!isNext(Kind))
421 return error(Msg: std::string("Expected ") + KindName + ", instead got: ",
422 Tok: Lexer.getTok());
423 return false;
424 }
425
426 StringRef expectIdent() {
427 if (!Lexer.is(K: AsmToken::Identifier)) {
428 error(Msg: "Expected identifier, got: ", Tok: Lexer.getTok());
429 return StringRef();
430 }
431 auto Name = Lexer.getTok().getString();
432 Parser.Lex();
433 return Name;
434 }
435
436 StringRef expectStringOrIdent() {
437 if (Lexer.is(K: AsmToken::String)) {
438 auto Str = Lexer.getTok().getStringContents();
439 Parser.Lex();
440 return Str;
441 }
442 if (Lexer.is(K: AsmToken::Identifier)) {
443 auto Name = Lexer.getTok().getString();
444 Parser.Lex();
445 return Name;
446 }
447 error(Msg: "Expected string or identifier, got: ", Tok: Lexer.getTok());
448 return StringRef();
449 }
450
451 bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
452 while (Lexer.is(K: AsmToken::Identifier)) {
453 auto Type = WebAssembly::parseType(Type: Lexer.getTok().getString());
454 if (!Type)
455 return error(Msg: "unknown type: ", Tok: Lexer.getTok());
456 Types.push_back(Elt: *Type);
457 Parser.Lex();
458 if (!isNext(Kind: AsmToken::Comma))
459 break;
460 }
461 return false;
462 }
463
464 void parseSingleInteger(bool IsNegative, OperandVector &Operands) {
465 auto &Int = Lexer.getTok();
466 int64_t Val = Int.getIntVal();
467 if (IsNegative)
468 Val = -Val;
469 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
470 args: Int.getLoc(), args: Int.getEndLoc(), args: WebAssemblyOperand::IntOp{.Val: Val}));
471 Parser.Lex();
472 }
473
474 bool parseSingleFloat(bool IsNegative, OperandVector &Operands) {
475 auto &Flt = Lexer.getTok();
476 double Val;
477 if (Flt.getString().getAsDouble(Result&: Val, AllowInexact: false))
478 return error(Msg: "Cannot parse real: ", Tok: Flt);
479 if (IsNegative)
480 Val = -Val;
481 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
482 args: Flt.getLoc(), args: Flt.getEndLoc(), args: WebAssemblyOperand::FltOp{.Val: Val}));
483 Parser.Lex();
484 return false;
485 }
486
487 bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {
488 if (Lexer.isNot(K: AsmToken::Identifier))
489 return true;
490 auto &Flt = Lexer.getTok();
491 auto S = Flt.getString();
492 double Val;
493 if (S.compare_insensitive(RHS: "infinity") == 0) {
494 Val = std::numeric_limits<double>::infinity();
495 } else if (S.compare_insensitive(RHS: "nan") == 0) {
496 Val = std::numeric_limits<double>::quiet_NaN();
497 } else {
498 return true;
499 }
500 if (IsNegative)
501 Val = -Val;
502 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
503 args: Flt.getLoc(), args: Flt.getEndLoc(), args: WebAssemblyOperand::FltOp{.Val: Val}));
504 Parser.Lex();
505 return false;
506 }
507
508 bool addMemOrderOrDefault(OperandVector &Operands) {
509 auto &Tok = Lexer.getTok();
510 int64_t Order = wasm::WASM_MEM_ORDER_SEQ_CST;
511 if (Tok.is(K: AsmToken::Identifier)) {
512 StringRef S = Tok.getString();
513 Order = StringSwitch<int64_t>(S)
514 .Case(S: "acqrel", Value: wasm::WASM_MEM_ORDER_ACQ_REL)
515 .Case(S: "seqcst", Value: wasm::WASM_MEM_ORDER_SEQ_CST)
516 .Default(Value: -1);
517 if (Order != -1) {
518 if (!STI->checkFeatures(FS: "+relaxed-atomics"))
519 return error(Msg: "memory ordering requires relaxed-atomics feature: ",
520 Tok);
521 Parser.Lex();
522 } else {
523 Order = wasm::WASM_MEM_ORDER_SEQ_CST;
524 }
525 }
526 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
527 args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::IntOp{.Val: Order}));
528 return false;
529 }
530
531 bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) {
532 // FIXME: there is probably a cleaner way to do this.
533 auto IsLoadStore = InstName.contains(Other: ".load") ||
534 InstName.contains(Other: ".store") ||
535 InstName.contains(Other: "prefetch");
536 auto IsAtomic = InstName.contains(Other: "atomic.");
537 if (IsLoadStore || IsAtomic) {
538 // Parse load/store operands of the form: offset:p2align=align
539 if (IsLoadStore && isNext(Kind: AsmToken::Colon)) {
540 auto Id = expectIdent();
541 if (Id != "p2align")
542 return error(Msg: "Expected p2align, instead got: " + Id);
543 if (expect(Kind: AsmToken::Equal, KindName: "="))
544 return true;
545 if (!Lexer.is(K: AsmToken::Integer))
546 return error(Msg: "Expected integer constant");
547 parseSingleInteger(IsNegative: false, Operands);
548 } else {
549 // v128.{load,store}{8,16,32,64}_lane has both a memarg and a lane
550 // index. We need to avoid parsing an extra alignment operand for the
551 // lane index.
552 auto IsLoadStoreLane = InstName.contains(Other: "_lane");
553 if (IsLoadStoreLane && Operands.size() == 4)
554 return false;
555 // Alignment not specified (or atomics, must use default alignment).
556 // We can't just call WebAssembly::GetDefaultP2Align since we don't have
557 // an opcode until after the assembly matcher, so set a default to fix
558 // up later.
559 auto Tok = Lexer.getTok();
560 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
561 args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::IntOp{.Val: -1}));
562 }
563 }
564 return false;
565 }
566
567 void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
568 WebAssembly::BlockType BT) {
569 if (BT == WebAssembly::BlockType::Void) {
570 TC.setLastSig(wasm::WasmSignature{});
571 } else {
572 wasm::WasmSignature Sig({static_cast<wasm::ValType>(BT)}, {});
573 TC.setLastSig(Sig);
574 NestingStack.back().Sig = Sig;
575 }
576 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
577 args&: NameLoc, args&: NameLoc, args: WebAssemblyOperand::IntOp{.Val: static_cast<int64_t>(BT)}));
578 }
579
580 bool parseLimits(wasm::WasmLimits *Limits) {
581 auto Tok = Lexer.getTok();
582 if (!Tok.is(K: AsmToken::Integer))
583 return error(Msg: "Expected integer constant, instead got: ", Tok);
584 int64_t Val = Tok.getIntVal();
585 assert(Val >= 0);
586 Limits->Minimum = Val;
587 Parser.Lex();
588
589 if (isNext(Kind: AsmToken::Comma)) {
590 Limits->Flags |= wasm::WASM_LIMITS_FLAG_HAS_MAX;
591 auto Tok = Lexer.getTok();
592 if (!Tok.is(K: AsmToken::Integer))
593 return error(Msg: "Expected integer constant, instead got: ", Tok);
594 int64_t Val = Tok.getIntVal();
595 assert(Val >= 0);
596 Limits->Maximum = Val;
597 Parser.Lex();
598 }
599 return false;
600 }
601
602 bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) {
603 if (STI->checkFeatures(FS: "+call-indirect-overlong") ||
604 STI->checkFeatures(FS: "+reference-types")) {
605 // If the call-indirect-overlong feature is enabled, or implied by the
606 // reference-types feature, there is an explicit table operand. To allow
607 // the same assembly to be compiled with or without
608 // call-indirect-overlong, we allow the operand to be omitted, in which
609 // case we default to __indirect_function_table.
610 auto &Tok = Lexer.getTok();
611 if (Tok.is(K: AsmToken::Identifier)) {
612 auto *Sym =
613 getOrCreateFunctionTableSymbol(Ctx&: getContext(), Name: Tok.getString(), Is64);
614 const auto *Val = MCSymbolRefExpr::create(Symbol: Sym, Ctx&: getContext());
615 *Op = std::make_unique<WebAssemblyOperand>(
616 args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::SymOp{.Exp: Val});
617 Parser.Lex();
618 return expect(Kind: AsmToken::Comma, KindName: ",");
619 }
620 const auto *Val =
621 MCSymbolRefExpr::create(Symbol: DefaultFunctionTable, Ctx&: getContext());
622 *Op = std::make_unique<WebAssemblyOperand>(
623 args: SMLoc(), args: SMLoc(), args: WebAssemblyOperand::SymOp{.Exp: Val});
624 return false;
625 }
626 // For the MVP there is at most one table whose number is 0, but we can't
627 // write a table symbol or issue relocations. Instead we just ensure the
628 // table is live and write a zero.
629 getStreamer().emitSymbolAttribute(Symbol: DefaultFunctionTable, Attribute: MCSA_NoDeadStrip);
630 *Op = std::make_unique<WebAssemblyOperand>(args: SMLoc(), args: SMLoc(),
631 args: WebAssemblyOperand::IntOp{.Val: 0});
632 return false;
633 }
634
635 bool parseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,
636 SMLoc NameLoc, OperandVector &Operands) override {
637 // Note: Name does NOT point into the sourcecode, but to a local, so
638 // use NameLoc instead.
639 Name = StringRef(NameLoc.getPointer(), Name.size());
640
641 // WebAssembly has instructions with / in them, which AsmLexer parses
642 // as separate tokens, so if we find such tokens immediately adjacent (no
643 // whitespace), expand the name to include them:
644 for (;;) {
645 auto &Sep = Lexer.getTok();
646 if (Sep.getLoc().getPointer() != Name.end() ||
647 Sep.getKind() != AsmToken::Slash)
648 break;
649 // Extend name with /
650 Name = StringRef(Name.begin(), Name.size() + Sep.getString().size());
651 Parser.Lex();
652 // We must now find another identifier, or error.
653 auto &Id = Lexer.getTok();
654 if (Id.getKind() != AsmToken::Identifier ||
655 Id.getLoc().getPointer() != Name.end())
656 return error(Msg: "Incomplete instruction name: ", Tok: Id);
657 Name = StringRef(Name.begin(), Name.size() + Id.getString().size());
658 Parser.Lex();
659 }
660
661 // Now construct the name as first operand.
662 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
663 args&: NameLoc, args: SMLoc::getFromPointer(Ptr: Name.end()),
664 args: WebAssemblyOperand::TokOp{.Tok: Name}));
665
666 // If this instruction is part of a control flow structure, ensure
667 // proper nesting.
668 bool ExpectBlockType = false;
669 bool ExpectFuncType = false;
670 bool ExpectCatchList = false;
671 std::unique_ptr<WebAssemblyOperand> FunctionTable;
672 if (Name == "block") {
673 push(NT: Block);
674 ExpectBlockType = true;
675 } else if (Name == "loop") {
676 push(NT: Loop);
677 ExpectBlockType = true;
678 } else if (Name == "try") {
679 push(NT: Try);
680 ExpectBlockType = true;
681 } else if (Name == "if") {
682 push(NT: If);
683 ExpectBlockType = true;
684 } else if (Name == "else") {
685 if (popAndPushWithSameSignature(Ins: Name, PopNT: If, PushNT: Else))
686 return true;
687 } else if (Name == "catch") {
688 if (popAndPushWithSameSignature(Ins: Name, PopNT: Try, PushNT: Try))
689 return true;
690 } else if (Name == "catch_all") {
691 if (popAndPushWithSameSignature(Ins: Name, PopNT: Try, PushNT: CatchAll))
692 return true;
693 } else if (Name == "try_table") {
694 push(NT: TryTable);
695 ExpectBlockType = true;
696 ExpectCatchList = true;
697 } else if (Name == "end_if") {
698 if (pop(Ins: Name, NT1: If, NT2: Else))
699 return true;
700 } else if (Name == "end_try") {
701 if (pop(Ins: Name, NT1: Try, NT2: CatchAll))
702 return true;
703 } else if (Name == "end_try_table") {
704 if (pop(Ins: Name, NT1: TryTable))
705 return true;
706 } else if (Name == "delegate") {
707 if (pop(Ins: Name, NT1: Try))
708 return true;
709 } else if (Name == "end_loop") {
710 if (pop(Ins: Name, NT1: Loop))
711 return true;
712 } else if (Name == "end_block") {
713 if (pop(Ins: Name, NT1: Block))
714 return true;
715 } else if (Name == "end_function") {
716 ensureLocals(Out&: getStreamer());
717 CurrentState = EndFunction;
718 if (pop(Ins: Name, NT1: Function) || ensureEmptyNestingStack())
719 return true;
720 } else if (Name == "call_indirect" || Name == "return_call_indirect") {
721 // These instructions have differing operand orders in the text format vs
722 // the binary formats. The MC instructions follow the binary format, so
723 // here we stash away the operand and append it later.
724 if (parseFunctionTableOperand(Op: &FunctionTable))
725 return true;
726 ExpectFuncType = true;
727 } else if (Name == "call_ref" || Name == "return_call_ref") {
728 // The typed function references forms take a function signature as
729 // their sole explicit operand (the funcref is popped from the stack).
730 ExpectFuncType = true;
731 } else if (Name == "ref.test") {
732 // When we get support for wasm-gc types, this should become
733 // ExpectRefType.
734 ExpectFuncType = true;
735 } else if (Name == "ref.cast") {
736 // When we get support for wasm-gc types, this should become
737 // ExpectRefType.
738 ExpectFuncType = true;
739 } else if (Name == "select") {
740 // The typed select instruction takes a vec of valtypes as its sole
741 // operand (select t*). Parse the list of value-type identifiers here
742 // and push a TypeList operand.
743 auto Op = std::make_unique<WebAssemblyOperand>(
744 args: Lexer.getLoc(), args: Lexer.getLoc(), args: WebAssemblyOperand::TyLOp{});
745 while (Lexer.is(K: AsmToken::Identifier)) {
746 auto &Id = Lexer.getTok();
747 auto Ty = WebAssembly::parseType(Type: Id.getString());
748 if (!Ty)
749 return error(Msg: "unknown value type in select operand list: ", Tok: Id);
750 Op->TyL.List.push_back(x: static_cast<uint8_t>(*Ty));
751 Op->EndLoc = Id.getEndLoc();
752 Parser.Lex();
753 }
754 Operands.push_back(Elt: std::move(Op));
755 }
756
757 if (Name.contains(Other: "atomic.")) {
758 if (addMemOrderOrDefault(Operands))
759 return true;
760 }
761
762 // Returns true if the next tokens are a catch clause
763 auto PeekCatchList = [&]() {
764 if (Lexer.isNot(K: AsmToken::LParen))
765 return false;
766 AsmToken NextTok = Lexer.peekTok();
767 return NextTok.getKind() == AsmToken::Identifier &&
768 NextTok.getIdentifier().starts_with(Prefix: "catch");
769 };
770
771 // Parse a multivalue block type
772 if (ExpectFuncType ||
773 (Lexer.is(K: AsmToken::LParen) && ExpectBlockType && !PeekCatchList())) {
774 // This has a special TYPEINDEX operand which in text we
775 // represent as a signature, such that we can re-build this signature,
776 // attach it to an anonymous symbol, which is what WasmObjectWriter
777 // expects to be able to recreate the actual unique-ified type indices.
778 auto &Ctx = getContext();
779 auto Loc = Parser.getTok();
780 auto *Signature = Ctx.createWasmSignature();
781 if (parseSignature(Signature))
782 return true;
783 // Got signature as block type, don't need more
784 TC.setLastSig(*Signature);
785 if (ExpectBlockType)
786 NestingStack.back().Sig = *Signature;
787 ExpectBlockType = false;
788 // The "true" here will cause this to be a nameless symbol.
789 MCSymbol *Sym = Ctx.createTempSymbol(Name: "typeindex", AlwaysAddSuffix: true);
790 auto *WasmSym = static_cast<MCSymbolWasm *>(Sym);
791 WasmSym->setSignature(Signature);
792 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
793 const MCExpr *Expr =
794 MCSymbolRefExpr::create(Symbol: WasmSym, specifier: WebAssembly::S_TYPEINDEX, Ctx);
795 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
796 args: Loc.getLoc(), args: Loc.getEndLoc(), args: WebAssemblyOperand::SymOp{.Exp: Expr}));
797 }
798
799 // If we are expecting a catch clause list, try to parse it here.
800 //
801 // If there is a multivalue block return type before this catch list, it
802 // should have been parsed above. If there is no return type before
803 // encountering this catch list, this means the type is void.
804 // The case when there is a single block return value and then a catch list
805 // will be handled below in the 'while' loop.
806 if (ExpectCatchList && PeekCatchList()) {
807 if (ExpectBlockType) {
808 ExpectBlockType = false;
809 addBlockTypeOperand(Operands, NameLoc, BT: WebAssembly::BlockType::Void);
810 }
811 if (parseCatchList(Operands))
812 return true;
813 ExpectCatchList = false;
814 }
815
816 while (Lexer.isNot(K: AsmToken::EndOfStatement)) {
817 auto &Tok = Lexer.getTok();
818 switch (Tok.getKind()) {
819 case AsmToken::Identifier: {
820 if (!parseSpecialFloatMaybe(IsNegative: false, Operands))
821 break;
822 auto &Id = Lexer.getTok();
823 if (ExpectBlockType) {
824 // Assume this identifier is a block_type.
825 auto BT = WebAssembly::parseBlockType(Type: Id.getString());
826 if (BT == WebAssembly::BlockType::Invalid)
827 return error(Msg: "Unknown block type: ", Tok: Id);
828 addBlockTypeOperand(Operands, NameLoc, BT);
829 ExpectBlockType = false;
830 Parser.Lex();
831 // Now that we've parsed a single block return type, if we are
832 // expecting a catch clause list, try to parse it.
833 if (ExpectCatchList && PeekCatchList()) {
834 if (parseCatchList(Operands))
835 return true;
836 ExpectCatchList = false;
837 }
838 } else {
839 // Assume this identifier is a label.
840 const MCExpr *Val;
841 SMLoc Start = Id.getLoc();
842 SMLoc End;
843 if (Parser.parseExpression(Res&: Val, EndLoc&: End))
844 return error(Msg: "Cannot parse symbol: ", Tok: Lexer.getTok());
845 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
846 args&: Start, args&: End, args: WebAssemblyOperand::SymOp{.Exp: Val}));
847 if (checkForP2AlignIfLoadStore(Operands, InstName: Name))
848 return true;
849 }
850 break;
851 }
852 case AsmToken::Minus:
853 Parser.Lex();
854 if (Lexer.is(K: AsmToken::Integer)) {
855 parseSingleInteger(IsNegative: true, Operands);
856 if (checkForP2AlignIfLoadStore(Operands, InstName: Name))
857 return true;
858 } else if (Lexer.is(K: AsmToken::Real)) {
859 if (parseSingleFloat(IsNegative: true, Operands))
860 return true;
861 } else if (!parseSpecialFloatMaybe(IsNegative: true, Operands)) {
862 } else {
863 return error(Msg: "Expected numeric constant instead got: ",
864 Tok: Lexer.getTok());
865 }
866 break;
867 case AsmToken::Integer:
868 parseSingleInteger(IsNegative: false, Operands);
869 if (checkForP2AlignIfLoadStore(Operands, InstName: Name))
870 return true;
871 break;
872 case AsmToken::Real: {
873 if (parseSingleFloat(IsNegative: false, Operands))
874 return true;
875 break;
876 }
877 case AsmToken::LCurly: {
878 Parser.Lex();
879 auto Op = std::make_unique<WebAssemblyOperand>(
880 args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::BrLOp{});
881 if (!Lexer.is(K: AsmToken::RCurly))
882 for (;;) {
883 Op->BrL.List.push_back(x: Lexer.getTok().getIntVal());
884 expect(Kind: AsmToken::Integer, KindName: "integer");
885 if (!isNext(Kind: AsmToken::Comma))
886 break;
887 }
888 expect(Kind: AsmToken::RCurly, KindName: "}");
889 Operands.push_back(Elt: std::move(Op));
890 break;
891 }
892 default:
893 return error(Msg: "Unexpected token in operand: ", Tok);
894 }
895 if (Lexer.isNot(K: AsmToken::EndOfStatement)) {
896 if (expect(Kind: AsmToken::Comma, KindName: ","))
897 return true;
898 }
899 }
900
901 // If we are still expecting to parse a block type or a catch list at this
902 // point, we set them to the default/empty state.
903
904 // Support blocks with no operands as default to void.
905 if (ExpectBlockType)
906 addBlockTypeOperand(Operands, NameLoc, BT: WebAssembly::BlockType::Void);
907 // If no catch list has been parsed, add an empty catch list operand.
908 if (ExpectCatchList)
909 Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>(
910 args&: NameLoc, args&: NameLoc, args: WebAssemblyOperand::CaLOp{}));
911
912 if (FunctionTable)
913 Operands.push_back(Elt: std::move(FunctionTable));
914 Parser.Lex();
915 return false;
916 }
917
918 bool parseSignature(wasm::WasmSignature *Signature) {
919 if (expect(Kind: AsmToken::LParen, KindName: "("))
920 return true;
921 if (parseRegTypeList(Types&: Signature->Params))
922 return true;
923 if (expect(Kind: AsmToken::RParen, KindName: ")"))
924 return true;
925 if (expect(Kind: AsmToken::MinusGreater, KindName: "->"))
926 return true;
927 if (expect(Kind: AsmToken::LParen, KindName: "("))
928 return true;
929 if (parseRegTypeList(Types&: Signature->Returns))
930 return true;
931 if (expect(Kind: AsmToken::RParen, KindName: ")"))
932 return true;
933 return false;
934 }
935
936 bool parseCatchList(OperandVector &Operands) {
937 auto Op = std::make_unique<WebAssemblyOperand>(
938 args: Lexer.getTok().getLoc(), args: SMLoc(), args: WebAssemblyOperand::CaLOp{});
939 SMLoc EndLoc;
940
941 while (Lexer.is(K: AsmToken::LParen)) {
942 if (expect(Kind: AsmToken::LParen, KindName: "("))
943 return true;
944
945 auto CatchStr = expectIdent();
946 if (CatchStr.empty())
947 return true;
948 uint8_t CatchOpcode =
949 StringSwitch<uint8_t>(CatchStr)
950 .Case(S: "catch", Value: wasm::WASM_OPCODE_CATCH)
951 .Case(S: "catch_ref", Value: wasm::WASM_OPCODE_CATCH_REF)
952 .Case(S: "catch_all", Value: wasm::WASM_OPCODE_CATCH_ALL)
953 .Case(S: "catch_all_ref", Value: wasm::WASM_OPCODE_CATCH_ALL_REF)
954 .Default(Value: 0xff);
955 if (CatchOpcode == 0xff)
956 return error(
957 Msg: "Expected catch/catch_ref/catch_all/catch_all_ref, instead got: " +
958 CatchStr);
959
960 const MCExpr *Tag = nullptr;
961 if (CatchOpcode == wasm::WASM_OPCODE_CATCH ||
962 CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) {
963 if (Parser.parseExpression(Res&: Tag))
964 return error(Msg: "Cannot parse symbol: ", Tok: Lexer.getTok());
965 }
966
967 auto &DestTok = Lexer.getTok();
968 if (DestTok.isNot(K: AsmToken::Integer))
969 return error(Msg: "Expected integer constant, instead got: ", Tok: DestTok);
970 unsigned Dest = DestTok.getIntVal();
971 Parser.Lex();
972
973 EndLoc = Lexer.getTok().getEndLoc();
974 if (expect(Kind: AsmToken::RParen, KindName: ")"))
975 return true;
976
977 Op->CaL.List.push_back(x: {.Opcode: CatchOpcode, .Tag: Tag, .Dest: Dest});
978 }
979
980 Op->EndLoc = EndLoc;
981 Operands.push_back(Elt: std::move(Op));
982 return false;
983 }
984
985 bool checkDataSection() {
986 if (CurrentState != DataSection) {
987 auto *WS = static_cast<const MCSectionWasm *>(
988 getStreamer().getCurrentSectionOnly());
989 if (WS && WS->isText())
990 return error(Msg: "data directive must occur in a data segment: ",
991 Tok: Lexer.getTok());
992 }
993 CurrentState = DataSection;
994 return false;
995 }
996
997 // This function processes wasm-specific directives streamed to
998 // WebAssemblyTargetStreamer, all others go to the generic parser
999 // (see WasmAsmParser).
1000 ParseStatus parseDirective(AsmToken DirectiveID) override {
1001 assert(DirectiveID.getKind() == AsmToken::Identifier);
1002 auto &Out = getStreamer();
1003 auto &TOut =
1004 reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());
1005 auto &Ctx = Out.getContext();
1006
1007 if (DirectiveID.getString() == ".globaltype") {
1008 auto SymName = expectIdent();
1009 if (SymName.empty())
1010 return ParseStatus::Failure;
1011 if (expect(Kind: AsmToken::Comma, KindName: ","))
1012 return ParseStatus::Failure;
1013 auto TypeTok = Lexer.getTok();
1014 auto TypeName = expectIdent();
1015 if (TypeName.empty())
1016 return ParseStatus::Failure;
1017 auto Type = WebAssembly::parseType(Type: TypeName);
1018 if (!Type)
1019 return error(Msg: "Unknown type in .globaltype directive: ", Tok: TypeTok);
1020 // Optional mutable modifier. Default to mutable for historical reasons.
1021 // Ideally we would have gone with immutable as the default and used `mut`
1022 // as the modifier to match the `.wat` format.
1023 bool Mutable = true;
1024 if (isNext(Kind: AsmToken::Comma)) {
1025 TypeTok = Lexer.getTok();
1026 auto Id = expectIdent();
1027 if (Id.empty())
1028 return ParseStatus::Failure;
1029 if (Id == "immutable")
1030 Mutable = false;
1031 else
1032 // Should we also allow `mutable` and `mut` here for clarity?
1033 return error(Msg: "Unknown type in .globaltype modifier: ", Tok: TypeTok);
1034 }
1035 // Now set this symbol with the correct type.
1036 auto *WasmSym =
1037 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1038 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
1039 WasmSym->setGlobalType(wasm::WasmGlobalType{.Type: uint8_t(*Type), .Mutable: Mutable});
1040 // And emit the directive again.
1041 TOut.emitGlobalType(Sym: WasmSym);
1042 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1043 }
1044
1045 if (DirectiveID.getString() == ".tabletype") {
1046 // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]]
1047 auto SymName = expectIdent();
1048 if (SymName.empty())
1049 return ParseStatus::Failure;
1050 if (expect(Kind: AsmToken::Comma, KindName: ","))
1051 return ParseStatus::Failure;
1052
1053 auto ElemTypeTok = Lexer.getTok();
1054 auto ElemTypeName = expectIdent();
1055 if (ElemTypeName.empty())
1056 return ParseStatus::Failure;
1057 std::optional<wasm::ValType> ElemType =
1058 WebAssembly::parseType(Type: ElemTypeName);
1059 if (!ElemType)
1060 return error(Msg: "Unknown type in .tabletype directive: ", Tok: ElemTypeTok);
1061
1062 wasm::WasmLimits Limits = defaultLimits();
1063 if (isNext(Kind: AsmToken::Comma) && parseLimits(Limits: &Limits))
1064 return ParseStatus::Failure;
1065
1066 // Now that we have the name and table type, we can actually create the
1067 // symbol
1068 auto *WasmSym =
1069 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1070 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
1071 if (Is64) {
1072 Limits.Flags |= wasm::WASM_LIMITS_FLAG_IS_64;
1073 }
1074 wasm::WasmTableType Type = {.ElemType: *ElemType, .Limits: Limits};
1075 WasmSym->setTableType(Type);
1076 TOut.emitTableType(Sym: WasmSym);
1077 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1078 }
1079
1080 if (DirectiveID.getString() == ".functype") {
1081 // This code has to send things to the streamer similar to
1082 // WebAssemblyAsmPrinter::EmitFunctionBodyStart.
1083 // TODO: would be good to factor this into a common function, but the
1084 // assembler and backend really don't share any common code, and this code
1085 // parses the locals separately.
1086 auto SymName = expectIdent();
1087 if (SymName.empty())
1088 return ParseStatus::Failure;
1089 auto *WasmSym =
1090 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1091 if (WasmSym->isDefined()) {
1092 // We push 'Function' either when a label is parsed or a .functype
1093 // directive is parsed. The reason it is not easy to do this uniformly
1094 // in a single place is,
1095 // 1. We can't do this at label parsing time only because there are
1096 // cases we don't have .functype directive before a function label,
1097 // in which case we don't know if the label is a function at the time
1098 // of parsing.
1099 // 2. We can't do this at .functype parsing time only because we want to
1100 // detect a function started with a label and not ended correctly
1101 // without encountering a .functype directive after the label.
1102 if (CurrentState != FunctionLabel) {
1103 // This .functype indicates a start of a function.
1104 if (ensureEmptyNestingStack())
1105 return ParseStatus::Failure;
1106 push(NT: Function);
1107 }
1108 CurrentState = FunctionStart;
1109 LastFunctionLabel = WasmSym;
1110 }
1111 auto *Signature = Ctx.createWasmSignature();
1112 if (parseSignature(Signature))
1113 return ParseStatus::Failure;
1114 if (CurrentState == FunctionStart)
1115 TC.funcDecl(Sig: *Signature);
1116 WasmSym->setSignature(Signature);
1117 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
1118 TOut.emitFunctionType(Sym: WasmSym);
1119 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
1120 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1121 }
1122
1123 if (DirectiveID.getString() == ".export_name") {
1124 auto SymName = expectIdent();
1125 if (SymName.empty())
1126 return ParseStatus::Failure;
1127 if (expect(Kind: AsmToken::Comma, KindName: ","))
1128 return ParseStatus::Failure;
1129 auto ExportName = expectStringOrIdent();
1130 if (ExportName.empty())
1131 return ParseStatus::Failure;
1132 auto *WasmSym =
1133 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1134 WasmSym->setExportName(Ctx.allocateString(s: ExportName));
1135 TOut.emitExportName(Sym: WasmSym, ExportName);
1136 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1137 }
1138
1139 if (DirectiveID.getString() == ".import_module") {
1140 auto SymName = expectIdent();
1141 if (SymName.empty())
1142 return ParseStatus::Failure;
1143 if (expect(Kind: AsmToken::Comma, KindName: ","))
1144 return ParseStatus::Failure;
1145 auto ImportModule = expectStringOrIdent();
1146 if (ImportModule.empty())
1147 return ParseStatus::Failure;
1148 auto *WasmSym =
1149 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1150 WasmSym->setImportModule(Ctx.allocateString(s: ImportModule));
1151 TOut.emitImportModule(Sym: WasmSym, ImportModule);
1152 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1153 }
1154
1155 if (DirectiveID.getString() == ".import_name") {
1156 auto SymName = expectIdent();
1157 if (SymName.empty())
1158 return ParseStatus::Failure;
1159 if (expect(Kind: AsmToken::Comma, KindName: ","))
1160 return ParseStatus::Failure;
1161 StringRef ImportName = expectStringOrIdent();
1162 if (ImportName.empty())
1163 return ParseStatus::Failure;
1164 auto *WasmSym =
1165 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1166 WasmSym->setImportName(Ctx.allocateString(s: ImportName));
1167 TOut.emitImportName(Sym: WasmSym, ImportName);
1168 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1169 }
1170
1171 if (DirectiveID.getString() == ".tagtype") {
1172 auto SymName = expectIdent();
1173 if (SymName.empty())
1174 return ParseStatus::Failure;
1175 auto *WasmSym =
1176 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name: SymName));
1177 auto *Signature = Ctx.createWasmSignature();
1178 if (parseRegTypeList(Types&: Signature->Params))
1179 return ParseStatus::Failure;
1180 WasmSym->setSignature(Signature);
1181 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
1182 TOut.emitTagType(Sym: WasmSym);
1183 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
1184 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1185 }
1186
1187 if (DirectiveID.getString() == ".local") {
1188 if (CurrentState != FunctionStart)
1189 return error(Msg: ".local directive should follow the start of a function: ",
1190 Tok: Lexer.getTok());
1191 SmallVector<wasm::ValType, 4> Locals;
1192 if (parseRegTypeList(Types&: Locals))
1193 return ParseStatus::Failure;
1194 TC.localDecl(Locals);
1195 TOut.emitLocal(Types: Locals);
1196 CurrentState = FunctionLocals;
1197 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1198 }
1199
1200 if (DirectiveID.getString() == ".int8" ||
1201 DirectiveID.getString() == ".int16" ||
1202 DirectiveID.getString() == ".int32" ||
1203 DirectiveID.getString() == ".int64") {
1204 if (checkDataSection())
1205 return ParseStatus::Failure;
1206 const MCExpr *Val;
1207 SMLoc End;
1208 if (Parser.parseExpression(Res&: Val, EndLoc&: End))
1209 return error(Msg: "Cannot parse .int expression: ", Tok: Lexer.getTok());
1210 size_t NumBits = 0;
1211 DirectiveID.getString().drop_front(N: 4).getAsInteger(Radix: 10, Result&: NumBits);
1212 Out.emitValue(Value: Val, Size: NumBits / 8, Loc: End);
1213 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1214 }
1215
1216 if (DirectiveID.getString() == ".asciz") {
1217 if (checkDataSection())
1218 return ParseStatus::Failure;
1219 std::string S;
1220 if (Parser.parseEscapedString(Data&: S))
1221 return error(Msg: "Cannot parse string constant: ", Tok: Lexer.getTok());
1222 Out.emitBytes(Data: StringRef(S.c_str(), S.length() + 1));
1223 return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL");
1224 }
1225
1226 return ParseStatus::NoMatch; // We didn't process this directive.
1227 }
1228
1229 // Called either when the first instruction is parsed of the function ends.
1230 void ensureLocals(MCStreamer &Out) {
1231 if (CurrentState == FunctionStart) {
1232 // We haven't seen a .local directive yet. The streamer requires locals to
1233 // be encoded as a prelude to the instructions, so emit an empty list of
1234 // locals here.
1235 auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>(
1236 *Out.getTargetStreamer());
1237 TOut.emitLocal(Types: SmallVector<wasm::ValType, 0>());
1238 CurrentState = FunctionLocals;
1239 }
1240 }
1241
1242 bool matchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/,
1243 OperandVector &Operands, MCStreamer &Out,
1244 uint64_t &ErrorInfo,
1245 bool MatchingInlineAsm) override {
1246 MCInst Inst;
1247 Inst.setLoc(IDLoc);
1248 FeatureBitset MissingFeatures;
1249 unsigned MatchResult = MatchInstructionImpl(
1250 Operands, Inst, ErrorInfo, MissingFeatures, matchingInlineAsm: MatchingInlineAsm);
1251 switch (MatchResult) {
1252 case Match_Success: {
1253 ensureLocals(Out);
1254 // Fix unknown p2align operands.
1255 const MCInstrDesc &Desc = MII.get(Opcode: Inst.getOpcode());
1256 auto Align = WebAssembly::GetDefaultP2AlignAny(Opc: Inst.getOpcode());
1257 if (Align != -1U) {
1258 unsigned I = 0;
1259 // It's operand 0 for regular memory ops and 1 for atomics.
1260 for (unsigned E = Desc.getNumOperands(); I < E; ++I) {
1261 if (Desc.operands()[I].OperandType == WebAssembly::OPERAND_P2ALIGN) {
1262 auto &Op = Inst.getOperand(i: I);
1263 if (Op.getImm() == -1) {
1264 Op.setImm(Align);
1265 }
1266 break;
1267 }
1268 }
1269 assert(I < 2 && "Default p2align set but operand not found");
1270 }
1271 if (Is64) {
1272 // Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having
1273 // an offset64 arg instead of offset32, but to the assembler matcher
1274 // they're both immediates so don't get selected for.
1275 auto Opc64 = WebAssembly::getWasm64Opcode(
1276 Opcode: static_cast<uint16_t>(Inst.getOpcode()));
1277 if (Opc64 >= 0) {
1278 Inst.setOpcode(Opc64);
1279 }
1280 }
1281 if (!SkipTypeCheck)
1282 TC.typeCheck(ErrorLoc: IDLoc, Inst, Operands);
1283 Out.emitInstruction(Inst, STI: getSTI());
1284 if (CurrentState == EndFunction) {
1285 onEndOfFunction(ErrorLoc: IDLoc);
1286 } else {
1287 CurrentState = Instructions;
1288 }
1289 return false;
1290 }
1291 case Match_MissingFeature: {
1292 assert(MissingFeatures.count() > 0 && "Expected missing features");
1293 SmallString<128> Message;
1294 raw_svector_ostream OS(Message);
1295 OS << "instruction requires:";
1296 for (unsigned I = 0, E = MissingFeatures.size(); I != E; ++I)
1297 if (MissingFeatures.test(I))
1298 OS << ' ' << getSubtargetFeatureName(Val: I);
1299 return Parser.Error(L: IDLoc, Msg: Message);
1300 }
1301 case Match_MnemonicFail:
1302 return Parser.Error(L: IDLoc, Msg: "invalid instruction");
1303 case Match_NearMisses:
1304 return Parser.Error(L: IDLoc, Msg: "ambiguous instruction");
1305 case Match_InvalidTiedOperand:
1306 case Match_InvalidOperand: {
1307 SMLoc ErrorLoc = IDLoc;
1308 if (ErrorInfo != ~0ULL) {
1309 if (ErrorInfo >= Operands.size())
1310 return Parser.Error(L: IDLoc, Msg: "too few operands for instruction");
1311 ErrorLoc = Operands[ErrorInfo]->getStartLoc();
1312 if (ErrorLoc == SMLoc())
1313 ErrorLoc = IDLoc;
1314 }
1315 return Parser.Error(L: ErrorLoc, Msg: "invalid operand for instruction");
1316 }
1317 }
1318 llvm_unreachable("Implement any new match types added!");
1319 }
1320
1321 void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override {
1322 // Code below only applies to labels in text sections.
1323 auto *CWS = static_cast<const MCSectionWasm *>(
1324 getStreamer().getCurrentSectionOnly());
1325 if (!CWS->isText())
1326 return;
1327
1328 auto *WasmSym = static_cast<MCSymbolWasm *>(Symbol);
1329 // Unlike other targets, we don't allow data in text sections (labels
1330 // declared with .type @object).
1331 if (WasmSym->getType() == wasm::WASM_SYMBOL_TYPE_DATA) {
1332 Parser.Error(L: IDLoc,
1333 Msg: "Wasm doesn\'t support data symbols in text sections");
1334 return;
1335 }
1336
1337 // Start a new section for the next function automatically, since our
1338 // object writer expects each function to have its own section. This way
1339 // The user can't forget this "convention".
1340 auto SymName = Symbol->getName();
1341 if (SymName.starts_with(Prefix: ".L"))
1342 return; // Local Symbol.
1343
1344 // TODO: If the user explicitly creates a new function section, we ignore
1345 // its name when we create this one. It would be nice to honor their
1346 // choice, while still ensuring that we create one if they forget.
1347 // (that requires coordination with WasmAsmParser::parseSectionDirective)
1348 std::string SecName = (".text." + SymName).str();
1349
1350 auto *Group = CWS->getGroup();
1351 // If the current section is a COMDAT, also set the flag on the symbol.
1352 // TODO: Currently the only place that the symbols' comdat flag matters is
1353 // for importing comdat functions. But there's no way to specify that in
1354 // assembly currently.
1355 if (Group)
1356 WasmSym->setComdat(true);
1357 auto *WS = getContext().getWasmSection(Section: SecName, K: SectionKind::getText(), Flags: 0,
1358 Group, UniqueID: MCSection::NonUniqueID);
1359 getStreamer().switchSection(Section: WS);
1360 // Also generate DWARF for this section if requested.
1361 if (getContext().getGenDwarfForAssembly())
1362 getContext().addGenDwarfSection(Sec: WS);
1363
1364 if (WasmSym->isFunction()) {
1365 // We give the location of the label (IDLoc) here, because otherwise the
1366 // lexer's next location will be used, which can be confusing. For
1367 // example:
1368 //
1369 // test0: ; This function does not end properly
1370 // ...
1371 //
1372 // test1: ; We would like to point to this line for error
1373 // ... . Not this line, which can contain any instruction
1374 ensureEmptyNestingStack(Loc: IDLoc);
1375 CurrentState = FunctionLabel;
1376 LastFunctionLabel = Symbol;
1377 push(NT: Function);
1378 }
1379 }
1380
1381 void onEndOfFunction(SMLoc ErrorLoc) {
1382 if (!SkipTypeCheck)
1383 TC.endOfFunction(ErrorLoc, ExactMatch: true);
1384 // Reset the type checker state.
1385 TC.clear();
1386 }
1387
1388 void onEndOfFile() override { ensureEmptyNestingStack(); }
1389};
1390} // end anonymous namespace
1391
1392// Force static initialization.
1393extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
1394LLVMInitializeWebAssemblyAsmParser() {
1395 RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32());
1396 RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64());
1397}
1398
1399#define GET_REGISTER_MATCHER
1400#define GET_SUBTARGET_FEATURE_NAME
1401#define GET_MATCHER_IMPLEMENTATION
1402#include "WebAssemblyGenAsmMatcher.inc"
1403
1404StringRef getMnemonic(unsigned Opc) {
1405 // FIXME: linear search!
1406 for (auto &ME : MatchTable0) {
1407 if (ME.Opcode == Opc) {
1408 return ME.getMnemonic();
1409 }
1410 }
1411 assert(false && "mnemonic not found");
1412 return StringRef();
1413}
1414