diff options
Diffstat (limited to 'llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp')
| -rw-r--r-- | llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp | 140 |
1 files changed, 128 insertions, 12 deletions
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index e29d85d7588d..60ac3248b9e7 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -35,10 +35,12 @@ using namespace llvm; #define DEBUG_TYPE "wasm-asm-parser" +static const char *getSubtargetFeatureName(uint64_t Val); + namespace { /// WebAssemblyOperand - Instances of this class represent the operands in a -/// parsed WASM machine instruction. +/// parsed Wasm machine instruction. struct WebAssemblyOperand : public MCParsedAsmOperand { enum KindTy { Token, Integer, Float, Symbol, BrList } Kind; @@ -158,6 +160,24 @@ struct WebAssemblyOperand : public MCParsedAsmOperand { } }; +static MCSymbolWasm *GetOrCreateFunctionTableSymbol(MCContext &Ctx, + const StringRef &Name) { + // FIXME: Duplicates functionality from + // MC/WasmObjectWriter::recordRelocation, as well as WebAssemblyCodegen's + // WebAssembly:getOrCreateFunctionTableSymbol. + MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); + if (Sym) { + if (!Sym->isFunctionTable()) + Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); + } else { + Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); + Sym->setFunctionTable(); + // The default function table is synthesized by the linker. + Sym->setUndefined(); + } + return Sym; +} + class WebAssemblyAsmParser final : public MCTargetAsmParser { MCAsmParser &Parser; MCAsmLexer &Lexer; @@ -320,8 +340,8 @@ public: Type == "i32x4" || Type == "i64x2" || Type == "f32x4" || Type == "f64x2") return wasm::ValType::V128; - if (Type == "exnref") - return wasm::ValType::EXNREF; + if (Type == "funcref") + return wasm::ValType::FUNCREF; if (Type == "externref") return wasm::ValType::EXTERNREF; return Optional<wasm::ValType>(); @@ -335,7 +355,8 @@ public: .Case("f32", WebAssembly::BlockType::F32) .Case("f64", WebAssembly::BlockType::F64) .Case("v128", WebAssembly::BlockType::V128) - .Case("exnref", WebAssembly::BlockType::Exnref) + .Case("funcref", WebAssembly::BlockType::Funcref) + .Case("externref", WebAssembly::BlockType::Externref) .Case("void", WebAssembly::BlockType::Void) .Default(WebAssembly::BlockType::Invalid); } @@ -403,7 +424,8 @@ public: bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) { // FIXME: there is probably a cleaner way to do this. auto IsLoadStore = InstName.find(".load") != StringRef::npos || - InstName.find(".store") != StringRef::npos; + InstName.find(".store") != StringRef::npos || + InstName.find("prefetch") != StringRef::npos; auto IsAtomic = InstName.find("atomic.") != StringRef::npos; if (IsLoadStore || IsAtomic) { // Parse load/store operands of the form: offset:p2align=align @@ -417,6 +439,12 @@ public: return error("Expected integer constant"); parseSingleInteger(false, Operands); } else { + // v128.{load,store}{8,16,32,64}_lane has both a memarg and a lane + // index. We need to avoid parsing an extra alignment operand for the + // lane index. + auto IsLoadStoreLane = InstName.find("_lane") != StringRef::npos; + if (IsLoadStoreLane && Operands.size() == 4) + return false; // Alignment not specified (or atomics, must use default alignment). // We can't just call WebAssembly::GetDefaultP2Align since we don't have // an opcode until after the assembly matcher, so set a default to fix @@ -430,6 +458,13 @@ public: return false; } + WebAssembly::HeapType parseHeapType(StringRef Id) { + return StringSwitch<WebAssembly::HeapType>(Id) + .Case("extern", WebAssembly::HeapType::Externref) + .Case("func", WebAssembly::HeapType::Funcref) + .Default(WebAssembly::HeapType::Invalid); + } + void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc, WebAssembly::BlockType BT) { Operands.push_back(std::make_unique<WebAssemblyOperand>( @@ -472,6 +507,7 @@ public: // proper nesting. bool ExpectBlockType = false; bool ExpectFuncType = false; + bool ExpectHeapType = false; if (Name == "block") { push(Block); ExpectBlockType = true; @@ -511,6 +547,17 @@ public: return true; } else if (Name == "call_indirect" || Name == "return_call_indirect") { ExpectFuncType = true; + // Ensure that the object file has a __indirect_function_table import, as + // we call_indirect against it. + auto &Ctx = getStreamer().getContext(); + MCSymbolWasm *Sym = + GetOrCreateFunctionTableSymbol(Ctx, "__indirect_function_table"); + // Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark + // it as NO_STRIP so as to ensure that the indirect function table makes + // it to linked output. + Sym->setNoStrip(); + } else if (Name == "ref.null") { + ExpectHeapType = true; } if (ExpectFuncType || (ExpectBlockType && Lexer.is(AsmToken::LParen))) { @@ -552,6 +599,15 @@ public: return error("Unknown block type: ", Id); addBlockTypeOperand(Operands, NameLoc, BT); Parser.Lex(); + } else if (ExpectHeapType) { + auto HeapType = parseHeapType(Id.getString()); + if (HeapType == WebAssembly::HeapType::Invalid) { + return error("Expected a heap type: ", Id); + } + Operands.push_back(std::make_unique<WebAssemblyOperand>( + WebAssemblyOperand::Integer, Id.getLoc(), Id.getEndLoc(), + WebAssemblyOperand::IntOp{static_cast<int64_t>(HeapType)})); + Parser.Lex(); } else { // Assume this identifier is a label. const MCExpr *Val; @@ -687,16 +743,52 @@ public: auto Type = parseType(TypeName); if (!Type) return error("Unknown type in .globaltype directive: ", TypeTok); + // Optional mutable modifier. Default to mutable for historical reasons. + // Ideally we would have gone with immutable as the default and used `mut` + // as the modifier to match the `.wat` format. + bool Mutable = true; + if (isNext(AsmToken::Comma)) { + TypeTok = Lexer.getTok(); + auto Id = expectIdent(); + if (Id == "immutable") + Mutable = false; + else + // Should we also allow `mutable` and `mut` here for clarity? + return error("Unknown type in .globaltype modifier: ", TypeTok); + } // Now set this symbol with the correct type. auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); WasmSym->setGlobalType( - wasm::WasmGlobalType{uint8_t(Type.getValue()), true}); + wasm::WasmGlobalType{uint8_t(Type.getValue()), Mutable}); // And emit the directive again. TOut.emitGlobalType(WasmSym); return expect(AsmToken::EndOfStatement, "EOL"); } + if (DirectiveID.getString() == ".tabletype") { + auto SymName = expectIdent(); + if (SymName.empty()) + return true; + if (expect(AsmToken::Comma, ",")) + return true; + auto TypeTok = Lexer.getTok(); + auto TypeName = expectIdent(); + if (TypeName.empty()) + return true; + auto Type = parseType(TypeName); + if (!Type) + return error("Unknown type in .tabletype directive: ", TypeTok); + + // Now that we have the name and table type, we can actually create the + // symbol + auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); + WasmSym->setTableType(Type.getValue()); + TOut.emitTableType(WasmSym); + return expect(AsmToken::EndOfStatement, "EOL"); + } + if (DirectiveID.getString() == ".functype") { // This code has to send things to the streamer similar to // WebAssemblyAsmPrinter::EmitFunctionBodyStart. @@ -836,8 +928,9 @@ public: bool MatchingInlineAsm) override { MCInst Inst; Inst.setLoc(IDLoc); - unsigned MatchResult = - MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + FeatureBitset MissingFeatures; + unsigned MatchResult = MatchInstructionImpl( + Operands, Inst, ErrorInfo, MissingFeatures, MatchingInlineAsm); switch (MatchResult) { case Match_Success: { ensureLocals(Out); @@ -866,9 +959,16 @@ public: } return false; } - case Match_MissingFeature: - return Parser.Error( - IDLoc, "instruction requires a WASM feature not currently enabled"); + case Match_MissingFeature: { + assert(MissingFeatures.count() > 0 && "Expected missing features"); + SmallString<128> Message; + raw_svector_ostream OS(Message); + OS << "instruction requires:"; + for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) + if (MissingFeatures.test(i)) + OS << ' ' << getSubtargetFeatureName(i); + return Parser.Error(IDLoc, Message); + } case Match_MnemonicFail: return Parser.Error(IDLoc, "invalid instruction"); case Match_NearMisses: @@ -896,12 +996,27 @@ public: auto SymName = Symbol->getName(); if (SymName.startswith(".L")) return; // Local Symbol. + // Only create a new text section if we're already in one. + // TODO: If the user explicitly creates a new function section, we ignore + // its name when we create this one. It would be nice to honor their + // choice, while still ensuring that we create one if they forget. + // (that requires coordination with WasmAsmParser::parseSectionDirective) auto CWS = cast<MCSectionWasm>(getStreamer().getCurrentSection().first); if (!CWS || !CWS->getKind().isText()) return; auto SecName = ".text." + SymName; - auto WS = getContext().getWasmSection(SecName, SectionKind::getText()); + + auto *Group = CWS->getGroup(); + // If the current section is a COMDAT, also set the flag on the symbol. + // TODO: Currently the only place that the symbols' comdat flag matters is + // for importing comdat functions. But there's no way to specify that in + // assembly currently. + if (Group) + cast<MCSymbolWasm>(Symbol)->setComdat(true); + auto *WS = + getContext().getWasmSection(SecName, SectionKind::getText(), Group, + MCContext::GenericSectionID, nullptr); getStreamer().SwitchSection(WS); // Also generate DWARF for this section if requested. if (getContext().getGenDwarfForAssembly()) @@ -932,5 +1047,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmParser() { } #define GET_REGISTER_MATCHER +#define GET_SUBTARGET_FEATURE_NAME #define GET_MATCHER_IMPLEMENTATION #include "WebAssemblyGenAsmMatcher.inc" |
