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 FeatureBitset FBS = ComputeAvailableFeatures(STI.getFeatureBits());
309
310 // bulk-memory implies bulk-memory-opt
311 if (FBS.test(WebAssembly::FeatureBulkMemory)) {
312 FBS.set(WebAssembly::FeatureBulkMemoryOpt);
313 }
314 // reference-types implies call-indirect-overlong
315 if (FBS.test(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(SM.getMainFileID()).Buffer->getBufferIdentifier();
325 if (BufferName == "<inline asm>")
326 SkipTypeCheck = true;
327 }
328
329 void Initialize(MCAsmParser &Parser) override {
331
332 DefaultFunctionTable = getOrCreateFunctionTableSymbol(
333 getContext(), "__indirect_function_table", Is64);
334 if (!STI->checkFeatures("+call-indirect-overlong") &&
335 !STI->checkFeatures("+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(Tok.getLoc(), Msg + Tok.getString());
353 }
354
355 bool error(const Twine &Msg, SMLoc Loc = SMLoc()) {
356 return Parser.Error(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({NT, Sig});
384 }
385
386 bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {
387 if (NestingStack.empty())
388 return error(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(Twine("Block construct type mismatch, expected: ") +
392 nestingString(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(Twine("End of block construct with no start: ") + Ins);
404 auto Sig = NestingStack.back().Sig;
405 if (pop(Ins, PopNT))
406 return true;
407 push(PushNT, Sig);
408 return false;
409 }
410
411 bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) {
412 auto Err = !NestingStack.empty();
413 while (!NestingStack.empty()) {
414 error(Twine("Unmatched block construct(s) at function end: ") +
415 nestingString(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(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(std::string("Expected ") + KindName + ", instead got: ",
432 Lexer.getTok());
433 return false;
434 }
435
436 StringRef expectIdent() {
437 if (!Lexer.is(AsmToken::Identifier)) {
438 error("Expected identifier, got: ", 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(AsmToken::String)) {
448 auto Str = Lexer.getTok().getStringContents();
449 Parser.Lex();
450 return Str;
451 }
452 if (Lexer.is(AsmToken::Identifier)) {
453 auto Name = Lexer.getTok().getString();
454 Parser.Lex();
455 return Name;
456 }
457 error("Expected string or identifier, got: ", Lexer.getTok());
458 return StringRef();
459 }
460
461 bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
462 while (Lexer.is(AsmToken::Identifier)) {
463 auto Type = WebAssembly::parseType(Lexer.getTok().getString());
464 if (!Type)
465 return error("unknown type: ", Lexer.getTok());
466 Types.push_back(*Type);
467 Parser.Lex();
468 if (!isNext(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(std::make_unique<WebAssemblyOperand>(
480 Int.getLoc(), Int.getEndLoc(), WebAssemblyOperand::IntOp{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(Val, false))
488 return error("Cannot parse real: ", Flt);
489 if (IsNegative)
490 Val = -Val;
491 Operands.push_back(std::make_unique<WebAssemblyOperand>(
492 Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{Val}));
493 Parser.Lex();
494 return false;
495 }
496
497 bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {
498 if (Lexer.isNot(AsmToken::Identifier))
499 return true;
500 auto &Flt = Lexer.getTok();
501 auto S = Flt.getString();
502 double Val;
503 if (S.compare_insensitive("infinity") == 0) {
504 Val = std::numeric_limits<double>::infinity();
505 } else if (S.compare_insensitive("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(std::make_unique<WebAssemblyOperand>(
513 Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{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(AsmToken::Identifier)) {
522 StringRef S = Tok.getString();
523 Order = StringSwitch<int64_t>(S)
524 .Case("acqrel", wasm::WASM_MEM_ORDER_ACQ_REL)
525 .Case("seqcst", wasm::WASM_MEM_ORDER_SEQ_CST)
526 .Default(-1);
527 if (Order != -1) {
528 if (!STI->checkFeatures("+relaxed-atomics"))
529 return error("memory ordering requires relaxed-atomics feature: ",
530 Tok);
531 Parser.Lex();
532 } else {
534 }
535 }
536 Operands.push_back(std::make_unique<WebAssemblyOperand>(
537 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::IntOp{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(".load") ||
544 InstName.contains(".store") ||
545 InstName.contains("prefetch");
546 auto IsAtomic = InstName.contains("atomic.");
547 if (IsLoadStore || IsAtomic) {
548 // Parse load/store operands of the form: offset:p2align=align
549 if (IsLoadStore && isNext(AsmToken::Colon)) {
550 auto Id = expectIdent();
551 if (Id != "p2align")
552 return error("Expected p2align, instead got: " + Id);
553 if (expect(AsmToken::Equal, "="))
554 return true;
555 if (!Lexer.is(AsmToken::Integer))
556 return error("Expected integer constant");
557 parseSingleInteger(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("_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(std::make_unique<WebAssemblyOperand>(
571 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::IntOp{-1}));
572 }
573 }
574 return false;
575 }
576
577 void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
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(std::make_unique<WebAssemblyOperand>(
587 NameLoc, NameLoc, WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
588 }
589
590 bool parseLimits(wasm::WasmLimits *Limits) {
591 auto Tok = Lexer.getTok();
592 if (!Tok.is(AsmToken::Integer))
593 return error("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(AsmToken::Comma)) {
601 auto Tok = Lexer.getTok();
602 if (!Tok.is(AsmToken::Integer))
603 return error("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("+call-indirect-overlong") ||
614 STI->checkFeatures("+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(AsmToken::Identifier)) {
622 auto *Sym =
624 const auto *Val = MCSymbolRefExpr::create(Sym, getContext());
625 *Op = std::make_unique<WebAssemblyOperand>(
626 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::SymOp{Val});
627 Parser.Lex();
628 return expect(AsmToken::Comma, ",");
629 }
630 const auto *Val =
631 MCSymbolRefExpr::create(DefaultFunctionTable, getContext());
632 *Op = std::make_unique<WebAssemblyOperand>(
633 SMLoc(), SMLoc(), WebAssemblyOperand::SymOp{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(DefaultFunctionTable, MCSA_NoDeadStrip);
640 *Op = std::make_unique<WebAssemblyOperand>(SMLoc(), SMLoc(),
641 WebAssemblyOperand::IntOp{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("Incomplete instruction name: ", 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(std::make_unique<WebAssemblyOperand>(
673 NameLoc, SMLoc::getFromPointer(Name.end()),
674 WebAssemblyOperand::TokOp{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(Block);
684 ExpectBlockType = true;
685 } else if (Name == "loop") {
686 push(Loop);
687 ExpectBlockType = true;
688 } else if (Name == "try") {
689 push(Try);
690 ExpectBlockType = true;
691 } else if (Name == "if") {
692 push(If);
693 ExpectBlockType = true;
694 } else if (Name == "else") {
695 if (popAndPushWithSameSignature(Name, If, Else))
696 return true;
697 } else if (Name == "catch") {
698 if (popAndPushWithSameSignature(Name, Try, Try))
699 return true;
700 } else if (Name == "catch_all") {
701 if (popAndPushWithSameSignature(Name, Try, CatchAll))
702 return true;
703 } else if (Name == "try_table") {
704 push(TryTable);
705 ExpectBlockType = true;
706 ExpectCatchList = true;
707 } else if (Name == "end_if") {
708 if (pop(Name, If, Else))
709 return true;
710 } else if (Name == "end_try") {
711 if (pop(Name, Try, CatchAll))
712 return true;
713 } else if (Name == "end_try_table") {
714 if (pop(Name, TryTable))
715 return true;
716 } else if (Name == "delegate") {
717 if (pop(Name, Try))
718 return true;
719 } else if (Name == "end_loop") {
720 if (pop(Name, Loop))
721 return true;
722 } else if (Name == "end_block") {
723 if (pop(Name, Block))
724 return true;
725 } else if (Name == "end_function") {
726 ensureLocals(getStreamer());
727 CurrentState = EndFunction;
728 if (pop(Name, 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(&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 Lexer.getLoc(), Lexer.getLoc(), WebAssemblyOperand::TyLOp{});
755 while (Lexer.is(AsmToken::Identifier)) {
756 auto &Id = Lexer.getTok();
757 auto Ty = WebAssembly::parseType(Id.getString());
758 if (!Ty)
759 return error("unknown value type in select operand list: ", Id);
760 Op->TyL.List.push_back(static_cast<uint8_t>(*Ty));
761 Op->EndLoc = Id.getEndLoc();
762 Parser.Lex();
763 }
764 Operands.push_back(std::move(Op));
765 }
766
767 if (Name.contains("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(AsmToken::LParen))
775 return false;
776 AsmToken NextTok = Lexer.peekTok();
777 return NextTok.getKind() == AsmToken::Identifier &&
778 NextTok.getIdentifier().starts_with("catch");
779 };
780
781 // Parse a multivalue block type
782 if (ExpectFuncType ||
783 (Lexer.is(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("typeindex", true);
800 auto *WasmSym = static_cast<MCSymbolWasm *>(Sym);
801 WasmSym->setSignature(Signature);
802 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
803 const MCExpr *Expr =
805 Operands.push_back(std::make_unique<WebAssemblyOperand>(
806 Loc.getLoc(), Loc.getEndLoc(), WebAssemblyOperand::SymOp{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, WebAssembly::BlockType::Void);
820 }
821 if (parseCatchList(Operands))
822 return true;
823 ExpectCatchList = false;
824 }
825
826 while (Lexer.isNot(AsmToken::EndOfStatement)) {
827 auto &Tok = Lexer.getTok();
828 switch (Tok.getKind()) {
830 if (!parseSpecialFloatMaybe(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(Id.getString());
836 if (BT == WebAssembly::BlockType::Invalid)
837 return error("Unknown block type: ", 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(Val, End))
854 return error("Cannot parse symbol: ", Lexer.getTok());
855 Operands.push_back(std::make_unique<WebAssemblyOperand>(
856 Start, End, WebAssemblyOperand::SymOp{Val}));
857 if (checkForP2AlignIfLoadStore(Operands, Name))
858 return true;
859 }
860 break;
861 }
862 case AsmToken::Minus:
863 Parser.Lex();
864 if (Lexer.is(AsmToken::Integer)) {
865 parseSingleInteger(true, Operands);
866 if (checkForP2AlignIfLoadStore(Operands, Name))
867 return true;
868 } else if (Lexer.is(AsmToken::Real)) {
869 if (parseSingleFloat(true, Operands))
870 return true;
871 } else if (!parseSpecialFloatMaybe(true, Operands)) {
872 } else {
873 return error("Expected numeric constant instead got: ",
874 Lexer.getTok());
875 }
876 break;
878 parseSingleInteger(false, Operands);
879 if (checkForP2AlignIfLoadStore(Operands, Name))
880 return true;
881 break;
882 case AsmToken::Real: {
883 if (parseSingleFloat(false, Operands))
884 return true;
885 break;
886 }
887 case AsmToken::LCurly: {
888 Parser.Lex();
889 auto Op = std::make_unique<WebAssemblyOperand>(
890 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::BrLOp{});
891 if (!Lexer.is(AsmToken::RCurly))
892 for (;;) {
893 Op->BrL.List.push_back(Lexer.getTok().getIntVal());
894 expect(AsmToken::Integer, "integer");
895 if (!isNext(AsmToken::Comma))
896 break;
897 }
898 expect(AsmToken::RCurly, "}");
899 Operands.push_back(std::move(Op));
900 break;
901 }
902 default:
903 return error("Unexpected token in operand: ", Tok);
904 }
905 if (Lexer.isNot(AsmToken::EndOfStatement)) {
906 if (expect(AsmToken::Comma, ","))
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, WebAssembly::BlockType::Void);
917 // If no catch list has been parsed, add an empty catch list operand.
918 if (ExpectCatchList)
919 Operands.push_back(std::make_unique<WebAssemblyOperand>(
920 NameLoc, NameLoc, WebAssemblyOperand::CaLOp{}));
921
922 if (FunctionTable)
923 Operands.push_back(std::move(FunctionTable));
924 Parser.Lex();
925 return false;
926 }
927
928 bool parseSignature(wasm::WasmSignature *Signature) {
929 if (expect(AsmToken::LParen, "("))
930 return true;
931 if (parseRegTypeList(Signature->Params))
932 return true;
933 if (expect(AsmToken::RParen, ")"))
934 return true;
935 if (expect(AsmToken::MinusGreater, "->"))
936 return true;
937 if (expect(AsmToken::LParen, "("))
938 return true;
939 if (parseRegTypeList(Signature->Returns))
940 return true;
941 if (expect(AsmToken::RParen, ")"))
942 return true;
943 return false;
944 }
945
946 bool parseCatchList(OperandVector &Operands) {
947 auto Op = std::make_unique<WebAssemblyOperand>(
948 Lexer.getTok().getLoc(), SMLoc(), WebAssemblyOperand::CaLOp{});
949 SMLoc EndLoc;
950
951 while (Lexer.is(AsmToken::LParen)) {
952 if (expect(AsmToken::LParen, "("))
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("catch", wasm::WASM_OPCODE_CATCH)
961 .Case("catch_ref", wasm::WASM_OPCODE_CATCH_REF)
962 .Case("catch_all", wasm::WASM_OPCODE_CATCH_ALL)
963 .Case("catch_all_ref", wasm::WASM_OPCODE_CATCH_ALL_REF)
964 .Default(0xff);
965 if (CatchOpcode == 0xff)
966 return error(
967 "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(Tag))
974 return error("Cannot parse symbol: ", Lexer.getTok());
975 }
976
977 auto &DestTok = Lexer.getTok();
978 if (DestTok.isNot(AsmToken::Integer))
979 return error("Expected integer constant, instead got: ", DestTok);
980 unsigned Dest = DestTok.getIntVal();
981 Parser.Lex();
982
983 EndLoc = Lexer.getTok().getEndLoc();
984 if (expect(AsmToken::RParen, ")"))
985 return true;
986
987 Op->CaL.List.push_back({CatchOpcode, Tag, Dest});
988 }
989
990 Op->EndLoc = EndLoc;
991 Operands.push_back(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("data directive must occur in a data segment: ",
1001 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(AsmToken::Comma, ","))
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(TypeName);
1028 if (!Type)
1029 return error("Unknown type in .globaltype directive: ", 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(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("Unknown type in .globaltype modifier: ", TypeTok);
1044 }
1045 // Now set this symbol with the correct type.
1046 auto *WasmSym =
1047 static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(SymName));
1048 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
1049 WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(*Type), Mutable});
1050 // And emit the directive again.
1051 TOut.emitGlobalType(WasmSym);
1052 return expect(AsmToken::EndOfStatement, "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(AsmToken::Comma, ","))
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(ElemTypeName);
1069 if (!ElemType)
1070 return error("Unknown type in .tabletype directive: ", ElemTypeTok);
1071
1072 wasm::WasmLimits Limits = defaultLimits();
1073 if (isNext(AsmToken::Comma) && parseLimits(&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(SymName));
1080 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
1081 if (Is64) {
1083 }
1084 wasm::WasmTableType Type = {*ElemType, Limits};
1085 WasmSym->setTableType(Type);
1086 TOut.emitTableType(WasmSym);
1087 return expect(AsmToken::EndOfStatement, "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(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(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(*Signature);
1126 WasmSym->setSignature(Signature);
1127 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
1128 TOut.emitFunctionType(WasmSym);
1129 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
1130 return expect(AsmToken::EndOfStatement, "EOL");
1131 }
1132
1133 if (DirectiveID.getString() == ".export_name") {
1134 auto SymName = expectIdent();
1135 if (SymName.empty())
1136 return ParseStatus::Failure;
1137 if (expect(AsmToken::Comma, ","))
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(SymName));
1144 WasmSym->setExportName(Ctx.allocateString(ExportName));
1145 TOut.emitExportName(WasmSym, ExportName);
1146 return expect(AsmToken::EndOfStatement, "EOL");
1147 }
1148
1149 if (DirectiveID.getString() == ".import_module") {
1150 auto SymName = expectIdent();
1151 if (SymName.empty())
1152 return ParseStatus::Failure;
1153 if (expect(AsmToken::Comma, ","))
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(SymName));
1160 WasmSym->setImportModule(Ctx.allocateString(ImportModule));
1161 TOut.emitImportModule(WasmSym, ImportModule);
1162 return expect(AsmToken::EndOfStatement, "EOL");
1163 }
1164
1165 if (DirectiveID.getString() == ".import_name") {
1166 auto SymName = expectIdent();
1167 if (SymName.empty())
1168 return ParseStatus::Failure;
1169 if (expect(AsmToken::Comma, ","))
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(SymName));
1176 WasmSym->setImportName(Ctx.allocateString(ImportName));
1177 TOut.emitImportName(WasmSym, ImportName);
1178 return expect(AsmToken::EndOfStatement, "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(SymName));
1187 auto *Signature = Ctx.createWasmSignature();
1188 if (parseRegTypeList(Signature->Params))
1189 return ParseStatus::Failure;
1190 WasmSym->setSignature(Signature);
1191 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
1192 TOut.emitTagType(WasmSym);
1193 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
1194 return expect(AsmToken::EndOfStatement, "EOL");
1195 }
1196
1197 if (DirectiveID.getString() == ".local") {
1198 if (CurrentState != FunctionStart)
1199 return error(".local directive should follow the start of a function: ",
1200 Lexer.getTok());
1202 if (parseRegTypeList(Locals))
1203 return ParseStatus::Failure;
1204 TC.localDecl(Locals);
1205 TOut.emitLocal(Locals);
1206 CurrentState = FunctionLocals;
1207 return expect(AsmToken::EndOfStatement, "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(Val, End))
1219 return error("Cannot parse .int expression: ", Lexer.getTok());
1220 size_t NumBits = 0;
1221 DirectiveID.getString().drop_front(4).getAsInteger(10, NumBits);
1222 Out.emitValue(Val, NumBits / 8, End);
1223 return expect(AsmToken::EndOfStatement, "EOL");
1224 }
1225
1226 if (DirectiveID.getString() == ".asciz") {
1227 if (checkDataSection())
1228 return ParseStatus::Failure;
1229 std::string S;
1230 if (Parser.parseEscapedString(S))
1231 return error("Cannot parse string constant: ", Lexer.getTok());
1232 Out.emitBytes(StringRef(S.c_str(), S.length() + 1));
1233 return expect(AsmToken::EndOfStatement, "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(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);
1261 switch (MatchResult) {
1262 case Match_Success: {
1263 ensureLocals(Out);
1264 // Fix unknown p2align operands.
1265 const MCInstrDesc &Desc = MII.get(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);
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 static_cast<uint16_t>(Inst.getOpcode()));
1287 if (Opc64 >= 0) {
1288 Inst.setOpcode(Opc64);
1289 }
1290 }
1291 if (!SkipTypeCheck)
1292 TC.typeCheck(IDLoc, Inst, Operands);
1293 Out.emitInstruction(Inst, getSTI());
1294 if (CurrentState == EndFunction) {
1295 onEndOfFunction(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(I);
1309 return Parser.Error(IDLoc, Message);
1310 }
1311 case Match_MnemonicFail:
1312 return Parser.Error(IDLoc, "invalid instruction");
1313 case Match_NearMisses:
1314 return Parser.Error(IDLoc, "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(IDLoc, "too few operands for instruction");
1321 ErrorLoc = Operands[ErrorInfo]->getStartLoc();
1322 if (ErrorLoc == SMLoc())
1323 ErrorLoc = IDLoc;
1324 }
1325 return Parser.Error(ErrorLoc, "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(IDLoc,
1343 "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(".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(SecName, SectionKind::getText(), 0,
1368 Group, MCSection::NonUniqueID);
1369 getStreamer().switchSection(WS);
1370 // Also generate DWARF for this section if requested.
1371 if (getContext().getGenDwarfForAssembly())
1372 getContext().addGenDwarfSection(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(IDLoc);
1385 CurrentState = FunctionLabel;
1386 LastFunctionLabel = Symbol;
1387 push(Function);
1388 }
1389 }
1390
1391 void onEndOfFunction(SMLoc ErrorLoc) {
1392 if (!SkipTypeCheck)
1393 TC.endOfFunction(ErrorLoc, 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
1408
1409#define GET_REGISTER_MATCHER
1410#define GET_SUBTARGET_FEATURE_NAME
1411#define GET_MATCHER_IMPLEMENTATION
1412#include "WebAssemblyGenAsmMatcher.inc"
1413
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}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define X(NUM, ENUM, NAME)
Definition ELF.h:851
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:213
#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)
#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
FeatureBitset & set()
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:90
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:333
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:214
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:55
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:489
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:257
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:140
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition StringRef.h:628
bool contains(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition StringRef.h:445
LLVM_ABI void clear()
Clears function-level state.
Definition Context.cpp:653
#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)
Context & getContext() const
Definition BasicBlock.h:99
@ 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