Path: blob/main/contrib/llvm-project/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
35293 views
//==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file is part of the WebAssembly Assembler.10///11/// It contains code to translate a parsed .s file into MCInsts.12///13//===----------------------------------------------------------------------===//1415#include "AsmParser/WebAssemblyAsmTypeCheck.h"16#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"17#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"18#include "MCTargetDesc/WebAssemblyTargetStreamer.h"19#include "TargetInfo/WebAssemblyTargetInfo.h"20#include "WebAssembly.h"21#include "llvm/MC/MCContext.h"22#include "llvm/MC/MCExpr.h"23#include "llvm/MC/MCInst.h"24#include "llvm/MC/MCInstrInfo.h"25#include "llvm/MC/MCParser/MCAsmLexer.h"26#include "llvm/MC/MCParser/MCParsedAsmOperand.h"27#include "llvm/MC/MCParser/MCTargetAsmParser.h"28#include "llvm/MC/MCSectionWasm.h"29#include "llvm/MC/MCStreamer.h"30#include "llvm/MC/MCSubtargetInfo.h"31#include "llvm/MC/MCSymbol.h"32#include "llvm/MC/MCSymbolWasm.h"33#include "llvm/MC/TargetRegistry.h"34#include "llvm/Support/SourceMgr.h"3536using namespace llvm;3738#define DEBUG_TYPE "wasm-asm-parser"3940static const char *getSubtargetFeatureName(uint64_t Val);4142namespace {4344/// WebAssemblyOperand - Instances of this class represent the operands in a45/// parsed Wasm machine instruction.46struct WebAssemblyOperand : public MCParsedAsmOperand {47enum KindTy { Token, Integer, Float, Symbol, BrList } Kind;4849SMLoc StartLoc, EndLoc;5051struct TokOp {52StringRef Tok;53};5455struct IntOp {56int64_t Val;57};5859struct FltOp {60double Val;61};6263struct SymOp {64const MCExpr *Exp;65};6667struct BrLOp {68std::vector<unsigned> List;69};7071union {72struct TokOp Tok;73struct IntOp Int;74struct FltOp Flt;75struct SymOp Sym;76struct BrLOp BrL;77};7879WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T)80: Kind(K), StartLoc(Start), EndLoc(End), Tok(T) {}81WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, IntOp I)82: Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}83WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, FltOp F)84: Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {}85WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp S)86: Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {}87WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End)88: Kind(K), StartLoc(Start), EndLoc(End), BrL() {}8990~WebAssemblyOperand() {91if (isBrList())92BrL.~BrLOp();93}9495bool isToken() const override { return Kind == Token; }96bool isImm() const override { return Kind == Integer || Kind == Symbol; }97bool isFPImm() const { return Kind == Float; }98bool isMem() const override { return false; }99bool isReg() const override { return false; }100bool isBrList() const { return Kind == BrList; }101102MCRegister getReg() const override {103llvm_unreachable("Assembly inspects a register operand");104return 0;105}106107StringRef getToken() const {108assert(isToken());109return Tok.Tok;110}111112SMLoc getStartLoc() const override { return StartLoc; }113SMLoc getEndLoc() const override { return EndLoc; }114115void addRegOperands(MCInst &, unsigned) const {116// Required by the assembly matcher.117llvm_unreachable("Assembly matcher creates register operands");118}119120void addImmOperands(MCInst &Inst, unsigned N) const {121assert(N == 1 && "Invalid number of operands!");122if (Kind == Integer)123Inst.addOperand(MCOperand::createImm(Int.Val));124else if (Kind == Symbol)125Inst.addOperand(MCOperand::createExpr(Sym.Exp));126else127llvm_unreachable("Should be integer immediate or symbol!");128}129130void addFPImmf32Operands(MCInst &Inst, unsigned N) const {131assert(N == 1 && "Invalid number of operands!");132if (Kind == Float)133Inst.addOperand(134MCOperand::createSFPImm(bit_cast<uint32_t>(float(Flt.Val))));135else136llvm_unreachable("Should be float immediate!");137}138139void addFPImmf64Operands(MCInst &Inst, unsigned N) const {140assert(N == 1 && "Invalid number of operands!");141if (Kind == Float)142Inst.addOperand(MCOperand::createDFPImm(bit_cast<uint64_t>(Flt.Val)));143else144llvm_unreachable("Should be float immediate!");145}146147void addBrListOperands(MCInst &Inst, unsigned N) const {148assert(N == 1 && isBrList() && "Invalid BrList!");149for (auto Br : BrL.List)150Inst.addOperand(MCOperand::createImm(Br));151}152153void print(raw_ostream &OS) const override {154switch (Kind) {155case Token:156OS << "Tok:" << Tok.Tok;157break;158case Integer:159OS << "Int:" << Int.Val;160break;161case Float:162OS << "Flt:" << Flt.Val;163break;164case Symbol:165OS << "Sym:" << Sym.Exp;166break;167case BrList:168OS << "BrList:" << BrL.List.size();169break;170}171}172};173174// Perhaps this should go somewhere common.175static wasm::WasmLimits DefaultLimits() {176return {wasm::WASM_LIMITS_FLAG_NONE, 0, 0};177}178179static MCSymbolWasm *GetOrCreateFunctionTableSymbol(MCContext &Ctx,180const StringRef &Name,181bool is64) {182MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));183if (Sym) {184if (!Sym->isFunctionTable())185Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");186} else {187Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));188Sym->setFunctionTable(is64);189// The default function table is synthesized by the linker.190Sym->setUndefined();191}192return Sym;193}194195class WebAssemblyAsmParser final : public MCTargetAsmParser {196MCAsmParser &Parser;197MCAsmLexer &Lexer;198199// Order of labels, directives and instructions in a .s file have no200// syntactical enforcement. This class is a callback from the actual parser,201// and yet we have to be feeding data to the streamer in a very particular202// order to ensure a correct binary encoding that matches the regular backend203// (the streamer does not enforce this). This "state machine" enum helps204// guarantee that correct order.205enum ParserState {206FileStart,207FunctionLabel,208FunctionStart,209FunctionLocals,210Instructions,211EndFunction,212DataSection,213} CurrentState = FileStart;214215// For ensuring blocks are properly nested.216enum NestingType {217Function,218Block,219Loop,220Try,221CatchAll,222If,223Else,224Undefined,225};226struct Nested {227NestingType NT;228wasm::WasmSignature Sig;229};230std::vector<Nested> NestingStack;231232MCSymbolWasm *DefaultFunctionTable = nullptr;233MCSymbol *LastFunctionLabel = nullptr;234235bool is64;236237WebAssemblyAsmTypeCheck TC;238// Don't type check if -no-type-check was set.239bool SkipTypeCheck;240241public:242WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,243const MCInstrInfo &MII, const MCTargetOptions &Options)244: MCTargetAsmParser(Options, STI, MII), Parser(Parser),245Lexer(Parser.getLexer()), is64(STI.getTargetTriple().isArch64Bit()),246TC(Parser, MII, is64), SkipTypeCheck(Options.MCNoTypeCheck) {247setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));248// Don't type check if this is inline asm, since that is a naked sequence of249// instructions without a function/locals decl.250auto &SM = Parser.getSourceManager();251auto BufferName =252SM.getBufferInfo(SM.getMainFileID()).Buffer->getBufferIdentifier();253if (BufferName == "<inline asm>")254SkipTypeCheck = true;255}256257void Initialize(MCAsmParser &Parser) override {258MCAsmParserExtension::Initialize(Parser);259260DefaultFunctionTable = GetOrCreateFunctionTableSymbol(261getContext(), "__indirect_function_table", is64);262if (!STI->checkFeatures("+reference-types"))263DefaultFunctionTable->setOmitFromLinkingSection();264}265266#define GET_ASSEMBLER_HEADER267#include "WebAssemblyGenAsmMatcher.inc"268269// TODO: This is required to be implemented, but appears unused.270bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override {271llvm_unreachable("parseRegister is not implemented.");272}273ParseStatus tryParseRegister(MCRegister &Reg, SMLoc &StartLoc,274SMLoc &EndLoc) override {275llvm_unreachable("tryParseRegister is not implemented.");276}277278bool error(const Twine &Msg, const AsmToken &Tok) {279return Parser.Error(Tok.getLoc(), Msg + Tok.getString());280}281282bool error(const Twine &Msg, SMLoc Loc = SMLoc()) {283return Parser.Error(Loc.isValid() ? Loc : Lexer.getTok().getLoc(), Msg);284}285286std::pair<StringRef, StringRef> nestingString(NestingType NT) {287switch (NT) {288case Function:289return {"function", "end_function"};290case Block:291return {"block", "end_block"};292case Loop:293return {"loop", "end_loop"};294case Try:295return {"try", "end_try/delegate"};296case CatchAll:297return {"catch_all", "end_try"};298case If:299return {"if", "end_if"};300case Else:301return {"else", "end_if"};302default:303llvm_unreachable("unknown NestingType");304}305}306307void push(NestingType NT, wasm::WasmSignature Sig = wasm::WasmSignature()) {308NestingStack.push_back({NT, Sig});309}310311bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {312if (NestingStack.empty())313return error(Twine("End of block construct with no start: ") + Ins);314auto Top = NestingStack.back();315if (Top.NT != NT1 && Top.NT != NT2)316return error(Twine("Block construct type mismatch, expected: ") +317nestingString(Top.NT).second + ", instead got: " + Ins);318TC.setLastSig(Top.Sig);319NestingStack.pop_back();320return false;321}322323// Pop a NestingType and push a new NestingType with the same signature. Used324// for if-else and try-catch(_all).325bool popAndPushWithSameSignature(StringRef Ins, NestingType PopNT,326NestingType PushNT) {327if (NestingStack.empty())328return error(Twine("End of block construct with no start: ") + Ins);329auto Sig = NestingStack.back().Sig;330if (pop(Ins, PopNT))331return true;332push(PushNT, Sig);333return false;334}335336bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) {337auto Err = !NestingStack.empty();338while (!NestingStack.empty()) {339error(Twine("Unmatched block construct(s) at function end: ") +340nestingString(NestingStack.back().NT).first,341Loc);342NestingStack.pop_back();343}344return Err;345}346347bool isNext(AsmToken::TokenKind Kind) {348auto Ok = Lexer.is(Kind);349if (Ok)350Parser.Lex();351return Ok;352}353354bool expect(AsmToken::TokenKind Kind, const char *KindName) {355if (!isNext(Kind))356return error(std::string("Expected ") + KindName + ", instead got: ",357Lexer.getTok());358return false;359}360361StringRef expectIdent() {362if (!Lexer.is(AsmToken::Identifier)) {363error("Expected identifier, got: ", Lexer.getTok());364return StringRef();365}366auto Name = Lexer.getTok().getString();367Parser.Lex();368return Name;369}370371bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {372while (Lexer.is(AsmToken::Identifier)) {373auto Type = WebAssembly::parseType(Lexer.getTok().getString());374if (!Type)375return error("unknown type: ", Lexer.getTok());376Types.push_back(*Type);377Parser.Lex();378if (!isNext(AsmToken::Comma))379break;380}381return false;382}383384void parseSingleInteger(bool IsNegative, OperandVector &Operands) {385auto &Int = Lexer.getTok();386int64_t Val = Int.getIntVal();387if (IsNegative)388Val = -Val;389Operands.push_back(std::make_unique<WebAssemblyOperand>(390WebAssemblyOperand::Integer, Int.getLoc(), Int.getEndLoc(),391WebAssemblyOperand::IntOp{Val}));392Parser.Lex();393}394395bool parseSingleFloat(bool IsNegative, OperandVector &Operands) {396auto &Flt = Lexer.getTok();397double Val;398if (Flt.getString().getAsDouble(Val, false))399return error("Cannot parse real: ", Flt);400if (IsNegative)401Val = -Val;402Operands.push_back(std::make_unique<WebAssemblyOperand>(403WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(),404WebAssemblyOperand::FltOp{Val}));405Parser.Lex();406return false;407}408409bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {410if (Lexer.isNot(AsmToken::Identifier))411return true;412auto &Flt = Lexer.getTok();413auto S = Flt.getString();414double Val;415if (S.compare_insensitive("infinity") == 0) {416Val = std::numeric_limits<double>::infinity();417} else if (S.compare_insensitive("nan") == 0) {418Val = std::numeric_limits<double>::quiet_NaN();419} else {420return true;421}422if (IsNegative)423Val = -Val;424Operands.push_back(std::make_unique<WebAssemblyOperand>(425WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(),426WebAssemblyOperand::FltOp{Val}));427Parser.Lex();428return false;429}430431bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) {432// FIXME: there is probably a cleaner way to do this.433auto IsLoadStore = InstName.contains(".load") ||434InstName.contains(".store") ||435InstName.contains("prefetch");436auto IsAtomic = InstName.contains("atomic.");437if (IsLoadStore || IsAtomic) {438// Parse load/store operands of the form: offset:p2align=align439if (IsLoadStore && isNext(AsmToken::Colon)) {440auto Id = expectIdent();441if (Id != "p2align")442return error("Expected p2align, instead got: " + Id);443if (expect(AsmToken::Equal, "="))444return true;445if (!Lexer.is(AsmToken::Integer))446return error("Expected integer constant");447parseSingleInteger(false, Operands);448} else {449// v128.{load,store}{8,16,32,64}_lane has both a memarg and a lane450// index. We need to avoid parsing an extra alignment operand for the451// lane index.452auto IsLoadStoreLane = InstName.contains("_lane");453if (IsLoadStoreLane && Operands.size() == 4)454return false;455// Alignment not specified (or atomics, must use default alignment).456// We can't just call WebAssembly::GetDefaultP2Align since we don't have457// an opcode until after the assembly matcher, so set a default to fix458// up later.459auto Tok = Lexer.getTok();460Operands.push_back(std::make_unique<WebAssemblyOperand>(461WebAssemblyOperand::Integer, Tok.getLoc(), Tok.getEndLoc(),462WebAssemblyOperand::IntOp{-1}));463}464}465return false;466}467468void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,469WebAssembly::BlockType BT) {470if (BT != WebAssembly::BlockType::Void) {471wasm::WasmSignature Sig({static_cast<wasm::ValType>(BT)}, {});472TC.setLastSig(Sig);473NestingStack.back().Sig = Sig;474}475Operands.push_back(std::make_unique<WebAssemblyOperand>(476WebAssemblyOperand::Integer, NameLoc, NameLoc,477WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));478}479480bool parseLimits(wasm::WasmLimits *Limits) {481auto Tok = Lexer.getTok();482if (!Tok.is(AsmToken::Integer))483return error("Expected integer constant, instead got: ", Tok);484int64_t Val = Tok.getIntVal();485assert(Val >= 0);486Limits->Minimum = Val;487Parser.Lex();488489if (isNext(AsmToken::Comma)) {490Limits->Flags |= wasm::WASM_LIMITS_FLAG_HAS_MAX;491auto Tok = Lexer.getTok();492if (!Tok.is(AsmToken::Integer))493return error("Expected integer constant, instead got: ", Tok);494int64_t Val = Tok.getIntVal();495assert(Val >= 0);496Limits->Maximum = Val;497Parser.Lex();498}499return false;500}501502bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) {503if (STI->checkFeatures("+reference-types")) {504// If the reference-types feature is enabled, there is an explicit table505// operand. To allow the same assembly to be compiled with or without506// reference types, we allow the operand to be omitted, in which case we507// default to __indirect_function_table.508auto &Tok = Lexer.getTok();509if (Tok.is(AsmToken::Identifier)) {510auto *Sym =511GetOrCreateFunctionTableSymbol(getContext(), Tok.getString(), is64);512const auto *Val = MCSymbolRefExpr::create(Sym, getContext());513*Op = std::make_unique<WebAssemblyOperand>(514WebAssemblyOperand::Symbol, Tok.getLoc(), Tok.getEndLoc(),515WebAssemblyOperand::SymOp{Val});516Parser.Lex();517return expect(AsmToken::Comma, ",");518} else {519const auto *Val =520MCSymbolRefExpr::create(DefaultFunctionTable, getContext());521*Op = std::make_unique<WebAssemblyOperand>(522WebAssemblyOperand::Symbol, SMLoc(), SMLoc(),523WebAssemblyOperand::SymOp{Val});524return false;525}526} else {527// For the MVP there is at most one table whose number is 0, but we can't528// write a table symbol or issue relocations. Instead we just ensure the529// table is live and write a zero.530getStreamer().emitSymbolAttribute(DefaultFunctionTable, MCSA_NoDeadStrip);531*Op = std::make_unique<WebAssemblyOperand>(WebAssemblyOperand::Integer,532SMLoc(), SMLoc(),533WebAssemblyOperand::IntOp{0});534return false;535}536}537538bool ParseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,539SMLoc NameLoc, OperandVector &Operands) override {540// Note: Name does NOT point into the sourcecode, but to a local, so541// use NameLoc instead.542Name = StringRef(NameLoc.getPointer(), Name.size());543544// WebAssembly has instructions with / in them, which AsmLexer parses545// as separate tokens, so if we find such tokens immediately adjacent (no546// whitespace), expand the name to include them:547for (;;) {548auto &Sep = Lexer.getTok();549if (Sep.getLoc().getPointer() != Name.end() ||550Sep.getKind() != AsmToken::Slash)551break;552// Extend name with /553Name = StringRef(Name.begin(), Name.size() + Sep.getString().size());554Parser.Lex();555// We must now find another identifier, or error.556auto &Id = Lexer.getTok();557if (Id.getKind() != AsmToken::Identifier ||558Id.getLoc().getPointer() != Name.end())559return error("Incomplete instruction name: ", Id);560Name = StringRef(Name.begin(), Name.size() + Id.getString().size());561Parser.Lex();562}563564// Now construct the name as first operand.565Operands.push_back(std::make_unique<WebAssemblyOperand>(566WebAssemblyOperand::Token, NameLoc, SMLoc::getFromPointer(Name.end()),567WebAssemblyOperand::TokOp{Name}));568569// If this instruction is part of a control flow structure, ensure570// proper nesting.571bool ExpectBlockType = false;572bool ExpectFuncType = false;573std::unique_ptr<WebAssemblyOperand> FunctionTable;574if (Name == "block") {575push(Block);576ExpectBlockType = true;577} else if (Name == "loop") {578push(Loop);579ExpectBlockType = true;580} else if (Name == "try") {581push(Try);582ExpectBlockType = true;583} else if (Name == "if") {584push(If);585ExpectBlockType = true;586} else if (Name == "else") {587if (popAndPushWithSameSignature(Name, If, Else))588return true;589} else if (Name == "catch") {590if (popAndPushWithSameSignature(Name, Try, Try))591return true;592} else if (Name == "catch_all") {593if (popAndPushWithSameSignature(Name, Try, CatchAll))594return true;595} else if (Name == "end_if") {596if (pop(Name, If, Else))597return true;598} else if (Name == "end_try") {599if (pop(Name, Try, CatchAll))600return true;601} else if (Name == "delegate") {602if (pop(Name, Try))603return true;604} else if (Name == "end_loop") {605if (pop(Name, Loop))606return true;607} else if (Name == "end_block") {608if (pop(Name, Block))609return true;610} else if (Name == "end_function") {611ensureLocals(getStreamer());612CurrentState = EndFunction;613if (pop(Name, Function) || ensureEmptyNestingStack())614return true;615} else if (Name == "call_indirect" || Name == "return_call_indirect") {616// These instructions have differing operand orders in the text format vs617// the binary formats. The MC instructions follow the binary format, so618// here we stash away the operand and append it later.619if (parseFunctionTableOperand(&FunctionTable))620return true;621ExpectFuncType = true;622}623624if (ExpectFuncType || (ExpectBlockType && Lexer.is(AsmToken::LParen))) {625// This has a special TYPEINDEX operand which in text we626// represent as a signature, such that we can re-build this signature,627// attach it to an anonymous symbol, which is what WasmObjectWriter628// expects to be able to recreate the actual unique-ified type indices.629auto &Ctx = getContext();630auto Loc = Parser.getTok();631auto Signature = Ctx.createWasmSignature();632if (parseSignature(Signature))633return true;634// Got signature as block type, don't need more635TC.setLastSig(*Signature);636if (ExpectBlockType)637NestingStack.back().Sig = *Signature;638ExpectBlockType = false;639// The "true" here will cause this to be a nameless symbol.640MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);641auto *WasmSym = cast<MCSymbolWasm>(Sym);642WasmSym->setSignature(Signature);643WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);644const MCExpr *Expr = MCSymbolRefExpr::create(645WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);646Operands.push_back(std::make_unique<WebAssemblyOperand>(647WebAssemblyOperand::Symbol, Loc.getLoc(), Loc.getEndLoc(),648WebAssemblyOperand::SymOp{Expr}));649}650651while (Lexer.isNot(AsmToken::EndOfStatement)) {652auto &Tok = Lexer.getTok();653switch (Tok.getKind()) {654case AsmToken::Identifier: {655if (!parseSpecialFloatMaybe(false, Operands))656break;657auto &Id = Lexer.getTok();658if (ExpectBlockType) {659// Assume this identifier is a block_type.660auto BT = WebAssembly::parseBlockType(Id.getString());661if (BT == WebAssembly::BlockType::Invalid)662return error("Unknown block type: ", Id);663addBlockTypeOperand(Operands, NameLoc, BT);664Parser.Lex();665} else {666// Assume this identifier is a label.667const MCExpr *Val;668SMLoc Start = Id.getLoc();669SMLoc End;670if (Parser.parseExpression(Val, End))671return error("Cannot parse symbol: ", Lexer.getTok());672Operands.push_back(std::make_unique<WebAssemblyOperand>(673WebAssemblyOperand::Symbol, Start, End,674WebAssemblyOperand::SymOp{Val}));675if (checkForP2AlignIfLoadStore(Operands, Name))676return true;677}678break;679}680case AsmToken::Minus:681Parser.Lex();682if (Lexer.is(AsmToken::Integer)) {683parseSingleInteger(true, Operands);684if (checkForP2AlignIfLoadStore(Operands, Name))685return true;686} else if (Lexer.is(AsmToken::Real)) {687if (parseSingleFloat(true, Operands))688return true;689} else if (!parseSpecialFloatMaybe(true, Operands)) {690} else {691return error("Expected numeric constant instead got: ",692Lexer.getTok());693}694break;695case AsmToken::Integer:696parseSingleInteger(false, Operands);697if (checkForP2AlignIfLoadStore(Operands, Name))698return true;699break;700case AsmToken::Real: {701if (parseSingleFloat(false, Operands))702return true;703break;704}705case AsmToken::LCurly: {706Parser.Lex();707auto Op = std::make_unique<WebAssemblyOperand>(708WebAssemblyOperand::BrList, Tok.getLoc(), Tok.getEndLoc());709if (!Lexer.is(AsmToken::RCurly))710for (;;) {711Op->BrL.List.push_back(Lexer.getTok().getIntVal());712expect(AsmToken::Integer, "integer");713if (!isNext(AsmToken::Comma))714break;715}716expect(AsmToken::RCurly, "}");717Operands.push_back(std::move(Op));718break;719}720default:721return error("Unexpected token in operand: ", Tok);722}723if (Lexer.isNot(AsmToken::EndOfStatement)) {724if (expect(AsmToken::Comma, ","))725return true;726}727}728if (ExpectBlockType && Operands.size() == 1) {729// Support blocks with no operands as default to void.730addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void);731}732if (FunctionTable)733Operands.push_back(std::move(FunctionTable));734Parser.Lex();735return false;736}737738bool parseSignature(wasm::WasmSignature *Signature) {739if (expect(AsmToken::LParen, "("))740return true;741if (parseRegTypeList(Signature->Params))742return true;743if (expect(AsmToken::RParen, ")"))744return true;745if (expect(AsmToken::MinusGreater, "->"))746return true;747if (expect(AsmToken::LParen, "("))748return true;749if (parseRegTypeList(Signature->Returns))750return true;751if (expect(AsmToken::RParen, ")"))752return true;753return false;754}755756bool CheckDataSection() {757if (CurrentState != DataSection) {758auto WS = cast<MCSectionWasm>(getStreamer().getCurrentSectionOnly());759if (WS && WS->isText())760return error("data directive must occur in a data segment: ",761Lexer.getTok());762}763CurrentState = DataSection;764return false;765}766767// This function processes wasm-specific directives streamed to768// WebAssemblyTargetStreamer, all others go to the generic parser769// (see WasmAsmParser).770ParseStatus parseDirective(AsmToken DirectiveID) override {771assert(DirectiveID.getKind() == AsmToken::Identifier);772auto &Out = getStreamer();773auto &TOut =774reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());775auto &Ctx = Out.getContext();776777if (DirectiveID.getString() == ".globaltype") {778auto SymName = expectIdent();779if (SymName.empty())780return ParseStatus::Failure;781if (expect(AsmToken::Comma, ","))782return ParseStatus::Failure;783auto TypeTok = Lexer.getTok();784auto TypeName = expectIdent();785if (TypeName.empty())786return ParseStatus::Failure;787auto Type = WebAssembly::parseType(TypeName);788if (!Type)789return error("Unknown type in .globaltype directive: ", TypeTok);790// Optional mutable modifier. Default to mutable for historical reasons.791// Ideally we would have gone with immutable as the default and used `mut`792// as the modifier to match the `.wat` format.793bool Mutable = true;794if (isNext(AsmToken::Comma)) {795TypeTok = Lexer.getTok();796auto Id = expectIdent();797if (Id.empty())798return ParseStatus::Failure;799if (Id == "immutable")800Mutable = false;801else802// Should we also allow `mutable` and `mut` here for clarity?803return error("Unknown type in .globaltype modifier: ", TypeTok);804}805// Now set this symbol with the correct type.806auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));807WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);808WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(*Type), Mutable});809// And emit the directive again.810TOut.emitGlobalType(WasmSym);811return expect(AsmToken::EndOfStatement, "EOL");812}813814if (DirectiveID.getString() == ".tabletype") {815// .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]]816auto SymName = expectIdent();817if (SymName.empty())818return ParseStatus::Failure;819if (expect(AsmToken::Comma, ","))820return ParseStatus::Failure;821822auto ElemTypeTok = Lexer.getTok();823auto ElemTypeName = expectIdent();824if (ElemTypeName.empty())825return ParseStatus::Failure;826std::optional<wasm::ValType> ElemType =827WebAssembly::parseType(ElemTypeName);828if (!ElemType)829return error("Unknown type in .tabletype directive: ", ElemTypeTok);830831wasm::WasmLimits Limits = DefaultLimits();832if (isNext(AsmToken::Comma) && parseLimits(&Limits))833return ParseStatus::Failure;834835// Now that we have the name and table type, we can actually create the836// symbol837auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));838WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);839if (is64) {840Limits.Flags |= wasm::WASM_LIMITS_FLAG_IS_64;841}842wasm::WasmTableType Type = {*ElemType, Limits};843WasmSym->setTableType(Type);844TOut.emitTableType(WasmSym);845return expect(AsmToken::EndOfStatement, "EOL");846}847848if (DirectiveID.getString() == ".functype") {849// This code has to send things to the streamer similar to850// WebAssemblyAsmPrinter::EmitFunctionBodyStart.851// TODO: would be good to factor this into a common function, but the852// assembler and backend really don't share any common code, and this code853// parses the locals separately.854auto SymName = expectIdent();855if (SymName.empty())856return ParseStatus::Failure;857auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));858if (WasmSym->isDefined()) {859// We push 'Function' either when a label is parsed or a .functype860// directive is parsed. The reason it is not easy to do this uniformly861// in a single place is,862// 1. We can't do this at label parsing time only because there are863// cases we don't have .functype directive before a function label,864// in which case we don't know if the label is a function at the time865// of parsing.866// 2. We can't do this at .functype parsing time only because we want to867// detect a function started with a label and not ended correctly868// without encountering a .functype directive after the label.869if (CurrentState != FunctionLabel) {870// This .functype indicates a start of a function.871if (ensureEmptyNestingStack())872return ParseStatus::Failure;873push(Function);874}875CurrentState = FunctionStart;876LastFunctionLabel = WasmSym;877}878auto Signature = Ctx.createWasmSignature();879if (parseSignature(Signature))880return ParseStatus::Failure;881TC.funcDecl(*Signature);882WasmSym->setSignature(Signature);883WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);884TOut.emitFunctionType(WasmSym);885// TODO: backend also calls TOut.emitIndIdx, but that is not implemented.886return expect(AsmToken::EndOfStatement, "EOL");887}888889if (DirectiveID.getString() == ".export_name") {890auto SymName = expectIdent();891if (SymName.empty())892return ParseStatus::Failure;893if (expect(AsmToken::Comma, ","))894return ParseStatus::Failure;895auto ExportName = expectIdent();896if (ExportName.empty())897return ParseStatus::Failure;898auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));899WasmSym->setExportName(Ctx.allocateString(ExportName));900TOut.emitExportName(WasmSym, ExportName);901return expect(AsmToken::EndOfStatement, "EOL");902}903904if (DirectiveID.getString() == ".import_module") {905auto SymName = expectIdent();906if (SymName.empty())907return ParseStatus::Failure;908if (expect(AsmToken::Comma, ","))909return ParseStatus::Failure;910auto ImportModule = expectIdent();911if (ImportModule.empty())912return ParseStatus::Failure;913auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));914WasmSym->setImportModule(Ctx.allocateString(ImportModule));915TOut.emitImportModule(WasmSym, ImportModule);916return expect(AsmToken::EndOfStatement, "EOL");917}918919if (DirectiveID.getString() == ".import_name") {920auto SymName = expectIdent();921if (SymName.empty())922return ParseStatus::Failure;923if (expect(AsmToken::Comma, ","))924return ParseStatus::Failure;925auto ImportName = expectIdent();926if (ImportName.empty())927return ParseStatus::Failure;928auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));929WasmSym->setImportName(Ctx.allocateString(ImportName));930TOut.emitImportName(WasmSym, ImportName);931return expect(AsmToken::EndOfStatement, "EOL");932}933934if (DirectiveID.getString() == ".tagtype") {935auto SymName = expectIdent();936if (SymName.empty())937return ParseStatus::Failure;938auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));939auto Signature = Ctx.createWasmSignature();940if (parseRegTypeList(Signature->Params))941return ParseStatus::Failure;942WasmSym->setSignature(Signature);943WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);944TOut.emitTagType(WasmSym);945// TODO: backend also calls TOut.emitIndIdx, but that is not implemented.946return expect(AsmToken::EndOfStatement, "EOL");947}948949if (DirectiveID.getString() == ".local") {950if (CurrentState != FunctionStart)951return error(".local directive should follow the start of a function: ",952Lexer.getTok());953SmallVector<wasm::ValType, 4> Locals;954if (parseRegTypeList(Locals))955return ParseStatus::Failure;956TC.localDecl(Locals);957TOut.emitLocal(Locals);958CurrentState = FunctionLocals;959return expect(AsmToken::EndOfStatement, "EOL");960}961962if (DirectiveID.getString() == ".int8" ||963DirectiveID.getString() == ".int16" ||964DirectiveID.getString() == ".int32" ||965DirectiveID.getString() == ".int64") {966if (CheckDataSection())967return ParseStatus::Failure;968const MCExpr *Val;969SMLoc End;970if (Parser.parseExpression(Val, End))971return error("Cannot parse .int expression: ", Lexer.getTok());972size_t NumBits = 0;973DirectiveID.getString().drop_front(4).getAsInteger(10, NumBits);974Out.emitValue(Val, NumBits / 8, End);975return expect(AsmToken::EndOfStatement, "EOL");976}977978if (DirectiveID.getString() == ".asciz") {979if (CheckDataSection())980return ParseStatus::Failure;981std::string S;982if (Parser.parseEscapedString(S))983return error("Cannot parse string constant: ", Lexer.getTok());984Out.emitBytes(StringRef(S.c_str(), S.length() + 1));985return expect(AsmToken::EndOfStatement, "EOL");986}987988return ParseStatus::NoMatch; // We didn't process this directive.989}990991// Called either when the first instruction is parsed of the function ends.992void ensureLocals(MCStreamer &Out) {993if (CurrentState == FunctionStart) {994// We haven't seen a .local directive yet. The streamer requires locals to995// be encoded as a prelude to the instructions, so emit an empty list of996// locals here.997auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>(998*Out.getTargetStreamer());999TOut.emitLocal(SmallVector<wasm::ValType, 0>());1000CurrentState = FunctionLocals;1001}1002}10031004bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/,1005OperandVector &Operands, MCStreamer &Out,1006uint64_t &ErrorInfo,1007bool MatchingInlineAsm) override {1008MCInst Inst;1009Inst.setLoc(IDLoc);1010FeatureBitset MissingFeatures;1011unsigned MatchResult = MatchInstructionImpl(1012Operands, Inst, ErrorInfo, MissingFeatures, MatchingInlineAsm);1013switch (MatchResult) {1014case Match_Success: {1015ensureLocals(Out);1016// Fix unknown p2align operands.1017auto Align = WebAssembly::GetDefaultP2AlignAny(Inst.getOpcode());1018if (Align != -1U) {1019auto &Op0 = Inst.getOperand(0);1020if (Op0.getImm() == -1)1021Op0.setImm(Align);1022}1023if (is64) {1024// Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having1025// an offset64 arg instead of offset32, but to the assembler matcher1026// they're both immediates so don't get selected for.1027auto Opc64 = WebAssembly::getWasm64Opcode(1028static_cast<uint16_t>(Inst.getOpcode()));1029if (Opc64 >= 0) {1030Inst.setOpcode(Opc64);1031}1032}1033if (!SkipTypeCheck && TC.typeCheck(IDLoc, Inst, Operands))1034return true;1035Out.emitInstruction(Inst, getSTI());1036if (CurrentState == EndFunction) {1037onEndOfFunction(IDLoc);1038} else {1039CurrentState = Instructions;1040}1041return false;1042}1043case Match_MissingFeature: {1044assert(MissingFeatures.count() > 0 && "Expected missing features");1045SmallString<128> Message;1046raw_svector_ostream OS(Message);1047OS << "instruction requires:";1048for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)1049if (MissingFeatures.test(i))1050OS << ' ' << getSubtargetFeatureName(i);1051return Parser.Error(IDLoc, Message);1052}1053case Match_MnemonicFail:1054return Parser.Error(IDLoc, "invalid instruction");1055case Match_NearMisses:1056return Parser.Error(IDLoc, "ambiguous instruction");1057case Match_InvalidTiedOperand:1058case Match_InvalidOperand: {1059SMLoc ErrorLoc = IDLoc;1060if (ErrorInfo != ~0ULL) {1061if (ErrorInfo >= Operands.size())1062return Parser.Error(IDLoc, "too few operands for instruction");1063ErrorLoc = Operands[ErrorInfo]->getStartLoc();1064if (ErrorLoc == SMLoc())1065ErrorLoc = IDLoc;1066}1067return Parser.Error(ErrorLoc, "invalid operand for instruction");1068}1069}1070llvm_unreachable("Implement any new match types added!");1071}10721073void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override {1074// Code below only applies to labels in text sections.1075auto CWS = cast<MCSectionWasm>(getStreamer().getCurrentSectionOnly());1076if (!CWS->isText())1077return;10781079auto WasmSym = cast<MCSymbolWasm>(Symbol);1080// Unlike other targets, we don't allow data in text sections (labels1081// declared with .type @object).1082if (WasmSym->getType() == wasm::WASM_SYMBOL_TYPE_DATA) {1083Parser.Error(IDLoc,1084"Wasm doesn\'t support data symbols in text sections");1085return;1086}10871088// Start a new section for the next function automatically, since our1089// object writer expects each function to have its own section. This way1090// The user can't forget this "convention".1091auto SymName = Symbol->getName();1092if (SymName.starts_with(".L"))1093return; // Local Symbol.10941095// TODO: If the user explicitly creates a new function section, we ignore1096// its name when we create this one. It would be nice to honor their1097// choice, while still ensuring that we create one if they forget.1098// (that requires coordination with WasmAsmParser::parseSectionDirective)1099auto SecName = ".text." + SymName;11001101auto *Group = CWS->getGroup();1102// If the current section is a COMDAT, also set the flag on the symbol.1103// TODO: Currently the only place that the symbols' comdat flag matters is1104// for importing comdat functions. But there's no way to specify that in1105// assembly currently.1106if (Group)1107WasmSym->setComdat(true);1108auto *WS = getContext().getWasmSection(SecName, SectionKind::getText(), 0,1109Group, MCContext::GenericSectionID);1110getStreamer().switchSection(WS);1111// Also generate DWARF for this section if requested.1112if (getContext().getGenDwarfForAssembly())1113getContext().addGenDwarfSection(WS);11141115if (WasmSym->isFunction()) {1116// We give the location of the label (IDLoc) here, because otherwise the1117// lexer's next location will be used, which can be confusing. For1118// example:1119//1120// test0: ; This function does not end properly1121// ...1122//1123// test1: ; We would like to point to this line for error1124// ... . Not this line, which can contain any instruction1125ensureEmptyNestingStack(IDLoc);1126CurrentState = FunctionLabel;1127LastFunctionLabel = Symbol;1128push(Function);1129}1130}11311132void onEndOfFunction(SMLoc ErrorLoc) {1133if (!SkipTypeCheck)1134TC.endOfFunction(ErrorLoc);1135// Reset the type checker state.1136TC.Clear();1137}11381139void onEndOfFile() override { ensureEmptyNestingStack(); }1140};1141} // end anonymous namespace11421143// Force static initialization.1144extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmParser() {1145RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32());1146RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64());1147}11481149#define GET_REGISTER_MATCHER1150#define GET_SUBTARGET_FEATURE_NAME1151#define GET_MATCHER_IMPLEMENTATION1152#include "WebAssemblyGenAsmMatcher.inc"11531154StringRef GetMnemonic(unsigned Opc) {1155// FIXME: linear search!1156for (auto &ME : MatchTable0) {1157if (ME.Opcode == Opc) {1158return ME.getMnemonic();1159}1160}1161assert(false && "mnemonic not found");1162return StringRef();1163}116411651166