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