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