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(MCOperand::createImm(Int.Val)); 142 else if (Kind == Symbol) 143 Inst.addOperand(MCOperand::createExpr(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 MCOperand::createSFPImm(bit_cast<uint32_t>(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(MCOperand::createDFPImm(bit_cast<uint64_t>(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(MCOperand::createImm(Br)); 169 } 170 171 void addCatchListOperands(MCInst &Inst, unsigned N) const { 172 assert(N == 1 && isCatchList() && "Invalid CatchList!"); 173 Inst.addOperand(MCOperand::createImm(CaL.List.size())); 174 for (auto Ca : CaL.List) { 175 Inst.addOperand(MCOperand::createImm(Ca.Opcode)); 176 if (Ca.Opcode == wasm::WASM_OPCODE_CATCH || 177 Ca.Opcode == wasm::WASM_OPCODE_CATCH_REF) 178 Inst.addOperand(MCOperand::createExpr(Ca.Tag)); 179 Inst.addOperand(MCOperand::createImm(Ca.Dest)); 180 } 181 } 182 183 void print(raw_ostream &OS, const MCAsmInfo &MAI) const override { 184 switch (Kind) { 185 case Token: 186 OS << "Tok:" << Tok.Tok; 187 break; 188 case Integer: 189 OS << "Int:" << Int.Val; 190 break; 191 case Float: 192 OS << "Flt:" << Flt.Val; 193 break; 194 case Symbol: 195 OS << "Sym:" << Sym.Exp; 196 break; 197 case BrList: 198 OS << "BrList:" << BrL.List.size(); 199 break; 200 case CatchList: 201 OS << "CaList:" << CaL.List.size(); 202 break; 203 } 204 } 205 }; 206 207 // Perhaps this should go somewhere common. 208 static wasm::WasmLimits defaultLimits() { 209 return {wasm::WASM_LIMITS_FLAG_NONE, 0, 0, 0}; 210 } 211 212 static MCSymbolWasm *getOrCreateFunctionTableSymbol(MCContext &Ctx, 213 const StringRef &Name, 214 bool Is64) { 215 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 216 if (Sym) { 217 if (!Sym->isFunctionTable()) 218 Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 219 } else { 220 Sym = cast<MCSymbolWasm>(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(STI.getFeatureBits()); 282 283 // bulk-memory implies bulk-memory-opt 284 if (FBS.test(WebAssembly::FeatureBulkMemory)) { 285 FBS.set(WebAssembly::FeatureBulkMemoryOpt); 286 } 287 // reference-types implies call-indirect-overlong 288 if (FBS.test(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(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 getContext(), "__indirect_function_table", Is64); 307 if (!STI->checkFeatures("+call-indirect-overlong") && 308 !STI->checkFeatures("+reference-types")) 309 DefaultFunctionTable->setOmitFromLinkingSection(); 310 } 311 312 #define GET_ASSEMBLER_HEADER 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(Tok.getLoc(), Msg + Tok.getString()); 326 } 327 328 bool error(const Twine &Msg, SMLoc Loc = SMLoc()) { 329 return Parser.Error(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({NT, Sig}); 357 } 358 359 bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) { 360 if (NestingStack.empty()) 361 return error(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(Twine("Block construct type mismatch, expected: ") + 365 nestingString(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(Twine("End of block construct with no start: ") + Ins); 377 auto Sig = NestingStack.back().Sig; 378 if (pop(Ins, PopNT)) 379 return true; 380 push(PushNT, Sig); 381 return false; 382 } 383 384 bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) { 385 auto Err = !NestingStack.empty(); 386 while (!NestingStack.empty()) { 387 error(Twine("Unmatched block construct(s) at function end: ") + 388 nestingString(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(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(std::string("Expected ") + KindName + ", instead got: ", 405 Lexer.getTok()); 406 return false; 407 } 408 409 StringRef expectIdent() { 410 if (!Lexer.is(AsmToken::Identifier)) { 411 error("Expected identifier, got: ", 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(AsmToken::Identifier)) { 421 auto Type = WebAssembly::parseType(Lexer.getTok().getString()); 422 if (!Type) 423 return error("unknown type: ", Lexer.getTok()); 424 Types.push_back(*Type); 425 Parser.Lex(); 426 if (!isNext(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(std::make_unique<WebAssemblyOperand>( 438 Int.getLoc(), Int.getEndLoc(), WebAssemblyOperand::IntOp{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(Val, false)) 446 return error("Cannot parse real: ", Flt); 447 if (IsNegative) 448 Val = -Val; 449 Operands.push_back(std::make_unique<WebAssemblyOperand>( 450 Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{Val})); 451 Parser.Lex(); 452 return false; 453 } 454 455 bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) { 456 if (Lexer.isNot(AsmToken::Identifier)) 457 return true; 458 auto &Flt = Lexer.getTok(); 459 auto S = Flt.getString(); 460 double Val; 461 if (S.compare_insensitive("infinity") == 0) { 462 Val = std::numeric_limits<double>::infinity(); 463 } else if (S.compare_insensitive("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(std::make_unique<WebAssemblyOperand>( 471 Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{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(".load") || 479 InstName.contains(".store") || 480 InstName.contains("prefetch"); 481 auto IsAtomic = InstName.contains("atomic."); 482 if (IsLoadStore || IsAtomic) { 483 // Parse load/store operands of the form: offset:p2align=align 484 if (IsLoadStore && isNext(AsmToken::Colon)) { 485 auto Id = expectIdent(); 486 if (Id != "p2align") 487 return error("Expected p2align, instead got: " + Id); 488 if (expect(AsmToken::Equal, "=")) 489 return true; 490 if (!Lexer.is(AsmToken::Integer)) 491 return error("Expected integer constant"); 492 parseSingleInteger(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("_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(std::make_unique<WebAssemblyOperand>( 506 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::IntOp{-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(std::make_unique<WebAssemblyOperand>( 522 NameLoc, NameLoc, WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)})); 523 } 524 525 bool parseLimits(wasm::WasmLimits *Limits) { 526 auto Tok = Lexer.getTok(); 527 if (!Tok.is(AsmToken::Integer)) 528 return error("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(AsmToken::Comma)) { 535 Limits->Flags |= wasm::WASM_LIMITS_FLAG_HAS_MAX; 536 auto Tok = Lexer.getTok(); 537 if (!Tok.is(AsmToken::Integer)) 538 return error("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("+call-indirect-overlong") || 549 STI->checkFeatures("+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(AsmToken::Identifier)) { 557 auto *Sym = 558 getOrCreateFunctionTableSymbol(getContext(), Tok.getString(), Is64); 559 const auto *Val = MCSymbolRefExpr::create(Sym, getContext()); 560 *Op = std::make_unique<WebAssemblyOperand>( 561 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::SymOp{Val}); 562 Parser.Lex(); 563 return expect(AsmToken::Comma, ","); 564 } 565 const auto *Val = 566 MCSymbolRefExpr::create(DefaultFunctionTable, getContext()); 567 *Op = std::make_unique<WebAssemblyOperand>( 568 SMLoc(), SMLoc(), WebAssemblyOperand::SymOp{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(DefaultFunctionTable, MCSA_NoDeadStrip); 575 *Op = std::make_unique<WebAssemblyOperand>(SMLoc(), SMLoc(), 576 WebAssemblyOperand::IntOp{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("Incomplete instruction name: ", 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(std::make_unique<WebAssemblyOperand>( 608 NameLoc, SMLoc::getFromPointer(Name.end()), 609 WebAssemblyOperand::TokOp{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(Block); 619 ExpectBlockType = true; 620 } else if (Name == "loop") { 621 push(Loop); 622 ExpectBlockType = true; 623 } else if (Name == "try") { 624 push(Try); 625 ExpectBlockType = true; 626 } else if (Name == "if") { 627 push(If); 628 ExpectBlockType = true; 629 } else if (Name == "else") { 630 if (popAndPushWithSameSignature(Name, If, Else)) 631 return true; 632 } else if (Name == "catch") { 633 if (popAndPushWithSameSignature(Name, Try, Try)) 634 return true; 635 } else if (Name == "catch_all") { 636 if (popAndPushWithSameSignature(Name, Try, CatchAll)) 637 return true; 638 } else if (Name == "try_table") { 639 push(TryTable); 640 ExpectBlockType = true; 641 ExpectCatchList = true; 642 } else if (Name == "end_if") { 643 if (pop(Name, If, Else)) 644 return true; 645 } else if (Name == "end_try") { 646 if (pop(Name, Try, CatchAll)) 647 return true; 648 } else if (Name == "end_try_table") { 649 if (pop(Name, TryTable)) 650 return true; 651 } else if (Name == "delegate") { 652 if (pop(Name, Try)) 653 return true; 654 } else if (Name == "end_loop") { 655 if (pop(Name, Loop)) 656 return true; 657 } else if (Name == "end_block") { 658 if (pop(Name, Block)) 659 return true; 660 } else if (Name == "end_function") { 661 ensureLocals(getStreamer()); 662 CurrentState = EndFunction; 663 if (pop(Name, 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(&FunctionTable)) 670 return true; 671 ExpectFuncType = true; 672 } else if (Name == "ref.test") { 673 // When we get support for wasm-gc types, this should become 674 // ExpectRefType. 675 ExpectFuncType = true; 676 } 677 678 // Returns true if the next tokens are a catch clause 679 auto PeekCatchList = [&]() { 680 if (Lexer.isNot(AsmToken::LParen)) 681 return false; 682 AsmToken NextTok = Lexer.peekTok(); 683 return NextTok.getKind() == AsmToken::Identifier && 684 NextTok.getIdentifier().starts_with("catch"); 685 }; 686 687 // Parse a multivalue block type 688 if (ExpectFuncType || 689 (Lexer.is(AsmToken::LParen) && ExpectBlockType && !PeekCatchList())) { 690 // This has a special TYPEINDEX operand which in text we 691 // represent as a signature, such that we can re-build this signature, 692 // attach it to an anonymous symbol, which is what WasmObjectWriter 693 // expects to be able to recreate the actual unique-ified type indices. 694 auto &Ctx = getContext(); 695 auto Loc = Parser.getTok(); 696 auto *Signature = Ctx.createWasmSignature(); 697 if (parseSignature(Signature)) 698 return true; 699 // Got signature as block type, don't need more 700 TC.setLastSig(*Signature); 701 if (ExpectBlockType) 702 NestingStack.back().Sig = *Signature; 703 ExpectBlockType = false; 704 // The "true" here will cause this to be a nameless symbol. 705 MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true); 706 auto *WasmSym = cast<MCSymbolWasm>(Sym); 707 WasmSym->setSignature(Signature); 708 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 709 const MCExpr *Expr = 710 MCSymbolRefExpr::create(WasmSym, WebAssembly::S_TYPEINDEX, Ctx); 711 Operands.push_back(std::make_unique<WebAssemblyOperand>( 712 Loc.getLoc(), Loc.getEndLoc(), WebAssemblyOperand::SymOp{Expr})); 713 } 714 715 // If we are expecting a catch clause list, try to parse it here. 716 // 717 // If there is a multivalue block return type before this catch list, it 718 // should have been parsed above. If there is no return type before 719 // encountering this catch list, this means the type is void. 720 // The case when there is a single block return value and then a catch list 721 // will be handled below in the 'while' loop. 722 if (ExpectCatchList && PeekCatchList()) { 723 if (ExpectBlockType) { 724 ExpectBlockType = false; 725 addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void); 726 } 727 if (parseCatchList(Operands)) 728 return true; 729 ExpectCatchList = false; 730 } 731 732 while (Lexer.isNot(AsmToken::EndOfStatement)) { 733 auto &Tok = Lexer.getTok(); 734 switch (Tok.getKind()) { 735 case AsmToken::Identifier: { 736 if (!parseSpecialFloatMaybe(false, Operands)) 737 break; 738 auto &Id = Lexer.getTok(); 739 if (ExpectBlockType) { 740 // Assume this identifier is a block_type. 741 auto BT = WebAssembly::parseBlockType(Id.getString()); 742 if (BT == WebAssembly::BlockType::Invalid) 743 return error("Unknown block type: ", Id); 744 addBlockTypeOperand(Operands, NameLoc, BT); 745 ExpectBlockType = false; 746 Parser.Lex(); 747 // Now that we've parsed a single block return type, if we are 748 // expecting a catch clause list, try to parse it. 749 if (ExpectCatchList && PeekCatchList()) { 750 if (parseCatchList(Operands)) 751 return true; 752 ExpectCatchList = false; 753 } 754 } else { 755 // Assume this identifier is a label. 756 const MCExpr *Val; 757 SMLoc Start = Id.getLoc(); 758 SMLoc End; 759 if (Parser.parseExpression(Val, End)) 760 return error("Cannot parse symbol: ", Lexer.getTok()); 761 Operands.push_back(std::make_unique<WebAssemblyOperand>( 762 Start, End, WebAssemblyOperand::SymOp{Val})); 763 if (checkForP2AlignIfLoadStore(Operands, Name)) 764 return true; 765 } 766 break; 767 } 768 case AsmToken::Minus: 769 Parser.Lex(); 770 if (Lexer.is(AsmToken::Integer)) { 771 parseSingleInteger(true, Operands); 772 if (checkForP2AlignIfLoadStore(Operands, Name)) 773 return true; 774 } else if (Lexer.is(AsmToken::Real)) { 775 if (parseSingleFloat(true, Operands)) 776 return true; 777 } else if (!parseSpecialFloatMaybe(true, Operands)) { 778 } else { 779 return error("Expected numeric constant instead got: ", 780 Lexer.getTok()); 781 } 782 break; 783 case AsmToken::Integer: 784 parseSingleInteger(false, Operands); 785 if (checkForP2AlignIfLoadStore(Operands, Name)) 786 return true; 787 break; 788 case AsmToken::Real: { 789 if (parseSingleFloat(false, Operands)) 790 return true; 791 break; 792 } 793 case AsmToken::LCurly: { 794 Parser.Lex(); 795 auto Op = std::make_unique<WebAssemblyOperand>( 796 Tok.getLoc(), Tok.getEndLoc(), WebAssemblyOperand::BrLOp{}); 797 if (!Lexer.is(AsmToken::RCurly)) 798 for (;;) { 799 Op->BrL.List.push_back(Lexer.getTok().getIntVal()); 800 expect(AsmToken::Integer, "integer"); 801 if (!isNext(AsmToken::Comma)) 802 break; 803 } 804 expect(AsmToken::RCurly, "}"); 805 Operands.push_back(std::move(Op)); 806 break; 807 } 808 default: 809 return error("Unexpected token in operand: ", Tok); 810 } 811 if (Lexer.isNot(AsmToken::EndOfStatement)) { 812 if (expect(AsmToken::Comma, ",")) 813 return true; 814 } 815 } 816 817 // If we are still expecting to parse a block type or a catch list at this 818 // point, we set them to the default/empty state. 819 820 // Support blocks with no operands as default to void. 821 if (ExpectBlockType) 822 addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void); 823 // If no catch list has been parsed, add an empty catch list operand. 824 if (ExpectCatchList) 825 Operands.push_back(std::make_unique<WebAssemblyOperand>( 826 NameLoc, NameLoc, WebAssemblyOperand::CaLOp{})); 827 828 if (FunctionTable) 829 Operands.push_back(std::move(FunctionTable)); 830 Parser.Lex(); 831 return false; 832 } 833 834 bool parseSignature(wasm::WasmSignature *Signature) { 835 if (expect(AsmToken::LParen, "(")) 836 return true; 837 if (parseRegTypeList(Signature->Params)) 838 return true; 839 if (expect(AsmToken::RParen, ")")) 840 return true; 841 if (expect(AsmToken::MinusGreater, "->")) 842 return true; 843 if (expect(AsmToken::LParen, "(")) 844 return true; 845 if (parseRegTypeList(Signature->Returns)) 846 return true; 847 if (expect(AsmToken::RParen, ")")) 848 return true; 849 return false; 850 } 851 852 bool parseCatchList(OperandVector &Operands) { 853 auto Op = std::make_unique<WebAssemblyOperand>( 854 Lexer.getTok().getLoc(), SMLoc(), WebAssemblyOperand::CaLOp{}); 855 SMLoc EndLoc; 856 857 while (Lexer.is(AsmToken::LParen)) { 858 if (expect(AsmToken::LParen, "(")) 859 return true; 860 861 auto CatchStr = expectIdent(); 862 if (CatchStr.empty()) 863 return true; 864 uint8_t CatchOpcode = 865 StringSwitch<uint8_t>(CatchStr) 866 .Case("catch", wasm::WASM_OPCODE_CATCH) 867 .Case("catch_ref", wasm::WASM_OPCODE_CATCH_REF) 868 .Case("catch_all", wasm::WASM_OPCODE_CATCH_ALL) 869 .Case("catch_all_ref", wasm::WASM_OPCODE_CATCH_ALL_REF) 870 .Default(0xff); 871 if (CatchOpcode == 0xff) 872 return error( 873 "Expected catch/catch_ref/catch_all/catch_all_ref, instead got: " + 874 CatchStr); 875 876 const MCExpr *Tag = nullptr; 877 if (CatchOpcode == wasm::WASM_OPCODE_CATCH || 878 CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) { 879 if (Parser.parseExpression(Tag)) 880 return error("Cannot parse symbol: ", Lexer.getTok()); 881 } 882 883 auto &DestTok = Lexer.getTok(); 884 if (DestTok.isNot(AsmToken::Integer)) 885 return error("Expected integer constant, instead got: ", DestTok); 886 unsigned Dest = DestTok.getIntVal(); 887 Parser.Lex(); 888 889 EndLoc = Lexer.getTok().getEndLoc(); 890 if (expect(AsmToken::RParen, ")")) 891 return true; 892 893 Op->CaL.List.push_back({CatchOpcode, Tag, Dest}); 894 } 895 896 Op->EndLoc = EndLoc; 897 Operands.push_back(std::move(Op)); 898 return false; 899 } 900 901 bool checkDataSection() { 902 if (CurrentState != DataSection) { 903 auto *WS = cast<MCSectionWasm>(getStreamer().getCurrentSectionOnly()); 904 if (WS && WS->isText()) 905 return error("data directive must occur in a data segment: ", 906 Lexer.getTok()); 907 } 908 CurrentState = DataSection; 909 return false; 910 } 911 912 // This function processes wasm-specific directives streamed to 913 // WebAssemblyTargetStreamer, all others go to the generic parser 914 // (see WasmAsmParser). 915 ParseStatus parseDirective(AsmToken DirectiveID) override { 916 assert(DirectiveID.getKind() == AsmToken::Identifier); 917 auto &Out = getStreamer(); 918 auto &TOut = 919 reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer()); 920 auto &Ctx = Out.getContext(); 921 922 if (DirectiveID.getString() == ".globaltype") { 923 auto SymName = expectIdent(); 924 if (SymName.empty()) 925 return ParseStatus::Failure; 926 if (expect(AsmToken::Comma, ",")) 927 return ParseStatus::Failure; 928 auto TypeTok = Lexer.getTok(); 929 auto TypeName = expectIdent(); 930 if (TypeName.empty()) 931 return ParseStatus::Failure; 932 auto Type = WebAssembly::parseType(TypeName); 933 if (!Type) 934 return error("Unknown type in .globaltype directive: ", TypeTok); 935 // Optional mutable modifier. Default to mutable for historical reasons. 936 // Ideally we would have gone with immutable as the default and used `mut` 937 // as the modifier to match the `.wat` format. 938 bool Mutable = true; 939 if (isNext(AsmToken::Comma)) { 940 TypeTok = Lexer.getTok(); 941 auto Id = expectIdent(); 942 if (Id.empty()) 943 return ParseStatus::Failure; 944 if (Id == "immutable") 945 Mutable = false; 946 else 947 // Should we also allow `mutable` and `mut` here for clarity? 948 return error("Unknown type in .globaltype modifier: ", TypeTok); 949 } 950 // Now set this symbol with the correct type. 951 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 952 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); 953 WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(*Type), Mutable}); 954 // And emit the directive again. 955 TOut.emitGlobalType(WasmSym); 956 return expect(AsmToken::EndOfStatement, "EOL"); 957 } 958 959 if (DirectiveID.getString() == ".tabletype") { 960 // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]] 961 auto SymName = expectIdent(); 962 if (SymName.empty()) 963 return ParseStatus::Failure; 964 if (expect(AsmToken::Comma, ",")) 965 return ParseStatus::Failure; 966 967 auto ElemTypeTok = Lexer.getTok(); 968 auto ElemTypeName = expectIdent(); 969 if (ElemTypeName.empty()) 970 return ParseStatus::Failure; 971 std::optional<wasm::ValType> ElemType = 972 WebAssembly::parseType(ElemTypeName); 973 if (!ElemType) 974 return error("Unknown type in .tabletype directive: ", ElemTypeTok); 975 976 wasm::WasmLimits Limits = defaultLimits(); 977 if (isNext(AsmToken::Comma) && parseLimits(&Limits)) 978 return ParseStatus::Failure; 979 980 // Now that we have the name and table type, we can actually create the 981 // symbol 982 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 983 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); 984 if (Is64) { 985 Limits.Flags |= wasm::WASM_LIMITS_FLAG_IS_64; 986 } 987 wasm::WasmTableType Type = {*ElemType, Limits}; 988 WasmSym->setTableType(Type); 989 TOut.emitTableType(WasmSym); 990 return expect(AsmToken::EndOfStatement, "EOL"); 991 } 992 993 if (DirectiveID.getString() == ".functype") { 994 // This code has to send things to the streamer similar to 995 // WebAssemblyAsmPrinter::EmitFunctionBodyStart. 996 // TODO: would be good to factor this into a common function, but the 997 // assembler and backend really don't share any common code, and this code 998 // parses the locals separately. 999 auto SymName = expectIdent(); 1000 if (SymName.empty()) 1001 return ParseStatus::Failure; 1002 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 1003 if (WasmSym->isDefined()) { 1004 // We push 'Function' either when a label is parsed or a .functype 1005 // directive is parsed. The reason it is not easy to do this uniformly 1006 // in a single place is, 1007 // 1. We can't do this at label parsing time only because there are 1008 // cases we don't have .functype directive before a function label, 1009 // in which case we don't know if the label is a function at the time 1010 // of parsing. 1011 // 2. We can't do this at .functype parsing time only because we want to 1012 // detect a function started with a label and not ended correctly 1013 // without encountering a .functype directive after the label. 1014 if (CurrentState != FunctionLabel) { 1015 // This .functype indicates a start of a function. 1016 if (ensureEmptyNestingStack()) 1017 return ParseStatus::Failure; 1018 push(Function); 1019 } 1020 CurrentState = FunctionStart; 1021 LastFunctionLabel = WasmSym; 1022 } 1023 auto *Signature = Ctx.createWasmSignature(); 1024 if (parseSignature(Signature)) 1025 return ParseStatus::Failure; 1026 if (CurrentState == FunctionStart) 1027 TC.funcDecl(*Signature); 1028 WasmSym->setSignature(Signature); 1029 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 1030 TOut.emitFunctionType(WasmSym); 1031 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented. 1032 return expect(AsmToken::EndOfStatement, "EOL"); 1033 } 1034 1035 if (DirectiveID.getString() == ".export_name") { 1036 auto SymName = expectIdent(); 1037 if (SymName.empty()) 1038 return ParseStatus::Failure; 1039 if (expect(AsmToken::Comma, ",")) 1040 return ParseStatus::Failure; 1041 auto ExportName = expectIdent(); 1042 if (ExportName.empty()) 1043 return ParseStatus::Failure; 1044 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 1045 WasmSym->setExportName(Ctx.allocateString(ExportName)); 1046 TOut.emitExportName(WasmSym, ExportName); 1047 return expect(AsmToken::EndOfStatement, "EOL"); 1048 } 1049 1050 if (DirectiveID.getString() == ".import_module") { 1051 auto SymName = expectIdent(); 1052 if (SymName.empty()) 1053 return ParseStatus::Failure; 1054 if (expect(AsmToken::Comma, ",")) 1055 return ParseStatus::Failure; 1056 auto ImportModule = expectIdent(); 1057 if (ImportModule.empty()) 1058 return ParseStatus::Failure; 1059 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 1060 WasmSym->setImportModule(Ctx.allocateString(ImportModule)); 1061 TOut.emitImportModule(WasmSym, ImportModule); 1062 return expect(AsmToken::EndOfStatement, "EOL"); 1063 } 1064 1065 if (DirectiveID.getString() == ".import_name") { 1066 auto SymName = expectIdent(); 1067 if (SymName.empty()) 1068 return ParseStatus::Failure; 1069 if (expect(AsmToken::Comma, ",")) 1070 return ParseStatus::Failure; 1071 auto ImportName = expectIdent(); 1072 if (ImportName.empty()) 1073 return ParseStatus::Failure; 1074 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 1075 WasmSym->setImportName(Ctx.allocateString(ImportName)); 1076 TOut.emitImportName(WasmSym, ImportName); 1077 return expect(AsmToken::EndOfStatement, "EOL"); 1078 } 1079 1080 if (DirectiveID.getString() == ".tagtype") { 1081 auto SymName = expectIdent(); 1082 if (SymName.empty()) 1083 return ParseStatus::Failure; 1084 auto *WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); 1085 auto *Signature = Ctx.createWasmSignature(); 1086 if (parseRegTypeList(Signature->Params)) 1087 return ParseStatus::Failure; 1088 WasmSym->setSignature(Signature); 1089 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG); 1090 TOut.emitTagType(WasmSym); 1091 // TODO: backend also calls TOut.emitIndIdx, but that is not implemented. 1092 return expect(AsmToken::EndOfStatement, "EOL"); 1093 } 1094 1095 if (DirectiveID.getString() == ".local") { 1096 if (CurrentState != FunctionStart) 1097 return error(".local directive should follow the start of a function: ", 1098 Lexer.getTok()); 1099 SmallVector<wasm::ValType, 4> Locals; 1100 if (parseRegTypeList(Locals)) 1101 return ParseStatus::Failure; 1102 TC.localDecl(Locals); 1103 TOut.emitLocal(Locals); 1104 CurrentState = FunctionLocals; 1105 return expect(AsmToken::EndOfStatement, "EOL"); 1106 } 1107 1108 if (DirectiveID.getString() == ".int8" || 1109 DirectiveID.getString() == ".int16" || 1110 DirectiveID.getString() == ".int32" || 1111 DirectiveID.getString() == ".int64") { 1112 if (checkDataSection()) 1113 return ParseStatus::Failure; 1114 const MCExpr *Val; 1115 SMLoc End; 1116 if (Parser.parseExpression(Val, End)) 1117 return error("Cannot parse .int expression: ", Lexer.getTok()); 1118 size_t NumBits = 0; 1119 DirectiveID.getString().drop_front(4).getAsInteger(10, NumBits); 1120 Out.emitValue(Val, NumBits / 8, End); 1121 return expect(AsmToken::EndOfStatement, "EOL"); 1122 } 1123 1124 if (DirectiveID.getString() == ".asciz") { 1125 if (checkDataSection()) 1126 return ParseStatus::Failure; 1127 std::string S; 1128 if (Parser.parseEscapedString(S)) 1129 return error("Cannot parse string constant: ", Lexer.getTok()); 1130 Out.emitBytes(StringRef(S.c_str(), S.length() + 1)); 1131 return expect(AsmToken::EndOfStatement, "EOL"); 1132 } 1133 1134 return ParseStatus::NoMatch; // We didn't process this directive. 1135 } 1136 1137 // Called either when the first instruction is parsed of the function ends. 1138 void ensureLocals(MCStreamer &Out) { 1139 if (CurrentState == FunctionStart) { 1140 // We haven't seen a .local directive yet. The streamer requires locals to 1141 // be encoded as a prelude to the instructions, so emit an empty list of 1142 // locals here. 1143 auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>( 1144 *Out.getTargetStreamer()); 1145 TOut.emitLocal(SmallVector<wasm::ValType, 0>()); 1146 CurrentState = FunctionLocals; 1147 } 1148 } 1149 1150 bool matchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/, 1151 OperandVector &Operands, MCStreamer &Out, 1152 uint64_t &ErrorInfo, 1153 bool MatchingInlineAsm) override { 1154 MCInst Inst; 1155 Inst.setLoc(IDLoc); 1156 FeatureBitset MissingFeatures; 1157 unsigned MatchResult = MatchInstructionImpl( 1158 Operands, Inst, ErrorInfo, MissingFeatures, MatchingInlineAsm); 1159 switch (MatchResult) { 1160 case Match_Success: { 1161 ensureLocals(Out); 1162 // Fix unknown p2align operands. 1163 auto Align = WebAssembly::GetDefaultP2AlignAny(Inst.getOpcode()); 1164 if (Align != -1U) { 1165 auto &Op0 = Inst.getOperand(0); 1166 if (Op0.getImm() == -1) 1167 Op0.setImm(Align); 1168 } 1169 if (Is64) { 1170 // Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having 1171 // an offset64 arg instead of offset32, but to the assembler matcher 1172 // they're both immediates so don't get selected for. 1173 auto Opc64 = WebAssembly::getWasm64Opcode( 1174 static_cast<uint16_t>(Inst.getOpcode())); 1175 if (Opc64 >= 0) { 1176 Inst.setOpcode(Opc64); 1177 } 1178 } 1179 if (!SkipTypeCheck) 1180 TC.typeCheck(IDLoc, Inst, Operands); 1181 Out.emitInstruction(Inst, getSTI()); 1182 if (CurrentState == EndFunction) { 1183 onEndOfFunction(IDLoc); 1184 } else { 1185 CurrentState = Instructions; 1186 } 1187 return false; 1188 } 1189 case Match_MissingFeature: { 1190 assert(MissingFeatures.count() > 0 && "Expected missing features"); 1191 SmallString<128> Message; 1192 raw_svector_ostream OS(Message); 1193 OS << "instruction requires:"; 1194 for (unsigned I = 0, E = MissingFeatures.size(); I != E; ++I) 1195 if (MissingFeatures.test(I)) 1196 OS << ' ' << getSubtargetFeatureName(I); 1197 return Parser.Error(IDLoc, Message); 1198 } 1199 case Match_MnemonicFail: 1200 return Parser.Error(IDLoc, "invalid instruction"); 1201 case Match_NearMisses: 1202 return Parser.Error(IDLoc, "ambiguous instruction"); 1203 case Match_InvalidTiedOperand: 1204 case Match_InvalidOperand: { 1205 SMLoc ErrorLoc = IDLoc; 1206 if (ErrorInfo != ~0ULL) { 1207 if (ErrorInfo >= Operands.size()) 1208 return Parser.Error(IDLoc, "too few operands for instruction"); 1209 ErrorLoc = Operands[ErrorInfo]->getStartLoc(); 1210 if (ErrorLoc == SMLoc()) 1211 ErrorLoc = IDLoc; 1212 } 1213 return Parser.Error(ErrorLoc, "invalid operand for instruction"); 1214 } 1215 } 1216 llvm_unreachable("Implement any new match types added!"); 1217 } 1218 1219 void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override { 1220 // Code below only applies to labels in text sections. 1221 auto *CWS = cast<MCSectionWasm>(getStreamer().getCurrentSectionOnly()); 1222 if (!CWS->isText()) 1223 return; 1224 1225 auto *WasmSym = cast<MCSymbolWasm>(Symbol); 1226 // Unlike other targets, we don't allow data in text sections (labels 1227 // declared with .type @object). 1228 if (WasmSym->getType() == wasm::WASM_SYMBOL_TYPE_DATA) { 1229 Parser.Error(IDLoc, 1230 "Wasm doesn\'t support data symbols in text sections"); 1231 return; 1232 } 1233 1234 // Start a new section for the next function automatically, since our 1235 // object writer expects each function to have its own section. This way 1236 // The user can't forget this "convention". 1237 auto SymName = Symbol->getName(); 1238 if (SymName.starts_with(".L")) 1239 return; // Local Symbol. 1240 1241 // TODO: If the user explicitly creates a new function section, we ignore 1242 // its name when we create this one. It would be nice to honor their 1243 // choice, while still ensuring that we create one if they forget. 1244 // (that requires coordination with WasmAsmParser::parseSectionDirective) 1245 std::string SecName = (".text." + SymName).str(); 1246 1247 auto *Group = CWS->getGroup(); 1248 // If the current section is a COMDAT, also set the flag on the symbol. 1249 // TODO: Currently the only place that the symbols' comdat flag matters is 1250 // for importing comdat functions. But there's no way to specify that in 1251 // assembly currently. 1252 if (Group) 1253 WasmSym->setComdat(true); 1254 auto *WS = getContext().getWasmSection(SecName, SectionKind::getText(), 0, 1255 Group, MCSection::NonUniqueID); 1256 getStreamer().switchSection(WS); 1257 // Also generate DWARF for this section if requested. 1258 if (getContext().getGenDwarfForAssembly()) 1259 getContext().addGenDwarfSection(WS); 1260 1261 if (WasmSym->isFunction()) { 1262 // We give the location of the label (IDLoc) here, because otherwise the 1263 // lexer's next location will be used, which can be confusing. For 1264 // example: 1265 // 1266 // test0: ; This function does not end properly 1267 // ... 1268 // 1269 // test1: ; We would like to point to this line for error 1270 // ... . Not this line, which can contain any instruction 1271 ensureEmptyNestingStack(IDLoc); 1272 CurrentState = FunctionLabel; 1273 LastFunctionLabel = Symbol; 1274 push(Function); 1275 } 1276 } 1277 1278 void onEndOfFunction(SMLoc ErrorLoc) { 1279 if (!SkipTypeCheck) 1280 TC.endOfFunction(ErrorLoc, true); 1281 // Reset the type checker state. 1282 TC.clear(); 1283 } 1284 1285 void onEndOfFile() override { ensureEmptyNestingStack(); } 1286 }; 1287 } // end anonymous namespace 1288 1289 // Force static initialization. 1290 extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void 1291 LLVMInitializeWebAssemblyAsmParser() { 1292 RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32()); 1293 RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64()); 1294 } 1295 1296 #define GET_REGISTER_MATCHER 1297 #define GET_SUBTARGET_FEATURE_NAME 1298 #define GET_MATCHER_IMPLEMENTATION 1299 #include "WebAssemblyGenAsmMatcher.inc" 1300 1301 StringRef getMnemonic(unsigned Opc) { 1302 // FIXME: linear search! 1303 for (auto &ME : MatchTable0) { 1304 if (ME.Opcode == Opc) { 1305 return ME.getMnemonic(); 1306 } 1307 } 1308 assert(false && "mnemonic not found"); 1309 return StringRef(); 1310 } 1311