Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local Variables #584

Merged
merged 5 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down
6 changes: 6 additions & 0 deletions effekt/shared/src/main/scala/effekt/machine/Analysis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
9 changes: 4 additions & 5 deletions effekt/shared/src/main/scala/effekt/machine/Transformer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -255,17 +255,16 @@ 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) =>
val stateType = transform(tpe)
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) =>
Expand All @@ -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))))
}
Expand Down
15 changes: 15 additions & 0 deletions effekt/shared/src/main/scala/effekt/machine/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
51 changes: 51 additions & 0 deletions libraries/llvm/rts.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, we now try to use the convention Foo_pointer. So this will be stackPointer_pointer

%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) {
Expand Down