Index: include/llvm/CodeGen/MachineModuleInfoImpls.h =================================================================== --- include/llvm/CodeGen/MachineModuleInfoImpls.h +++ include/llvm/CodeGen/MachineModuleInfoImpls.h @@ -17,6 +17,7 @@ #include "llvm/CodeGen/ValueTypes.h" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/Support/Wasm.h" namespace llvm { class MCSymbol; @@ -79,9 +80,10 @@ /// MachineModuleInfoWasm - This is a MachineModuleInfoImpl implementation /// for Wasm targets. class MachineModuleInfoWasm : public MachineModuleInfoImpl { - /// GVStubs - These stubs are used to materialize global addresses in PIC - /// mode. - std::vector Globals; + /// WebAssembly global variables defined by CodeGen. + std::vector Globals; + + /// The WebAssembly global variable which is the stack pointer. unsigned StackPointerGlobal; virtual void anchor(); // Out of line virtual method. @@ -89,8 +91,8 @@ MachineModuleInfoWasm(const MachineModuleInfo &) : StackPointerGlobal(-1U) {} - void addGlobal(MVT VT) { Globals.push_back(VT); } - const std::vector &getGlobals() const { return Globals; } + void addGlobal(const wasm::Global &G) { Globals.push_back(G); } + const std::vector &getGlobals() const { return Globals; } bool hasStackPointerGlobal() const { return StackPointerGlobal != -1U; Index: include/llvm/Support/Wasm.h =================================================================== --- include/llvm/Support/Wasm.h +++ include/llvm/Support/Wasm.h @@ -94,12 +94,30 @@ F64 = WASM_TYPE_F64, }; +// Linking metadata kinds. +enum : unsigned { + WEBASSEMBLY_STACK_POINTER = 0x1, +}; + #define WASM_RELOC(name, value) name = value, enum : unsigned { #include "WasmRelocs/WebAssembly.def" }; +struct Global { + ValType Type; + bool Mutable; + + // The initial value for this global is either the value of an imported + // global, in which case InitialModule and InitialName specify the global + // import, or a value, in which case InitialModule is null and InitialValue + // holds the value. + const char *InitialModule; + const char *InitialName; + uint64_t InitialValue; +}; + } // end namespace wasm } // end namespace llvm Index: include/llvm/Support/WasmRelocs/WebAssembly.def =================================================================== --- include/llvm/Support/WasmRelocs/WebAssembly.def +++ include/llvm/Support/WasmRelocs/WebAssembly.def @@ -9,3 +9,5 @@ WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_LEB, 3) WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_SLEB, 4) WASM_RELOC(R_WEBASSEMBLY_GLOBAL_ADDR_I32, 5) +WASM_RELOC(R_WEBASSEMBLY_TYPE_INDEX_LEB, 6) +WASM_RELOC(R_WEBASSEMBLY_GLOBAL_INDEX_LEB, 7) Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -50,16 +50,6 @@ uint64_t ContentsOffset; }; -// This record records information about a call_indirect which needs its -// type index fixed up once we've computed type indices. -struct TypeIndexFixup { - uint64_t Offset; - const MCSymbolWasm *Symbol; - const MCSectionWasm *FixupSection; - TypeIndexFixup(uint64_t O, const MCSymbolWasm *S, MCSectionWasm *F) - : Offset(O), Symbol(S), FixupSection(F) {} -}; - class WasmObjectWriter : public MCObjectWriter { /// Helper struct for containing some precomputed information on symbols. struct WasmSymbolData { @@ -80,7 +70,7 @@ std::vector DataRelocations; // Fixups for call_indirect type indices. - std::vector TypeIndexFixups; + std::vector TypeIndexFixups; // Index values to use for fixing up call_indirect type indices. std::vector TypeIndexFixupTypes; @@ -269,8 +259,11 @@ if (RefA) { if (RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX) { - TypeIndexFixups.push_back(TypeIndexFixup(FixupOffset, SymA, - &FixupSection)); + assert(C == 0); + WasmRelocationEntry Rec(FixupOffset, SymA, C, + wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB, + &FixupSection); + TypeIndexFixups.push_back(Rec); return; } } @@ -358,7 +351,9 @@ struct WasmGlobal { wasm::ValType Type; bool IsMutable; - uint32_t InitialValue; + bool HasImport; + uint64_t InitialValue; + uint32_t ImportIndex; }; } // end anonymous namespace @@ -507,6 +502,29 @@ } } +// Write out the the type relocation records that the linker will +// need to handle. +static void WriteTypeRelocations( + ArrayRef TypeIndexFixups, + ArrayRef TypeIndexFixupTypes, + raw_pwrite_stream &Stream) +{ + for (size_t i = 0, e = TypeIndexFixups.size(); i < e; ++i) { + const WasmRelocationEntry &Fixup = TypeIndexFixups[i]; + uint32_t Type = TypeIndexFixupTypes[i]; + + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); + assert(Fixup.Addend == 0); + + uint64_t Offset = Fixup.Offset + + Fixup.FixupSection->getSectionOffset(); + + encodeULEB128(Fixup.Type, Stream); + encodeULEB128(Offset, Stream); + encodeULEB128(Type, Stream); + } +} + void WasmObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { MCContext &Ctx = Asm.getContext(); @@ -526,6 +544,8 @@ unsigned NumFuncImports = 0; unsigned NumGlobalImports = 0; SmallVector DataBytes; + uint32_t StackPointerGlobal = 0; + bool HasStackPointer = false; // Populate the IsAddressTaken set. for (WasmRelocationEntry RelEntry : CodeRelocations) { @@ -605,15 +625,68 @@ if (!DataFrag.getFixups().empty()) report_fatal_error("fixups not supported in .global_variables"); const SmallVectorImpl &Contents = DataFrag.getContents(); - for (char p : Contents) { + for (const uint8_t *p = (const uint8_t *)Contents.data(), + *end = (const uint8_t *)Contents.data() + Contents.size(); + p != end; ) { WasmGlobal G; - G.Type = wasm::ValType(p); - G.IsMutable = true; - G.InitialValue = 0; + if (end - p < 3) + report_fatal_error("truncated global variable encoding"); + G.Type = wasm::ValType(int8_t(*p++)); + G.IsMutable = bool(*p++); + G.HasImport = bool(*p++); + if (G.HasImport) { + G.InitialValue = 0; + + WasmImport Import; + Import.ModuleName = (const char *)p; + const uint8_t *nul = (const uint8_t *)memchr(p, '\0', end - p); + if (!nul) + report_fatal_error("global module name must be nul-terminated"); + p = nul + 1; + nul = (const uint8_t *)memchr(p, '\0', end - p); + if (!nul) + report_fatal_error("global base name must be nul-terminated"); + Import.FieldName = (const char *)p; + p = nul + 1; + + Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Import.Type = int32_t(G.Type); + + G.ImportIndex = NumGlobalImports; + ++NumGlobalImports; + + Imports.push_back(Import); + } else { + unsigned n; + G.InitialValue = decodeSLEB128(p, &n); + G.ImportIndex = 0; + if (n > end - p) + report_fatal_error("global initial value must be valid SLEB128"); + p += n; + } Globals.push_back(G); } } + // In the special .stack_pointer section, we've encoded the stack pointer + // index. + MCSectionWasm *StackPtr = Ctx.getWasmSection(".stack_pointer", 0, 0); + if (!StackPtr->getFragmentList().empty()) { + if (StackPtr->getFragmentList().size() != 1) + report_fatal_error("only one .stack_pointer fragment supported"); + const MCFragment &Frag = *StackPtr->begin(); + if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data) + report_fatal_error("only data supported in .stack_pointer"); + const MCDataFragment &DataFrag = cast(Frag); + if (!DataFrag.getFixups().empty()) + report_fatal_error("fixups not supported in .stack_pointer"); + const SmallVectorImpl &Contents = DataFrag.getContents(); + if (Contents.size() != 4) + report_fatal_error("only one entry supported in .stack_pointer"); + HasStackPointer = true; + StackPointerGlobal = NumGlobalImports + *(const int32_t *)Contents.data(); + } + // Handle defined symbols. for (const MCSymbol &S : Asm.symbols()) { // Ignore unnamed temporary symbols, which aren't ever exported, imported, @@ -712,7 +785,9 @@ WasmGlobal Global; Global.Type = PtrType; Global.IsMutable = false; + Global.HasImport = false; Global.InitialValue = DataSection.getSectionOffset(); + Global.ImportIndex = 0; SymbolIndices[&WS] = Index; Globals.push_back(Global); } @@ -736,7 +811,10 @@ } // Add types for indirect function calls. - for (const TypeIndexFixup &Fixup : TypeIndexFixups) { + for (const WasmRelocationEntry &Fixup : TypeIndexFixups) { + assert(Fixup.Addend == 0); + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); + WasmFunctionType F; F.Returns = Fixup.Symbol->getReturns(); F.Params = Fixup.Symbol->getParams(); @@ -793,7 +871,7 @@ encodeULEB128(Import.Type, getStream()); break; case wasm::WASM_EXTERNAL_GLOBAL: - encodeSLEB128(Import.Type, getStream()); + encodeSLEB128(int32_t(Import.Type), getStream()); encodeULEB128(0, getStream()); // mutability break; default: @@ -853,8 +931,15 @@ writeValueType(Global.Type); write8(Global.IsMutable); - write8(wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(Global.InitialValue, getStream()); // offset + if (Global.HasImport) { + assert(Global.InitialValue == 0); + write8(wasm::WASM_OPCODE_GET_GLOBAL); + encodeULEB128(Global.ImportIndex, getStream()); + } else { + assert(Global.ImportIndex == 0); + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Global.InitialValue, getStream()); // offset + } write8(wasm::WASM_OPCODE_END); } @@ -944,7 +1029,9 @@ uint32_t Type = TypeIndexFixupTypes[i]; unsigned Padding = PaddingFor5ByteULEB128(Type); - const TypeIndexFixup &Fixup = TypeIndexFixups[i]; + const WasmRelocationEntry &Fixup = TypeIndexFixups[i]; + assert(Fixup.Addend == 0); + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); uint64_t Offset = Fixup.Offset + Fixup.FixupSection->getSectionOffset(); @@ -1021,6 +1108,7 @@ encodeULEB128(CodeRelocations.size(), getStream()); WriteRelocations(CodeRelocations, getStream(), SymbolIndices); + WriteTypeRelocations(TypeIndexFixups, TypeIndexFixupTypes, getStream()); endSection(Section); } @@ -1038,6 +1126,18 @@ endSection(Section); } + // === Linking Metadata Section ============================================== + if (HasStackPointer) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); + + encodeULEB128(1, getStream()); // count + + encodeULEB128(wasm::WEBASSEMBLY_STACK_POINTER, getStream()); // type + encodeULEB128(StackPointerGlobal, getStream()); // id + + endSection(Section); + } + // TODO: Translate the .comment section to the output. // TODO: Translate debug sections to the output. Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/MachineValueType.h" #include "llvm/MC/MCInstPrinter.h" +#include "llvm/Support/Wasm.h" namespace llvm { @@ -50,6 +51,7 @@ namespace WebAssembly { const char *TypeToString(MVT Ty); +const char *TypeToString(wasm::ValType Type); } // end namespace WebAssembly Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -242,3 +242,18 @@ llvm_unreachable("unsupported type"); } } + +const char *llvm::WebAssembly::TypeToString(wasm::ValType Type) { + switch (Type) { + case wasm::ValType::I32: + return "i32"; + case wasm::ValType::I64: + return "i64"; + case wasm::ValType::F32: + return "f32"; + case wasm::ValType::F64: + return "f64"; + default: + llvm_unreachable("unsupported type"); + } +} Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -38,7 +38,9 @@ /// .local virtual void emitLocal(ArrayRef Types) = 0; /// .globalvar - virtual void emitGlobal(ArrayRef Types) = 0; + virtual void emitGlobal(ArrayRef Globals) = 0; + /// .stack_pointer + virtual void emitStackPointer(uint32_t Index) = 0; /// .endfunc virtual void emitEndFunc() = 0; /// .functype @@ -66,7 +68,8 @@ void emitParam(MCSymbol *Symbol, ArrayRef Types) override; void emitResult(MCSymbol *Symbol, ArrayRef Types) override; void emitLocal(ArrayRef Types) override; - void emitGlobal(ArrayRef Types) override; + void emitGlobal(ArrayRef Globals) override; + void emitStackPointer(uint32_t Index) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, SmallVectorImpl &Params, @@ -83,7 +86,8 @@ void emitParam(MCSymbol *Symbol, ArrayRef Types) override; void emitResult(MCSymbol *Symbol, ArrayRef Types) override; void emitLocal(ArrayRef Types) override; - void emitGlobal(ArrayRef Types) override; + void emitGlobal(ArrayRef Globals) override; + void emitStackPointer(uint32_t Index) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, SmallVectorImpl &Params, @@ -100,7 +104,8 @@ void emitParam(MCSymbol *Symbol, ArrayRef Types) override; void emitResult(MCSymbol *Symbol, ArrayRef Types) override; void emitLocal(ArrayRef Types) override; - void emitGlobal(ArrayRef Types) override; + void emitGlobal(ArrayRef Globals) override; + void emitStackPointer(uint32_t Index) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, SmallVectorImpl &Params, Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -25,7 +25,6 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" -#include "llvm/Support/Wasm.h" using namespace llvm; WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S) @@ -88,13 +87,31 @@ } } -void WebAssemblyTargetAsmStreamer::emitGlobal(ArrayRef Types) { - if (!Types.empty()) { +void WebAssemblyTargetAsmStreamer::emitGlobal( + ArrayRef Globals) { + if (!Globals.empty()) { OS << "\t.globalvar \t"; - PrintTypes(OS, Types); + + bool First = true; + for (const wasm::Global &G : Globals) { + if (First) + First = false; + else + OS << ", "; + OS << WebAssembly::TypeToString(G.Type); + if (G.InitialModule) + OS << '=' << G.InitialModule << ':' << G.InitialName; + else + OS << '=' << G.InitialValue; + } + OS << '\n'; } } +void WebAssemblyTargetAsmStreamer::emitStackPointer(uint32_t Index) { + OS << "\t.stack_pointer\t" << Index << '\n'; +} + void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; } void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType( @@ -135,10 +152,16 @@ emitValueType(WebAssembly::toValType(Type)); } -void WebAssemblyTargetELFStreamer::emitGlobal(ArrayRef Types) { +void WebAssemblyTargetELFStreamer::emitGlobal( + ArrayRef Globals) { llvm_unreachable(".globalvar encoding not yet implemented"); } +void WebAssemblyTargetELFStreamer::emitStackPointer( + uint32_t Index) { + llvm_unreachable(".stack_pointer encoding not yet implemented"); +} + void WebAssemblyTargetELFStreamer::emitEndFunc() { Streamer.EmitIntValue(WebAssembly::End, 1); } @@ -190,15 +213,36 @@ } } -void WebAssemblyTargetWasmStreamer::emitGlobal(ArrayRef Types) { +void WebAssemblyTargetWasmStreamer::emitGlobal( + ArrayRef Globals) { // Encode the globals use by the funciton into the special .global_variables // section. This will later be decoded and turned into contents for the // Globals Section. Streamer.PushSection(); Streamer.SwitchSection(Streamer.getContext() .getWasmSection(".global_variables", 0, 0)); - for (MVT Ty : Types) - Streamer.EmitIntValue(int64_t(WebAssembly::toValType(Ty)), 1); + for (const wasm::Global &G : Globals) { + Streamer.EmitIntValue(int32_t(G.Type), 1); + Streamer.EmitIntValue(G.Mutable, 1); + if (!G.InitialModule) { + Streamer.EmitIntValue(0, 1); // indicate that we have an int value + Streamer.EmitSLEB128IntValue(0); + } else { + Streamer.EmitIntValue(1, 1); // indicate that we have a module import + Streamer.EmitBytes(G.InitialModule); + Streamer.EmitIntValue(0, 1); // nul-terminate + Streamer.EmitBytes(G.InitialName); + Streamer.EmitIntValue(0, 1); // nul-terminate + } + } + Streamer.PopSection(); +} + +void WebAssemblyTargetWasmStreamer::emitStackPointer(uint32_t Index) { + Streamer.PushSection(); + Streamer.SwitchSection(Streamer.getContext() + .getWasmSection(".stack_pointer", 0, 0)); + Streamer.EmitIntValue(Index, 4); Streamer.PopSection(); } Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -97,6 +97,8 @@ if (!TM.getTargetTriple().isOSBinFormatELF()) { MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo(); getTargetStreamer()->emitGlobal(MMIW.getGlobals()); + if (MMIW.hasStackPointerGlobal()) + getTargetStreamer()->emitStackPointer(MMIW.getStackPointerGlobal()); } } Index: lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -192,7 +192,16 @@ auto &MMIW = MF.getMMI().getObjFileInfo(); if (!MMIW.hasStackPointerGlobal()) { MMIW.setStackPointerGlobal(MMIW.getGlobals().size()); - MMIW.addGlobal(MVT::i32); + + // Create the stack-pointer global. For now, just use the + // Emscripten/Binaryen ABI names. + wasm::Global G; + G.Type = wasm::ValType::I32; + G.Mutable = true; + G.InitialValue = 0; + G.InitialModule = "env"; + G.InitialName = "STACKTOP"; + MMIW.addGlobal(G); } BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GET_GLOBAL_I32), SPReg) .addImm(MMIW.getStackPointerGlobal());