From 1c7179aa39f7df9b4a050d9a1968a0271cd68e6d Mon Sep 17 00:00:00 2001 From: Serkan Muhcu Date: Wed, 4 Dec 2024 11:09:03 +0100 Subject: [PATCH] Use noalias on StackValues (#704) Co-authored-by: Philipp Schuster --- .../effekt/generator/llvm/PrettyPrinter.scala | 4 +- .../effekt/generator/llvm/Transformer.scala | 56 +++++++----------- libraries/llvm/rts.ll | 57 ++++++++++++++----- 3 files changed, 66 insertions(+), 51 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 5e42c1525..a166d46a0 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/PrettyPrinter.scala @@ -72,12 +72,12 @@ ${indentedLines(instructions.map(show).mkString("\n"))} C.abort(s"tail call to non-void function returning: $tpe") case Load(result, tpe, LocalReference(PointerType(), name)) => - s"${localName(result)} = load ${show(tpe)}, ${show(LocalReference(PointerType(), name))}" + s"${localName(result)} = load ${show(tpe)}, ${show(LocalReference(PointerType(), name))}, !noalias !2" case Load(_, _, operand) => C.abort(s"WIP: loading anything but local references not yet implemented: $operand") // TODO [jfrech, 2022-07-26] Why does `Load` explicitly check for a local reference and `Store` does not? case Store(address, value) => - s"store ${show(value)}, ${show(address)}" + s"store ${show(value)}, ${show(address)}, !noalias !2" case GetElementPtr(result, tpe, ptr @ LocalReference(_, name), i :: is) => s"${localName(result)} = getelementptr ${show(tpe)}, ${show(ptr)}, i64 $i" + is.map(", i32 " + _).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 d57ab7f25..5cb66a27b 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -217,8 +217,7 @@ object Transformer { emit(Call(name, Ccc(), referenceType, newReference, List(getStack()))) shareValues(environment, freeVariables(rest)); - pushEnvironmentOnto(getStack(), environment); - pushReturnAddressOnto(getStack(), returnAddressName, sharer, eraser); + pushFrameOnto(getStack(), environment, returnAddressName, sharer, eraser); transform(rest) @@ -262,6 +261,7 @@ object Transformer { val parameters = frame.parameters.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } defineLabel(returnAddressName, parameters) { emit(Comment(s"pushFrame / return address, ${frameEnvironment.length} free variables")) + emit(Call("", Ccc(), VoidType(), ConstantGlobal("assumeFrameHeaderWasPopped"), List(getStack()))) popEnvironmentFrom(getStack(), frameEnvironment); // eraseValues(frameEnvironment, frameEnvironment) (unnecessary) eraseValues(frame.parameters, freeVariables(frame.body)) @@ -273,8 +273,7 @@ object Transformer { val eraser = getEraser(frameEnvironment, StackFrameEraser) shareValues(frameEnvironment, freeVariables(rest)); - pushEnvironmentOnto(getStack(), frameEnvironment); - pushReturnAddressOnto(getStack(), returnAddressName, sharer, eraser); + pushFrameOnto(getStack(), frameEnvironment, returnAddressName, sharer, eraser); transform(rest) @@ -318,8 +317,7 @@ object Transformer { shareValues(frameEnvironment, freeVariables(rest)); - pushEnvironmentOnto(getStack(), frameEnvironment); - pushReturnAddressOnto(getStack(), returnAddressName, sharer, eraser); + pushFrameOnto(getStack(), frameEnvironment, returnAddressName, sharer, eraser); transform(rest) @@ -582,15 +580,24 @@ object Transformer { } } - def pushEnvironmentOnto(stack: Operand, environment: machine.Environment)(using ModuleContext, FunctionContext, BlockContext): Unit = { - if (environment.isEmpty) { - () - } else { - val stackPointer = LocalReference(stackPointerType, freshName("stackPointer")); - val size = ConstantInt(environmentSize(environment)); - emit(Call(stackPointer.name, Ccc(), stackPointer.tpe, stackAllocate, List(stack, size))); - storeEnvironmentAt(stackPointer, environment); - } + 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); + emit(Call(stackPointer.name, Ccc(), stackPointer.tpe, stackAllocate, List(stack, size))); + + val frameType = StructureType(List(environmentType(environment), frameHeaderType)); + storeEnvironmentAt(stackPointer, environment); + + 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 = { @@ -671,25 +678,6 @@ object Transformer { }.map(emit) } - def pushReturnAddressOnto(stack: Operand, returnAddressName: String, sharer: Operand, eraser: Operand)(using ModuleContext, FunctionContext, BlockContext): Unit = { - - val stackPointer = LocalReference(stackPointerType, freshName("stackPointer")); - // TODO properly find size - val size = ConstantInt(24); - emit(Call(stackPointer.name, Ccc(), stackPointer.tpe, stackAllocate, List(stack, size))); - - val returnAddressPointer = LocalReference(PointerType(), freshName("returnAddress_pointer")); - emit(GetElementPtr(returnAddressPointer.name, frameHeaderType, stackPointer, List(0, 0))); - val sharerPointer = LocalReference(PointerType(), freshName("sharer_pointer")); - emit(GetElementPtr(sharerPointer.name, frameHeaderType, stackPointer, List(0, 1))); - val eraserPointer = LocalReference(PointerType(), freshName("eraser_pointer")); - emit(GetElementPtr(eraserPointer.name, frameHeaderType, stackPointer, List(0, 2))); - - emit(Store(returnAddressPointer, ConstantGlobal(returnAddressName))); - emit(Store(sharerPointer, sharer)); - emit(Store(eraserPointer, eraser)); - } - def popReturnAddressFrom(stack: Operand, returnAddressName: String)(using ModuleContext, FunctionContext, BlockContext): Unit = { val stackPointer = LocalReference(stackPointerType, freshName("stackPointer")); diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 301b7b543..f69fd3dac 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -111,6 +111,7 @@ declare double @llvm.ceil.f64(double) declare double @llvm.floor.f64(double) declare void @print(i64) declare void @exit(i64) +declare void @llvm.assume(i1) ; Prompts @@ -258,20 +259,19 @@ define private %StackPointer @stackAllocate(%Stack %stack, i64 %n) { %stackPointer_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 0 %limit_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 2 - %currentStackPointer = load %StackPointer, ptr %stackPointer_pointer - %limit = load %Limit, ptr %limit_pointer - + %currentStackPointer = load %StackPointer, ptr %stackPointer_pointer, !alias.scope !2 + %limit = load %Limit, ptr %limit_pointer, !alias.scope !2 %nextStackPointer = getelementptr i8, %StackPointer %currentStackPointer, i64 %n %isInside = icmp ule %StackPointer %nextStackPointer, %limit br i1 %isInside, label %continue, label %realloc continue: - store %StackPointer %nextStackPointer, ptr %stackPointer_pointer + store %StackPointer %nextStackPointer, ptr %stackPointer_pointer, !alias.scope !2 ret %StackPointer %currentStackPointer realloc: %base_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 1 - %base = load %Base, ptr %base_pointer + %base = load %Base, ptr %base_pointer, !alias.scope !2 %intStackPointer = ptrtoint %StackPointer %currentStackPointer to i64 %intBase = ptrtoint %Base %base to i64 @@ -285,31 +285,48 @@ realloc: %newStackPointer = getelementptr i8, %Base %newBase, i64 %size %newNextStackPointer = getelementptr i8, %StackPointer %newStackPointer, i64 %n - store %StackPointer %newNextStackPointer, ptr %stackPointer_pointer - store %Base %newBase, ptr %base_pointer - store %Limit %newLimit, ptr %limit_pointer + store %StackPointer %newNextStackPointer, ptr %stackPointer_pointer, !alias.scope !2 + store %Base %newBase, ptr %base_pointer, !alias.scope !2 + store %Limit %newLimit, ptr %limit_pointer, !alias.scope !2 ret %StackPointer %newStackPointer } define private %StackPointer @stackDeallocate(%Stack %stack, i64 %n) { %stackPointer_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 0 - %stackPointer = load %StackPointer, ptr %stackPointer_pointer + %stackPointer = load %StackPointer, ptr %stackPointer_pointer, !alias.scope !2 + + %limit_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 2 + %limit = load %Limit, ptr %limit_pointer, !alias.scope !2 + %isInside = icmp ule %StackPointer %stackPointer, %limit + call void @llvm.assume(i1 %isInside) %o = sub i64 0, %n %newStackPointer = getelementptr i8, %StackPointer %stackPointer, i64 %o - store %StackPointer %newStackPointer, ptr %stackPointer_pointer + store %StackPointer %newStackPointer, ptr %stackPointer_pointer, !alias.scope !2 ret %StackPointer %newStackPointer } -define i64 @nextPowerOfTwo(i64 %x) { +define private i64 @nextPowerOfTwo(i64 %x) { %leadingZeros = call i64 @llvm.ctlz.i64(i64 %x, i1 false) %numBits = sub i64 64, %leadingZeros %result = shl i64 1, %numBits ret i64 %result } +define private void @assumeFrameHeaderWasPopped(%Stack %stack) alwaysinline { + %stackPointer_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 0 + %stackPointer = load %StackPointer, ptr %stackPointer_pointer, !alias.scope !2 + %oldStackPointer = getelementptr %FrameHeader, %StackPointer %stackPointer, i64 1 + + %limit_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 1, i32 2 + %limit = load %Limit, ptr %limit_pointer, !alias.scope !2 + %isInside = icmp ule %StackPointer %oldStackPointer, %limit + call void @llvm.assume(i1 %isInside) + ret void +} + ; Meta-stack management define private %Memory @newMemory() { @@ -374,7 +391,7 @@ update: ret void } -define void @displace(%Stack %stack, %Stack %end) { +define private void @displace(%Stack %stack, %Stack %end) { %prompt_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 2 %next_pointer = getelementptr %StackValue, %Stack %stack, i64 0, i32 3 %prompt = load %Prompt, ptr %prompt_pointer @@ -393,7 +410,7 @@ continue: ret void } -define %Stack @resume(%Resumption %resumption, %Stack %oldStack) { +define private %Stack @resume(%Resumption %resumption, %Stack %oldStack) { %uniqueResumption = call %Resumption @uniqueStack(%Resumption %resumption) %rest_pointer = getelementptr %StackValue, %Resumption %uniqueResumption, i64 0, i32 3 %start = load %Stack, ptr %rest_pointer @@ -423,7 +440,7 @@ define private void @eraseMemory(%Memory %memory) { ret void } -define void @erasePrompt(%Prompt %prompt) alwaysinline { +define private void @erasePrompt(%Prompt %prompt) alwaysinline { %referenceCount_pointer = getelementptr %PromptValue, %Prompt %prompt, i64 0, i32 0 %referenceCount = load %ReferenceCount, ptr %referenceCount_pointer switch %ReferenceCount %referenceCount, label %decrement [%ReferenceCount 0, label %free] @@ -438,7 +455,7 @@ free: ret void } -define void @sharePrompt(%Prompt %prompt) alwaysinline { +define private void @sharePrompt(%Prompt %prompt) alwaysinline { %referenceCount_pointer = getelementptr %PromptValue, %Prompt %prompt, i64 0, i32 0 %referenceCount = load %ReferenceCount, ptr %referenceCount_pointer %newReferenceCount = add %ReferenceCount %referenceCount, 1 @@ -755,3 +772,13 @@ define void @run_Pos(%Neg %f, %Pos %argument) { tail call tailcc %Pos %functionPointer(%Object %object, %Evidence 0, %Pos %argument, %Stack %stack) ret void } + + +; Scope domains +!0 = !{!"types"} + +; Scopes +!1 = !{!"stackValues", !0} + +; Scope lists +!2 = !{!1}