Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal static class WasmObjectNodeSection
public static readonly ObjectNodeSection CombinedDataSection = new ObjectNodeSection("wasm.alldata", SectionType.Writeable, needsAlign: false);
public static readonly ObjectNodeSection FunctionSection = new ObjectNodeSection("wasm.function", SectionType.ReadOnly, needsAlign: false);
public static readonly ObjectNodeSection ExportSection = new ObjectNodeSection("wasm.export", SectionType.ReadOnly, needsAlign: false);
public static readonly ObjectNodeSection ElementSection = new ObjectNodeSection("wasm.element", SectionType.ReadOnly, needsAlign: false);
public static readonly ObjectNodeSection MemorySection = new ObjectNodeSection("wasm.memory", SectionType.ReadOnly, needsAlign: false);
public static readonly ObjectNodeSection TableSection = new ObjectNodeSection("wasm.table", SectionType.ReadOnly, needsAlign: false);
public static readonly ObjectNodeSection ImportSection = new ObjectNodeSection("wasm.import", SectionType.ReadOnly, needsAlign: false);
Expand Down Expand Up @@ -143,6 +144,28 @@ private void WriteMemoryExport(string name, int memoryIndex) =>
private void WriteGlobalExport(string name, int globalIndex) =>
WriteExport(name, WasmExportKind.Global, globalIndex);

private int _numElements;
private void WriteFunctionElement(WasmInstructionGroup e0, ReadOnlySpan<int> functionIndices)
{
SectionWriter writer = GetOrCreateSection(WasmObjectNodeSection.ElementSection);
// e0:expr y*:list(funcidx)
// elem (ref func) (ref.func y)* (active 0 e0)
writer.WriteULEB128(0);

// FIXME: Add a way to encode directly into the writer without a scratch buffer
int encodeSize = e0.EncodeSize();
int bytesWritten = e0.Encode(writer.Buffer.GetSpan(encodeSize));
Debug.Assert(bytesWritten == encodeSize);
writer.Buffer.Advance((int)bytesWritten);

writer.WriteULEB128((ulong)functionIndices.Length);

foreach (int index in functionIndices)
writer.WriteULEB128((ulong)index);

_numElements++;
}

private List<WasmSection> _sections = new();
private Dictionary<string, int> _sectionNameToIndex = new();
private Dictionary<ObjectNodeSection, WasmSectionType> _sectionToType = new()
Expand All @@ -151,6 +174,7 @@ private void WriteGlobalExport(string name, int globalIndex) =>
{ WasmObjectNodeSection.FunctionSection, WasmSectionType.Function },
{ WasmObjectNodeSection.TableSection, WasmSectionType.Table },
{ WasmObjectNodeSection.ExportSection, WasmSectionType.Export },
{ WasmObjectNodeSection.ElementSection, WasmSectionType.Element },
{ WasmObjectNodeSection.ImportSection, WasmSectionType.Import },
{ ObjectNodeSection.WasmTypeSection, WasmSectionType.Type },
{ ObjectNodeSection.WasmCodeSection, WasmSectionType.Code }
Expand All @@ -171,12 +195,21 @@ protected internal override void UpdateSectionAlignment(int sectionIndex, int al
// This is a no-op for now under Wasm
}

WasmInstructionGroup GetImageFunctionPointerBaseOffset(int offset)
{
return new WasmInstructionGroup([
Global.Get(ImageFunctionPointerBaseGlobalIndex),
I32.Const(offset),
I32.Add,
]);
}

private WasmDataSection CreateCombinedDataSection()
{
WasmInstructionGroup GetR2RStartOffset(int offset)
WasmInstructionGroup GetImageBaseOffset(int offset)
{
return new WasmInstructionGroup([
Global.Get(R2RStartGlobalIndex),
Global.Get(ImageBaseGlobalIndex),
I32.Const(offset),
I32.Add,
]);
Expand All @@ -189,7 +222,7 @@ WasmInstructionGroup GetR2RStartOffset(int offset)
{
Debug.Assert(wasmSection.Type == WasmSectionType.Data);
WasmDataSegment segment = new WasmDataSegment(wasmSection.Stream, wasmSection.Name, WasmDataSectionType.Active,
GetR2RStartOffset(offset));
GetImageBaseOffset(offset));
segments.Add(segment);
offset += segment.ContentSize;
}
Expand Down Expand Up @@ -267,8 +300,8 @@ private void WriteTableSection()
writer.WriteByte(0x01); // number of tables
writer.WriteByte(0x70); // element type: funcref
writer.WriteByte(0x01); // table limits: flags (1 = has maximum)
writer.WriteULEB128((ulong)0);
writer.WriteULEB128((ulong)_methodCount); // table limits: initial size in number of entries
writer.WriteULEB128((ulong)_methodCount); // minimum
writer.WriteULEB128((ulong)_methodCount); // maximum
}

