Index: lib/MC/MCWasmStreamer.cpp =================================================================== --- lib/MC/MCWasmStreamer.cpp +++ lib/MC/MCWasmStreamer.cpp @@ -61,7 +61,7 @@ void MCWasmStreamer::ChangeSection(MCSection *Section, const MCExpr *Subsection) { MCAssembler &Asm = getAssembler(); - auto *SectionWasm = static_cast(Section); + auto *SectionWasm = cast(Section); const MCSymbol *Grp = SectionWasm->getGroup(); if (Grp) Asm.registerSymbol(*Grp); Index: lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp =================================================================== --- lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -25,6 +25,7 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolWasm.h" #include "llvm/Support/Endian.h" #include "llvm/Support/TargetRegistry.h" @@ -131,14 +132,14 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser { MCAsmParser &Parser; MCAsmLexer &Lexer; - MCSymbol *LastLabel; + MCSymbolWasm *LastSymbol; public: - WebAssemblyAsmParser(const MCSubtargetInfo &sti, MCAsmParser &Parser, - const MCInstrInfo &mii, const MCTargetOptions &Options) - : MCTargetAsmParser(Options, sti, mii), Parser(Parser), - Lexer(Parser.getLexer()), LastLabel(nullptr) { - setAvailableFeatures(ComputeAvailableFeatures(sti.getFeatureBits())); + WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, STI, MII), Parser(Parser), + Lexer(Parser.getLexer()), LastSymbol(nullptr) { + setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); } #define GET_ASSEMBLER_HEADER @@ -168,24 +169,26 @@ return false; } - MVT::SimpleValueType ParseRegType(const StringRef &RegType) { + + std::pair + ParseRegType(const StringRef &RegType) { // Derive type from .param .local decls, or the instruction itself. - return StringSwitch(RegType) - .Case("i32", MVT::i32) - .Case("i64", MVT::i64) - .Case("f32", MVT::f32) - .Case("f64", MVT::f64) - .Case("i8x16", MVT::v16i8) - .Case("i16x8", MVT::v8i16) - .Case("i32x4", MVT::v4i32) - .Case("i64x2", MVT::v2i64) - .Case("f32x4", MVT::v4f32) - .Case("f64x2", MVT::v2f64) + return StringSwitch>(RegType) + .Case("i32", {MVT::i32, wasm::WASM_TYPE_I32}) + .Case("i64", {MVT::i64, wasm::WASM_TYPE_I64}) + .Case("f32", {MVT::f32, wasm::WASM_TYPE_F32}) + .Case("f64", {MVT::f64, wasm::WASM_TYPE_F64}) + .Case("i8x16", {MVT::v16i8, wasm::WASM_TYPE_V128}) + .Case("i16x8", {MVT::v8i16, wasm::WASM_TYPE_V128}) + .Case("i32x4", {MVT::v4i32, wasm::WASM_TYPE_V128}) + .Case("i64x2", {MVT::v2i64, wasm::WASM_TYPE_V128}) + .Case("f32x4", {MVT::v4f32, wasm::WASM_TYPE_V128}) + .Case("f64x2", {MVT::v2f64, wasm::WASM_TYPE_V128}) // arbitrarily chosen vector type to associate with "v128" // FIXME: should these be EVTs to avoid this arbitrary hack? Do we want // to accept more specific SIMD register types? - .Case("v128", MVT::v16i8) - .Default(MVT::INVALID_SIMPLE_VALUE_TYPE); + .Case("v128", {MVT::v16i8, wasm::WASM_TYPE_V128}) + .Default({MVT::INVALID_SIMPLE_VALUE_TYPE, wasm::WASM_TYPE_NORESULT}); } void ParseSingleInteger(bool IsNegative, OperandVector &Operands) { @@ -311,24 +314,84 @@ return false; } - void onLabelParsed(MCSymbol *Symbol) override { LastLabel = Symbol; } + void onLabelParsed(MCSymbol *Symbol) override { + LastSymbol = cast(Symbol); + } bool ParseDirective(AsmToken DirectiveID) override { + // This function has a really weird return value behavior that is different + // from all the other parsing functions: + // - return true && no tokens consumed -> don't know this directive / let + // the generic parser handle it. + // - return true && tokens consumed -> a parsing error occurred. + // - return false -> processed this directive successfully. assert(DirectiveID.getKind() == AsmToken::Identifier); auto &Out = getStreamer(); auto &TOut = reinterpret_cast(*Out.getTargetStreamer()); - // TODO: we're just parsing the subset of directives we're interested in, - // and ignoring ones we don't recognise. We should ideally verify - // all directives here. + // TODO: any time we return an error, at least one token must have been + // consumed, otherwise this will not signal an error to the caller. if (DirectiveID.getString() == ".type") { // This could be the start of a function, check if followed by // "label,@function" - if (!(IsNext(AsmToken::Identifier) && IsNext(AsmToken::Comma) && - IsNext(AsmToken::At) && Lexer.is(AsmToken::Identifier))) + if (!Lexer.is(AsmToken::Identifier)) + return Error("Expected label after .type directive, got: ", + Lexer.getTok()); + auto WasmSym = cast( + TOut.getStreamer().getContext().getOrCreateSymbol( + Lexer.getTok().getString())); + Parser.Lex(); + if (!(IsNext(AsmToken::Comma) && IsNext(AsmToken::At) && + Lexer.is(AsmToken::Identifier))) return Error("Expected label,@type declaration, got: ", Lexer.getTok()); + auto TypeName = Lexer.getTok().getString(); + if (TypeName == "function") + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + else if (TypeName == "global") + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); + else + return Error("Unknown WASM symbol type: ", Lexer.getTok()); + Parser.Lex(); + return Expect(AsmToken::EndOfStatement, "EOL"); + } else if (DirectiveID.getString() == ".size") { + if (!Lexer.is(AsmToken::Identifier)) + return Error("Expected label after .size directive, got: ", + Lexer.getTok()); + auto WasmSym = cast( + TOut.getStreamer().getContext().getOrCreateSymbol( + Lexer.getTok().getString())); Parser.Lex(); - // Out.EmitSymbolAttribute(??, MCSA_ELF_TypeFunction); + if (!IsNext(AsmToken::Comma)) + return Error("Expected `,`, got: ", Lexer.getTok()); + const MCExpr *Exp; + if (Parser.parseExpression(Exp)) + return Error("Cannot parse .size expression: ", Lexer.getTok()); + WasmSym->setSize(Exp); + return Expect(AsmToken::EndOfStatement, "EOL"); + } else if (DirectiveID.getString() == ".globaltype") { + if (!Lexer.is(AsmToken::Identifier)) + return Error("Expected symbol name after .globaltype directive, got: ", + Lexer.getTok()); + auto Name = Lexer.getTok().getString(); + Parser.Lex(); + if (!IsNext(AsmToken::Comma)) + return Error("Expected `,`, got: ", Lexer.getTok()); + if (!Lexer.is(AsmToken::Identifier)) + return Error("Expected type in .globaltype directive, got: ", + Lexer.getTok()); + auto Type = ParseRegType(Lexer.getTok().getString()).second; + if (Type == wasm::WASM_TYPE_NORESULT) + return Error("Unknown type in .globaltype directive: ", + Lexer.getTok()); + Parser.Lex(); + // Now set this symbol with the correct type. + auto WasmSym = cast( + TOut.getStreamer().getContext().getOrCreateSymbol(Name)); + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); + WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(Type), true}); + // And emit the directive again. + TOut.emitGlobalType(WasmSym); + return Expect(AsmToken::EndOfStatement, "EOL"); } else if (DirectiveID.getString() == ".param" || DirectiveID.getString() == ".local") { // Track the number of locals, needed for correct virtual register @@ -337,7 +400,7 @@ std::vector Params; std::vector Locals; while (Lexer.is(AsmToken::Identifier)) { - auto RegType = ParseRegType(Lexer.getTok().getString()); + auto RegType = ParseRegType(Lexer.getTok().getString()).first; if (RegType == MVT::INVALID_SIMPLE_VALUE_TYPE) return true; if (DirectiveID.getString() == ".param") { @@ -349,15 +412,20 @@ if (!IsNext(AsmToken::Comma)) break; } - assert(LastLabel); - TOut.emitParam(LastLabel, Params); + assert(LastSymbol); + // TODO: LastSymbol isn't even used by emitParam, so could be removed. + TOut.emitParam(LastSymbol, Params); TOut.emitLocal(Locals); + return Expect(AsmToken::EndOfStatement, "EOL"); } else { - // For now, ignore anydirective we don't recognize: + // TODO: remove. while (Lexer.isNot(AsmToken::EndOfStatement)) Parser.Lex(); + return Expect(AsmToken::EndOfStatement, "EOL"); } - return Expect(AsmToken::EndOfStatement, "EOL"); + // TODO: current ELF directive parsing is broken, fix this is a followup. + //return true; // We didn't process this directive. + return false; } bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/, Index: test/MC/WebAssembly/basic-assembly.s =================================================================== --- test/MC/WebAssembly/basic-assembly.s +++ test/MC/WebAssembly/basic-assembly.s @@ -1,4 +1,6 @@ # RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s +# this one is just here to see if it converts to .o without errors, but doesn't check any output: +# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+simd128,+nontrapping-fptoint,+exception-handling < %s .text .type test0,@function @@ -56,7 +58,9 @@ #i32.trunc_s:sat/f32 get_global __stack_pointer@GLOBAL end_function - +.Lfunc_end0: + .size test0, .Lfunc_end0-test0 + .globaltype __stack_pointer, i32 # CHECK: .text # CHECK-LABEL: test0: @@ -104,3 +108,5 @@ # CHECK-NEXT: end_try # CHECK-NEXT: get_global __stack_pointer@GLOBAL # CHECK-NEXT: end_function + +# CHECK: .globaltype __stack_pointer, i32