diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-07-19 22:10:39 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-07-19 22:10:39 +0100 |
| commit | 51c202c469f884d1c67987282ed952cc303f43d7 (patch) | |
| tree | 448b564d29bfd779fec61393c404ac67cc972ad8 /src/main/java | |
| parent | 7e1063d13f4d810ec8520367e7a5a1e9438d05aa (diff) | |
Add Patio code gen for call
Diffstat (limited to 'src/main/java')
6 files changed, 209 insertions, 11 deletions
diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java index c0d52b54..6d07971f 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java @@ -1,35 +1,94 @@ package org.shedlang.compiler.patio; -import org.pcollections.PVector; import org.shedlang.compiler.patio.instructions.*; import org.shedlang.compiler.stackir.instructions.*; -import org.shedlang.compiler.util.P; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; + +import static org.shedlang.compiler.util.Lists.reversed; public class PatioCodeGen { private PatioCodeGen() {} - public static PVector<PatioInstruction> generateInstruction(Instruction stackIrInstruction) { + public static PatioCodeGenContext generateInstruction( + Instruction stackIrInstruction, + PatioCodeGenContext context + ) { return switch (stackIrInstruction) { + case CallInstruction call -> { + var namedArgsByName = new ArrayList<Map.Entry<String, PatioLocal>>(); + for (var argName : reversed(call.namedArgNames())) { + var argResult = context.allocateLocal( + PatioCodeGenLocalSpace.CALL_ARG_NAMED, + argName + ); + var arg = argResult.value(); + context = argResult.context(); + + context = context.addInstruction( + new PatioLocalStore(arg.name(), stackIrInstruction.source()) + ); + + namedArgsByName.add(Map.entry(argName, arg)); + } + + var positionalArgs = new ArrayList<PatioLocal>(); + for (var i = 0; i < call.positionalArgCount(); i++) { + var argResult = context.allocateLocal( + PatioCodeGenLocalSpace.CALL_ARG_POSITIONAL, + Integer.toString(call.positionalArgCount() - i - 1) + ); + var arg = argResult.value(); + context = argResult.context(); + + context = context.addInstruction( + new PatioLocalStore(arg.name(), stackIrInstruction.source()) + ); + + positionalArgs.add(arg); + } + Collections.reverse(positionalArgs); + + var receiverResult = context.allocateLocal(PatioCodeGenLocalSpace.RECEIVER); + var receiver = receiverResult.value(); + context = receiverResult.context(); + + context = context.addInstruction(new PatioLocalStore( + receiver.name(), + stackIrInstruction.source() + )); + + yield PatioCodeGenClosures.call( + receiver, + positionalArgs, + namedArgsByName, + stackIrInstruction.source(), + context + ); + } + case DiscardInstruction discard -> - P.vector(new PatioDiscard(discard.source())); + context.addInstruction(new PatioDiscard(discard.source())); case LocalLoadInstruction localLoad -> - P.vector(new PatioLocalLoad(localLoad.name(), localLoad.source())); + context.addInstruction(new PatioLocalLoad(localLoad.name(), localLoad.source())); case LocalStoreInstruction localStore -> - P.vector(new PatioLocalStore(localStore.name(), localStore.source())); + context.addInstruction(new PatioLocalStore(localStore.name(), localStore.source())); case PushBoolInstruction pushBool -> - P.vector(new PatioPushInt(pushBool.value() ? 1 : 0, pushBool.source())); + context.addInstruction(new PatioPushInt(pushBool.value() ? 1 : 0, pushBool.source())); case PushIntInstruction pushInt -> - P.vector(new PatioPushInt(pushInt.value().intValueExact(), pushInt.source())); + context.addInstruction(new PatioPushInt(pushInt.value().intValueExact(), pushInt.source())); case PushUnitInstruction pushUnit -> - P.vector(new PatioPushInt(0, pushUnit.source())); + context.addInstruction(new PatioPushInt(0, pushUnit.source())); case ReturnInstruction return_ -> - P.vector(new PatioReturn(return_.source())); + context.addInstruction(new PatioReturn(return_.source())); default -> throw new IllegalStateException("Unexpected value: " + stackIrInstruction); diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java new file mode 100644 index 00000000..0cd31616 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java @@ -0,0 +1,47 @@ +package org.shedlang.compiler.patio; + +import org.shedlang.compiler.patio.instructions.PatioCallPointer; +import org.shedlang.compiler.patio.instructions.PatioLocalLoad; +import org.shedlang.compiler.patio.instructions.PatioMemoryLoad; +import org.shedlang.compiler.sources.Source; + +import java.util.List; +import java.util.Map; + +import static org.shedlang.compiler.util.Iterables.sortedBy; + +class PatioCodeGenClosures { + private PatioCodeGenClosures() {} + + private static final int FUNCTION_POINTER_OFFSET = 0; + + static PatioCodeGenContext call( + PatioLocal closure, + List<PatioLocal> positionalArgs, + List<Map.Entry<String, PatioLocal>> namedArgsByName, + Source source, + PatioCodeGenContext context + ) { + // TODO: WASM expects the receiver to be pushed last, but we push it first. + // Do we change our instruction set to match WASM? Or have a call + // mechanism less reliant on stack order? + context = context + .addInstruction(new PatioLocalLoad(closure.name(), source)) + .addInstruction(new PatioMemoryLoad(FUNCTION_POINTER_OFFSET, source)); + + context = context.addInstruction(new PatioLocalLoad(closure.name(), source)); + + for (var positionalArg : positionalArgs) { + context = context.addInstruction(new PatioLocalLoad(positionalArg.name(), source)); + } + + for (var namedArg : sortedBy(namedArgsByName, x -> x.getKey())) { + context = context.addInstruction(new PatioLocalLoad(namedArg.getValue().name(), source)); + } + + var argCount = positionalArgs.size() + namedArgsByName.size(); + + return context + .addInstruction(new PatioCallPointer(argCount, source)); + } +} diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java index 675925e3..7ac7b263 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java @@ -1,4 +1,70 @@ package org.shedlang.compiler.patio; -public class PatioCodeGenContext { +import org.pcollections.PVector; +import org.shedlang.compiler.backends.llvm.NameAllocator; +import org.shedlang.compiler.patio.instructions.PatioInstruction; +import org.shedlang.compiler.util.P; + +import java.util.Optional; + +public record PatioCodeGenContext( + FunctionContext functionContext +) { + public record FunctionContext( + PVector<PatioInstruction> instructions, + NameAllocator<PatioCodeGenLocalSpace> localNames + ) { + public static final FunctionContext EMPTY = new FunctionContext( + P.vector(), + NameAllocator.empty() + ); + + public FunctionContext addInstruction(PatioInstruction instruction) { + return new FunctionContext( + this.instructions.plus(instruction), + this.localNames + ); + } + + public Result<PatioLocal> allocateLocal(PatioCodeGenLocalSpace space, Optional<String> name) { + var result = this.localNames.generateName(space, name); + + return new FunctionContext( + this.instructions, + result.allocator() + ).toResult(new PatioLocal(result.name())); + } + + private <T> Result<T> toResult(T value) { + return new Result<>(value, this); + } + + public record Result<T>(T value, FunctionContext context) {} + } + + public static final PatioCodeGenContext EMPTY = new PatioCodeGenContext( + FunctionContext.EMPTY + ); + + public PatioCodeGenContext addInstruction(PatioInstruction instruction) { + return new PatioCodeGenContext( + this.functionContext.addInstruction(instruction) + ); + } + + public PatioCodeGenResult<PatioLocal> allocateLocal(PatioCodeGenLocalSpace space, String name) { + var result = this.functionContext.allocateLocal(space, Optional.of(name)); + + return new PatioCodeGenContext(result.context).toResult(result.value); + } + + public PatioCodeGenResult<PatioLocal> allocateLocal(PatioCodeGenLocalSpace space) { + var result = this.functionContext.allocateLocal(space, Optional.empty()); + + return new PatioCodeGenContext(result.context).toResult(result.value); + } + + private <T> PatioCodeGenResult<T> toResult(T value) { + return new PatioCodeGenResult<>(value, this); + } } diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java new file mode 100644 index 00000000..dfbcf4ee --- /dev/null +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java @@ -0,0 +1,18 @@ +package org.shedlang.compiler.patio; + +public enum PatioCodeGenLocalSpace { + CALL_ARG_POSITIONAL("callArgPositional"), + CALL_ARG_NAMED("callArgNamed"), + RECEIVER("receiver");; + + private final String name; + + PatioCodeGenLocalSpace(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenResult.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenResult.java new file mode 100644 index 00000000..5644a3b1 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenResult.java @@ -0,0 +1,4 @@ +package org.shedlang.compiler.patio; + +public record PatioCodeGenResult<T>(T value, PatioCodeGenContext context) { +} diff --git a/src/main/java/org/shedlang/compiler/patio/PatioLocal.java b/src/main/java/org/shedlang/compiler/patio/PatioLocal.java new file mode 100644 index 00000000..bdd1a7ab --- /dev/null +++ b/src/main/java/org/shedlang/compiler/patio/PatioLocal.java @@ -0,0 +1,4 @@ +package org.shedlang.compiler.patio; + +public record PatioLocal(String name) { +} |
