Skip to content

Commit

Permalink
Merge pull request #45 from dylibso/call-spec
Browse files Browse the repository at this point in the history
Call spec
  • Loading branch information
bhelx authored Oct 20, 2023
2 parents a29869b + 62594f1 commit c48e471
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 107 deletions.
4 changes: 3 additions & 1 deletion runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
float_literals.wast</wastToProcess>
<orderedWastToProcess>memory_grow.wast,
memory_size.wast,
stack.wast</orderedWastToProcess>
stack.wast,
call.wast</orderedWastToProcess>
<excludedTests>
<!-- Assertion failures -->
SpecV1MemoryGrowTest.test67,
Expand Down Expand Up @@ -113,6 +114,7 @@
SpecV1AddressTest.test251,
SpecV1AddressTest.test254,
SpecV1AlignTest.test129,
SpecV1CallTest.test51,
<!-- Invalid and Malformed failures, can be ignored for now -->
SpecV1LocalGetTest.test29,
SpecV1LocalGetTest.test30,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
*/
@FunctionalInterface
public interface ExportFunction {
Value apply(Value... args) throws ChicoryException;
Value[] apply(Value... args) throws ChicoryException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class Instance {
private FunctionType[] types;
private int[] functionTypes;
private HostFunction[] imports;
private Table table;

public Instance(
Module module,
Expand All @@ -21,7 +22,8 @@ public Instance(
FunctionBody[] functions,
FunctionType[] types,
int[] functionTypes,
HostFunction[] imports) {
HostFunction[] imports,
Table table) {
this.module = module;
this.globalInitalizers = globalInitalizers;
this.globals = globals;
Expand All @@ -31,6 +33,7 @@ public Instance(
this.functionTypes = functionTypes;
this.imports = imports;
this.machine = new Machine(this);
this.table = table;
}

public ExportFunction getExport(String name) {
Expand Down Expand Up @@ -87,4 +90,8 @@ public int[] getFunctionTypes() {
public HostFunction[] getImports() {
return imports;
}

public Table getTable() {
return table;
}
}
48 changes: 25 additions & 23 deletions runtime/src/main/java/com/dylibso/chicory/runtime/Machine.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public Machine(Instance instance) {
this.callStack = new Stack<>();
}

public Value call(int funcId, Value[] args, boolean popResults) throws ChicoryException {
public Value[] call(int funcId, Value[] args, boolean popResults) throws ChicoryException {
var func = instance.getFunction(funcId);
if (func != null) {
this.callStack.push(new StackFrame(funcId, 0, args, func.getLocals()));
Expand All @@ -46,13 +46,20 @@ public Value call(int funcId, Value[] args, boolean popResults) throws ChicoryEx
var type = instance.getTypes()[typeId];
if (type.getReturns().length == 0) return null;
if (this.stack.size() == 0) return null;
return this.stack.pop();

var totalResults = type.getReturns().length;
var results = new Value[totalResults];
for (var i = totalResults - 1; i >= 0; i--) {
results[i] = this.stack.pop();
}
return results;
}

void eval(List<Instruction> code) throws ChicoryException {
try {
var frame = callStack.peek();
boolean shouldReturn = false;

loop:
while (frame.pc < code.size()) {
if (shouldReturn) return;
Expand Down Expand Up @@ -123,27 +130,22 @@ void eval(List<Instruction> code) throws ChicoryException {
case RETURN:
shouldReturn = true;
break;
// case CALL_INDIRECT:
// {
// // var index =
// this.stack.pop().asInt();
// // var funcId =
// // instance.getTable().getFuncRef(index);
// // var typeId =
// // instance.getFunctionTypes().get(funcId);
// // var type =
// instance.getTypes().get(typeId);
// // // given a list of param
// types, let's pop those
// // params off the stack
// // // and pass as args to
// the function call
// // var args =
// // extractArgsForParams(type.paramTypes());
// // call(funcId, args,
// false);
// break;
// }
case CALL_INDIRECT:
{
var tableIdx = operands[1];
if (tableIdx != 0)
throw new ChicoryException(
"We only support a table index of 0 in call-indirect");
var funcTableIdx = this.stack.pop().asInt();
var funcId = instance.getTable().getFuncRef(funcTableIdx);
var typeId = (int) operands[0];
var type = instance.getTypes()[typeId];
// given a list of param types, let's pop those params off the stack
// and pass as args to the function call
var args = extractArgsForParams(type.getParams());
call(funcId, args, false);
break;
}
case DROP:
this.stack.pop();
break;
Expand Down
36 changes: 29 additions & 7 deletions runtime/src/main/java/com/dylibso/chicory/runtime/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,18 @@ public Instance instantiate(HostFunction[] hostFunctions) {
if (g.getInit().length > 2)
throw new RuntimeException("We don't support this global initializer");
var instr = g.getInit()[0];
// TODO we're assuming this is a const value, do we need to eval it?
if (instr.getOpcode() != OpCode.I32_CONST && instr.getOpcode() != OpCode.I64_CONST) {
throw new RuntimeException(
"We only support I32_CONST and I64_CONST on global initializers right now");
}
if (instr.getOpcode() == OpCode.I32_CONST) {
globals[i] = Value.i32(instr.getOperands()[0]);
} else {
} else if (instr.getOpcode() == OpCode.I64_CONST) {
globals[i] = Value.i64(instr.getOperands()[0]);
} else if (instr.getOpcode() == OpCode.F32_CONST) {
globals[i] = Value.f32(instr.getOperands()[0]);
} else if (instr.getOpcode() == OpCode.F64_CONST) {
globals[i] = Value.f64(instr.getOperands()[0]);
} else {
throw new RuntimeException(
"We only support i32,i64,f32,f64 const opcodes on global initializers right"
+ " now");
}
}

Expand Down Expand Up @@ -143,6 +146,24 @@ public Instance instantiate(HostFunction[] hostFunctions) {
exports.put("_start", export);
}

Table table = null;
if (module.getTableSection() != null) {
if (module.getTableSection().getTables().length > 1) {
throw new ChicoryException("We don't currently support more than 1 table");
}
table = module.getTableSection().getTables()[0];
if (module.getElementSection() != null) {
for (var el : module.getElementSection().getElements()) {
var idx = el.getTableIndex();
if (idx != 0)
throw new ChicoryException("We don't currently support more than 1 table");
for (var fi : el.getFuncIndices()) {
table.addFuncRef((int) fi);
}
}
}
}

return new Instance(
this,
globalInitializers,
Expand All @@ -151,7 +172,8 @@ public Instance instantiate(HostFunction[] hostFunctions) {
functions,
types,
functionTypes,
hostFuncs);
hostFuncs,
table);
}

private HostFunction[] mapHostFunctions(Import[] imports, HostFunction[] hostFunctions) {
Expand Down
47 changes: 24 additions & 23 deletions runtime/src/test/java/com/dylibso/chicory/runtime/ModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,27 @@ public void shouldWorkFactorial() {
var module = Module.build("src/test/resources/wasm/iterfact.wat.wasm");
var instance = module.instantiate();
var iterFact = instance.getExport("iterFact");
var result = iterFact.apply(Value.i32(5));
var result = iterFact.apply(Value.i32(5))[0];
assertEquals(120, result.asInt());
}

@Test
public void shouldSupportBrTable() {
var instance = Module.build("src/test/resources/wasm/br_table.wat.wasm").instantiate();
var switchLike = instance.getExport("switch_like");
var result = switchLike.apply(Value.i32(0));
var result = switchLike.apply(Value.i32(0))[0];
assertEquals(102, result.asInt());
result = switchLike.apply(Value.i32(1));
result = switchLike.apply(Value.i32(1))[0];
assertEquals(101, result.asInt());
result = switchLike.apply(Value.i32(2));
result = switchLike.apply(Value.i32(2))[0];
assertEquals(100, result.asInt());
result = switchLike.apply(Value.i32(-1));
result = switchLike.apply(Value.i32(-1))[0];
assertEquals(103, result.asInt());
result = switchLike.apply(Value.i32(3));
result = switchLike.apply(Value.i32(3))[0];
assertEquals(103, result.asInt());
result = switchLike.apply(Value.i32(4));
result = switchLike.apply(Value.i32(4))[0];
assertEquals(103, result.asInt());
result = switchLike.apply(Value.i32(100));
result = switchLike.apply(Value.i32(100))[0];
assertEquals(103, result.asInt());
}

Expand All @@ -70,14 +70,14 @@ public void shouldExerciseBranches() {
var module = Module.build("src/test/resources/wasm/branching.wat.wasm").instantiate();
var foo = module.getExport("foo");

var result = foo.apply(Value.i32(0));
var result = foo.apply(Value.i32(0))[0];
assertEquals(42, result.asInt());

result = foo.apply(Value.i32(1));
result = foo.apply(Value.i32(1))[0];
assertEquals(99, result.asInt());

for (var i = 2; i < 100; i++) {
result = foo.apply(Value.i32(i));
result = foo.apply(Value.i32(i))[0];
assertEquals(7, result.asInt());
}
}
Expand Down Expand Up @@ -113,7 +113,7 @@ public void shouldComputeFactorial() {

// don't make this too big we will overflow 32 bits
for (var i = 0; i < 10; i++) {
var result = iterFact.apply(Value.i32(i));
var result = iterFact.apply(Value.i32(i))[0];
// test against an oracle Java implementation
assertEquals(factorial(i), result.asInt());
}
Expand Down Expand Up @@ -159,7 +159,7 @@ public void shouldTrapOnUnreachable() {
public void shouldSupportGlobals() {
var instance = Module.build("src/test/resources/wasm/globals.wat.wasm").instantiate();
var doit = instance.getExport("doit");
var result = doit.apply(Value.i32(32));
var result = doit.apply(Value.i32(32))[0];
assertEquals(42, result.asInt());
}

Expand All @@ -172,19 +172,19 @@ public void shouldCountVowels() {
var memory = instance.getMemory();
var message = "Hello, World!";
var len = message.getBytes().length;
var ptr = alloc.apply(Value.i32(len)).asInt();
var ptr = alloc.apply(Value.i32(len))[0].asInt();
memory.put(ptr, message);
var result = countVowels.apply(Value.i32(ptr), Value.i32(len));
dealloc.apply(Value.i32(ptr), Value.i32(len));
assertEquals(3, result.asInt());
assertEquals(3, result[0].asInt());
}

@Test
public void shouldRunBasicCProgram() {
// check with: wasmtime src/test/resources/wasm/basic.c.wasm --invoke run
var instance = Module.build("src/test/resources/wasm/basic.c.wasm").instantiate();
var run = instance.getExport("run");
var result = run.apply();
var result = run.apply()[0];
assertEquals(42, result.asInt());
}

Expand All @@ -210,25 +210,26 @@ public void shouldRunBasicCProgram() {
public void shouldWorkWithMemoryOps() {
var instance = Module.build("src/test/resources/wasm/memory.wat.wasm").instantiate();
var run = instance.getExport("run32");
var result = run.apply(Value.i32(42));
var results = run.apply(Value.i32(42));
var result = results[0];
assertEquals(42, result.asInt());

result = run.apply(Value.i32(Integer.MAX_VALUE));
result = run.apply(Value.i32(Integer.MAX_VALUE))[0];
assertEquals(Integer.MAX_VALUE, result.asInt());

result = run.apply(Value.i32(Integer.MIN_VALUE));
result = run.apply(Value.i32(Integer.MIN_VALUE))[0];
assertEquals(Integer.MIN_VALUE, result.asInt());

run = instance.getExport("run64");
result = run.apply(Value.i64(42));
result = run.apply(Value.i64(42))[0];
assertEquals(42L, result.asLong());

run = instance.getExport("run64");
result = run.apply(Value.i64(Long.MIN_VALUE));
result = run.apply(Value.i64(Long.MIN_VALUE))[0];
assertEquals(Long.MIN_VALUE, result.asLong());

run = instance.getExport("run64");
result = run.apply(Value.i64(Long.MAX_VALUE));
result = run.apply(Value.i64(Long.MAX_VALUE))[0];
assertEquals(Long.MAX_VALUE, result.asLong());
}

Expand All @@ -237,7 +238,7 @@ public void shouldRunKitchenSink() {
// check with: wasmtime src/test/resources/wasm/kitchensink.wat.wasm --invoke run 100
var instance = Module.build("src/test/resources/wasm/kitchensink.wat.wasm").instantiate();
var run = instance.getExport("run");
assertEquals(6, run.apply(Value.i32(100)).asInt());
assertEquals(6, run.apply(Value.i32(100))[0].asInt());
}

// @Test
Expand Down
Loading

0 comments on commit c48e471

Please sign in to comment.