Skip to content

Commit

Permalink
Add support for linking global-variable-relative data segments.
Browse files Browse the repository at this point in the history
  • Loading branch information
axel22 committed Dec 17, 2019
1 parent 94c7393 commit 9234d40
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
;;
(module
(type (;0;) (func))
(import "runtime" "memory" (memory (;0;) 4))
(import "runtime" "heap" (memory (;0;) 4))
(import "runtime" "heap_base" (global (;0;) i32))
(global (;1;) i32 (global.get 0))
(export "string_constants_base" (global 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
;;
(module
(type (;0;) (func (result i32)))
(import "memory" "memory" (memory (;0;) 4))
(import "runtime" "heap" (memory (;0;) 4))
(import "constant-pools" "string_constants_base" (global (;0;) i32))
(func (export "_main") (type 0)
(local i32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
;; SOFTWARE.
;;
(module
(memory 4 4)
(import "memory" "memory" (memory (;0;) 4))
(global (;0;) i32 (i32.const 1096))
(export "memory" (memory 0))
(export "heap" (memory 0))
(export "heap_base" (global 0))
)
45 changes: 26 additions & 19 deletions wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import org.graalvm.wasm.constants.GlobalModifier;
import org.graalvm.wasm.constants.ImportIdentifier;
import org.graalvm.wasm.constants.LimitsPrefix;
import org.graalvm.wasm.exception.WasmException;
import org.graalvm.wasm.exception.WasmLinkerException;
import org.graalvm.wasm.memory.WasmMemory;
import org.graalvm.wasm.nodes.WasmBlockNode;
Expand Down Expand Up @@ -1018,8 +1017,6 @@ private void readElementSection(WasmContext context) {
case Instructions.I32_CONST: {
elemOffset = readSignedInt32();
readEnd();
// int[] contents = readElemContents();
// module.symbolTable().initializeTableWithFunctions(context, elementOffset, contents);
break;
}
case Instructions.GLOBAL_GET: {
Expand Down Expand Up @@ -1174,50 +1171,60 @@ private void readDataSection(WasmContext context) {
// At the moment, WebAssembly only supports one memory instance, thus the only valid
// memory index is 0.
Assert.assertIntEqual(memIndex, 0, "Invalid memory index, only the memory index 0 is currently supported.");
long dataOffset = 0;
byte instruction = read1();

// Data dataOffset expression must be a constant expression with result type i32.
// https://webassembly.github.io/spec/core/syntax/modules.html#data-segments
// https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions

// Read the offset expression.
int offsetAddress = -1;
int offsetGlobalIndex = -1;
switch (instruction) {
case Instructions.I32_CONST:
dataOffset = readSignedInt32();
offsetAddress = readSignedInt32();
readEnd();
break;
case Instructions.GLOBAL_GET:
int globalIndex = readGlobalIndex();
offsetGlobalIndex = readGlobalIndex();
readEnd();
// TODO: Implement GLOBAL_GET case for data sections (and add tests).
throw new WasmException("GLOBAL_GET in data section not implemented.");
break;
default:
Assert.fail(String.format("Invalid instruction for data offset expression: 0x%02X", instruction));
}
// Try to immediately resolve the global's value, if that global is initialized.
// Test functions that re-read the data section to reset the memory depend on this,
// since they need to avoid re-linking.
if (offsetGlobalIndex != -1 && module.symbolTable().isGlobalInitialized(offsetGlobalIndex)) {
int offsetGlobalAddress = module.symbolTable().globalAddress(offsetGlobalIndex);
offsetAddress = context.globals().loadAsInt(offsetGlobalAddress);
offsetGlobalIndex = -1;
}

// Copy the contents, or schedule a linker task for this.
int byteLength = readVectorLength();
long baseAddress = dataOffset;
final WasmMemory memory = module.symbolTable().memory();
if (memory != null && allDataSectionsResolved) {
// A data section can be loaded directly into memory only if there are no prior
// unresolved data sections.
memory.validateAddress(null, baseAddress, byteLength);
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = read1();
memory.store_i32_8(baseAddress + writeOffset, b);
}
} else {
if (memory == null || !allDataSectionsResolved || offsetGlobalIndex != -1) {
// A data section can only be resolved after the memory is resolved.
// If the data section is offset by a global variable,
// then the data section can only be resolved after the global is resolved.
// When some data section is not resolved, all the later data sections must be
// resolved after it.
byte[] dataSegment = new byte[byteLength];
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = read1();
dataSegment[writeOffset] = b;
}
context.linker().resolveDataSegment(module, dataSegmentId, baseAddress, byteLength, dataSegment, allDataSectionsResolved);
context.linker().resolveDataSegment(context, module, dataSegmentId, offsetAddress, offsetGlobalIndex, byteLength, dataSegment, allDataSectionsResolved);
allDataSectionsResolved = false;
} else {
// A data section can be loaded directly into memory only if there are no prior
// unresolved data sections.
memory.validateAddress(null, offsetAddress, byteLength);
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = read1();
memory.store_i32_8(offsetAddress + writeOffset, b);
}
}
}
}
Expand Down
102 changes: 50 additions & 52 deletions wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,22 +195,36 @@ void resolveGlobalImport(WasmContext context, WasmModule module, ImportDescripto
int address = importedModule.symbolTable().globalAddress(exportedGlobalIndex);
module.symbolTable().setGlobalAddress(globalIndex, address);
};
final ImportGlobalSym importGlobalSym = new ImportGlobalSym(module.name(), importDescriptor);
final Sym[] dependencies = new Sym[]{new ExportGlobalSym(importedModuleName, importedGlobalName)};
resolutionDag.resolveLater(new ImportGlobalSym(module.name(), importDescriptor), dependencies, resolveAction);
resolutionDag.resolveLater(importGlobalSym, dependencies, resolveAction);
resolutionDag.resolveLater(new InitializeGlobalSym(module.name(), globalIndex), new Sym[]{importGlobalSym}, () -> {
module.symbolTable().initializeGlobal(globalIndex);
});
}

void resolveGlobalExport(WasmModule module, String globalName, int globalIndex) {
final Sym[] dependencies;
dependencies = new Sym[]{new InitializeGlobalSym(module.name(), globalIndex)};
resolutionDag.resolveLater(new ExportGlobalSym(module.name(), globalName), dependencies, ResolutionDag.NO_RESOLVE_ACTION);
}

void resolveGlobalInitialization(WasmModule module, int globalIndex) {
module.symbolTable().initializeGlobal(globalIndex);
final Sym[] dependencies = ResolutionDag.NO_DEPENDENCIES;
resolutionDag.resolveLater(new InitializeGlobalSym(module.name(), globalIndex), dependencies, NO_RESOLVE_ACTION);
}

void resolveGlobalInitialization(WasmContext context, WasmModule module, int globalIndex, int sourceGlobalIndex) {
final Runnable resolveAction = () -> {
final int sourceAddress = module.symbolTable().globalAddress(sourceGlobalIndex);
final long sourceValue = context.globals().loadAsLong(sourceAddress);
final int address = module.symbolTable().globalAddress(globalIndex);
context.globals().storeLong(address, sourceValue);
module.symbolTable().initializeGlobal(globalIndex);
};
final Sym[] dependencies;
final ImportDescriptor importDescriptor = module.symbolTable().importedGlobals().get(globalIndex);
if (importDescriptor != null) {
dependencies = new Sym[]{new ImportGlobalSym(module.name(), importDescriptor)};
} else {
// TODO: Handle the case in which the exported global's initializing expression is unresolved.
dependencies = ResolutionDag.NO_DEPENDENCIES;
}
resolutionDag.resolveLater(new ExportGlobalSym(module.name(), globalName), dependencies, resolveAction);
final Sym[] dependencies = new Sym[]{new InitializeGlobalSym(module.name(), sourceGlobalIndex)};
resolutionDag.resolveLater(new InitializeGlobalSym(module.name(), globalIndex), dependencies, resolveAction);
}

void resolveFunctionImport(WasmContext context, WasmModule module, WasmFunction function) {
Expand All @@ -236,37 +250,10 @@ void resolveFunctionImport(WasmContext context, WasmModule module, WasmFunction
resolutionDag.resolveLater(new ImportFunctionSym(module.name(), function.importDescriptor()), dependencies, resolveAction);
}

void resolveGlobalInitialization(WasmModule module, int globalIndex) {
final Runnable resolveAction = () -> {
};
final Sym[] dependencies = ResolutionDag.NO_DEPENDENCIES;
resolutionDag.resolveLater(new InitializeGlobalSym(module.name(), globalIndex), dependencies, resolveAction);
}

void resolveGlobalInitialization(WasmContext context, WasmModule module, int globalIndex, int sourceGlobalIndex) {
final Runnable resolveAction = () -> {
final int sourceAddress = module.symbolTable().globalAddress(sourceGlobalIndex);
final long sourceValue = context.globals().loadAsLong(sourceAddress);
final int address = module.symbolTable().globalAddress(globalIndex);
context.globals().storeLong(address, sourceValue);
};
final SymbolTable symtab = module.symbolTable();
final Sym[] dependencies;
final ImportDescriptor importDescriptor = symtab.importedGlobals().get(sourceGlobalIndex);
if (importDescriptor != null) {
dependencies = new Sym[]{new ImportGlobalSym(module.name(), importDescriptor)};
} else {
dependencies = new Sym[]{new InitializeGlobalSym(module.name(), sourceGlobalIndex)};
}
resolutionDag.resolveLater(new InitializeGlobalSym(module.name(), globalIndex), dependencies, resolveAction);
}

void resolveFunctionExport(WasmModule module, int functionIndex, String exportedFunctionName) {
final Runnable resolveAction = () -> {
};
final ImportDescriptor importDescriptor = module.symbolTable().function(functionIndex).importDescriptor();
final Sym[] dependencies = (importDescriptor != null) ? new Sym[]{new ImportFunctionSym(module.name(), importDescriptor)} : ResolutionDag.NO_DEPENDENCIES;
resolutionDag.resolveLater(new ExportFunctionSym(module.name(), exportedFunctionName), dependencies, resolveAction);
resolutionDag.resolveLater(new ExportFunctionSym(module.name(), exportedFunctionName), dependencies, NO_RESOLVE_ACTION);
}

void resolveCallsite(WasmModule module, WasmBlockNode block, int controlTableOffset, WasmFunction function) {
Expand All @@ -278,9 +265,7 @@ void resolveCallsite(WasmModule module, WasmBlockNode block, int controlTableOff
}

void resolveCodeEntry(WasmModule module, int functionIndex) {
final Runnable resolveAction = () -> {
};
resolutionDag.resolveLater(new CodeEntrySym(module.name(), functionIndex), ResolutionDag.NO_DEPENDENCIES, resolveAction);
resolutionDag.resolveLater(new CodeEntrySym(module.name(), functionIndex), ResolutionDag.NO_DEPENDENCIES, NO_RESOLVE_ACTION);
}

void resolveMemoryImport(WasmContext context, WasmModule module, ImportDescriptor importDescriptor, int initSize, int maxSize) {
Expand Down Expand Up @@ -316,27 +301,40 @@ void resolveMemoryImport(WasmContext context, WasmModule module, ImportDescripto
}

void resolveMemoryExport(WasmModule module, String exportedMemoryName) {
final Runnable resolveAction = () -> {
};
final ImportDescriptor importDescriptor = module.symbolTable().importedMemory();
final Sym[] dependencies = importDescriptor != null ? new Sym[]{new ImportMemorySym(module.name(), importDescriptor)} : ResolutionDag.NO_DEPENDENCIES;
resolutionDag.resolveLater(new ExportMemorySym(module.name(), exportedMemoryName), dependencies, resolveAction);
resolutionDag.resolveLater(new ExportMemorySym(module.name(), exportedMemoryName), dependencies, NO_RESOLVE_ACTION);
}

void resolveDataSegment(WasmModule module, int dataSegmentId, long baseAddress, int byteLength, byte[] data, boolean priorDataSectionsResolved) {
void resolveDataSegment(WasmContext context, WasmModule module, int dataSegmentId, int offsetAddress, int offsetGlobalIndex, int byteLength, byte[] data, boolean priorDataSectionsResolved) {
Assert.assertNotNull(module.symbolTable().importedMemory(), String.format("No memory declared or imported in the module '%s'", module.name()));
final Runnable resolveAction = () -> {
assert (offsetAddress != -1) ^ (offsetGlobalIndex != -1) : "Both an offset address and a offset global are specified for the data segment.";
WasmMemory memory = module.symbolTable().memory();
Assert.assertNotNull(memory, String.format("No memory declared or imported in the module '%s'", module.name()));
long baseAddress;
if (offsetGlobalIndex != -1) {
baseAddress = context.globals().loadAsInt(offsetGlobalIndex);
} else {
baseAddress = offsetAddress;
}
memory.validateAddress(null, baseAddress, byteLength);
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = data[writeOffset];
memory.store_i32_8(baseAddress + writeOffset, b);
}
};
final ImportMemorySym importMemorySym = new ImportMemorySym(module.name(), module.symbolTable().importedMemory());
final Sym[] dependencies = priorDataSectionsResolved ? new Sym[]{importMemorySym} : new Sym[]{importMemorySym, new DataSym(module.name(), dataSegmentId - 1)};
resolutionDag.resolveLater(new DataSym(module.name(), dataSegmentId), dependencies, resolveAction);
final ArrayList<Sym> dependencies = new ArrayList<>();
if (module.symbolTable().importedMemory() != null) {
dependencies.add(new ImportMemorySym(module.name(), module.symbolTable().importedMemory()));
}
if (!priorDataSectionsResolved) {
dependencies.add(new DataSym(module.name(), dataSegmentId - 1));
}
if (offsetGlobalIndex != -1) {
dependencies.add(new InitializeGlobalSym(module.name(), offsetGlobalIndex));
}
resolutionDag.resolveLater(new DataSym(module.name(), dataSegmentId), dependencies.toArray(new Sym[dependencies.size()]), resolveAction);
}

void resolveTableImport(WasmContext context, WasmModule module, ImportDescriptor importDescriptor, int initSize, int maxSize) {
Expand Down Expand Up @@ -375,11 +373,9 @@ void resolveTableImport(WasmContext context, WasmModule module, ImportDescriptor
}

void resolveTableExport(WasmModule module, String exportedTableName) {
final Runnable resolveAction = () -> {
};
final ImportDescriptor importDescriptor = module.symbolTable().importedTable();
final Sym[] dependencies = importDescriptor != null ? new Sym[]{new ImportTableSym(module.name(), importDescriptor)} : ResolutionDag.NO_DEPENDENCIES;
resolutionDag.resolveLater(new ExportTableSym(module.name(), exportedTableName), dependencies, resolveAction);
resolutionDag.resolveLater(new ExportTableSym(module.name(), exportedTableName), dependencies, NO_RESOLVE_ACTION);
}

void resolveElemSegment(WasmModule module, int elemSegmentId, int elemOffset, int segmentLength, WasmFunction[] functions) {
Expand All @@ -397,6 +393,8 @@ void resolveElemSegment(WasmModule module, int elemSegmentId, int elemOffset, in
}

static class ResolutionDag {
public static final Runnable NO_RESOLVE_ACTION = () -> {
};
private static final Sym[] NO_DEPENDENCIES = new Sym[0];

abstract static class Sym {
Expand Down Expand Up @@ -471,7 +469,7 @@ static class InitializeGlobalSym extends Sym {

@Override
public String toString() {
return String.format("(init global %d in %d)", globalIndex, moduleName);
return String.format("(init global %d in %s)", globalIndex, moduleName);
}

@Override
Expand Down
12 changes: 11 additions & 1 deletion wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/SymbolTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class SymbolTable {
private static final int UNINITIALIZED_GLOBAL_ADDRESS = -1;
private static final int GLOBAL_MUTABLE_BIT = 0x0100;
private static final int GLOBAL_EXPORT_BIT = 0x0200;
private static final int GLOBAL_INITIALIZED_BIT = 0x0400;
private static final int NO_EQUIVALENCE_CLASS = 0;
static final int FIRST_EQUIVALENCE_CLASS = NO_EQUIVALENCE_CLASS + 1;

Expand Down Expand Up @@ -196,7 +197,7 @@ public boolean equals(Object object) {
* The 1st byte is organized like this:
*
* <code>
* | . | . | . | . | . | . | exported flag | mutable flag |
* | . | . | . | . | . | initialized flag | exported flag | mutable flag |
* </code>
*/
@CompilationFinal(dimensions = 1) short[] globalTypes;
Expand Down Expand Up @@ -592,6 +593,15 @@ public byte globalValueType(int index) {
return (byte) (globalTypes[index] & 0xff);
}

void initializeGlobal(int index) {
checkNotLinked();
globalTypes[index] |= GLOBAL_INITIALIZED_BIT;
}

boolean isGlobalInitialized(int index) {
return (globalTypes[index] & GLOBAL_INITIALIZED_BIT) != 0;
}

Map<String, Integer> exportedGlobals() {
return exportedGlobals;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
package org.graalvm.wasm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import com.oracle.truffle.api.CallTarget;
Expand Down Expand Up @@ -70,7 +70,7 @@ public WasmContext(Env env, WasmLanguage language) {
this.globals = new GlobalRegistry();
this.tableRegistry = new TableRegistry();
this.memoryRegistry = new MemoryRegistry();
this.modules = new HashMap<>();
this.modules = new LinkedHashMap<>();
this.linker = new Linker(language);
initializeBuiltinModules();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ public String builtinNodeName() {

@CompilerDirectives.TruffleBoundary
private void resetModuleState(boolean zeroMemory) {
boolean first = true;
WasmContext context = contextReference().get();
for (WasmModule m : contextReference().get().modules().values()) {
for (WasmModule m : context.modules().values()) {
if (!m.isBuiltin()) {
contextReference().get().linker().resetModuleState(context, m, m.data(), zeroMemory);
context.linker().resetModuleState(context, m, m.data(), first && zeroMemory);
}
first = false;
}
}
}

0 comments on commit 9234d40

Please sign in to comment.