From 17bcdbfef0e40bedd8e8c16e3b6eb6d134635d49 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 26 Jun 2023 21:46:39 +0100 Subject: Add WASM code gen for call instructions with args --- ...mpiler.testing.snapshots.Snapshotter)].expected | 54 ++++++++++++++++++++++ ...mpiler.testing.snapshots.Snapshotter)].expected | 4 +- .../compiler/backends/llvm/LlvmCodeGen.java | 6 +-- .../backends/llvm/LlvmCodeGenClosures.java | 17 ++++--- .../compiler/backends/wasm/WasmCodeGen.java | 42 ++++++++++++++++- .../backends/wasm/WasmCodeGenClosures.java | 16 ++++++- .../backends/wasm/WasmCodeGenLocalSpace.java | 2 + .../java/org/shedlang/compiler/util/Iterables.java | 12 +++++ .../backends/wasm/WasmCodeGenCallTests.java | 11 +++++ 9 files changed, 146 insertions(+), 18 deletions(-) create mode 100644 snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected diff --git a/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected new file mode 100644 index 00000000..bc5ab15b --- /dev/null +++ b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected @@ -0,0 +1,54 @@ + +module.wasm: file format wasm 0x1 + +Section Details: + +Type[2]: + - type[0] (i32) -> i32 + - type[1] () -> nil +Import[2]: + - table[0] type=funcref initial=0 <- env.__indirect_function_table + - memory[0] pages: initial=0 <- env.__linear_memory +Function[1]: + - func[0] sig=1 +Code[1]: + - func[0] size=44 +Custom: + - name: "name" + - func[0] + - func[0] local[0] + - func[0] local[1] + - func[0] local[2] + - func[0] local[3] + - func[0] local[4] +Custom: + - name: "linking" + - symbol table [count=1] + - 0: F func=0 [ binding=global vis=default ] +Custom: + - name: "reloc.CODE" + - relocations for section: 3 (Code) [1] + - R_WASM_TYPE_INDEX_LEB offset=0x00002b(file=0x000090) type=0 + +Code Disassembly: + +00006b func[0] : + 00006c: 01 7f | local[1] type=i32 + 00006e: 01 7f | local[2] type=i32 + 000070: 01 7f | local[3] type=i32 + 000072: 01 7f | local[4] type=i32 + 000074: 01 7f | local[5] type=i32 + 000076: 21 00 | local.set 0 + 000078: 21 01 | local.set 1 + 00007a: 21 02 | local.set 2 + 00007c: 21 03 | local.set 3 + 00007e: 21 04 | local.set 4 + 000080: 20 04 | local.get 4 + 000082: 20 03 | local.get 3 + 000084: 20 02 | local.get 2 + 000086: 20 00 | local.get 0 + 000088: 20 01 | local.get 1 + 00008a: 20 04 | local.get 4 + 00008c: 28 02 00 | i32.load 2 0 + 00008f: 11 80 80 80 80 00 00 | call_indirect 0 0 + 000096: 0b | end \ No newline at end of file diff --git a/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithNoArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithNoArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected index c8012fb8..5c7bf893 100644 --- a/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithNoArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected +++ b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenCallTests]/[method:canCallFunctionWithNoArgs(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected @@ -32,7 +32,7 @@ Code Disassembly: 00006c: 01 7f | local[1] type=i32 00006e: 21 00 | local.set 0 000070: 20 00 | local.get 0 - 000072: 28 02 00 | i32.load 2 0 - 000075: 20 00 | local.get 0 + 000072: 20 00 | local.get 0 + 000074: 28 02 00 | i32.load 2 0 000077: 11 80 80 80 80 00 00 | call_indirect 0 0 00007e: 0b | end \ No newline at end of file diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGen.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGen.java index 7c4427b4..5eb418a9 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGen.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGen.java @@ -36,7 +36,7 @@ public class LlvmCodeGen { var field = fieldResult.value(); mainContext = fieldResult.context(); - mainContext = LlvmCodeGenClosures.call(field, List.of(), Map.of(), mainContext); + mainContext = LlvmCodeGenClosures.call(field, List.of(), List.of(), mainContext); mainContext = mainContext.addTerminatorInstruction( new LlvmReturn(LlvmTypes.INT_32, new LlvmOperandInt(0)) @@ -97,12 +97,12 @@ public class LlvmCodeGen { public static LlvmCodeGenContext generateInstruction(Instruction instruction, LlvmCodeGenContext context) { return switch (instruction) { case CallInstruction call -> { - var namedArgsByName = new HashMap(); + var namedArgsByName = new ArrayList>(); for (var argName : reversed(call.namedArgNames())) { var argResult = context.pop(); var arg = argResult.value(); context = argResult.context(); - namedArgsByName.put(argName, arg); + namedArgsByName.add(Map.entry(argName, arg)); } var positionalArgs = new ArrayList(); diff --git a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGenClosures.java b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGenClosures.java index 098acbdd..62212a34 100644 --- a/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGenClosures.java +++ b/src/main/java/org/shedlang/compiler/backends/llvm/LlvmCodeGenClosures.java @@ -11,12 +11,11 @@ import org.shedlang.compiler.backends.llvm.ir.ast.operands.LlvmVariableGlobal; import org.shedlang.compiler.backends.llvm.ir.ast.operands.LlvmVariableLocal; import org.shedlang.compiler.backends.llvm.ir.ast.types.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Function; +import static org.shedlang.compiler.util.Iterables.sortedBy; + public class LlvmCodeGenClosures { private LlvmCodeGenClosures() { } @@ -157,7 +156,7 @@ public class LlvmCodeGenClosures { public static LlvmCodeGenContext call( LlvmOperand closure, List positionalArgs, - Map namedArgsByName, + List> namedArgsByName, LlvmCodeGenContext context ) { var closurePointerResult = castValueToClosurePointer(closure, context); @@ -176,14 +175,14 @@ public class LlvmCodeGenClosures { var fullArgs = new ArrayList(); fullArgs.add(new LlvmTypedOperand(CALLABLE_TYPES.closurePointer(), closurePointer)); + for (var arg : positionalArgs) { fullArgs.add(new LlvmTypedOperand(LlvmCodeGenTypes.VALUE_TYPE, arg)); } - namedArgsByName.entrySet() - .stream().sorted(Map.Entry.comparingByKey()) - .map(x -> x.getValue()) - .forEachOrdered(x -> fullArgs.add(new LlvmTypedOperand(LlvmCodeGenTypes.VALUE_TYPE, x))); + for (var arg : sortedBy(namedArgsByName, x -> x.getKey())) { + fullArgs.add(new LlvmTypedOperand(LlvmCodeGenTypes.VALUE_TYPE, arg.getValue())); + } return context.addInstruction(new LlvmCall( callTarget, diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java index 3d466648..5926af9c 100644 --- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java +++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java @@ -1,11 +1,15 @@ package org.shedlang.compiler.backends.wasm; import org.shedlang.compiler.backends.wasm.structure.ast.instructions.*; +import org.shedlang.compiler.backends.wasm.structure.ast.modules.WasmLocalIndex; import org.shedlang.compiler.stackir.instructions.*; -import java.util.List; +import java.util.ArrayList; +import java.util.Collections; import java.util.Map; +import static org.shedlang.compiler.util.Lists.reversed; + public class WasmCodeGen { private WasmCodeGen() { } @@ -13,13 +17,47 @@ public class WasmCodeGen { public static WasmCodeGenContext generateInstruction(Instruction instruction, WasmCodeGenContext context) { return switch (instruction) { case CallInstruction call -> { + var namedArgsByName = new ArrayList>(); + for (var argName : reversed(call.namedArgNames())) { + var argResult = context.generateLocal( + WasmCodeGenLocalSpace.CALL_ARG_NAMED, + argName + ); + var arg = argResult.value(); + context = argResult.context(); + + context = context.addInstruction(new WasmLocalSet(arg)); + + namedArgsByName.add(Map.entry(argName, arg)); + } + + var positionalArgs = new ArrayList(); + for (var i = 0; i < call.positionalArgCount(); i++) { + var argResult = context.generateLocal( + WasmCodeGenLocalSpace.CALL_ARG_POSITIONAL, + Integer.toString(call.positionalArgCount() - i - 1) + ); + var arg = argResult.value(); + context = argResult.context(); + + context = context.addInstruction(new WasmLocalSet(arg)); + + positionalArgs.add(arg); + } + Collections.reverse(positionalArgs); + var receiverResult = context.generateLocal(WasmCodeGenLocalSpace.RECEIVER); var receiver = receiverResult.value(); context = receiverResult.context(); context = context.addInstruction(new WasmLocalSet(receiver)); - yield WasmCodeGenClosures.call(receiver, List.of(), Map.of(), context); + yield WasmCodeGenClosures.call( + receiver, + positionalArgs, + namedArgsByName, + context + ); } case DefineFunctionInstruction defineFunctionInstruction -> { throw new UnsupportedOperationException("TODO"); diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenClosures.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenClosures.java index 86ae472d..a2d34df7 100644 --- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenClosures.java +++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenClosures.java @@ -12,6 +12,8 @@ import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmValueType; import java.util.List; import java.util.Map; +import static org.shedlang.compiler.util.Iterables.sortedBy; + public class WasmCodeGenClosures { private WasmCodeGenClosures() { } @@ -21,14 +23,24 @@ public class WasmCodeGenClosures { public static WasmCodeGenContext call( WasmLocalIndex closure, List positionalArgs, - Map namedArgsByName, + List> namedArgsByName, WasmCodeGenContext context ) { + context = context.addInstruction(new WasmLocalGet(closure)); + + for (var positionalArg : positionalArgs) { + context = context.addInstruction(new WasmLocalGet(positionalArg)); + } + + for (var namedArg : sortedBy(namedArgsByName, x -> x.getKey())) { + context = context.addInstruction(new WasmLocalGet(namedArg.getValue())); + } + context = loadClosureFunctionTableIndex(closure, context); var funcType = new WasmFuncType(List.of(CLOSURE_TYPE), List.of(WasmCodeGenTypes.VALUE_TYPE)); + return context - .addInstruction(new WasmLocalGet(closure)) .addType(funcType) .addInstruction(new WasmCallIndirect(context.indirectFunctionTableIndex(), funcType)); } diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenLocalSpace.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenLocalSpace.java index 80e89155..eeda312a 100644 --- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenLocalSpace.java +++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenLocalSpace.java @@ -1,6 +1,8 @@ package org.shedlang.compiler.backends.wasm; public enum WasmCodeGenLocalSpace { + CALL_ARG_POSITIONAL("callArgPositional"), + CALL_ARG_NAMED("callArgNamed"), LOCAL("local"), RECEIVER("receiver"); diff --git a/src/main/java/org/shedlang/compiler/util/Iterables.java b/src/main/java/org/shedlang/compiler/util/Iterables.java index ff043c13..38730594 100644 --- a/src/main/java/org/shedlang/compiler/util/Iterables.java +++ b/src/main/java/org/shedlang/compiler/util/Iterables.java @@ -1,5 +1,10 @@ package org.shedlang.compiler.util; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; + public class Iterables { private Iterables() { } @@ -26,4 +31,11 @@ public class Iterables { first = false; } } + + public static > List sortedBy( + Collection collection, + Function key + ) { + return collection.stream().sorted(Comparator.comparing(key)).toList(); + } } diff --git a/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenCallTests.java b/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenCallTests.java index 35255ed8..5c89e0df 100644 --- a/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenCallTests.java +++ b/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenCallTests.java @@ -23,4 +23,15 @@ public class WasmCodeGenCallTests { var module = result.toFunction().toModule(); snapshotter.assertSnapshot(objdump(module)); } + + @Test + public void canCallFunctionWithArgs(Snapshotter snapshotter) throws IOException, InterruptedException { + var instruction = Instructions.call(2, List.of("b", "a")); + var context = WasmCodeGenContext.EMPTY; + + var result = WasmCodeGen.generateInstruction(instruction, context); + + var module = result.toFunction().toModule(); + snapshotter.assertSnapshot(objdump(module)); + } } -- cgit v1.2.3