Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call spec #45

Merged
merged 7 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
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
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIce!

}
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is a little bit naive on my part but I think it works for the time being. We can update it when we get to the other specs that address tables and elements.

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];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal for now, but I wonder if it makes sense to have another method similar to get export, or maybe something on ExportFunction that can make this a little cleaner if you know it only has 1 return.

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