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