From 62f55dc669ce263656228ad7bf96a800ddb9abe3 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Wed, 20 Dec 2023 16:46:28 +0000 Subject: Rename WasmPatioCodeGen to PatioToWasm --- .../compiler/backends/wasm/PatioToWasm.java | 960 +++++++++++++++++++++ .../compiler/backends/wasm/WasmBackend.java | 4 +- .../compiler/backends/wasm/WasmPatioCodeGen.java | 960 --------------------- 3 files changed, 962 insertions(+), 962 deletions(-) create mode 100644 src/main/java/org/shedlang/compiler/backends/wasm/PatioToWasm.java delete mode 100644 src/main/java/org/shedlang/compiler/backends/wasm/WasmPatioCodeGen.java (limited to 'src/main/java') diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/PatioToWasm.java b/src/main/java/org/shedlang/compiler/backends/wasm/PatioToWasm.java new file mode 100644 index 00000000..44abd376 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/backends/wasm/PatioToWasm.java @@ -0,0 +1,960 @@ +package org.shedlang.compiler.backends.wasm; + +import org.pcollections.PMap; +import org.pcollections.PStack; +import org.pcollections.PVector; +import org.shedlang.compiler.backends.wasm.structure.ast.instructions.*; +import org.shedlang.compiler.backends.wasm.structure.ast.linking.WasmMemoryAddress; +import org.shedlang.compiler.backends.wasm.structure.ast.modules.*; +import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmFuncType; +import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmValueType; +import org.shedlang.compiler.patio.ast.*; +import org.shedlang.compiler.patio.ast.instructions.*; +import org.shedlang.compiler.patio.ast.operands.*; +import org.shedlang.compiler.patio.ast.types.*; +import org.shedlang.compiler.util.Lists; +import org.shedlang.compiler.util.P; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +public class PatioToWasm { + public static final String ALLOC_NAME = "shed__runtime__gc_alloc"; + public static final String STACK_ALLOC_NAME = "malloc"; + + public static WasmModule generateCompilationUnit( + PatioCompilationUnit patioCompilationUnit + ) { + var module = WasmModule.EMPTY.addImport(new WasmImport( + "env", + ALLOC_NAME, + new WasmImportDescriptor.Func( + ALLOC_NAME, + new WasmFuncType( + List.of(WasmValueType.I32, WasmValueType.I32, WasmValueType.I32), + List.of(WasmValueType.I32) + ) + ) + )); + + module = module.addImport(new WasmImport( + "env", + STACK_ALLOC_NAME, + new WasmImportDescriptor.Func( + STACK_ALLOC_NAME, + new WasmFuncType( + List.of(WasmValueType.I32), + List.of(WasmValueType.I32) + ) + ) + )); + + var wasmMemoryBuilder = WasmMemoryBuilder.EMPTY; + + for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { + switch (patioCompilationUnitElement) { + case PatioDataElementImmutable patioDataElementImmutable -> { + wasmMemoryBuilder = wasmMemoryBuilder.add( + patioDataElementImmutable.name(), + patioDataValueToBytes(patioDataElementImmutable.value()), + WasmAlignment.ALIGN_32_BIT + ).memory(); + } + case PatioDataElementMutable patioDataElementMutable -> { + wasmMemoryBuilder = wasmMemoryBuilder.add( + patioDataElementMutable.name(), + patioDataValueToBytes(patioDataElementMutable.initialValue()), + WasmAlignment.ALIGN_32_BIT + ).memory(); + } + case PatioFunctionDeclaration patioFunctionDeclaration -> { + var name = patioFunctionDeclaration.name(); + + var wasmFuncType = new WasmFuncType( + patioFunctionDeclaration.params().stream() + .map(param -> patioTypeToWasmValueType(param.type())) + .toList(), + patioTypeToWasmResultTypes(patioFunctionDeclaration.returnType()) + ); + + module = module.addImport(new WasmImport( + "env", + name, + new WasmImportDescriptor.Func(name, wasmFuncType) + )); + } + case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> { + module = module.addTag(new WasmTag( + WasmTagType.EXCEPTION, + patioExceptionTypeDefinition.name(), + List.of(WasmValueType.I32) + )); + } + case PatioFunctionDefinition patioFunctionDefinition -> { + var name = patioFunctionDefinition.name(); + + var wasmParams = new ArrayList(); + var wasmLocals = new ArrayList(); + + for (var patioLocal : patioFunctionDefinition.locals()) { + var wasmLocal = patioLocalToWasmLocal(patioLocal); + switch (patioLocal.localType()) { + case PARAM -> { + wasmParams.add(wasmLocal); + } + case VAL, VAR -> { + wasmLocals.add(wasmLocal); + } + } + } + + var wasmResultTypes = patioTypeToWasmResultTypes(patioFunctionDefinition.returnType()); + + var context = generateInstructions( + patioFunctionDefinition.body(), + Context.forFunction(module, Lists.concat(wasmParams, wasmLocals)) + ); + + module = context.module; + + module = module.addFunction(new WasmFunction( + name, + wasmParams, + wasmLocals, + wasmResultTypes, + new WasmExpression(context.instructions) + )); + } + } + } + + module = wasmMemoryBuilder.toModule(module); + + return module; + } + + private static Context generateInstructions( + List patioInstructions, + Context context + ) { + for (var patioInstruction : patioInstructions) { + context = generateInstruction(patioInstruction, context); + } + + return context; + } + + private static Context generateInstruction( + PatioInstruction patioInstruction, + Context context + ) { + return switch (patioInstruction) { + case PatioAllocHeap patioAllocHeap -> { + context = context + .addInstruction(new WasmI32Const(sizeOf(patioAllocHeap.layout()))) + .addInstruction(new WasmI32Const(offsetOf( + patioAllocHeap.layout(), + patioAllocHeap.gcChildrenOffset() + ))) + .addInstruction(new WasmI32Const(patioAllocHeap.gcChildrenCount())) + .addInstruction(new WasmCall(new WasmFuncName(ALLOC_NAME))) + .addInstruction(new WasmLocalSet(context.wasmLocalIndex(patioAllocHeap.target()))); + + yield context; + } + + case PatioAllocStack patioAllocStack -> { + context = context + .addInstruction(new WasmI32Const(sizeOf(patioAllocStack.layout()))) + .addInstruction(new WasmCall(new WasmFuncName(STACK_ALLOC_NAME))) + .addInstruction(new WasmLocalSet(context.wasmLocalIndex(patioAllocStack.target()))); + + yield context; + } + + case PatioBlock patioBlock -> + generateInstructions(patioBlock.body(), context); + + case PatioBoolToInt patioBoolToInt -> + generateOperandToPatioLocal( + patioBoolToInt.target(), + patioBoolToInt.value(), + context + ); + + case PatioCall patioCall -> { + for (var patioArg : patioCall.args()) { + context = generateOperand(patioArg, context); + } + + context = context.addInstruction(new WasmCall(new WasmFuncName(patioCall.functionName()))); + + if (patioCall.target().isPresent()) { + context = popIntoPatioLocal(patioCall.target().get(), context); + } + + yield context; + } + + case PatioCallPointer patioCallPointer -> { + for (var patioArg : patioCallPointer.args()) { + context = generateOperand(patioArg, context); + } + + context = generateOperand(patioCallPointer.functionPointer(), context); + + var wasmFuncType = new WasmFuncType( + patioCallPointer.args().stream() + .map(arg -> patioOperandToWasmValueType(arg)) + .toList(), + List.of(patioOperandToWasmValueType(patioCallPointer.target())) + ); + + context = context.addType(wasmFuncType); + + context = context.addInstruction(new WasmCallIndirect( + // TODO: remove hard-coded zero + new WasmTableIndex(0), + wasmFuncType + )); + + context = popIntoPatioLocal(patioCallPointer.target(), context); + + yield context; + } + + case PatioDataPtrToInt patioDataPtrToInt -> + generateOperandToPatioLocal( + patioDataPtrToInt.target(), + patioDataPtrToInt.value(), + context + ); + + case PatioIf patioIf -> { + context = generateOperand(patioIf.condition(), context); + + var wasmBlockType = generateBlockType(patioIf.targets()); + + context = context.enterIfInstruction(wasmBlockType); + context = generateBlockBody(patioIf.trueBody(), context); + + context = context.enterElse(); + context = generateBlockBody(patioIf.falseBody(), context); + + context = context.exitIfInstruction(); + + context = popIntoPatioLocals(patioIf.targets(), context); + + yield context; + } + + case PatioIntAdd patioIntAdd -> + generateBinaryOperation(patioIntAdd, new WasmI32Add(), context); + + case PatioIntEquals patioIntEquals -> + generateBinaryOperation(patioIntEquals, new WasmI32Eq(), context); + + case PatioIntNotEqual patioIntNotEqual -> + generateBinaryOperation(patioIntNotEqual, new WasmI32Ne(), context); + + case PatioIntSub patioIntSub -> + generateBinaryOperation(patioIntSub, new WasmI32Sub(), context); + + case PatioIntToBool patioIntToBool -> + generateOperandToPatioLocal( + patioIntToBool.target(), + patioIntToBool.value(), + context + ); + + case PatioIntToDataPtr patioIntToDataPtr -> + generateOperandToPatioLocal( + patioIntToDataPtr.target(), + patioIntToDataPtr.value(), + context + ); + + case PatioLocalStore patioLocalStore -> + generateOperandToPatioLocal( + patioLocalStore.target(), + patioLocalStore.value(), + context + ); + + case PatioLoop patioLoop -> { + context = context.enterBlockInstruction(); + context = context.enterLoopInstruction(); + context = generateInstructions(patioLoop.body(), context); + context = context.exitLoopInstruction(); + context = context.exitBlockInstruction(); + + yield context; + } + + case PatioLoopBreak patioLoopBreak -> { + context = context.addBrToBlockInstruction(); + + yield context; + } + + case PatioLoopContinue patioLoopContinue -> { + context = context.addBrToLoopInstruction(); + + yield context; + } + + case PatioMemoryLoad patioMemoryLoad -> { + context = generateOperand(patioMemoryLoad.pointer(), context); + + context = context.addInstruction(new WasmI32Load( + wasmMemoryArg(patioMemoryLoad.layout(), patioMemoryLoad.offset()) + )); + + context = popIntoPatioLocal(patioMemoryLoad.target(), context); + + yield context; + } + + case PatioMemoryStore patioMemoryStore -> { + context = generateOperand(patioMemoryStore.pointer(), context); + + context = generateOperand(patioMemoryStore.value(), context); + + context = context.addInstruction(new WasmI32Store( + wasmMemoryArg(patioMemoryStore.layout(), patioMemoryStore.offset()) + )); + + yield context; + } + + case PatioResume patioResume -> { + context = generateOperand(patioResume.value(), context); + + context = context.addInstruction(new WasmReturn()); + + yield context; + } + + case PatioReturn patioReturn -> { + context = generateOperand(patioReturn.value(), context); + + context = context.addInstruction(new WasmReturn()); + + yield context; + } + + case PatioReturnVoid patioReturnVoid -> + context.addInstruction(new WasmReturn()); + + case PatioSwitch patioSwitch -> { + // TODO: use br_table? + var wasmBlockType = generateBlockType(patioSwitch.targets()); + + for (var patioSwitchCase : patioSwitch.cases()) { + context = generateOperand(patioSwitch.value(), context); + context = context.addInstruction(new WasmI32Const(patioSwitchCase.value())); + context = context.addInstruction(new WasmI32Eq()); + context = context.enterIfInstruction(wasmBlockType); + context = generateBlockBody(patioSwitchCase.body(), context); + context = context.enterElse(); + } + + context = generateBlockBody(patioSwitch.defaultBody(), context); + + for (var patioSwitchCase : patioSwitch.cases()) { + context = context.exitIfInstruction(); + } + + context = popIntoPatioLocals(patioSwitch.targets(), context); + + yield context; + } + + case PatioThrow throw_ -> { + context = generateOperand(throw_.value(), context); + + context = context.addInstruction(new WasmThrow(new WasmTagName(throw_.exceptionTypeName()))); + + yield context; + } + + case PatioTry try_ -> { + context = context.enterTryInstruction( + new WasmBlockType(List.of(WasmValueType.I32)) + ); + + context = generateBlockBody(try_.body(), context); + + for (var catchClause : try_.catchClauses()) { + context = context.enterCatch(new WasmTagName(catchClause.exceptionTypeName())); + + context = popIntoPatioLocal(catchClause.target(), context); + context = generateBlockBody(catchClause.body(), context); + } + + context = context.exitTryInstruction(); + + context = popIntoPatioLocals(try_.targets(), context); + + yield context; + } + + case PatioUnreachable patioUnreachable -> + context.addInstruction(new WasmUnreachable()); + + case PatioVarLoad patioVarLoad -> { + yield generateOperandToPatioLocal( + patioVarLoad.target(), + patioVarLoad.var(), + context + ); + } + + case PatioVarStore patioVarStore -> { + yield generateOperandToPatioLocal( + patioVarStore.var(), + patioVarStore.value(), + context + ); + } + }; + } + + private static WasmBlockType generateBlockType(List targets) { + return new WasmBlockType( + targets.stream() + .map(patioTarget -> patioOperandToWasmValueType(patioTarget)) + .toList() + ); + } + + private static Context generateBlockBody( + PatioBlock block, + Context context + ) { + context = generateInstructions(block.body(), context); + + for (var patioResult : block.results()) { + context = generateOperand(patioResult, context); + } + + return context; + } + + private static Context generateBinaryOperation( + PatioInstructionBinaryOperation patioInstruction, + WasmInstruction wasmInstruction, + Context context + ) { + context = generateOperand(patioInstruction.left(), context); + + context = generateOperand(patioInstruction.right(), context); + + context = context.addInstruction(wasmInstruction); + + context = popIntoPatioLocal(patioInstruction.target(), context); + + return context; + } + + private static Context generateOperandToPatioLocal( + PatioLocalRef target, + PatioOperand value, + Context context + ) { + context = generateOperand(value, context); + + context = popIntoPatioLocal(target, context); + + return context; + } + + private static Context popIntoPatioLocal(PatioLocalRef target, Context context) { + var targetWasmLocalIndex = context.wasmLocalIndex(target); + return context.addInstruction(new WasmLocalSet(targetWasmLocalIndex)); + } + + private static Context popIntoPatioLocals( + List targets, + Context context + ) { + for (var patioTarget : targets.reversed()) { + context = popIntoPatioLocal(patioTarget, context); + } + return context; + } + + private static Context generateOperand( + PatioOperand patioOperand, + Context context + ) { + return switch (patioOperand) { + case PatioBool patioBool -> + context.addInstruction(new WasmI32Const(patioBool.value() ? 1 : 0)); + + case PatioDataPtr patioDataPtr -> { + var wasmMemoryAddress = new WasmMemoryAddress(patioDataPtr.name()); + yield context.addInstruction(new WasmMemoryAddressConst(wasmMemoryAddress)); + } + + case PatioFunctionPtr patioFunctionPtr -> { + var wasmFuncName = new WasmFuncName(patioFunctionPtr.name()); + yield context.addInstruction(new WasmTableElementIndexConst(wasmFuncName)); + } + + case PatioInt patioInt -> + context.addInstruction(new WasmI32Const(patioInt.value())); + + case PatioLocalRef patioLocalRef -> { + var wasmLocalIndex = context.patioLocalNameToWasmLocalIndex.get(patioLocalRef.name()); + yield context.addInstruction(new WasmLocalGet(wasmLocalIndex)); + } + }; + } + + private static int sizeOf(PatioDataLayout layout) { + return switch (layout) { + case PatioDataLayoutArray patioDataLayoutArray -> + sizeOf(patioDataLayoutArray.element()) * patioDataLayoutArray.size(); + + case PatioDataLayoutBool patioDataLayoutBool -> + 1; + + case PatioDataLayoutByte patioDataLayoutByte -> + 1; + + case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> + 4; + + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> + 4; + + case PatioDataLayoutInt patioDataLayoutInt -> + 4; + + case PatioDataLayoutStruct patioDataLayoutStruct -> + patioDataLayoutStruct.elements().stream() + .map(element -> sizeOf(element)) + .reduce(0, Integer::sum); + }; + } + + private static int offsetOf( + PatioDataLayout patioDataLayout, + List patioOffsets + ) { + var wasmOffset = 0; + + for (var patioOffset : patioOffsets) { + switch (patioDataLayout) { + case PatioDataLayoutArray patioDataLayoutArray -> { + wasmOffset += sizeOf(patioDataLayoutArray.element()) * patioOffset; + patioDataLayout = patioDataLayoutArray.element(); + } + case PatioDataLayoutBool patioDataLayoutBool -> { + throw new RuntimeException("offset not allowed for bools"); + } + case PatioDataLayoutByte patioDataLayoutByte -> { + throw new RuntimeException("offset not allowed for bytes"); + } + case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> { + throw new RuntimeException("offset not allowed for data ptrs"); + } + case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> { + throw new RuntimeException("offset not allowed for function ptrs"); + } + case PatioDataLayoutInt patioDataLayoutInt -> { + throw new RuntimeException("offset not allowed for ints"); + } + case PatioDataLayoutStruct patioDataLayoutStruct -> { + for (var i = 0; i < patioOffset; i++) { + wasmOffset += sizeOf(patioDataLayoutStruct.elements().get(i)); + } + patioDataLayout = patioDataLayoutStruct.elements().get(patioOffset); + } + } + } + + return wasmOffset; + } + + private static WasmMemoryArg wasmMemoryArg( + PatioDataLayout patioDataLayout, + List patioOffsets + ) { + var wasmOffset = offsetOf(patioDataLayout, patioOffsets); + + // TODO: alignment + return new WasmMemoryArg(wasmOffset, WasmAlignment.NONE); + } + + private static byte[] patioDataValueToBytes(PatioDataValue value) { + var bytes = ByteBuffer.allocate(sizeOf(value)); + bytes.order(ByteOrder.LITTLE_ENDIAN); + + patioDataValueToBytes(value, bytes); + + return bytes.array(); + } + + private static void patioDataValueToBytes(PatioDataValue value, ByteBuffer bytes) { + switch (value) { + case PatioDataValueArray patioDataValueArray -> { + for (var element : patioDataValueArray.elements()) { + patioDataValueToBytes(element, bytes); + } + } + case PatioDataValueBool patioDataValueBool -> { + bytes.put(patioDataValueBool.value() ? (byte) 1 : (byte) 0); + } + case PatioDataValueByte patioDataValueByte -> { + bytes.put(patioDataValueByte.value()); + } + case PatioDataValueDataPointerNull patioDataValueDataPointerNull -> { + bytes.putInt(0); + } + case PatioDataValueInt patioDataValueInt -> { + bytes.putInt(patioDataValueInt.value()); + } + case PatioDataValueStruct patioDataValueStruct -> { + for (var element : patioDataValueStruct.elements()) { + patioDataValueToBytes(element, bytes); + } + } + } + } + + private static int sizeOf(PatioDataValue value) { + return sizeOf(value.layout()); + } + + private static WasmLocal patioLocalToWasmLocal(PatioLocal local) { + return new WasmLocal(local.name(), patioTypeToWasmValueType(local.type())); + } + + private static WasmValueType patioOperandToWasmValueType(PatioOperand operand) { + return WasmValueType.I32; + } + + private static WasmValueType patioTypeToWasmValueType(PatioType type) { + return WasmValueType.I32; + } + + private static List patioTypeToWasmResultTypes(PatioType type) { + return type instanceof PatioVoidType ? List.of() : List.of(WasmValueType.I32); + } + + private static class Context { + public static Context forFunction(WasmModule module, List locals) { + var patioLocalNameToWasmLocalIndex = P.map(); + + for (var local : locals) { + patioLocalNameToWasmLocalIndex = patioLocalNameToWasmLocalIndex.plus( + local.name(), + new WasmLocalIndex(patioLocalNameToWasmLocalIndex.size()) + ); + } + + return new Context( + module, + P.vector(), + P.stack(), + patioLocalNameToWasmLocalIndex, + new NullStructuredInstructionBuilder(), + P.stack() + ); + } + + private final WasmModule module; + private final PVector instructions; + private final PStack> instructionsStack; + private final PMap patioLocalNameToWasmLocalIndex; + private final WasmStructuredInstructionBuilder structuredInstructionBuilder; + private final PStack structuredInstructionBuilderStack; + + private Context( + WasmModule module, + PVector instructions, + PStack> instructionsStack, + PMap patioLocalNameToWasmLocalIndex, + WasmStructuredInstructionBuilder structuredInstructionBuilder, + PStack structuredInstructionBuilderStack + ) { + this.module = module; + this.instructions = instructions; + this.instructionsStack = instructionsStack; + this.patioLocalNameToWasmLocalIndex = patioLocalNameToWasmLocalIndex; + this.structuredInstructionBuilder = structuredInstructionBuilder; + this.structuredInstructionBuilderStack = structuredInstructionBuilderStack; + } + + private Context addType(WasmFuncType type) { + return new Context( + this.module.addType(type), + this.instructions, + this.instructionsStack, + this.patioLocalNameToWasmLocalIndex, + this.structuredInstructionBuilder, + this.structuredInstructionBuilderStack + ); + } + + private Context addInstruction(WasmInstruction instruction) { + return new Context( + this.module, + this.instructions.plus(instruction), + this.instructionsStack, + this.patioLocalNameToWasmLocalIndex, + this.structuredInstructionBuilder, + this.structuredInstructionBuilderStack + ); + } + + private WasmLocalIndex wasmLocalIndex(PatioLocalRef patioLocalRef) { + return this.patioLocalNameToWasmLocalIndex.get(patioLocalRef.name()); + } + + public Context enterBlockInstruction() { + return new Context( + this.module, + P.vector(), + this.instructionsStack.plus(this.instructions), + this.patioLocalNameToWasmLocalIndex, + new WasmBlockInstructionBuilder(), + this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) + ); + } + + public Context exitBlockInstruction() { + // TODO: verify matching enter/exit? + return exitStructuredInstruction(); + } + + public Context addBrToBlockInstruction() { + return addBr(WasmBlockInstructionBuilder.class); + } + + public Context enterLoopInstruction() { + return new Context( + this.module, + P.vector(), + this.instructionsStack.plus(this.instructions), + this.patioLocalNameToWasmLocalIndex, + new WasmLoopInstructionBuilder(), + this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) + ); + } + + public Context exitLoopInstruction() { + // TODO: verify matching enter/exit? + return exitStructuredInstruction(); + } + + public Context addBrToLoopInstruction() { + return addBr(WasmLoopInstructionBuilder.class); + } + + public Context enterIfInstruction(WasmBlockType blockType) { + return new Context( + this.module, + P.vector(), + this.instructionsStack.plus(this.instructions), + this.patioLocalNameToWasmLocalIndex, + new WasmIfInstructionBuilder(blockType), + this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) + ); + } + + public Context enterElse() { + return new Context( + this.module, + P.vector(), + this.instructionsStack, + this.patioLocalNameToWasmLocalIndex, + this.structuredInstructionBuilder.enterElse(this.instructions), + this.structuredInstructionBuilderStack + ); + } + + public Context exitIfInstruction() { + // TODO: verify matching enter/exit? + return exitStructuredInstruction(); + } + + public Context enterTryInstruction(WasmBlockType blockType) { + return new Context( + this.module, + P.vector(), + this.instructionsStack.plus(this.instructions), + this.patioLocalNameToWasmLocalIndex, + new WasmTryInstructionBuilder(blockType), + this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) + ); + } + + public Context enterCatch(WasmTagName tagName) { + return new Context( + this.module, + P.vector(), + this.instructionsStack, + this.patioLocalNameToWasmLocalIndex, + this.structuredInstructionBuilder.enterCatch(tagName, this.instructions), + this.structuredInstructionBuilderStack + ); + } + + public Context exitTryInstruction() { + // TODO: verify matching enter/exit? + return exitStructuredInstruction(); + } + + public Context exitStructuredInstruction() { + var context = new Context( + this.module, + this.instructionsStack.get(0), + this.instructionsStack.minus(0), + this.patioLocalNameToWasmLocalIndex, + this.structuredInstructionBuilderStack.get(0), + this.structuredInstructionBuilderStack.minus(0) + ); + + var instruction = this.structuredInstructionBuilder + .exit(this.instructions); + + return context.addInstruction(instruction); + } + + private Context addBr(Class clazz) { + var labelIndex = findLabelIndex(clazz); + + return this.addInstruction(new WasmBr(labelIndex)); + } + + private int findLabelIndex(Class clazz) { + if (clazz.isInstance(this.structuredInstructionBuilder)) { + return 0; + } + + for (var i = 0; i < this.structuredInstructionBuilderStack.size(); i++) { + if (clazz.isInstance(this.structuredInstructionBuilderStack.get(i))) { + return i + 1; + } + } + + throw new IllegalStateException("cannot find label index"); + } + } + + private interface WasmStructuredInstructionBuilder { + WasmInstruction exit(PVector instructions); + + default WasmStructuredInstructionBuilder enterElse(PVector instructions) { + throw new UnsupportedOperationException(); + } + + default WasmStructuredInstructionBuilder enterCatch( + WasmTagName tagName, + PVector instructions + ) { + throw new UnsupportedOperationException(); + } + } + + private static class NullStructuredInstructionBuilder implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + throw new UnsupportedOperationException(); + } + } + + private static class WasmBlockInstructionBuilder implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + return new WasmBlock(new WasmBlockType(List.of()), instructions); + } + } + + private static class WasmLoopInstructionBuilder implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + return new WasmLoop(new WasmBlockType(List.of()), instructions); + } + } + + private record WasmIfInstructionBuilder( + WasmBlockType blockType + ) implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + throw new IllegalStateException("if instruction without else not supported"); + } + + @Override + public WasmStructuredInstructionBuilder enterElse(PVector instructions) { + return new WasmIfElseInstructionBuilder(blockType, instructions); + } + } + + private record WasmIfElseInstructionBuilder( + WasmBlockType blockType, + PVector ifTrue + ) implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + return new WasmIf(blockType, ifTrue, instructions); + } + } + + private record WasmTryInstructionBuilder( + WasmBlockType blockType + ) implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + return new WasmTry(blockType, List.of(), instructions); + } + + @Override + public WasmStructuredInstructionBuilder enterCatch( + WasmTagName tagName, + PVector instructions + ) { + return new WasmTryCatchInstructionBuilder( + blockType, + instructions, + tagName, + P.vector() + ); + } + } + + private record WasmTryCatchInstructionBuilder( + WasmBlockType blockType, + List tryBodyInstructions, + WasmTagName tagName, + PVector catchBlocks + ) implements WasmStructuredInstructionBuilder { + @Override + public WasmInstruction exit(PVector instructions) { + return new WasmTry( + this.blockType, + this.catchBlocks.plus(new WasmCatchBlock(this.tagName, instructions)), + this.tryBodyInstructions + ); + } + + @Override + public WasmStructuredInstructionBuilder enterCatch( + WasmTagName tagName, + PVector instructions + ) { + return new WasmTryCatchInstructionBuilder( + this.blockType, + this.tryBodyInstructions, + tagName, + this.catchBlocks.plus(new WasmCatchBlock(this.tagName, instructions)) + ); + } + } +} diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmBackend.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmBackend.java index ad311877..b4af6a7c 100644 --- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmBackend.java +++ b/src/main/java/org/shedlang/compiler/backends/wasm/WasmBackend.java @@ -51,7 +51,7 @@ public class WasmBackend implements Backend { PatioCodeGenContext.forTypeInfos(typeInfos) ); - var wasmModule = WasmPatioCodeGen.generateCompilationUnit(patioCompilationUnit); + var wasmModule = PatioToWasm.generateCompilationUnit(patioCompilationUnit); var objectPath = writeObjectFile( wasmModule, @@ -100,7 +100,7 @@ public class WasmBackend implements Backend { var patioCompilationUnit = context.toCompilationUnit(source); - var wasmModule = WasmPatioCodeGen.generateCompilationUnit(patioCompilationUnit); + var wasmModule = PatioToWasm.generateCompilationUnit(patioCompilationUnit); wasmModule = generateNativeRuntime(wasmModule); diff --git a/src/main/java/org/shedlang/compiler/backends/wasm/WasmPatioCodeGen.java b/src/main/java/org/shedlang/compiler/backends/wasm/WasmPatioCodeGen.java deleted file mode 100644 index 1e22c558..00000000 --- a/src/main/java/org/shedlang/compiler/backends/wasm/WasmPatioCodeGen.java +++ /dev/null @@ -1,960 +0,0 @@ -package org.shedlang.compiler.backends.wasm; - -import org.pcollections.PMap; -import org.pcollections.PStack; -import org.pcollections.PVector; -import org.shedlang.compiler.backends.wasm.structure.ast.instructions.*; -import org.shedlang.compiler.backends.wasm.structure.ast.linking.WasmMemoryAddress; -import org.shedlang.compiler.backends.wasm.structure.ast.modules.*; -import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmFuncType; -import org.shedlang.compiler.backends.wasm.structure.ast.types.WasmValueType; -import org.shedlang.compiler.patio.ast.*; -import org.shedlang.compiler.patio.ast.instructions.*; -import org.shedlang.compiler.patio.ast.operands.*; -import org.shedlang.compiler.patio.ast.types.*; -import org.shedlang.compiler.util.Lists; -import org.shedlang.compiler.util.P; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; - -public class WasmPatioCodeGen { - public static final String ALLOC_NAME = "shed__runtime__gc_alloc"; - public static final String STACK_ALLOC_NAME = "malloc"; - - public static WasmModule generateCompilationUnit( - PatioCompilationUnit patioCompilationUnit - ) { - var module = WasmModule.EMPTY.addImport(new WasmImport( - "env", - ALLOC_NAME, - new WasmImportDescriptor.Func( - ALLOC_NAME, - new WasmFuncType( - List.of(WasmValueType.I32, WasmValueType.I32, WasmValueType.I32), - List.of(WasmValueType.I32) - ) - ) - )); - - module = module.addImport(new WasmImport( - "env", - STACK_ALLOC_NAME, - new WasmImportDescriptor.Func( - STACK_ALLOC_NAME, - new WasmFuncType( - List.of(WasmValueType.I32), - List.of(WasmValueType.I32) - ) - ) - )); - - var wasmMemoryBuilder = WasmMemoryBuilder.EMPTY; - - for (var patioCompilationUnitElement : patioCompilationUnit.elements()) { - switch (patioCompilationUnitElement) { - case PatioDataElementImmutable patioDataElementImmutable -> { - wasmMemoryBuilder = wasmMemoryBuilder.add( - patioDataElementImmutable.name(), - patioDataValueToBytes(patioDataElementImmutable.value()), - WasmAlignment.ALIGN_32_BIT - ).memory(); - } - case PatioDataElementMutable patioDataElementMutable -> { - wasmMemoryBuilder = wasmMemoryBuilder.add( - patioDataElementMutable.name(), - patioDataValueToBytes(patioDataElementMutable.initialValue()), - WasmAlignment.ALIGN_32_BIT - ).memory(); - } - case PatioFunctionDeclaration patioFunctionDeclaration -> { - var name = patioFunctionDeclaration.name(); - - var wasmFuncType = new WasmFuncType( - patioFunctionDeclaration.params().stream() - .map(param -> patioTypeToWasmValueType(param.type())) - .toList(), - patioTypeToWasmResultTypes(patioFunctionDeclaration.returnType()) - ); - - module = module.addImport(new WasmImport( - "env", - name, - new WasmImportDescriptor.Func(name, wasmFuncType) - )); - } - case PatioExceptionTypeDefinition patioExceptionTypeDefinition -> { - module = module.addTag(new WasmTag( - WasmTagType.EXCEPTION, - patioExceptionTypeDefinition.name(), - List.of(WasmValueType.I32) - )); - } - case PatioFunctionDefinition patioFunctionDefinition -> { - var name = patioFunctionDefinition.name(); - - var wasmParams = new ArrayList(); - var wasmLocals = new ArrayList(); - - for (var patioLocal : patioFunctionDefinition.locals()) { - var wasmLocal = patioLocalToWasmLocal(patioLocal); - switch (patioLocal.localType()) { - case PARAM -> { - wasmParams.add(wasmLocal); - } - case VAL, VAR -> { - wasmLocals.add(wasmLocal); - } - } - } - - var wasmResultTypes = patioTypeToWasmResultTypes(patioFunctionDefinition.returnType()); - - var context = generateInstructions( - patioFunctionDefinition.body(), - Context.forFunction(module, Lists.concat(wasmParams, wasmLocals)) - ); - - module = context.module; - - module = module.addFunction(new WasmFunction( - name, - wasmParams, - wasmLocals, - wasmResultTypes, - new WasmExpression(context.instructions) - )); - } - } - } - - module = wasmMemoryBuilder.toModule(module); - - return module; - } - - private static Context generateInstructions( - List patioInstructions, - Context context - ) { - for (var patioInstruction : patioInstructions) { - context = generateInstruction(patioInstruction, context); - } - - return context; - } - - private static Context generateInstruction( - PatioInstruction patioInstruction, - Context context - ) { - return switch (patioInstruction) { - case PatioAllocHeap patioAllocHeap -> { - context = context - .addInstruction(new WasmI32Const(sizeOf(patioAllocHeap.layout()))) - .addInstruction(new WasmI32Const(offsetOf( - patioAllocHeap.layout(), - patioAllocHeap.gcChildrenOffset() - ))) - .addInstruction(new WasmI32Const(patioAllocHeap.gcChildrenCount())) - .addInstruction(new WasmCall(new WasmFuncName(ALLOC_NAME))) - .addInstruction(new WasmLocalSet(context.wasmLocalIndex(patioAllocHeap.target()))); - - yield context; - } - - case PatioAllocStack patioAllocStack -> { - context = context - .addInstruction(new WasmI32Const(sizeOf(patioAllocStack.layout()))) - .addInstruction(new WasmCall(new WasmFuncName(STACK_ALLOC_NAME))) - .addInstruction(new WasmLocalSet(context.wasmLocalIndex(patioAllocStack.target()))); - - yield context; - } - - case PatioBlock patioBlock -> - generateInstructions(patioBlock.body(), context); - - case PatioBoolToInt patioBoolToInt -> - generateOperandToPatioLocal( - patioBoolToInt.target(), - patioBoolToInt.value(), - context - ); - - case PatioCall patioCall -> { - for (var patioArg : patioCall.args()) { - context = generateOperand(patioArg, context); - } - - context = context.addInstruction(new WasmCall(new WasmFuncName(patioCall.functionName()))); - - if (patioCall.target().isPresent()) { - context = popIntoPatioLocal(patioCall.target().get(), context); - } - - yield context; - } - - case PatioCallPointer patioCallPointer -> { - for (var patioArg : patioCallPointer.args()) { - context = generateOperand(patioArg, context); - } - - context = generateOperand(patioCallPointer.functionPointer(), context); - - var wasmFuncType = new WasmFuncType( - patioCallPointer.args().stream() - .map(arg -> patioOperandToWasmValueType(arg)) - .toList(), - List.of(patioOperandToWasmValueType(patioCallPointer.target())) - ); - - context = context.addType(wasmFuncType); - - context = context.addInstruction(new WasmCallIndirect( - // TODO: remove hard-coded zero - new WasmTableIndex(0), - wasmFuncType - )); - - context = popIntoPatioLocal(patioCallPointer.target(), context); - - yield context; - } - - case PatioDataPtrToInt patioDataPtrToInt -> - generateOperandToPatioLocal( - patioDataPtrToInt.target(), - patioDataPtrToInt.value(), - context - ); - - case PatioIf patioIf -> { - context = generateOperand(patioIf.condition(), context); - - var wasmBlockType = generateBlockType(patioIf.targets()); - - context = context.enterIfInstruction(wasmBlockType); - context = generateBlockBody(patioIf.trueBody(), context); - - context = context.enterElse(); - context = generateBlockBody(patioIf.falseBody(), context); - - context = context.exitIfInstruction(); - - context = popIntoPatioLocals(patioIf.targets(), context); - - yield context; - } - - case PatioIntAdd patioIntAdd -> - generateBinaryOperation(patioIntAdd, new WasmI32Add(), context); - - case PatioIntEquals patioIntEquals -> - generateBinaryOperation(patioIntEquals, new WasmI32Eq(), context); - - case PatioIntNotEqual patioIntNotEqual -> - generateBinaryOperation(patioIntNotEqual, new WasmI32Ne(), context); - - case PatioIntSub patioIntSub -> - generateBinaryOperation(patioIntSub, new WasmI32Sub(), context); - - case PatioIntToBool patioIntToBool -> - generateOperandToPatioLocal( - patioIntToBool.target(), - patioIntToBool.value(), - context - ); - - case PatioIntToDataPtr patioIntToDataPtr -> - generateOperandToPatioLocal( - patioIntToDataPtr.target(), - patioIntToDataPtr.value(), - context - ); - - case PatioLocalStore patioLocalStore -> - generateOperandToPatioLocal( - patioLocalStore.target(), - patioLocalStore.value(), - context - ); - - case PatioLoop patioLoop -> { - context = context.enterBlockInstruction(); - context = context.enterLoopInstruction(); - context = generateInstructions(patioLoop.body(), context); - context = context.exitLoopInstruction(); - context = context.exitBlockInstruction(); - - yield context; - } - - case PatioLoopBreak patioLoopBreak -> { - context = context.addBrToBlockInstruction(); - - yield context; - } - - case PatioLoopContinue patioLoopContinue -> { - context = context.addBrToLoopInstruction(); - - yield context; - } - - case PatioMemoryLoad patioMemoryLoad -> { - context = generateOperand(patioMemoryLoad.pointer(), context); - - context = context.addInstruction(new WasmI32Load( - wasmMemoryArg(patioMemoryLoad.layout(), patioMemoryLoad.offset()) - )); - - context = popIntoPatioLocal(patioMemoryLoad.target(), context); - - yield context; - } - - case PatioMemoryStore patioMemoryStore -> { - context = generateOperand(patioMemoryStore.pointer(), context); - - context = generateOperand(patioMemoryStore.value(), context); - - context = context.addInstruction(new WasmI32Store( - wasmMemoryArg(patioMemoryStore.layout(), patioMemoryStore.offset()) - )); - - yield context; - } - - case PatioResume patioResume -> { - context = generateOperand(patioResume.value(), context); - - context = context.addInstruction(new WasmReturn()); - - yield context; - } - - case PatioReturn patioReturn -> { - context = generateOperand(patioReturn.value(), context); - - context = context.addInstruction(new WasmReturn()); - - yield context; - } - - case PatioReturnVoid patioReturnVoid -> - context.addInstruction(new WasmReturn()); - - case PatioSwitch patioSwitch -> { - // TODO: use br_table? - var wasmBlockType = generateBlockType(patioSwitch.targets()); - - for (var patioSwitchCase : patioSwitch.cases()) { - context = generateOperand(patioSwitch.value(), context); - context = context.addInstruction(new WasmI32Const(patioSwitchCase.value())); - context = context.addInstruction(new WasmI32Eq()); - context = context.enterIfInstruction(wasmBlockType); - context = generateBlockBody(patioSwitchCase.body(), context); - context = context.enterElse(); - } - - context = generateBlockBody(patioSwitch.defaultBody(), context); - - for (var patioSwitchCase : patioSwitch.cases()) { - context = context.exitIfInstruction(); - } - - context = popIntoPatioLocals(patioSwitch.targets(), context); - - yield context; - } - - case PatioThrow throw_ -> { - context = generateOperand(throw_.value(), context); - - context = context.addInstruction(new WasmThrow(new WasmTagName(throw_.exceptionTypeName()))); - - yield context; - } - - case PatioTry try_ -> { - context = context.enterTryInstruction( - new WasmBlockType(List.of(WasmValueType.I32)) - ); - - context = generateBlockBody(try_.body(), context); - - for (var catchClause : try_.catchClauses()) { - context = context.enterCatch(new WasmTagName(catchClause.exceptionTypeName())); - - context = popIntoPatioLocal(catchClause.target(), context); - context = generateBlockBody(catchClause.body(), context); - } - - context = context.exitTryInstruction(); - - context = popIntoPatioLocals(try_.targets(), context); - - yield context; - } - - case PatioUnreachable patioUnreachable -> - context.addInstruction(new WasmUnreachable()); - - case PatioVarLoad patioVarLoad -> { - yield generateOperandToPatioLocal( - patioVarLoad.target(), - patioVarLoad.var(), - context - ); - } - - case PatioVarStore patioVarStore -> { - yield generateOperandToPatioLocal( - patioVarStore.var(), - patioVarStore.value(), - context - ); - } - }; - } - - private static WasmBlockType generateBlockType(List targets) { - return new WasmBlockType( - targets.stream() - .map(patioTarget -> patioOperandToWasmValueType(patioTarget)) - .toList() - ); - } - - private static Context generateBlockBody( - PatioBlock block, - Context context - ) { - context = generateInstructions(block.body(), context); - - for (var patioResult : block.results()) { - context = generateOperand(patioResult, context); - } - - return context; - } - - private static Context generateBinaryOperation( - PatioInstructionBinaryOperation patioInstruction, - WasmInstruction wasmInstruction, - Context context - ) { - context = generateOperand(patioInstruction.left(), context); - - context = generateOperand(patioInstruction.right(), context); - - context = context.addInstruction(wasmInstruction); - - context = popIntoPatioLocal(patioInstruction.target(), context); - - return context; - } - - private static Context generateOperandToPatioLocal( - PatioLocalRef target, - PatioOperand value, - Context context - ) { - context = generateOperand(value, context); - - context = popIntoPatioLocal(target, context); - - return context; - } - - private static Context popIntoPatioLocal(PatioLocalRef target, Context context) { - var targetWasmLocalIndex = context.wasmLocalIndex(target); - return context.addInstruction(new WasmLocalSet(targetWasmLocalIndex)); - } - - private static Context popIntoPatioLocals( - List targets, - Context context - ) { - for (var patioTarget : targets.reversed()) { - context = popIntoPatioLocal(patioTarget, context); - } - return context; - } - - private static Context generateOperand( - PatioOperand patioOperand, - Context context - ) { - return switch (patioOperand) { - case PatioBool patioBool -> - context.addInstruction(new WasmI32Const(patioBool.value() ? 1 : 0)); - - case PatioDataPtr patioDataPtr -> { - var wasmMemoryAddress = new WasmMemoryAddress(patioDataPtr.name()); - yield context.addInstruction(new WasmMemoryAddressConst(wasmMemoryAddress)); - } - - case PatioFunctionPtr patioFunctionPtr -> { - var wasmFuncName = new WasmFuncName(patioFunctionPtr.name()); - yield context.addInstruction(new WasmTableElementIndexConst(wasmFuncName)); - } - - case PatioInt patioInt -> - context.addInstruction(new WasmI32Const(patioInt.value())); - - case PatioLocalRef patioLocalRef -> { - var wasmLocalIndex = context.patioLocalNameToWasmLocalIndex.get(patioLocalRef.name()); - yield context.addInstruction(new WasmLocalGet(wasmLocalIndex)); - } - }; - } - - private static int sizeOf(PatioDataLayout layout) { - return switch (layout) { - case PatioDataLayoutArray patioDataLayoutArray -> - sizeOf(patioDataLayoutArray.element()) * patioDataLayoutArray.size(); - - case PatioDataLayoutBool patioDataLayoutBool -> - 1; - - case PatioDataLayoutByte patioDataLayoutByte -> - 1; - - case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> - 4; - - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> - 4; - - case PatioDataLayoutInt patioDataLayoutInt -> - 4; - - case PatioDataLayoutStruct patioDataLayoutStruct -> - patioDataLayoutStruct.elements().stream() - .map(element -> sizeOf(element)) - .reduce(0, Integer::sum); - }; - } - - private static int offsetOf( - PatioDataLayout patioDataLayout, - List patioOffsets - ) { - var wasmOffset = 0; - - for (var patioOffset : patioOffsets) { - switch (patioDataLayout) { - case PatioDataLayoutArray patioDataLayoutArray -> { - wasmOffset += sizeOf(patioDataLayoutArray.element()) * patioOffset; - patioDataLayout = patioDataLayoutArray.element(); - } - case PatioDataLayoutBool patioDataLayoutBool -> { - throw new RuntimeException("offset not allowed for bools"); - } - case PatioDataLayoutByte patioDataLayoutByte -> { - throw new RuntimeException("offset not allowed for bytes"); - } - case PatioDataLayoutDataPointer patioDataLayoutDataPointer -> { - throw new RuntimeException("offset not allowed for data ptrs"); - } - case PatioDataLayoutFunctionPointer patioDataLayoutFunctionPointer -> { - throw new RuntimeException("offset not allowed for function ptrs"); - } - case PatioDataLayoutInt patioDataLayoutInt -> { - throw new RuntimeException("offset not allowed for ints"); - } - case PatioDataLayoutStruct patioDataLayoutStruct -> { - for (var i = 0; i < patioOffset; i++) { - wasmOffset += sizeOf(patioDataLayoutStruct.elements().get(i)); - } - patioDataLayout = patioDataLayoutStruct.elements().get(patioOffset); - } - } - } - - return wasmOffset; - } - - private static WasmMemoryArg wasmMemoryArg( - PatioDataLayout patioDataLayout, - List patioOffsets - ) { - var wasmOffset = offsetOf(patioDataLayout, patioOffsets); - - // TODO: alignment - return new WasmMemoryArg(wasmOffset, WasmAlignment.NONE); - } - - private static byte[] patioDataValueToBytes(PatioDataValue value) { - var bytes = ByteBuffer.allocate(sizeOf(value)); - bytes.order(ByteOrder.LITTLE_ENDIAN); - - patioDataValueToBytes(value, bytes); - - return bytes.array(); - } - - private static void patioDataValueToBytes(PatioDataValue value, ByteBuffer bytes) { - switch (value) { - case PatioDataValueArray patioDataValueArray -> { - for (var element : patioDataValueArray.elements()) { - patioDataValueToBytes(element, bytes); - } - } - case PatioDataValueBool patioDataValueBool -> { - bytes.put(patioDataValueBool.value() ? (byte) 1 : (byte) 0); - } - case PatioDataValueByte patioDataValueByte -> { - bytes.put(patioDataValueByte.value()); - } - case PatioDataValueDataPointerNull patioDataValueDataPointerNull -> { - bytes.putInt(0); - } - case PatioDataValueInt patioDataValueInt -> { - bytes.putInt(patioDataValueInt.value()); - } - case PatioDataValueStruct patioDataValueStruct -> { - for (var element : patioDataValueStruct.elements()) { - patioDataValueToBytes(element, bytes); - } - } - } - } - - private static int sizeOf(PatioDataValue value) { - return sizeOf(value.layout()); - } - - private static WasmLocal patioLocalToWasmLocal(PatioLocal local) { - return new WasmLocal(local.name(), patioTypeToWasmValueType(local.type())); - } - - private static WasmValueType patioOperandToWasmValueType(PatioOperand operand) { - return WasmValueType.I32; - } - - private static WasmValueType patioTypeToWasmValueType(PatioType type) { - return WasmValueType.I32; - } - - private static List patioTypeToWasmResultTypes(PatioType type) { - return type instanceof PatioVoidType ? List.of() : List.of(WasmValueType.I32); - } - - private static class Context { - public static Context forFunction(WasmModule module, List locals) { - var patioLocalNameToWasmLocalIndex = P.map(); - - for (var local : locals) { - patioLocalNameToWasmLocalIndex = patioLocalNameToWasmLocalIndex.plus( - local.name(), - new WasmLocalIndex(patioLocalNameToWasmLocalIndex.size()) - ); - } - - return new Context( - module, - P.vector(), - P.stack(), - patioLocalNameToWasmLocalIndex, - new NullStructuredInstructionBuilder(), - P.stack() - ); - } - - private final WasmModule module; - private final PVector instructions; - private final PStack> instructionsStack; - private final PMap patioLocalNameToWasmLocalIndex; - private final WasmStructuredInstructionBuilder structuredInstructionBuilder; - private final PStack structuredInstructionBuilderStack; - - private Context( - WasmModule module, - PVector instructions, - PStack> instructionsStack, - PMap patioLocalNameToWasmLocalIndex, - WasmStructuredInstructionBuilder structuredInstructionBuilder, - PStack structuredInstructionBuilderStack - ) { - this.module = module; - this.instructions = instructions; - this.instructionsStack = instructionsStack; - this.patioLocalNameToWasmLocalIndex = patioLocalNameToWasmLocalIndex; - this.structuredInstructionBuilder = structuredInstructionBuilder; - this.structuredInstructionBuilderStack = structuredInstructionBuilderStack; - } - - private Context addType(WasmFuncType type) { - return new Context( - this.module.addType(type), - this.instructions, - this.instructionsStack, - this.patioLocalNameToWasmLocalIndex, - this.structuredInstructionBuilder, - this.structuredInstructionBuilderStack - ); - } - - private Context addInstruction(WasmInstruction instruction) { - return new Context( - this.module, - this.instructions.plus(instruction), - this.instructionsStack, - this.patioLocalNameToWasmLocalIndex, - this.structuredInstructionBuilder, - this.structuredInstructionBuilderStack - ); - } - - private WasmLocalIndex wasmLocalIndex(PatioLocalRef patioLocalRef) { - return this.patioLocalNameToWasmLocalIndex.get(patioLocalRef.name()); - } - - public Context enterBlockInstruction() { - return new Context( - this.module, - P.vector(), - this.instructionsStack.plus(this.instructions), - this.patioLocalNameToWasmLocalIndex, - new WasmBlockInstructionBuilder(), - this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) - ); - } - - public Context exitBlockInstruction() { - // TODO: verify matching enter/exit? - return exitStructuredInstruction(); - } - - public Context addBrToBlockInstruction() { - return addBr(WasmBlockInstructionBuilder.class); - } - - public Context enterLoopInstruction() { - return new Context( - this.module, - P.vector(), - this.instructionsStack.plus(this.instructions), - this.patioLocalNameToWasmLocalIndex, - new WasmLoopInstructionBuilder(), - this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) - ); - } - - public Context exitLoopInstruction() { - // TODO: verify matching enter/exit? - return exitStructuredInstruction(); - } - - public Context addBrToLoopInstruction() { - return addBr(WasmLoopInstructionBuilder.class); - } - - public Context enterIfInstruction(WasmBlockType blockType) { - return new Context( - this.module, - P.vector(), - this.instructionsStack.plus(this.instructions), - this.patioLocalNameToWasmLocalIndex, - new WasmIfInstructionBuilder(blockType), - this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) - ); - } - - public Context enterElse() { - return new Context( - this.module, - P.vector(), - this.instructionsStack, - this.patioLocalNameToWasmLocalIndex, - this.structuredInstructionBuilder.enterElse(this.instructions), - this.structuredInstructionBuilderStack - ); - } - - public Context exitIfInstruction() { - // TODO: verify matching enter/exit? - return exitStructuredInstruction(); - } - - public Context enterTryInstruction(WasmBlockType blockType) { - return new Context( - this.module, - P.vector(), - this.instructionsStack.plus(this.instructions), - this.patioLocalNameToWasmLocalIndex, - new WasmTryInstructionBuilder(blockType), - this.structuredInstructionBuilderStack.plus(this.structuredInstructionBuilder) - ); - } - - public Context enterCatch(WasmTagName tagName) { - return new Context( - this.module, - P.vector(), - this.instructionsStack, - this.patioLocalNameToWasmLocalIndex, - this.structuredInstructionBuilder.enterCatch(tagName, this.instructions), - this.structuredInstructionBuilderStack - ); - } - - public Context exitTryInstruction() { - // TODO: verify matching enter/exit? - return exitStructuredInstruction(); - } - - public Context exitStructuredInstruction() { - var context = new Context( - this.module, - this.instructionsStack.get(0), - this.instructionsStack.minus(0), - this.patioLocalNameToWasmLocalIndex, - this.structuredInstructionBuilderStack.get(0), - this.structuredInstructionBuilderStack.minus(0) - ); - - var instruction = this.structuredInstructionBuilder - .exit(this.instructions); - - return context.addInstruction(instruction); - } - - private Context addBr(Class clazz) { - var labelIndex = findLabelIndex(clazz); - - return this.addInstruction(new WasmBr(labelIndex)); - } - - private int findLabelIndex(Class clazz) { - if (clazz.isInstance(this.structuredInstructionBuilder)) { - return 0; - } - - for (var i = 0; i < this.structuredInstructionBuilderStack.size(); i++) { - if (clazz.isInstance(this.structuredInstructionBuilderStack.get(i))) { - return i + 1; - } - } - - throw new IllegalStateException("cannot find label index"); - } - } - - private interface WasmStructuredInstructionBuilder { - WasmInstruction exit(PVector instructions); - - default WasmStructuredInstructionBuilder enterElse(PVector instructions) { - throw new UnsupportedOperationException(); - } - - default WasmStructuredInstructionBuilder enterCatch( - WasmTagName tagName, - PVector instructions - ) { - throw new UnsupportedOperationException(); - } - } - - private static class NullStructuredInstructionBuilder implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - throw new UnsupportedOperationException(); - } - } - - private static class WasmBlockInstructionBuilder implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - return new WasmBlock(new WasmBlockType(List.of()), instructions); - } - } - - private static class WasmLoopInstructionBuilder implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - return new WasmLoop(new WasmBlockType(List.of()), instructions); - } - } - - private record WasmIfInstructionBuilder( - WasmBlockType blockType - ) implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - throw new IllegalStateException("if instruction without else not supported"); - } - - @Override - public WasmStructuredInstructionBuilder enterElse(PVector instructions) { - return new WasmIfElseInstructionBuilder(blockType, instructions); - } - } - - private record WasmIfElseInstructionBuilder( - WasmBlockType blockType, - PVector ifTrue - ) implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - return new WasmIf(blockType, ifTrue, instructions); - } - } - - private record WasmTryInstructionBuilder( - WasmBlockType blockType - ) implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - return new WasmTry(blockType, List.of(), instructions); - } - - @Override - public WasmStructuredInstructionBuilder enterCatch( - WasmTagName tagName, - PVector instructions - ) { - return new WasmTryCatchInstructionBuilder( - blockType, - instructions, - tagName, - P.vector() - ); - } - } - - private record WasmTryCatchInstructionBuilder( - WasmBlockType blockType, - List tryBodyInstructions, - WasmTagName tagName, - PVector catchBlocks - ) implements WasmStructuredInstructionBuilder { - @Override - public WasmInstruction exit(PVector instructions) { - return new WasmTry( - this.blockType, - this.catchBlocks.plus(new WasmCatchBlock(this.tagName, instructions)), - this.tryBodyInstructions - ); - } - - @Override - public WasmStructuredInstructionBuilder enterCatch( - WasmTagName tagName, - PVector instructions - ) { - return new WasmTryCatchInstructionBuilder( - this.blockType, - this.tryBodyInstructions, - tagName, - this.catchBlocks.plus(new WasmCatchBlock(this.tagName, instructions)) - ); - } - } -} -- cgit v1.2.3