From 41fcbdb71e6603919f0b751998452a45b8d9a0a3 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 2 Dec 2023 21:24:23 +0000 Subject: Support effects in effect handler methods --- .../org/shedlang/compiler/patio/PatioCodeGen.java | 55 ++++-- .../compiler/patio/PatioCodeGenClosures.java | 32 ++-- .../PatioCodeGenCompilationUnitElementSpace.java | 1 + .../compiler/patio/PatioCodeGenContext.java | 70 +++++-- .../compiler/patio/PatioCodeGenEffects.java | 208 ++++++++++++++++++++- .../compiler/patio/PatioCodeGenLocalSpace.java | 2 + 6 files changed, 320 insertions(+), 48 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java index b5b3aa0b..78c43e5c 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGen.java @@ -19,6 +19,7 @@ import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.util.P; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; public class PatioCodeGen { @@ -234,26 +235,40 @@ public class PatioCodeGen { var target = targetResult.value(); context = targetResult.context(); - context = PatioCodeGenClosures.create( - target, - defineFunction.name(), - defineFunction.positionalParams(), - defineFunction.namedParams(), - defineFunction.freeVariables(), - functionContext -> { - // TODO: explicitly store locals on a function? - - functionContext = declareShidLocals( - defineFunction.body(), - defineFunction.freeVariables(), - functionContext - ); + Function generateBody = functionContext -> { + // TODO: explicitly store locals on a function? - return generateInstructions(defineFunction.body(), functionContext); - }, - defineFunction.source(), - context - ); + functionContext = declareShidLocals( + defineFunction.body(), + defineFunction.freeVariables(), + functionContext + ); + + return generateInstructions(defineFunction.body(), functionContext); + }; + + context = switch (defineFunction.variant()) { + case EFFECT_HANDLER_METHOD -> PatioCodeGenEffects.createEffectHandlerMethod( + target, + defineFunction.name(), + defineFunction.positionalParams(), + defineFunction.namedParams(), + defineFunction.freeVariables(), + generateBody, + defineFunction.source(), + context + ); + case FUNCTION -> PatioCodeGenClosures.create( + target, + defineFunction.name(), + defineFunction.positionalParams(), + defineFunction.namedParams(), + defineFunction.freeVariables(), + generateBody, + defineFunction.source(), + context + ); + }; context = context.temporaryPush(target); @@ -550,7 +565,7 @@ public class PatioCodeGen { var value = valueResult.value(); context = valueResult.context(); - yield context.addInstruction(new PatioResume(value, resume.source())); + yield PatioCodeGenEffects.resume(value, resume.source(), context); } case ShidReturn return_ -> { diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java index 36f83cb2..9030a5d0 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenClosures.java @@ -26,6 +26,7 @@ class PatioCodeGenClosures { private PatioCodeGenClosures() {} private static final List FUNCTION_POINTER_OFFSET = List.of(0); + private static final int FREE_VARIABLES_OFFSET = 1; private static PatioDataLayout closureDataLayoutForCall() { return new PatioDataLayoutStruct(List.of( @@ -42,23 +43,18 @@ class PatioCodeGenClosures { PatioDataLayouts.struct( freeVariables.stream() .map(freeVariable -> PatioDataLayouts.fromType( - freeVariableType(freeVariable, context) + context.shidLocalType(freeVariable.name()) )) .toList() ) )); } - private static PatioType freeVariableType( - FreeVariable freeVariable, - PatioCodeGenContext context + private static List freeVariableOffset( + int freeVariablesOffset, + FreeVariable freeVariable ) { - var local = context.shidLocalNameToLocal(freeVariable.name()); - return context.localType(local); - } - - private static List freeVariableOffset(FreeVariable freeVariable) { - return List.of(1, freeVariable.index()); + return List.of(freeVariablesOffset, freeVariable.index()); } public static PatioCodeGenContext create( @@ -94,6 +90,7 @@ class PatioCodeGenClosures { bodyContext = loadFreeVariables( freeVariables, closurePointer, + FREE_VARIABLES_OFFSET, layout, source, bodyContext, @@ -132,6 +129,7 @@ class PatioCodeGenClosures { context = storeFreeVariables( freeVariables, closurePointer, + FREE_VARIABLES_OFFSET, layout, source, context @@ -140,7 +138,7 @@ class PatioCodeGenClosures { return context; } - private static PatioCodeGenContext defineParams( + static PatioCodeGenContext defineParams( List positionalParams, List namedParams, PatioCodeGenContext bodyContext @@ -155,9 +153,10 @@ class PatioCodeGenClosures { return bodyContext; } - private static PatioCodeGenContext storeFreeVariables( + static PatioCodeGenContext storeFreeVariables( FreeVariables freeVariables, PatioLocalRef closurePointer, + int freeVariablesOffset, PatioDataLayout closureLayout, Source source, PatioCodeGenContext context @@ -168,7 +167,7 @@ class PatioCodeGenClosures { c -> c.addInstruction(new PatioMemoryStore( closurePointer, closureLayout, - freeVariableOffset(freeVariable), + freeVariableOffset(freeVariablesOffset, freeVariable), c.shidLocalNameToLocal(freeVariable.name()), source )) @@ -177,9 +176,10 @@ class PatioCodeGenClosures { return context; } - private static PatioCodeGenContext loadFreeVariables( + static PatioCodeGenContext loadFreeVariables( FreeVariables freeVariables, PatioLocalRef closurePointer, + int freeVariablesOffset, PatioDataLayout closureLayout, Source source, PatioCodeGenContext bodyContext, @@ -188,7 +188,7 @@ class PatioCodeGenClosures { for (var freeVariable : freeVariables) { var freeVariableLocalResult = bodyContext.declareShidVal( freeVariable.name(), - freeVariableType(freeVariable, bodyContextParent) + bodyContextParent.shidLocalType(freeVariable.name()) ); var freeVariableLocal = freeVariableLocalResult.value(); bodyContext = freeVariableLocalResult.context(); @@ -197,7 +197,7 @@ class PatioCodeGenClosures { freeVariableLocal, closurePointer, closureLayout, - freeVariableOffset(freeVariable), + freeVariableOffset(freeVariablesOffset, freeVariable), source )); diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenCompilationUnitElementSpace.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenCompilationUnitElementSpace.java index d6bd46fa..b39d2a37 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenCompilationUnitElementSpace.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenCompilationUnitElementSpace.java @@ -1,6 +1,7 @@ package org.shedlang.compiler.patio; public enum PatioCodeGenCompilationUnitElementSpace { + EFFECT_HANDLER_METHOD("effectHandlerMethod"), FUNCTION("function"), MAIN("main"), STRING("string"); diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java index 2dabfae0..219f9ec1 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenContext.java @@ -9,6 +9,7 @@ import org.shedlang.compiler.patio.ast.*; import org.shedlang.compiler.patio.ast.instructions.PatioInstruction; import org.shedlang.compiler.patio.ast.instructions.PatioLocalStore; import org.shedlang.compiler.patio.ast.operands.PatioLocalRef; +import org.shedlang.compiler.patio.ast.operands.PatioOperand; import org.shedlang.compiler.patio.ast.types.PatioDataLayout; import org.shedlang.compiler.patio.ast.types.PatioType; import org.shedlang.compiler.patio.errors.DuplicateCompilationUnitElementName; @@ -185,14 +186,16 @@ public record PatioCodeGenContext( PVector instructions, OrderedPMap localsByName, PMap shidNameToLocal, - PMap>> waitingForShidLocal + PMap>> waitingForShidLocal, + Optional stashedEffectHandler ) { public static final FunctionContext EMPTY = new FunctionContext( P.stack(), P.vector(), OrderedPMap.empty(), P.map(), - P.map() + P.map(), + Optional.empty() ); @CheckReturnValue @@ -202,7 +205,8 @@ public record PatioCodeGenContext( this.instructions, this.localsByName, this.shidNameToLocal, - this.waitingForShidLocal + this.waitingForShidLocal, + this.stashedEffectHandler ).toResult(this.temporaryStack.get(0)); } @@ -213,7 +217,8 @@ public record PatioCodeGenContext( this.instructions, this.localsByName, this.shidNameToLocal, - this.waitingForShidLocal + this.waitingForShidLocal, + this.stashedEffectHandler ); } @@ -224,7 +229,8 @@ public record PatioCodeGenContext( this.instructions.plus(instruction), this.localsByName, this.shidNameToLocal, - this.waitingForShidLocal + this.waitingForShidLocal, + this.stashedEffectHandler ); } @@ -245,7 +251,8 @@ public record PatioCodeGenContext( this.instructions, this.localsByName.plus(fullName, createLocal.apply(fullName)), this.shidNameToLocal, - this.waitingForShidLocal + this.waitingForShidLocal, + this.stashedEffectHandler ).toResult(new PatioLocalRef(fullName)); } @@ -279,7 +286,8 @@ public record PatioCodeGenContext( ), // TODO: the mapping is redundant given we fix the mapping this.shidNameToLocal.plus(name, localRef), - this.waitingForShidLocal.plus(name, P.vector()) + this.waitingForShidLocal.plus(name, P.vector()), + this.stashedEffectHandler ); return context.toResult(localRef); @@ -292,7 +300,8 @@ public record PatioCodeGenContext( this.instructions, this.localsByName, this.shidNameToLocal, - this.waitingForShidLocal.minus(name) + this.waitingForShidLocal.minus(name), + this.stashedEffectHandler ); return context.toResult(this.waitingForShidLocal.getOrDefault(name, P.vector())); @@ -341,10 +350,31 @@ public record PatioCodeGenContext( this.waitingForShidLocal.plus( name, this.waitingForShidLocal.getOrDefault(name, P.vector()).plus(func) - ) + ), + this.stashedEffectHandler + ); + } + + @CheckReturnValue + public FunctionContext stashEffectHandler(PatioOperand effectHandler) { + return new FunctionContext( + this.temporaryStack, + this.instructions, + this.localsByName, + this.shidNameToLocal, + this.waitingForShidLocal, + // TODO: check current value is empty + Optional.of(effectHandler) ); } + @CheckReturnValue + public PatioOperand stashedEffectHandlerOrThrow() { + // TODO: better error + return this.stashedEffectHandler + .orElseThrow(() -> new RuntimeException("No stashed effect handler")); + } + private Result toResult(T value) { return new Result<>(value, this); } @@ -590,6 +620,12 @@ public record PatioCodeGenContext( return this.functionContext.localType(local); } + @CheckReturnValue + public PatioType shidLocalType(String name) { + var local = shidLocalNameToLocal(name); + return localType(local); + } + @CheckReturnValue public PatioCodeGenContext createFunction( String name, @@ -635,7 +671,8 @@ public record PatioCodeGenContext( P.vector(), this.functionContext.localsByName, this.functionContext.shidNameToLocal, - this.functionContext.waitingForShidLocal + this.functionContext.waitingForShidLocal, + this.functionContext.stashedEffectHandler ) ); } @@ -655,11 +692,22 @@ public record PatioCodeGenContext( this.blockStack.get(0).instructions, this.functionContext.localsByName, this.functionContext.shidNameToLocal, - this.functionContext.waitingForShidLocal + this.functionContext.waitingForShidLocal, + this.functionContext.stashedEffectHandler ) ).toResult(this.functionContext.instructions); } + @CheckReturnValue + public PatioCodeGenContext stashEffectHandler(PatioOperand effectHandler) { + return withFunctionContext(this.functionContext.stashEffectHandler(effectHandler)); + } + + @CheckReturnValue + public PatioOperand stashedEffectHandler() { + return this.functionContext.stashedEffectHandlerOrThrow(); + } + @CheckReturnValue private PatioCodeGenContext withCompilationUnitContext(CompilationUnitContext compilationUnitContext) { return new PatioCodeGenContext(this.globalContext, compilationUnitContext, this.blockStack, this.functionContext); diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenEffects.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenEffects.java index 5f32e90e..74073b2f 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenEffects.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenEffects.java @@ -3,7 +3,9 @@ package org.shedlang.compiler.patio; import org.shedlang.compiler.ast.typed.FreeVariables; import org.shedlang.compiler.patio.ast.PatioFunctionDeclaration; import org.shedlang.compiler.patio.ast.PatioLocal; +import org.shedlang.compiler.patio.ast.PatioVisibility; import org.shedlang.compiler.patio.ast.instructions.*; +import org.shedlang.compiler.patio.ast.operands.PatioFunctionPtr; import org.shedlang.compiler.patio.ast.operands.PatioInt; import org.shedlang.compiler.patio.ast.operands.PatioLocalRef; import org.shedlang.compiler.patio.ast.operands.PatioOperand; @@ -23,11 +25,35 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.IntStream; +import static org.shedlang.compiler.patio.PatioCodeGenClosures.*; + public class PatioCodeGenEffects { private static final PatioType EFFECT_TAG_TYPE = PatioTypes.INT; private static final PatioType EFFECT_METHOD_INDEX_TYPE = PatioTypes.INT; private static final PatioType EFFECT_HANDLER_TYPE = PatioTypes.DATA_POINTER; - private static final PatioType EFFECT_HANDLER_METHOD_TYPE = PatioCodeGenClosures.CLOSURE_TYPE; + private static final PatioType EFFECT_HANDLER_METHOD_POINTER_TYPE = PatioTypes.DATA_POINTER; + private static final PatioType EFFECT_HANDLER_METHOD_TYPE = PatioCodeGenTypes.VALUE_TYPE; + + private static final List CLOSURE_FUNCTION_POINTER_OFFSET = List.of(0); + private static final List CLOSURE_EFFECT_HANDLER_OFFSET = List.of(1); + private static final int CLOSURE_FREE_VARIABLES_OFFSET = 2; + + private static PatioDataLayout effectHandlerMethodDataLayout( + FreeVariables freeVariables, + PatioCodeGenContext context + ) { + return PatioDataLayouts.struct(List.of( + PatioDataLayouts.FUNCTION_POINTER, + PatioDataLayouts.DATA_POINTER, + PatioDataLayouts.struct( + freeVariables.stream() + .map(freeVariable -> PatioDataLayouts.fromType( + context.shidLocalType(freeVariable.name()) + )) + .toList() + ) + )); + } private static final PatioFunctionDeclaration FIND_HANDLER_METHOD_DECLARATION = new PatioFunctionDeclaration( "shed__runtime__effects_find_handler_method", @@ -39,6 +65,20 @@ public class PatioCodeGenEffects { Sources.RUNTIME ); + private static final PatioFunctionDeclaration GET_HANDLER_DECLARATION = new PatioFunctionDeclaration( + "shed__runtime__effects_get_handler", + List.of(), + EFFECT_HANDLER_TYPE, + Sources.RUNTIME + ); + + private static final PatioFunctionDeclaration SET_HANDLER_DECLARATION = new PatioFunctionDeclaration( + "shed__runtime__effects_set_handler", + List.of(PatioLocal.param("effectHandler", EFFECT_HANDLER_TYPE)), + PatioTypes.VOID, + Sources.RUNTIME + ); + private static final PatioFunctionDeclaration PUSH_HANDLER_DECLARATION = new PatioFunctionDeclaration( "shed__runtime__effects_push_handler", List.of( @@ -279,6 +319,172 @@ public class PatioCodeGenEffects { return context; } + public static PatioCodeGenContext createEffectHandlerMethod( + PatioLocalRef target, + String name, + List positionalParams, + List namedParams, + FreeVariables freeVariables, + Function generateBody, + Source source, + PatioCodeGenContext context + ) { + context = context + .ensureCompilationUnitElement(GET_HANDLER_DECLARATION) + .ensureCompilationUnitElement(SET_HANDLER_DECLARATION); + + // TODO: remove duplication with PatioCodeGenClosures + var layout = effectHandlerMethodDataLayout(freeVariables, context); + + var fullNameResult = context.allocateCompilationUnitElementName( + PatioCodeGenCompilationUnitElementSpace.EFFECT_HANDLER_METHOD, + name + ); + var fullName = fullNameResult.value(); + context = fullNameResult.context(); + + var bodyContextParent = context; + context = context.createFunction(fullName, PatioVisibility.PRIVATE, PatioCodeGenTypes.VALUE_TYPE, source, bodyContext -> { + var closurePointerResult = bodyContext.allocateParam( + PatioCodeGenLocalSpace.EFFECT_HANDLER_METHOD_POINTER, + EFFECT_HANDLER_METHOD_POINTER_TYPE + ); + var closurePointer = closurePointerResult.value(); + bodyContext = closurePointerResult.context(); + + bodyContext = defineParams(positionalParams, namedParams, bodyContext); + + var previousEffectHandlerResult = bodyContext.allocateVal( + PatioCodeGenLocalSpace.EFFECT_HANDLER, + EFFECT_HANDLER_METHOD_POINTER_TYPE + ); + var previousEffectHandler = previousEffectHandlerResult.value(); + bodyContext = previousEffectHandlerResult.context(); + + bodyContext = getCurrentEffectHandler(previousEffectHandler, source, bodyContext); + + bodyContext = bodyContext.stashEffectHandler(previousEffectHandler); + + var effectHandlerResult = bodyContext.allocateVal( + PatioCodeGenLocalSpace.EFFECT_HANDLER, + EFFECT_HANDLER_METHOD_POINTER_TYPE + ); + var effectHandler = effectHandlerResult.value(); + bodyContext = effectHandlerResult.context(); + + bodyContext = bodyContext.addInstruction(new PatioMemoryLoad( + effectHandler, + closurePointer, + layout, + CLOSURE_EFFECT_HANDLER_OFFSET, + source + )); + + bodyContext = bodyContext.addInstruction(new PatioCall( + Optional.empty(), + SET_HANDLER_DECLARATION.name(), + List.of(effectHandler), + source + )); + + bodyContext = loadFreeVariables( + freeVariables, + closurePointer, + CLOSURE_FREE_VARIABLES_OFFSET, + layout, + source, + bodyContext, + bodyContextParent + ); + + bodyContext = generateBody.apply(bodyContext); + + return bodyContext; + }); + + var closurePointerResult = context.allocateVal( + PatioCodeGenLocalSpace.EFFECT_HANDLER_METHOD_POINTER, + name, + PatioTypes.DATA_POINTER + ); + var closurePointer = closurePointerResult.value(); + context = closurePointerResult.context(); + + context = context.addInstruction(new PatioAllocHeap(closurePointer, layout, source)); + + context = context.addInstruction(new PatioMemoryStore( + closurePointer, + layout, + CLOSURE_FUNCTION_POINTER_OFFSET, + new PatioFunctionPtr(fullName), + source + )); + + var effectHandlerResult = context.allocateVal( + PatioCodeGenLocalSpace.EFFECT_HANDLER_CURRENT, + EFFECT_HANDLER_TYPE + ); + var effectHandler = effectHandlerResult.value(); + context = effectHandlerResult.context(); + + context = getCurrentEffectHandler(effectHandler, source, context); + + context = context.addInstruction(new PatioMemoryStore( + closurePointer, + layout, + CLOSURE_EFFECT_HANDLER_OFFSET, + effectHandler, + source + )); + + context = storeFreeVariables( + freeVariables, + closurePointer, + CLOSURE_FREE_VARIABLES_OFFSET, + layout, + source, + context + ); + + context = context.addInstruction(new PatioDataPtrToInt( + target, + closurePointer, + source + )); + + return context; + } + + public static PatioCodeGenContext resume( + PatioOperand value, + Source source, + PatioCodeGenContext context + ) { + context = context.addInstruction(new PatioCall( + Optional.empty(), + SET_HANDLER_DECLARATION.name(), + List.of(context.stashedEffectHandler()), + source + )); + + context = context.addInstruction(new PatioResume(value, source)); + + return context; + } + + private static PatioCodeGenContext getCurrentEffectHandler( + PatioLocalRef target, + Source source, + PatioCodeGenContext context + ) { + return context.addInstruction(new PatioCall( + Optional.of(target), + GET_HANDLER_DECLARATION.name(), + List.of(), + source + )); + } + private static PatioDataLayout userDefinedEffectLayout( UserDefinedEffect effect, PatioCodeGenContext context diff --git a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java index 14ba666c..1fd6a5fd 100644 --- a/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java +++ b/src/main/java/org/shedlang/compiler/patio/PatioCodeGenLocalSpace.java @@ -8,7 +8,9 @@ public enum PatioCodeGenLocalSpace { CLOSURE_POINTER("closurePointer"), CLOSURE_FUNCTION_POINTER("closureFunctionPointer"), EFFECT_HANDLER("effectHandler"), + EFFECT_HANDLER_CURRENT("effectHandlerCurrent"), EFFECT_HANDLER_METHOD("effectHandlerMethod"), + EFFECT_HANDLER_METHOD_POINTER("effectHandlerMethodPointer"), MAIN("main"), NAMESPACE_IS_INITED_POINTER("namespaceIsInitedPointer"), NAMESPACE_POINTER("namespacePointer"), -- cgit v1.2.3