Skip to content

Commit

Permalink
Local Variables (#584)
Browse files Browse the repository at this point in the history
  • Loading branch information
b-studios authored Sep 13, 2024
2 parents 4e81699 + c45bcdf commit 19089d3
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 5 deletions.
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
%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

0 comments on commit 19089d3

Please sign in to comment.