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 {
std::vector<WasmRelocationEntry> DataRelocations;
// Fixups for call_indirect type indices.
- std::vector<TypeIndexFixup> TypeIndexFixups;
+ std::vector<WasmRelocationEntry> TypeIndexFixups;
// Index values to use for fixing up call_indirect type indices.
std::vector<uint32_t> TypeIndexFixupTypes;
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;
}
}
struct WasmGlobal {
wasm::ValType Type;
bool IsMutable;
- uint32_t InitialValue;
+ bool HasImport;
+ uint64_t InitialValue;
+ uint32_t ImportIndex;
};
} // end anonymous namespace
}
}
+// Write out the the type relocation records that the linker will
+// need to handle.
+static void WriteTypeRelocations(
+ ArrayRef<WasmRelocationEntry> TypeIndexFixups,
+ ArrayRef<uint32_t> 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();
unsigned NumFuncImports = 0;
unsigned NumGlobalImports = 0;
SmallVector<char, 0> DataBytes;
+ uint32_t StackPointerGlobal = 0;
+ bool HasStackPointer = false;
// Populate the IsAddressTaken set.
for (WasmRelocationEntry RelEntry : CodeRelocations) {
if (!DataFrag.getFixups().empty())
report_fatal_error("fixups not supported in .global_variables");
const SmallVectorImpl<char> &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<MCDataFragment>(Frag);
+ if (!DataFrag.getFixups().empty())
+ report_fatal_error("fixups not supported in .stack_pointer");
+ const SmallVectorImpl<char> &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,
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);
}
}
// 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();
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:
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);
}
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();
encodeULEB128(CodeRelocations.size(), getStream());
WriteRelocations(CodeRelocations, getStream(), SymbolIndices);
+ WriteTypeRelocations(TypeIndexFixups, TypeIndexFixupTypes, getStream());
endSection(Section);
}
endSection(Section);
}
+ // === Linking Metadata Section ==============================================
+ if (HasStackPointer) {
+ startSection(Section, wasm::WASM_SEC_CUSTOM, "linking");
+
+ encodeULEB128(1, getStream()); // count
+
+ encodeULEB128(wasm::WASM_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.
#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)
}
}
-void WebAssemblyTargetAsmStreamer::emitGlobal(ArrayRef<MVT> Types) {
- if (!Types.empty()) {
+void WebAssemblyTargetAsmStreamer::emitGlobal(
+ ArrayRef<wasm::Global> 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.empty())
+ 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(
emitValueType(WebAssembly::toValType(Type));
}
-void WebAssemblyTargetELFStreamer::emitGlobal(ArrayRef<MVT> Types) {
+void WebAssemblyTargetELFStreamer::emitGlobal(
+ ArrayRef<wasm::Global> 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);
}
}
}
-void WebAssemblyTargetWasmStreamer::emitGlobal(ArrayRef<MVT> Types) {
+void WebAssemblyTargetWasmStreamer::emitGlobal(
+ ArrayRef<wasm::Global> 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.empty()) {
+ 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();
}
/// .local
virtual void emitLocal(ArrayRef<MVT> Types) = 0;
/// .globalvar
- virtual void emitGlobal(ArrayRef<MVT> Types) = 0;
+ virtual void emitGlobal(ArrayRef<wasm::Global> Globals) = 0;
+ /// .stack_pointer
+ virtual void emitStackPointer(uint32_t Index) = 0;
/// .endfunc
virtual void emitEndFunc() = 0;
/// .functype
void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
- void emitGlobal(ArrayRef<MVT> Types) override;
+ void emitGlobal(ArrayRef<wasm::Global> Globals) override;
+ void emitStackPointer(uint32_t Index) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &Params,
void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
- void emitGlobal(ArrayRef<MVT> Types) override;
+ void emitGlobal(ArrayRef<wasm::Global> Globals) override;
+ void emitStackPointer(uint32_t Index) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &Params,
void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
- void emitGlobal(ArrayRef<MVT> Types) override;
+ void emitGlobal(ArrayRef<wasm::Global> Globals) override;
+ void emitStackPointer(uint32_t Index) override;
void emitEndFunc() override;
void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &Params,