Skip to content

Commit

Permalink
Primitive, non boxing, long array based MStack (#531)
Browse files Browse the repository at this point in the history
Supersede #447 this is a complete implementation, already passing all
the tests up to the interpreter, can be tested with something like:

```
 mvn clean install -pl runtime-tests -am
```

I started, but got a bit confused about the changes necessary in "aot",
@electrum or @danielperano I would love some help in finalizing this PR
:pray: and learning a bit more how `asm` works ...

This is a prerequisite to avoid arrays construction
[here](https://github.com/dylibso/chicory/pull/528/files#diff-f9f476fefd928e6770830c090018da2b95a054834c70bf472460d568fd1a9d2bR184),
the plan is to open up the underlying array so that we can perform
operations just using the offset 🤞
  • Loading branch information
andreaTP authored Oct 2, 2024
1 parent ffa9170 commit 3a46c2b
Show file tree
Hide file tree
Showing 51 changed files with 1,139 additions and 1,176 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ when we want to go back to Java. This export function takes an `i32` argument. W
on the return value to get back the Java integer:

```java
Value result = iterFact.apply(Value.i32(5))[0];
System.out.println("Result: " + result.asInt()); // should print 120 (5!)
long result = iterFact.apply(5)[0];
System.out.println("Result: " + result); // should print 120 (5!)
```

<!--
```java
writeResultFile("factorial.result", "" + result.asInt());
writeResultFile("factorial.result", "" + result);
```
-->

Expand Down Expand Up @@ -192,7 +192,7 @@ Memory memory = instance.memory();
String message = "Hello, World!";
int len = message.getBytes().length;
// allocate {len} bytes of memory, this returns a pointer to that memory
int ptr = alloc.apply(Value.i32(len))[0].asInt();
int ptr = (int) alloc.apply(len)[0];
// We can now write the message to the module's memory:
memory.writeString(ptr, message);
```
Expand All @@ -201,14 +201,14 @@ Now we can call `countVowels` with this pointer to the string. It will do it's j
call `dealloc` to free that memory in the module. Though the module could do this itself if you want:

```java
Value result = countVowels.apply(Value.i32(ptr), Value.i32(len))[0];
dealloc.apply(Value.i32(ptr), Value.i32(len));
assert(3 == result.asInt()); // 3 vowels in Hello, World!
var result = countVowels.apply(ptr, len)[0];
dealloc.apply(ptr, len);
assert(3L == result); // 3 vowels in Hello, World!
```

<!--
```java
writeResultFile("countVowels.result", "" + result.asInt());
writeResultFile("countVowels.result", "" + result);
```
-->

Expand Down Expand Up @@ -253,9 +253,9 @@ import com.dylibso.chicory.wasm.types.ValueType;
var func = new HostFunction(
"console",
"log",
(Instance instance, Value... args) -> { // decompiled is: console_log(13, 0);
var len = args[0].asInt();
var offset = args[1].asInt();
(Instance instance, long... args) -> { // decompiled is: console_log(13, 0);
var len = (int) args[0];
var offset = (int) args[1];
var message = instance.memory().readString(offset, len);
println(message);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// https://github.com/WebAssembly/spec/blob/ee82c8e50c5106e0cedada0a083d4cc4129034a2/interpreter/host/spectest.ml
public final class Spectest {
private static final WasmFunctionHandle noop = (Instance instance, Value... args) -> null;
private static final WasmFunctionHandle noop = (Instance instance, long... args) -> null;

private Spectest() {}

Expand Down
1 change: 0 additions & 1 deletion aot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.approvaltests</groupId>
Expand Down
24 changes: 11 additions & 13 deletions aot/src/main/java/com/dylibso/chicory/aot/AotEmitters.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static com.dylibso.chicory.aot.AotMethods.CHECK_INTERRUPTION;
import static com.dylibso.chicory.aot.AotMethods.INSTANCE_READ_GLOBAL;
import static com.dylibso.chicory.aot.AotMethods.INSTANCE_SET_ELEMENT;
import static com.dylibso.chicory.aot.AotMethods.INSTANCE_WRITE_GLOBAL;
import static com.dylibso.chicory.aot.AotMethods.MEMORY_COPY;
import static com.dylibso.chicory.aot.AotMethods.MEMORY_DROP;
import static com.dylibso.chicory.aot.AotMethods.MEMORY_FILL;
Expand Down Expand Up @@ -31,12 +30,14 @@
import static com.dylibso.chicory.aot.AotMethods.TABLE_SET;
import static com.dylibso.chicory.aot.AotMethods.TABLE_SIZE;
import static com.dylibso.chicory.aot.AotMethods.THROW_OUT_OF_BOUNDS_MEMORY_ACCESS;
import static com.dylibso.chicory.aot.AotMethods.WRITE_GLOBAL;
import static com.dylibso.chicory.aot.AotUtil.StackSize;
import static com.dylibso.chicory.aot.AotUtil.boxer;
import static com.dylibso.chicory.aot.AotUtil.callIndirectMethodName;
import static com.dylibso.chicory.aot.AotUtil.callIndirectMethodType;
import static com.dylibso.chicory.aot.AotUtil.emitInvokeStatic;
import static com.dylibso.chicory.aot.AotUtil.emitInvokeVirtual;
import static com.dylibso.chicory.aot.AotUtil.emitJvmToLong;
import static com.dylibso.chicory.aot.AotUtil.emitLongToJvm;
import static com.dylibso.chicory.aot.AotUtil.emitPop;
import static com.dylibso.chicory.aot.AotUtil.jvmType;
import static com.dylibso.chicory.aot.AotUtil.loadTypeOpcode;
Expand All @@ -45,7 +46,6 @@
import static com.dylibso.chicory.aot.AotUtil.methodTypeFor;
import static com.dylibso.chicory.aot.AotUtil.stackSize;
import static com.dylibso.chicory.aot.AotUtil.storeTypeOpcode;
import static com.dylibso.chicory.aot.AotUtil.unboxer;
import static com.dylibso.chicory.aot.AotUtil.validateArgumentType;
import static com.dylibso.chicory.wasm.types.Value.REF_NULL_VALUE;

Expand Down Expand Up @@ -218,21 +218,19 @@ public static void GLOBAL_GET(AotContext ctx, AnnotatedInstruction ins, MethodVi
asm.visitLdcInsn(globalIndex);
emitInvokeVirtual(asm, INSTANCE_READ_GLOBAL);

Method unboxer = unboxer(ctx.globalTypes().get(globalIndex));
emitInvokeVirtual(asm, unboxer);
var globalType = ctx.globalTypes().get(globalIndex);
emitLongToJvm(asm, ctx.globalTypes().get(globalIndex));

ctx.pushStackSize(stackSize(unboxer.getReturnType()));
ctx.pushStackSize(stackSize(globalType));
}

public static void GLOBAL_SET(AotContext ctx, AnnotatedInstruction ins, MethodVisitor asm) {
int globalIndex = (int) ins.operand(0);

emitInvokeStatic(asm, boxer(ctx.globalTypes().get(globalIndex)));
asm.visitVarInsn(Opcodes.ALOAD, ctx.instanceSlot());
asm.visitInsn(Opcodes.SWAP);
emitJvmToLong(asm, ctx.globalTypes().get(globalIndex));
asm.visitLdcInsn(globalIndex);
asm.visitInsn(Opcodes.SWAP);
emitInvokeVirtual(asm, INSTANCE_WRITE_GLOBAL);
asm.visitVarInsn(Opcodes.ALOAD, ctx.instanceSlot());
emitInvokeStatic(asm, WRITE_GLOBAL);
}

public static void TABLE_GET(AotContext ctx, AnnotatedInstruction ins, MethodVisitor asm) {
Expand Down Expand Up @@ -772,8 +770,8 @@ private static void emitUnboxResult(MethodVisitor asm, AotContext ctx, List<Valu
ValueType type = types.get(i);
asm.visitVarInsn(Opcodes.ALOAD, ctx.tempSlot());
asm.visitLdcInsn(i);
asm.visitInsn(Opcodes.AALOAD);
emitInvokeVirtual(asm, unboxer(type));
asm.visitInsn(Opcodes.LALOAD);
emitLongToJvm(asm, type);
}
}
}
43 changes: 21 additions & 22 deletions aot/src/main/java/com/dylibso/chicory/aot/AotMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
import static com.dylibso.chicory.aot.AotMethods.CHECK_INTERRUPTION;
import static com.dylibso.chicory.aot.AotMethods.INSTANCE_CALL_HOST_FUNCTION;
import static com.dylibso.chicory.aot.AotMethods.THROW_TRAP_EXCEPTION;
import static com.dylibso.chicory.aot.AotUtil.boxer;
import static com.dylibso.chicory.aot.AotUtil.boxerHandle;
import static com.dylibso.chicory.aot.AotUtil.callIndirectMethodName;
import static com.dylibso.chicory.aot.AotUtil.callIndirectMethodType;
import static com.dylibso.chicory.aot.AotUtil.defaultValue;
import static com.dylibso.chicory.aot.AotUtil.emitInvokeStatic;
import static com.dylibso.chicory.aot.AotUtil.emitInvokeVirtual;
import static com.dylibso.chicory.aot.AotUtil.emitJvmToLong;
import static com.dylibso.chicory.aot.AotUtil.emitLongToJvm;
import static com.dylibso.chicory.aot.AotUtil.emitPop;
import static com.dylibso.chicory.aot.AotUtil.jvmReturnType;
import static com.dylibso.chicory.aot.AotUtil.jvmToLongHandle;
import static com.dylibso.chicory.aot.AotUtil.jvmTypes;
import static com.dylibso.chicory.aot.AotUtil.loadTypeOpcode;
import static com.dylibso.chicory.aot.AotUtil.localType;
import static com.dylibso.chicory.aot.AotUtil.longToJvmHandle;
import static com.dylibso.chicory.aot.AotUtil.methodNameFor;
import static com.dylibso.chicory.aot.AotUtil.methodTypeFor;
import static com.dylibso.chicory.aot.AotUtil.slotCount;
import static com.dylibso.chicory.aot.AotUtil.storeTypeOpcode;
import static com.dylibso.chicory.aot.AotUtil.unboxer;
import static com.dylibso.chicory.aot.AotUtil.unboxerHandle;
import static com.dylibso.chicory.wasm.types.Instruction.EMPTY_OPERANDS;
import static java.lang.invoke.MethodHandles.filterArguments;
import static java.lang.invoke.MethodHandles.filterReturnValue;
Expand Down Expand Up @@ -52,7 +52,6 @@
import com.dylibso.chicory.wasm.types.GlobalImport;
import com.dylibso.chicory.wasm.types.Instruction;
import com.dylibso.chicory.wasm.types.OpCode;
import com.dylibso.chicory.wasm.types.Value;
import com.dylibso.chicory.wasm.types.ValueType;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -354,9 +353,9 @@ private static List<FunctionType> getFunctionTypes(Module module) {
}

@Override
public Value[] call(int funcId, Value[] args) throws ChicoryException {
public long[] call(int funcId, long[] args) throws ChicoryException {
try {
return (Value[]) compiledFunctions[funcId].invokeExact(args);
return (long[]) compiledFunctions[funcId].invokeExact(args);
} catch (ChicoryException e) {
// propagate ChicoryExceptions
throw e;
Expand Down Expand Up @@ -528,19 +527,19 @@ private static MethodHandle adaptSignature(FunctionType type, MethodHandle handl
var argTypes = type.params();
var argHandlers = new MethodHandle[type.params().size()];
for (int i = 0; i < argHandlers.length; i++) {
argHandlers[i] = unboxerHandle(argTypes.get(i));
argHandlers[i] = longToJvmHandle(argTypes.get(i));
}
MethodHandle result = filterArguments(handle, 0, argHandlers);
result = result.asSpreader(Value[].class, argTypes.size());
result = result.asSpreader(long[].class, argTypes.size());

if (type.returns().isEmpty()) {
return result.asType(result.type().changeReturnType(Value[].class));
return result.asType(result.type().changeReturnType(long[].class));
}
if (type.returns().size() > 1) {
return result;
}
result = filterReturnValue(result, boxerHandle(type.returns().get(0)));
return filterReturnValue(result, ValueWrapper.HANDLE);
result = filterReturnValue(result, jvmToLongHandle(type.returns().get(0)));
return filterReturnValue(result, LongArrayWrapper.HANDLE);
}

private static void emitConstructor(ClassVisitor writer) {
Expand Down Expand Up @@ -592,16 +591,16 @@ private static void compileHostFunction(int funcId, FunctionType type, MethodVis

private static void emitBoxArguments(MethodVisitor asm, List<ValueType> types) {
int slot = 0;
// box the arguments into Value[]
// box the arguments into long[]
asm.visitLdcInsn(types.size());
asm.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(Value.class));
asm.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_LONG); // long
for (int i = 0; i < types.size(); i++) {
asm.visitInsn(Opcodes.DUP);
asm.visitLdcInsn(i);
ValueType valueType = types.get(i);
asm.visitVarInsn(loadTypeOpcode(valueType), slot);
emitInvokeStatic(asm, boxer(valueType));
asm.visitInsn(Opcodes.AASTORE);
emitJvmToLong(asm, valueType);
asm.visitInsn(Opcodes.LASTORE);
slot += slotCount(valueType);
}
}
Expand All @@ -610,13 +609,13 @@ private static void emitUnboxResult(FunctionType type, MethodVisitor asm) {
Class<?> returnType = jvmReturnType(type);
if (returnType == void.class) {
asm.visitInsn(Opcodes.RETURN);
} else if (returnType == Value[].class) {
} else if (returnType == long[].class) {
asm.visitInsn(Opcodes.ARETURN);
} else {
// unbox the result from Value[0]
// unbox the result from long[0]
asm.visitLdcInsn(0);
asm.visitInsn(Opcodes.AALOAD);
emitInvokeVirtual(asm, unboxer(type.returns().get(0)));
asm.visitInsn(Opcodes.LALOAD);
emitLongToJvm(asm, type.returns().get(0));
asm.visitInsn(returnTypeOpcode(type));
}
}
Expand Down Expand Up @@ -879,7 +878,7 @@ private FunctionType blockType(Instruction ins) {
}

private static MethodType valueMethodType(List<ValueType> types) {
return methodType(Value[].class, jvmTypes(types));
return methodType(long[].class, jvmTypes(types));
}

private static String valueMethodName(List<ValueType> types) {
Expand All @@ -891,7 +890,7 @@ private static String valueMethodName(List<ValueType> types) {

private static int returnTypeOpcode(FunctionType type) {
Class<?> returnType = jvmReturnType(type);
if (returnType == Value[].class) {
if (returnType == long[].class) {
return Opcodes.ARETURN;
}
if (returnType == int.class) {
Expand Down
24 changes: 15 additions & 9 deletions aot/src/main/java/com/dylibso/chicory/aot/AotMethods.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.dylibso.chicory.wasm.exceptions.ChicoryException;
import com.dylibso.chicory.wasm.types.Element;
import com.dylibso.chicory.wasm.types.FunctionType;
import com.dylibso.chicory.wasm.types.Value;
import java.lang.reflect.Method;
import java.util.List;

Expand All @@ -22,7 +21,7 @@ public final class AotMethods {
static final Method CALL_INDIRECT;
static final Method INSTANCE_CALL_HOST_FUNCTION;
static final Method INSTANCE_READ_GLOBAL;
static final Method INSTANCE_WRITE_GLOBAL;
static final Method WRITE_GLOBAL;
static final Method INSTANCE_SET_ELEMENT;
static final Method MEMORY_COPY;
static final Method MEMORY_FILL;
Expand Down Expand Up @@ -60,15 +59,17 @@ public final class AotMethods {
CALL_INDIRECT =
AotMethods.class.getMethod(
"callIndirect",
Value[].class,
long[].class,
int.class,
int.class,
int.class,
Instance.class);
INSTANCE_CALL_HOST_FUNCTION =
Instance.class.getMethod("callHostFunction", int.class, Value[].class);
Instance.class.getMethod("callHostFunction", int.class, long[].class);
INSTANCE_READ_GLOBAL = Instance.class.getMethod("readGlobal", int.class);
INSTANCE_WRITE_GLOBAL = Instance.class.getMethod("writeGlobal", int.class, Value.class);
WRITE_GLOBAL =
AotMethods.class.getMethod(
"writeGlobal", long.class, int.class, Instance.class);
INSTANCE_SET_ELEMENT = Instance.class.getMethod("setElement", int.class, Element.class);
MEMORY_COPY =
AotMethods.class.getMethod(
Expand Down Expand Up @@ -165,13 +166,13 @@ public final class AotMethods {
private AotMethods() {}

@UsedByGeneratedCode
public static Value[] callIndirect(
Value[] args, int typeId, int funcTableIdx, int tableIdx, Instance instance) {
public static long[] callIndirect(
long[] args, int typeId, int funcTableIdx, int tableIdx, Instance instance) {
TableInstance table = instance.table(tableIdx);

instance = requireNonNullElse(table.instance(funcTableIdx), instance);

int funcId = table.ref(funcTableIdx).asFuncRef();
int funcId = table.ref(funcTableIdx);
if (funcId == REF_NULL_VALUE) {
throw new ChicoryException("uninitialized element " + funcTableIdx);
}
Expand All @@ -193,7 +194,7 @@ public static boolean isRefNull(int ref) {

@UsedByGeneratedCode
public static int tableGet(int index, int tableIndex, Instance instance) {
return OpcodeImpl.TABLE_GET(instance, tableIndex, index).asFuncRef();
return (int) OpcodeImpl.TABLE_GET(instance, tableIndex, index);
}

@UsedByGeneratedCode
Expand Down Expand Up @@ -341,4 +342,9 @@ public static void checkInterruption() {
throw new ChicoryException("Thread interrupted");
}
}

@UsedByGeneratedCode
public static void writeGlobal(long value, int index, Instance instance) {
instance.writeGlobal(index, value);
}
}
Loading

0 comments on commit 3a46c2b

Please sign in to comment.