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