aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-07-14 23:04:25 +0100
committerMichael Williamson <mike@zwobble.org>2023-07-14 23:05:18 +0100
commit8b4e7af45f229fcce74cd380cc64399bc1d03384 (patch)
treee03d6cf5cf9f3b3ba65894dbd3042530a8770056
parenteeef2f1636e2fc96c0018532d1e6945ee97c43c6 (diff)
Support if instructions in WASM backend
-rw-r--r--snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenIfTests]/[method:ifIsCompiledToWasmIf(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected33
-rw-r--r--src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java35
-rw-r--r--src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenContext.java65
-rw-r--r--src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenIfTests.java34
4 files changed, 151 insertions, 16 deletions
diff --git a/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenIfTests]/[method:ifIsCompiledToWasmIf(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenIfTests]/[method:ifIsCompiledToWasmIf(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected
new file mode 100644
index 00000000..35f5f3d8
--- /dev/null
+++ b/snapshots/[engine:junit-jupiter]/[class:org.shedlang.compiler.backends.wasm.WasmCodeGenIfTests]/[method:ifIsCompiledToWasmIf(org.shedlang.compiler.testing.snapshots.Snapshotter)].expected
@@ -0,0 +1,33 @@
+
+module.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> 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=0 <f>
+Code[1]:
+ - func[0] size=16 <f>
+Custom:
+ - name: "linking"
+ - symbol table [count=1]
+ - 0: F <f> func=0 [ binding=global vis=default ]
+Custom:
+ - name: "name"
+ - func[0] <f>
+
+Code Disassembly:
+
+000066 func[0] <f>:
+ 000067: 04 7f | if i32
+ 000069: 41 01 | i32.const 1
+ 00006b: 41 02 | i32.const 2
+ 00006d: 05 | else
+ 00006e: 41 e5 00 | i32.const 101
+ 000071: 41 e6 00 | i32.const 102
+ 000074: 0b | end
+ 000075: 0b | end \ No newline at end of file
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 cd5f3b24..ac8c8d62 100644
--- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java
+++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGen.java
@@ -155,9 +155,7 @@ public class WasmCodeGen {
defineFunction.namedParams(),
defineFunction.freeVariables(),
functionContext -> {
- for (var bodyInstruction : defineFunction.body()) {
- functionContext = generateInstruction(bodyInstruction, functionContext);
- }
+ functionContext = generateInstructions(defineFunction.body(), functionContext);
return functionContext;
},
context
@@ -181,8 +179,25 @@ public class WasmCodeGen {
);
}
- case IfInstruction if_ ->
- throw new UnsupportedOperationException("TODO");
+ case IfInstruction if_ -> {
+ context = context.enterBlock();
+ context = generateInstructions(if_.trueBody(), context);
+ var trueBodyResult = context.exitBlock();
+ var trueBody = trueBodyResult.value();
+ context = trueBodyResult.context();
+
+ context = context.enterBlock();
+ context = generateInstructions(if_.falseBody(), context);
+ var falseBodyResult = context.exitBlock();
+ var falseBody = falseBodyResult.value();
+ context = falseBodyResult.context();
+
+ yield context.addInstruction(new WasmIf(
+ new WasmBlockType(List.of(WasmCodeGenTypes.VALUE_TYPE)),
+ trueBody,
+ falseBody
+ ));
+ }
case IntAddInstruction intAdd ->
context.addInstruction(new WasmI32Add());
@@ -251,4 +266,14 @@ public class WasmCodeGen {
WasmCodeGenStrings.add(context);
};
}
+
+ private static WasmCodeGenContext generateInstructions(
+ List<Instruction> instructions,
+ WasmCodeGenContext context
+ ) {
+ for (var instruction : instructions) {
+ context = generateInstruction(instruction, context);
+ }
+ return context;
+ }
}
diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenContext.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenContext.java
index 8e147ad6..1cb0e2e2 100644
--- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenContext.java
+++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmCodeGenContext.java
@@ -11,6 +11,7 @@ import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmLimits;
import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmReferenceType;
import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmTableType;
import org.shedlang.compiler.typechecker.TypeInfo;
+import org.shedlang.compiler.util.P;
import java.util.List;
import java.util.Optional;
@@ -168,25 +169,54 @@ public record WasmCodeGenContext(
record FunctionContext(
WasmFuncType funcType,
PVector<WasmInstruction> instructions,
+ PStack<PVector<WasmInstruction>> blockStack,
OrderedPMap<String, WasmLocal> locals,
PMap<String, WasmLocalIndex> shedLocalNameToLocalIndex
) {
- public static final FunctionContext EMPTY = new FunctionContext(
- new WasmFuncType(List.of(), List.of()),
- Empty.vector(),
- OrderedPMap.empty(),
- Empty.map()
+ public static final FunctionContext EMPTY = forFuncType(
+ new WasmFuncType(List.of(), List.of())
);
+ public static FunctionContext forFuncType(WasmFuncType funcType) {
+ return new FunctionContext(
+ funcType,
+ Empty.vector(),
+ Empty.stack(),
+ OrderedPMap.empty(),
+ Empty.map()
+ );
+ }
+
public FunctionContext addInstruction(WasmInstruction instruction) {
return new FunctionContext(
this.funcType,
this.instructions.plus(instruction),
+ this.blockStack,
this.locals,
this.shedLocalNameToLocalIndex
);
}
+ public FunctionContext enterBlock() {
+ return new FunctionContext(
+ this.funcType,
+ P.vector(),
+ this.blockStack.plus(this.instructions),
+ this.locals,
+ this.shedLocalNameToLocalIndex
+ );
+ }
+
+ public Result<PVector<WasmInstruction>> exitBlock() {
+ return new FunctionContext(
+ this.funcType,
+ this.blockStack.get(0),
+ this.blockStack.minus(0),
+ this.locals,
+ this.shedLocalNameToLocalIndex
+ ).toResult(this.instructions);
+ }
+
public Result<WasmLocalIndex> generateLocal(WasmCodeGenLocalSpace space, Optional<String> name) {
var localIndex = new WasmLocalIndex(locals.size());
var localName = NameAllocator.generateName(
@@ -197,6 +227,7 @@ public record WasmCodeGenContext(
return new FunctionContext(
this.funcType,
this.instructions,
+ this.blockStack,
this.locals.plus(
localName,
new WasmLocal(localName, WasmCodeGenTypes.VALUE_TYPE)
@@ -213,6 +244,7 @@ public record WasmCodeGenContext(
return new FunctionContext(
context.funcType,
context.instructions,
+ context.blockStack,
context.locals,
context.shedLocalNameToLocalIndex.plus(name, localIndex)
).toResult(localIndex);
@@ -322,6 +354,22 @@ public record WasmCodeGenContext(
);
}
+ public WasmCodeGenContext enterBlock() {
+ return new WasmCodeGenContext(
+ this.moduleContext,
+ this.functionContext.enterBlock()
+ );
+ }
+
+ public WasmCodeGenResult<List<WasmInstruction>> exitBlock() {
+ var exitResult = this.functionContext.exitBlock();
+
+ return new WasmCodeGenContext(
+ this.moduleContext,
+ exitResult.context
+ ).toResult(exitResult.value);
+ }
+
public WasmFunction toFunction(String name) {
return this.functionContext.toFunction(name);
}
@@ -359,12 +407,7 @@ public record WasmCodeGenContext(
) {
var bodyContext = new WasmCodeGenContext(
this.moduleContext,
- new FunctionContext(
- funcType,
- Empty.vector(),
- OrderedPMap.empty(),
- Empty.map()
- )
+ FunctionContext.forFuncType(funcType)
);
bodyContext = create.apply(bodyContext);
diff --git a/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenIfTests.java b/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenIfTests.java
new file mode 100644
index 00000000..82c034a5
--- /dev/null
+++ b/src/test/java/org/shedlang/compiler/backends/wasm/WasmCodeGenIfTests.java
@@ -0,0 +1,34 @@
+package org.shedlang.compiler.backends.wasm;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.shedlang.compiler.stackir.instructions.Instructions;
+import org.shedlang.compiler.testing.snapshots.SnapshotResolver;
+import org.shedlang.compiler.testing.snapshots.Snapshotter;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.shedlang.compiler.backends.wasm.WasmCodeGenTesting.objdumpContext;
+
+@ExtendWith({SnapshotResolver.class})
+public class WasmCodeGenIfTests {
+ @Test
+ public void ifIsCompiledToWasmIf(Snapshotter snapshotter) throws IOException, InterruptedException {
+ var instruction = Instructions.if_(
+ List.of(
+ Instructions.pushInt(1),
+ Instructions.pushInt(2)
+ ),
+ List.of(
+ Instructions.pushInt(101),
+ Instructions.pushInt(102)
+ )
+ );
+ var context = WasmCodeGenContext.EMPTY;
+
+ var result = WasmCodeGen.generateInstruction(instruction, context);
+
+ snapshotter.assertSnapshot(objdumpContext(result));
+ }
+}