From 3f7c77c121cf9afbf6b3996934b76c34919be51c Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Thu, 12 Dec 2024 17:13:51 +0100 Subject: [PATCH 1/3] Attach sharer and eraser to return functions --- .../effekt/generator/llvm/PrettyPrinter.scala | 8 ++++- .../effekt/generator/llvm/Transformer.scala | 36 +++++++++---------- .../scala/effekt/generator/llvm/Tree.scala | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala index a166d46a0..22d16737b 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala @@ -12,7 +12,13 @@ object PrettyPrinter { definitions.map(show).mkString("\n\n") def show(definition: Definition)(using C: Context): LLVMString = definition match { - case Function(callingConvention, returnType, name, parameters, basicBlocks) => + case Function(callingConvention, returnType, name, parameters, Some(prefix), basicBlocks) => + s""" +define ${show(callingConvention)} ${show(returnType)} ${globalName(name)}(${commaSeparated(parameters.map(show))}) prefix ${show(prefix)} { + ${indentedLines(basicBlocks.map(show).mkString)} +} +""" + case Function(callingConvention, returnType, name, parameters, None, basicBlocks) => s""" define ${show(callingConvention)} ${show(returnType)} ${globalName(name)}(${commaSeparated(parameters.map(show))}) { ${indentedLines(basicBlocks.map(show).mkString)} 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 476ad2329..43f5390ea 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -31,10 +31,10 @@ object Transformer { val transitionJump = Call("_", Tailcc(false), VoidType(), ConstantGlobal("effektMainTailcc"), List()) val transitionBlock = BasicBlock("transition", List(transitionJump), RetVoid()) - val transitionFunction = Function(Ccc(), VoidType(), "effektMain", List(), List(transitionBlock)) + val transitionFunction = Function(Ccc(), VoidType(), "effektMain", List(), None, List(transitionBlock)) val entryBlock = BasicBlock("entry", instructions, terminator) - val effektMain = Function(Tailcc(true), VoidType(), "effektMainTailcc", List(), entryBlock :: basicBlocks) + val effektMain = Function(Tailcc(true), VoidType(), "effektMainTailcc", List(), None, entryBlock :: basicBlocks) declarations.map(transform) ++ definitions :+ transitionFunction :+ effektMain } @@ -197,11 +197,14 @@ object Transformer { case machine.Var(ref @ machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => val environment = List(init) + val sharer = getSharer(environment, StackFrameSharer) + val eraser = getEraser(environment, StackFrameEraser) + val returnAddressName = freshName("returnAddress") val returnType = transform(retType) val returnValue = freshName("returnValue") val parameters = List(Parameter(returnType, returnValue)) - defineLabel(returnAddressName, parameters) { + defineLabel(returnAddressName, parameters, Some(ConstantArray(PointerType(), List(sharer, eraser)))) { emit(Comment(s"var $name / return address")) popEnvironmentFrom(getStack(), environment) eraseValue(init) @@ -211,9 +214,6 @@ object Transformer { RetVoid() } - val sharer = getSharer(environment, StackFrameSharer) - val eraser = getEraser(environment, StackFrameEraser) - emit(Call(name, Ccc(), referenceType, newReference, List(getStack()))) shareValues(environment, freeVariables(rest)); @@ -257,9 +257,12 @@ object Transformer { case machine.PushFrame(frame, rest) => val frameEnvironment = freeVariables(frame).toList; + val sharer = getSharer(frameEnvironment, StackFrameSharer) + val eraser = getEraser(frameEnvironment, StackFrameEraser) + val returnAddressName = freshName("returnAddress"); val parameters = frame.parameters.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } - defineLabel(returnAddressName, parameters) { + defineLabel(returnAddressName, parameters, Some(ConstantArray(PointerType(), List(sharer, eraser)))) { emit(Comment(s"pushFrame / return address, ${frameEnvironment.length} free variables")) emit(Call("", Ccc(), VoidType(), ConstantGlobal("assumeFrameHeaderWasPopped"), List(getStack()))) popEnvironmentFrom(getStack(), frameEnvironment); @@ -269,9 +272,6 @@ object Transformer { transform(frame.body); } - val sharer = getSharer(frameEnvironment, StackFrameSharer) - val eraser = getEraser(frameEnvironment, StackFrameEraser) - shareValues(frameEnvironment, freeVariables(rest)); pushFrameOnto(getStack(), frameEnvironment, returnAddressName, sharer, eraser); @@ -297,9 +297,12 @@ object Transformer { val frameEnvironment = freeVariables(frame).toList; + val sharer = getSharer(frameEnvironment, StackSharer) + val eraser = getEraser(frameEnvironment, StackEraser) + val returnAddressName = freshName("returnAddress"); val parameters = frame.parameters.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } - defineLabel(returnAddressName, parameters) { + defineLabel(returnAddressName, parameters, Some(ConstantArray(PointerType(), List(sharer, eraser)))) { emit(Comment(s"Reset / return address, ${frameEnvironment.length} free variables")) popEnvironmentFrom(getStack(), frameEnvironment); // eraseValues(frameEnvironment, frameEnvironment) (unnecessary) @@ -312,9 +315,6 @@ object Transformer { transform(frame.body); } - val sharer = getSharer(frameEnvironment, StackSharer) - val eraser = getEraser(frameEnvironment, StackEraser) - shareValues(frameEnvironment, freeVariables(rest)); pushFrameOnto(getStack(), frameEnvironment, returnAddressName, sharer, eraser); @@ -435,7 +435,7 @@ object Transformer { case machine.Type.Reference(_) => 16 } - def defineFunction(name: String, parameters: List[Parameter])(prog: (FunctionContext, BlockContext) ?=> Terminator): ModuleContext ?=> Unit = { + def defineFunction(name: String, parameters: List[Parameter], prefix: Option[Operand] = None)(prog: (FunctionContext, BlockContext) ?=> Terminator): ModuleContext ?=> Unit = { implicit val FC = FunctionContext(); implicit val BC = BlockContext(); @@ -445,12 +445,12 @@ object Transformer { val instructions = BC.instructions; BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); - val function = Function(Ccc(), VoidType(), name, parameters, entryBlock :: basicBlocks); + val function = Function(Ccc(), VoidType(), name, parameters, prefix, entryBlock :: basicBlocks); emit(function) } - def defineLabel(name: String, parameters: List[Parameter])(prog: (FunctionContext, BlockContext) ?=> Terminator): ModuleContext ?=> Unit = { + def defineLabel(name: String, parameters: List[Parameter], prefix: Option[Operand] = None)(prog: (FunctionContext, BlockContext) ?=> Terminator): ModuleContext ?=> Unit = { implicit val FC = FunctionContext(); implicit val BC = BlockContext(); @@ -460,7 +460,7 @@ object Transformer { val instructions = BC.instructions; BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); - val function = Function(Tailcc(true), VoidType(), name, parameters :+ Parameter(stackType, "stack"), entryBlock :: basicBlocks); + val function = Function(Tailcc(true), VoidType(), name, parameters :+ Parameter(stackType, "stack"), prefix, entryBlock :: basicBlocks); emit(function) } diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Tree.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Tree.scala index 4dfdbb198..908805c1d 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Tree.scala @@ -7,7 +7,7 @@ package llvm * see: https://hackage.haskell.org/package/llvm-hs-pure-9.0.0/docs/LLVM-AST.html#t:Definition */ enum Definition { - case Function(callingConvention: CallingConvention, returnType: Type, name: String, parameters: List[Parameter], basicBlocks: List[BasicBlock]) + case Function(callingConvention: CallingConvention, returnType: Type, name: String, parameters: List[Parameter], prefix: Option[Operand], basicBlocks: List[BasicBlock]) case VerbatimFunction(callingConvention: CallingConvention, returnType: Type, name: String, parameters: List[Parameter], body: String) case Verbatim(content: String) case GlobalConstant(name: String, initializer: Operand) // initializer should be constant From 3f6d3cdf63b14b5c23a601c7a37b640029ff8653 Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Thu, 12 Dec 2024 17:38:06 +0100 Subject: [PATCH 2/3] Remove sharer and eraser from FrameHeader --- .../effekt/generator/llvm/Transformer.scala | 10 ++----- libraries/llvm/rts.ll | 30 ++++++++----------- 2 files changed, 14 insertions(+), 26 deletions(-) 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 43f5390ea..73d651e3f 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -583,7 +583,7 @@ object Transformer { def pushFrameOnto(stack: Operand, environment: machine.Environment, returnAddressName: String, sharer: Operand, eraser: Operand)(using ModuleContext, FunctionContext, BlockContext) = { val stackPointer = LocalReference(stackPointerType, freshName("stackPointer")); - val size = ConstantInt(environmentSize(environment) + 24); + val size = ConstantInt(environmentSize(environment) + 8); emit(Call(stackPointer.name, Ccc(), stackPointer.tpe, stackAllocate, List(stack, size))); val frameType = StructureType(List(environmentType(environment), frameHeaderType)); @@ -591,14 +591,8 @@ object Transformer { val returnAddressPointer = LocalReference(PointerType(), freshName("returnAddress_pointer")); emit(GetElementPtr(returnAddressPointer.name, frameType, stackPointer, List(0, 1, 0))); - val sharerPointer = LocalReference(PointerType(), freshName("sharer_pointer")); - emit(GetElementPtr(sharerPointer.name, frameType, stackPointer, List(0, 1, 1))); - val eraserPointer = LocalReference(PointerType(), freshName("eraser_pointer")); - emit(GetElementPtr(eraserPointer.name, frameType, stackPointer, List(0, 1, 2))); emit(Store(returnAddressPointer, ConstantGlobal(returnAddressName))); - emit(Store(sharerPointer, sharer)); - emit(Store(eraserPointer, eraser)); } def popEnvironmentFrom(stack: Operand, environment: machine.Environment)(using ModuleContext, FunctionContext, BlockContext): Unit = { @@ -683,7 +677,7 @@ object Transformer { val stackPointer = LocalReference(stackPointerType, freshName("stackPointer")); // TODO properly find size - val size = ConstantInt(24); + val size = ConstantInt(8); emit(Call(stackPointer.name, Ccc(), stackPointer.tpe, stackDeallocate, List(stack, size))); val returnAddressPointer = LocalReference(PointerType(), freshName("returnAddress_pointer")); diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index cd3fb0d8a..6d23c8762 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -53,7 +53,7 @@ %Base = type %StackPointer %Limit = type %StackPointer %ReturnAddress = type ptr -%FrameHeader = type { %ReturnAddress, %Sharer, %Eraser } +%FrameHeader = type { %ReturnAddress } ; Pointers for a heap allocated stack %Memory = type { %StackPointer, %Base, %Limit } @@ -502,10 +502,6 @@ define private %Stack @underflowStack(%Stack %stack) { ret %Stack %rest } -define private void @nop(%Stack %stack) { - ret void -} - define private %Memory @copyMemory(%Memory %memory) alwaysinline { %stackPointer = extractvalue %Memory %memory, 0 %base = extractvalue %Memory %memory, 1 @@ -658,23 +654,25 @@ done: define private void @shareFrames(%StackPointer %stackPointer) alwaysinline { %newStackPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 -1 - %stackSharer = getelementptr %FrameHeader, %StackPointer %newStackPointer, i64 0, i32 1 - %sharer = load %Sharer, ptr %stackSharer + %returnAdress = load %ReturnAddress, ptr %newStackPointer + %sharer_pointer = getelementptr ptr, ptr %returnAdress, i64 -2 + %sharer = load %Sharer, ptr %sharer_pointer tail call void %sharer(%StackPointer %newStackPointer) ret void } define private void @eraseFrames(%StackPointer %stackPointer) alwaysinline { %newStackPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 -1 - %stackEraser = getelementptr %FrameHeader, %StackPointer %newStackPointer, i64 0, i32 2 - %eraser = load %Eraser, ptr %stackEraser + %returnAdress = load %ReturnAddress, ptr %newStackPointer + %eraser_pointer = getelementptr ptr, ptr %returnAdress, i64 -1 + %eraser = load %Eraser, ptr %eraser_pointer tail call void %eraser(%StackPointer %newStackPointer) ret void } ; RTS initialization -define private tailcc void @topLevel(%Pos %val, %Stack %stack) { +define private tailcc void @topLevel(%Pos %val, %Stack %stack) prefix { %Sharer, %Eraser } { %Sharer @topLevelSharer, %Eraser @topLevelEraser } { %rest = call %Stack @underflowStack(%Stack %stack) ; rest holds global variables call void @eraseStack(%Stack %rest) @@ -693,6 +691,10 @@ define private void @topLevelEraser(%Environment %environment) { @global = private global { i64, %Stack } { i64 0, %Stack null } +define private void @nop(%Stack %stack) prefix { %Sharer, %Eraser } { %Sharer @nop, %Eraser @free } { + ret void +} + define private %Stack @withEmptyStack() { %globals = call %Stack @reset(%Stack null) @@ -700,12 +702,8 @@ define private %Stack @withEmptyStack() { %globalsStackPointer = load %StackPointer, ptr %globalsStackPointer_pointer %returnAddressPointer.0 = getelementptr %FrameHeader, %StackPointer %globalsStackPointer, i64 0, i32 0 - %sharerPointer.0 = getelementptr %FrameHeader, %StackPointer %globalsStackPointer, i64 0, i32 1 - %eraserPointer.0 = getelementptr %FrameHeader, %StackPointer %globalsStackPointer, i64 0, i32 2 store ptr @nop, ptr %returnAddressPointer.0 - store ptr @nop, ptr %sharerPointer.0 - store ptr @free, ptr %eraserPointer.0 %globalsStackPointer_2 = getelementptr %FrameHeader, %StackPointer %globalsStackPointer, i64 1 store %StackPointer %globalsStackPointer_2, ptr %globalsStackPointer_pointer @@ -719,12 +717,8 @@ define private %Stack @withEmptyStack() { %stackPointer = load %StackPointer, ptr %stackStackPointer %returnAddressPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 0, i32 0 - %sharerPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 0, i32 1 - %eraserPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 0, i32 2 store %ReturnAddress @topLevel, ptr %returnAddressPointer - store %Sharer @topLevelSharer, ptr %sharerPointer - store %Eraser @topLevelEraser, ptr %eraserPointer %stackPointer_2 = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 1 store %StackPointer %stackPointer_2, ptr %stackStackPointer From f5852e502141f53ae68e70867518faa865d5fe0c Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Thu, 9 Jan 2025 16:07:07 +0100 Subject: [PATCH 3/3] store vtables in function prefix --- .../effekt/generator/llvm/Transformer.scala | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) 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 73d651e3f..751f87821 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -155,10 +155,12 @@ object Transformer { val closureEnvironment = freeVariables(clauses).toList; emit(Comment(s"new ${variable.name}, ${clauses.length} clauses, ${closureEnvironment.size} free variables")) - val clauseNames = clauses.map { clause => - val clauseName = freshName(variable.name + "_clause"); + val names = List.range(0, clauses.length).map(i => freshName(variable.name + i)) + val vtable = ConstantArray(PointerType(), names.drop(1).reverse.map(ConstantGlobal(_))) // these are indexed in reverse + val clauseNames = clauses.zip(names).map { (clause, clauseName) => val parameters = clause.parameters.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } - defineLabel(clauseName, Parameter(objectType, "closure") +: parameters) { + val prefix = if (clause == clauses.head) then Some(vtable) else None + defineLabel(clauseName, Parameter(objectType, "closure") +: parameters, prefix) { emit(Comment(s"new ${clauseName}, ${clause.parameters.length} parameters")) consumeObject(LocalReference(objectType, "closure"), closureEnvironment, freeVariables(clause)); eraseValues(clause.parameters, freeVariables(clause.body)); @@ -167,13 +169,10 @@ object Transformer { ConstantGlobal(clauseName) } - val vtableName = freshName("vtable") - emit(GlobalConstant(vtableName, ConstantArray(methodType, clauseNames))) - - val vtable = produceObject("closure", closureEnvironment, freeVariables(rest)); + val environment = produceObject("closure", closureEnvironment, freeVariables(rest)); val temporaryName = freshName("vtable_temporary"); - emit(InsertValue(temporaryName, ConstantAggregateZero(negativeType), ConstantGlobal(vtableName), 0)); - emit(InsertValue(variable.name, LocalReference(negativeType, temporaryName), vtable, 1)); + emit(InsertValue(temporaryName, ConstantAggregateZero(negativeType), names.headOption.map(ConstantGlobal(_)).getOrElse(ConstantNull(PointerType())), 0)); + emit(InsertValue(variable.name, LocalReference(negativeType, temporaryName), environment, 1)); eraseValues(List(variable), freeVariables(rest)); transform(rest) @@ -182,17 +181,22 @@ object Transformer { emit(Comment(s"invoke ${value.name}, tag ${tag}, ${values.length} values")) shareValues(value :: values, Set()); - val vtableName = freshName("vtable"); + val function0 = freshName("function0Pointer"); val objectName = freshName("closure"); val pointerName = freshName("functionPointer_pointer"); val functionName = freshName("functionPointer"); val arguments = values.map(transform) - emit(ExtractValue(vtableName, transform(value), 0)); + emit(ExtractValue(function0, transform(value), 0)); emit(ExtractValue(objectName, transform(value), 1)); - emit(GetElementPtr(pointerName, methodType, LocalReference(PointerType(), vtableName), List(tag))) - emit(Load(functionName, methodType, LocalReference(PointerType(), pointerName))) - emit(callLabel(LocalReference(methodType, functionName), LocalReference(objectType, objectName) +: arguments)) + if (tag == 0) { + emit(callLabel(LocalReference(methodType, function0), LocalReference(objectType, objectName) +: arguments)) + } + else { + emit(GetElementPtr(pointerName, methodType, LocalReference(PointerType(), function0), List(-tag))) + emit(Load(functionName, methodType, LocalReference(PointerType(), pointerName))) + emit(callLabel(LocalReference(methodType, functionName), LocalReference(objectType, objectName) +: arguments)) + } RetVoid() case machine.Var(ref @ machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) =>