private void PrependCount(WasmSection section, int count)
Expand All @@ -289,6 +322,7 @@ private WasmSection SectionByName(string name)
WasmObjectNodeSection.FunctionSection.Name,
WasmObjectNodeSection.TableSection.Name,
WasmObjectNodeSection.ExportSection.Name,
WasmObjectNodeSection.ElementSection.Name,
ObjectNodeSection.WasmCodeSection.Name,
WasmObjectNodeSection.CombinedDataSection.Name,
];
Expand Down Expand Up @@ -394,7 +428,7 @@ private unsafe void ResolveRelocations(MemoryStream sectionStream, List<Symbolic

Span<byte> ReadRelocToDataSpan(SymbolicRelocation reloc, byte[] buffer)
{
Span<byte> relocContents = buffer.AsSpan(0, Relocation.GetSize(reloc.Type));
Span<byte> relocContents = buffer.AsSpan(0, Relocation.GetSize(reloc.Type));
sectionStream.Position = reloc.Offset;
sectionStream.ReadExactly(relocContents);
return relocContents;
Expand All @@ -408,13 +442,15 @@ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte *pData)
}

const int StackPointerGlobalIndex = 0;
const int R2RStartGlobalIndex = 1;
const int ImageBaseGlobalIndex = 1;
const int ImageFunctionPointerBaseGlobalIndex = 2;

private WasmImport[] _defaultImports = new[]
{
null, // placeholder for memory, which is set up dynamically in WriteImports()
new WasmImport("env", "__stack_pointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex),
new WasmImport("env", "__r2r_start", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: R2RStartGlobalIndex),
new WasmImport("env", "__image_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex),
new WasmImport("env", "__image_function_pointer_base", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageFunctionPointerBaseGlobalIndex),
};

private void WriteImports()
Expand Down Expand Up @@ -454,11 +490,28 @@ private void WriteExports()
}
}

private void WriteElements()
{
// Generate the function pointer table element that contains function pointers for all of our functions
int[] functionIndices = new int[_uniqueSymbols.Count];
// NOTE: This relies on items in _uniqueSymbols being assigned sequentially and that iteration over Values is order-preserving.
// BCL Dictionary preserves insertion order so as long as we keep using it, we would get the function indices in the order they were added.
_uniqueSymbols.Values.CopyTo(functionIndices, 0);
// Enforce that the function pointers are sequential so that (image_function_pointer_base + 0) == ftn index 0
#if DEBUG
for (int i = 0; i < _uniqueSymbols.Count; i++) {
Debug.Assert(functionIndices[i] == i);
}
#endif
WriteFunctionElement(GetImageFunctionPointerBaseOffset(0), functionIndices);
}

// For now, this function just prepares the function, exports, and type sections for emission by prepending the counts.
private protected override void EmitSymbolTable(IDictionary<Utf8String, SymbolDefinition> definedSymbols, SortedSet<Utf8String> undefinedSymbols)
{
WriteImports();
WriteExports();
WriteElements();

int funcIdx = _sectionNameToIndex[WasmObjectNodeSection.FunctionSection.Name];
PrependCount(_sections[funcIdx], _methodCount);
Expand All @@ -469,6 +522,11 @@ private protected override void EmitSymbolTable(IDictionary<Utf8String, SymbolDe
int exportIdx = _sectionNameToIndex[WasmObjectNodeSection.ExportSection.Name];
PrependCount(_sections[exportIdx], _numExports);

if (_sectionNameToIndex.TryGetValue(WasmObjectNodeSection.ElementSection.Name, out int elementIdx))
{
PrependCount(_sections[elementIdx], _numElements);
}

PrependCount(SectionByName(WasmObjectNodeSection.ImportSection.Name), _numImports);
}
}
Expand Down
Loading