From 5858b73066af16306ab12c6613a0c2d50d67bf7a Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Tue, 10 Sep 2024 16:29:01 +0200 Subject: [PATCH 1/5] Add Machine statements for local variables --- .../src/main/scala/effekt/machine/Tree.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/effekt/shared/src/main/scala/effekt/machine/Tree.scala b/effekt/shared/src/main/scala/effekt/machine/Tree.scala index 9740443fd..7fae0c520 100644 --- a/effekt/shared/src/main/scala/effekt/machine/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/machine/Tree.scala @@ -158,6 +158,21 @@ enum Statement { */ case Store(reference: Variable, value: Variable, rest: Statement) + /** + * e.g. var x = 42; s + */ + case Var(name: Variable, init: Variable, returnType: Type, rest: Statement) + + /** + * e.g. let y = loadVar(x); s + */ + case LoadVar(name: Variable, ref: Variable, rest: Statement) + + /** + * e.g. storeVar(x, 42); s + */ + case StoreVar(ref: Variable, value: Variable, rest: Statement) + /** * e.g. push { (x, ...) => s }; s */ From 4c6cc9662cb3821d390214a4b66735c296a4512f Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Tue, 10 Sep 2024 16:29:49 +0200 Subject: [PATCH 2/5] Translate Local variables to special stack frames --- .../effekt/generator/llvm/Transformer.scala | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 29717d617..8df91c9df 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -242,6 +242,90 @@ object Transformer { shareValues(List(value), freeVariables(rest)) transform(rest) + case machine.Var(ref @ machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => + val environment = List(init) + val returnAddressName = freshName("returnAddress") + val returnValue = freshName("returnValue") + val returnType = transform(retType) + val parameters = List(Parameter(returnType, returnValue)) + + defineLabel(returnAddressName, parameters) { + emit(Comment(s"var $name / return address")) + popEnvironmentFrom(getStack(), environment) + eraseValue(init) + val nextReturn = LocalReference(returnAddressType, freshName("returnAddress")) + popReturnAddressFrom(getStack(), nextReturn.name) + emit(callLabel(nextReturn, List(LocalReference(returnType, returnValue)))) + RetVoid() + } + + val sharerName = freshName("sharer"); + defineFunction(sharerName, List(Parameter(stackPointerType, "stackPointer"))) { + emit(Comment(s"sharer, ${environment.length} free variables")) + + val nextStackPointer = LocalReference(stackPointerType, freshName("stackPointer")); + emit(GetElementPtr(nextStackPointer.name, environmentType(environment), LocalReference(stackPointerType, "stackPointer"), List(-1))); + loadEnvironmentAt(nextStackPointer, environment); + + shareValues(environment, Set.from(environment)); + emit(Call("_", Ccc(), VoidType(), shareFrames, List(nextStackPointer))); + RetVoid() + } + + val eraserName = freshName("eraser"); + defineFunction(eraserName, List(Parameter(stackPointerType, "stackPointer"))) { + emit(Comment(s"eraser, ${environment.length} free variables")) + + val nextStackPointer = LocalReference(stackPointerType, freshName("stackPointer")); + emit(GetElementPtr(nextStackPointer.name, environmentType(environment), LocalReference(stackPointerType, "stackPointer"), List(-1))); + loadEnvironmentAt(nextStackPointer, environment); + + eraseValues(environment, Set()); + emit(Call("_", Ccc(), VoidType(), eraseFrames, List(nextStackPointer))); + RetVoid() + } + + emit(Call(name, Ccc(), referenceType, newReference, List(getStack()))) + + shareValues(environment, freeVariables(rest)); + pushEnvironmentOnto(getStack(), environment); + pushReturnAddressOnto(getStack(), returnAddressName, sharerName, eraserName); + + transform(rest) + + case machine.Var(_, _, _, _) => ??? + + case machine.LoadVar(name, ref, rest) => + emit(Comment(s"loadvar ${name.name}, reference ${ref.name}")) + + val ptr = freshName(name.name + "_pointer"); + val ptrRef = LocalReference(PointerType(), ptr) + emit(Call(ptr, Ccc(), PointerType(), getVarPointer, List(transform(ref), getStack()))) + + // TODO why do we need this? + val oldVal = machine.Variable(freshName(ref.name + "_old"), name.tpe) + emit(Load(oldVal.name, transform(oldVal.tpe), ptrRef)) + shareValue(oldVal) + + emit(Load(name.name, transform(name.tpe), ptrRef)) + eraseValues(List(name), freeVariables(rest)) + transform(rest) + + case machine.StoreVar(ref, value, rest) => + emit(Comment(s"storevar ${ref.name}, value ${value.name}")) + + val ptr = freshName(ref.name + "pointer"); + val ptrRef = LocalReference(PointerType(), ptr) + emit(Call(ptr, Ccc(), PointerType(), getVarPointer, List(transform(ref), getStack()))) + + val oldVal = machine.Variable(freshName(ref.name + "_old"), value.tpe) + emit(Load(oldVal.name, transform(oldVal.tpe), ptrRef)) + eraseValue(oldVal) + + emit(Store(ptrRef, transform(value))) + shareValues(List(value), freeVariables(rest)) + transform(rest) + case machine.PushFrame(frame, rest) => val frameEnvironment = freeVariables(frame).toList; @@ -744,6 +828,9 @@ object Transformer { def alloc = ConstantGlobal(PointerType(), "alloc") def getPointer = ConstantGlobal(PointerType(), "getPointer") + def newReference = ConstantGlobal(PointerType(), "newReference") + def getVarPointer = ConstantGlobal(PointerType(), "getVarPointer") + def newStack = ConstantGlobal(PointerType(), "newStack"); def pushStack = ConstantGlobal(PointerType(), "pushStack"); def popStacks = ConstantGlobal(PointerType(), "popStacks"); From d6c2e5f4e2eda9912099eaab049161604b69f19e Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Tue, 10 Sep 2024 16:30:44 +0200 Subject: [PATCH 3/5] Generate local vars in machine transformer --- .../src/main/scala/effekt/machine/Transformer.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala index 4746c4023..70c3eab7d 100644 --- a/effekt/shared/src/main/scala/effekt/machine/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/machine/Transformer.scala @@ -255,9 +255,8 @@ object Transformer { val prompt = Variable(freshName("prompt"), Type.Prompt()) transform(init).run { value => - CurrentPrompt(prompt, - Allocate(reference, value, prompt, - transform(body))) + Var(reference, value, transform(body.tpe), + transform(body)) } case core.Get(id, capt, tpe) => @@ -265,7 +264,7 @@ object Transformer { val reference = Variable(transform(id), Type.Reference(stateType)) val variable = Variable(freshName("get"), stateType) - Load(variable, reference, + LoadVar(variable, reference, Return(List(variable))) case core.Put(id, capt, arg) => @@ -274,7 +273,7 @@ object Transformer { val variable = Variable(freshName("put"), Positive()) transform(arg).run { value => - Store(reference, value, + StoreVar(reference, value, Construct(variable, builtins.Unit, List(), Return(List(variable)))) } From 2b783f8badaa82f97633559b40b7132cecdd97b5 Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Tue, 10 Sep 2024 16:31:18 +0200 Subject: [PATCH 4/5] Helper functions in rts --- libraries/llvm/rts.ll | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index b60a2349a..7059220c4 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -308,6 +308,57 @@ define ptr @getPointer(%Reference %reference, i64 %index, %Stack %stack) { ret ptr %pointer } +define %Stack @getStack(%Stack %stack, %Prompt %prompt) { + %promptPointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 3 + %thisPrompt = load %Prompt, ptr %promptPointer + %found = icmp eq %Prompt %prompt, %thisPrompt + br i1 %found, label %done, label %recurse + +done: + ret %Stack %stack + +recurse: + %nextStackPointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 4 + %nextStack = load %Stack, ptr %nextStackPointer + %result = call %Stack @getStack(%Stack %nextStack, %Prompt %prompt) + ret %Stack %result +} + +define ptr @getVarPointer(%Reference %reference, %Stack %stack) { + %prompt32 = extractvalue %Reference %reference, 0 + %offset32 = extractvalue %Reference %reference, 1 + %prompt = zext i32 %prompt32 to i64 + %offset = zext i32 %offset32 to i64 + + %targetStack = call %Stack @getStack(%Stack %stack, %Prompt %prompt) + %basePointer = getelementptr %StackValue, %Stack %targetStack, i64 0, i32 1, i32 1 + %base = load %Base, ptr %basePointer + %varPointer = getelementptr i8, %Base %base, i64 %offset + ret ptr %varPointer +} + +define %Reference @newReference(%Stack %stack) alwaysinline { + %stackPointerPointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 0 + %basePointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 1 + + %stackPointer = load %StackPointer, ptr %stackPointerPointer + %base = load %StackPointer, ptr %basePointer + + %intStack = ptrtoint %StackPointer %stackPointer to i64 + %intBase = ptrtoint %StackPointer %base to i64 + + %offset = sub i64 %intStack, %intBase + %offset32 = trunc i64 %offset to i32 + + %prompt = call %Prompt @currentPrompt(%Stack %stack) + %prompt32 = trunc %Prompt %prompt to i32 + + %reference..1 = insertvalue %Reference undef, i32 %prompt32, 0 + %reference = insertvalue %Reference %reference..1, i32 %offset32, 1 + + ret %Reference %reference +} + ; Stack management define %StackPointer @stackAllocate(%Stack %stack, i64 %n) { From c45bcdfce5ce210c896fc999c43946716292aba1 Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Tue, 10 Sep 2024 16:32:27 +0200 Subject: [PATCH 5/5] Remaining functions for new statements --- .../shared/src/main/scala/effekt/machine/Analysis.scala | 6 ++++++ .../src/main/scala/effekt/machine/PrettyPrinter.scala | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/effekt/shared/src/main/scala/effekt/machine/Analysis.scala b/effekt/shared/src/main/scala/effekt/machine/Analysis.scala index 9993f3e83..9b8113331 100644 --- a/effekt/shared/src/main/scala/effekt/machine/Analysis.scala +++ b/effekt/shared/src/main/scala/effekt/machine/Analysis.scala @@ -35,6 +35,12 @@ def freeVariables(statement: Statement): Set[Variable] = Set(ref) ++ freeVariables(rest) -- Set(name) case Store(ref, value, rest) => Set(ref, value) ++ freeVariables(rest) + case Var(name, init, tpe, rest) => + freeVariables(rest) ++ Set(init) -- Set(name) + case LoadVar(name, ref, rest) => + Set(ref) ++ freeVariables(rest) -- Set(name) + case StoreVar(ref, value, rest) => + Set(ref, value) ++ freeVariables(rest) case PushFrame(frame, rest) => freeVariables(frame) ++ freeVariables(rest) case Return(values) => diff --git a/effekt/shared/src/main/scala/effekt/machine/PrettyPrinter.scala b/effekt/shared/src/main/scala/effekt/machine/PrettyPrinter.scala index d8a7fb31a..60019ff7b 100644 --- a/effekt/shared/src/main/scala/effekt/machine/PrettyPrinter.scala +++ b/effekt/shared/src/main/scala/effekt/machine/PrettyPrinter.scala @@ -69,6 +69,15 @@ object PrettyPrinter extends ParenPrettyPrinter { case Store(reference, value, rest) => "store" <> parens(List(reference, value) map toDoc) <> ";" <> line <> toDoc(rest) + case Var(name, init, _, rest) => + "var" <+> name <+> "=" <+> toDoc(init) <> ";" <> line <> toDoc(rest) + + case LoadVar(name, reference, rest) => + "let" <+> name <+> "=" <+> "loadVar" <> parens(toDoc(reference)) <> ";" <> line <> toDoc(rest) + + case StoreVar(reference, value, rest) => + "storeVar" <> parens(List(reference, value) map toDoc) <> ";" <> line <> toDoc(rest) + case PushFrame(frame, rest) => "push" <+> toDoc(frame) <> ";" <> line <> toDoc(rest)