Skip to content
Open
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 @@ -76,21 +76,12 @@ object Transformer {
def transform(statement: machine.Statement)(using ModuleContext, FunctionContext, BlockContext): Terminator =
statement match {

case machine.Jump(label) =>
case machine.Jump(label, arguments) =>
emit(Comment(s"jump ${label.name}"))
shareValues(label.environment, Set())

val arguments = label.environment.map(transform)
emit(callLabel(transform(label), arguments))
shareValues(arguments, Set())
emit(callLabel(transform(label), arguments.map(transform)))
RetVoid()

case machine.Substitute(bindings, rest) =>
emit(Comment("substitution"))
bindings.foreach { (from, to) => emit(Comment(s"substitution [${from.name} !-> ${to.name}]")) }
withBindings(bindings) { () =>
transform(rest)
}

case machine.Construct(variable, tag, values, rest) =>
emit(Comment(s"construct ${variable.name}, tag ${tag}, ${values.length} values"))
val fields = produceObject("fields", values, freeVariables(rest))
Expand Down Expand Up @@ -410,7 +401,7 @@ object Transformer {
}

def transform(value: machine.Variable)(using FunctionContext): Operand =
substitute(value) match {
value match {
case machine.Variable(name, tpe) => LocalReference(transform(tpe), name)
}

Expand Down Expand Up @@ -671,10 +662,10 @@ object Transformer {
values match {
case Nil => ()
case value :: values =>
if values.map(substitute).contains(substitute(value)) then {
if values.contains(value) then {
shareValue(value);
loop(values)
} else if freeInBody.map(substitute).contains(substitute(value)) then {
} else if freeInBody.contains(value) then {
shareValue(value);
loop(values)
} else {
Expand All @@ -687,7 +678,7 @@ object Transformer {

def eraseValues(environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Unit =
environment.foreach { value =>
if !freeInBody.map(substitute).contains(substitute(value)) then eraseValue(value)
if !freeInBody.contains(value) then eraseValue(value)
}

def shareValue(value: machine.Variable)(using FunctionContext, BlockContext): Unit = {
Expand Down Expand Up @@ -767,24 +758,12 @@ object Transformer {
}

class FunctionContext() {
var substitution: Map[machine.Variable, machine.Variable] = Map();
var basicBlocks: List[BasicBlock] = List();
}

def emit(basicBlock: BasicBlock)(using C: FunctionContext) =
C.basicBlocks = C.basicBlocks :+ basicBlock

def withBindings[R](bindings: List[(machine.Variable, machine.Variable)])(prog: () => R)(using C: FunctionContext): R = {
val substitution = C.substitution;
C.substitution = substitution ++ bindings.map { case (variable -> value) => (variable -> substitution.getOrElse(value, value) ) }.toMap;
val result = prog();
C.substitution = substitution
result
}

def substitute(value: machine.Variable)(using C: FunctionContext): machine.Variable =
C.substitution.toMap.getOrElse(value, value)

class BlockContext() {
var stack: Operand = LocalReference(stackType, "stack");
var instructions: List[Instruction] = List();
Expand Down
6 changes: 2 additions & 4 deletions effekt/shared/src/main/scala/effekt/machine/Analysis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ def freeVariables(taggedClause: (Int, Clause)): Set[Variable] = freeVariables(ta

def freeVariables(statement: Statement): Set[Variable] =
statement match {
case Jump(Label(_, environment)) =>
environment.toSet
case Substitute(bindings, rest) =>
freeVariables(rest).map { variable => bindings.toMap.getOrElse(variable, variable) }
case Jump(_, arguments) =>
arguments.toSet
case Construct(name, tag, values, rest) =>
Set.from(values) ++ (freeVariables(rest) -- Set(name))
case Switch(value, clauses, default: Option[Clause]) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,8 @@ object PrettyPrinter extends ParenPrettyPrinter {

def toDocStmts(stmt: Statement): Doc = stmt match {

case Jump(label) =>
"jump" <+> label

case Substitute(bindings, rest) =>
hsep(bindings map { case (left, right) => left <+> ":=" <+> right }, comma) <> ";" <> line <> toDocStmts(rest)
case Jump(label, arguments) =>
"jump" <+> label <> parens(arguments map toDoc)

case Construct(name, tag, arguments, rest) =>
"let" <+> name <+> "=" <+> tag.toString <> parens(arguments map toDoc) <> ";" <> line <> toDocStmts(rest)
Expand Down
82 changes: 41 additions & 41 deletions effekt/shared/src/main/scala/effekt/machine/Transformer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package effekt
package machine

import effekt.context.Context
import effekt.core.substitutions.{ Substitution, substitute }
import effekt.core.{ Block, DeclarationContext, Toplevel, Id, given }
import effekt.symbols.{ Symbol, TermSymbol }
import effekt.symbols.builtins.TState
Expand Down Expand Up @@ -95,7 +96,7 @@ object Transformer {
case core.Variable.Block(id, core.Type.TRegion, capt) =>
Set(Variable(transform(id), Type.Prompt()))

// Coercsions are blocks and can be free, but do not have info.
// Coercions are blocks and can be free, but do not have info.
case core.Variable.Block(id, _, _) if id.name.name.startsWith("@coerce") =>
Set.empty

Expand Down Expand Up @@ -130,24 +131,27 @@ object Transformer {
getBlockInfo(other) match {
case BlockInfo.Definition(free, params) =>
noteDefinition(id, free, params)
emitDefinition(transformLabel(id), Jump(transformLabel(other)))
val label = transformLabel(id)
emitDefinition(label, Jump(transformLabel(other), label.environment))
transform(rest)
case BlockInfo.Parameter(_) =>
noteParameter(id, tpe)
Substitute(List(Variable(transform(id), transform(tpe)) -> Variable(transform(other), transform(tpe))), transform(rest))
transform(substitute(rest)(using Substitution(Map(), Map(), Map(), Map(id -> core.BlockVar(other, tpe, capt)))))
}

case core.Def(id, block @ core.Unbox(pure), rest) =>
case core.Def(id, block @ core.Unbox(expr), rest) =>
noteParameter(id, block.tpe)
transform(pure).run { boxed =>
transform(expr).run { boxed =>
Coerce(Variable(transform(id), Type.Negative()), boxed, transform(rest))
}

case core.Let(id, tpe, binding, rest) =>
transform(binding).run { value =>
// TODO consider passing the environment to [[transform]] instead of explicit substitutions here.
// TODO it is important that we use the inferred [[binding.tpe]] and not the annotated type [[tpe]], but why?
Substitute(List(Variable(transform(id), transform(binding.tpe)) -> value), transform(rest))
case core.Let(id, tpe, core.ValueVar(otherId, otherTpe), rest) =>
transform(substitute(rest)(using Substitution(Map(), Map(), Map(id -> core.ValueVar(otherId, otherTpe)), Map())))

case core.Let(id, tpe, expr, rest) =>
// TODO this needs to be expr.tpe and not tpe, but why?
transformNamed(Variable(transform(id), transform(expr.tpe)), expr).run { _ =>
transform(rest)
}

case s @ core.ImpureApp(id, core.BlockVar(blockName: symbols.ExternFunction, _, capt), targs, vargs, bargs, rest) =>
Expand Down Expand Up @@ -179,18 +183,13 @@ object Transformer {

// Known Jump
case BlockInfo.Definition(freeParams, blockParams) =>
val label = machine.Label(transform(id), blockParams ++ freeParams)
Substitute(label.environment.zip(values ++ blocks), Jump(label))
Jump(Label(transform(id), blockParams ++ freeParams), values ++ blocks ++ freeParams)

case _ => ErrorReporter.panic("Applying an object")
}

case Block.BlockLit(tparams, cparams, vparams, bparams, body) =>
// TODO subsitute targs for tparams
noteParameters(bparams)
val valueNames = vparams.map(transform).zip(values)
val blockNames = bparams.map(transform).zip(blocks)
Substitute(valueNames ++ blockNames, transform(body))
transform(substitute(Block.BlockLit(tparams, cparams, vparams, bparams, body), targs, vargs, bargs))

case Block.Unbox(pure) =>
transform(pure).run { boxedCallee =>
Expand All @@ -212,8 +211,9 @@ object Transformer {
transform(vargs, bargs).run { (values, blocks) =>
callee match {
case Block.BlockVar(id, tpe, capt) if BPC.globals contains id =>
val label = BPC.globals(id)
val variable = Variable(freshName("receiver"), transform(tpe))
PushFrame(Clause(List(variable), Invoke(variable, opTag, values ++ blocks)), Jump(BPC.globals(id)))
PushFrame(Clause(List(variable), Invoke(variable, opTag, values ++ blocks)), Jump(label, label.environment))

case Block.BlockVar(id, tpe, capt) =>
Invoke(Variable(transform(id), transform(tpe)), opTag, values ++ blocks)
Expand Down Expand Up @@ -331,19 +331,20 @@ object Transformer {

def transformBlockArg(block: core.Block)(using BPC: BlocksParamsContext, DC: DeclarationContext, E: ErrorReporter): Binding[Variable] = block match {
case core.BlockVar(id, tpe, _) if BPC.globals contains id =>
val label = BPC.globals(id)
val variable = Variable(transform(id), transform(tpe))
shift { k =>
PushFrame(Clause(List(variable), k(variable)), Jump(BPC.globals(id)))
PushFrame(Clause(List(variable), k(variable)), Jump(label, label.environment))
}
case core.BlockVar(id, tpe, capt) => getBlockInfo(id) match {
case BlockInfo.Definition(_, parameters) =>
// Passing a top-level function directly, so we need to eta-expand turning it into a closure
// TODO cache the closure somehow to prevent it from being created on every call
val label = transformLabel(id)
val variable = Variable(freshName(id.name.name ++ "$closure"), Negative())
shift { k =>
New(variable, List(Clause(parameters,
// conceptually: Substitute(parameters zip parameters, Jump(...)) but the Substitute is a no-op here
Jump(transformLabel(id))
Jump(label, label.environment)
)), k(variable))
}
case BlockInfo.Parameter(tpe) =>
Expand All @@ -369,57 +370,60 @@ object Transformer {
}

def transform(expr: core.Expr)(using BC: BlocksParamsContext, DC: DeclarationContext, E: ErrorReporter): Binding[Variable] = expr match {

case core.ValueVar(id, tpe) if BC.globals contains id =>
val variable = Variable(freshName("run"), transform(tpe))
val label = BC.globals(id)
val variable = Variable(freshName("x"), transform(expr.tpe))
shift { k =>
// TODO this might introduce too many pushes.
PushFrame(Clause(List(variable), k(variable)),
Substitute(Nil, Jump(BC.globals(id))))
Jump(label, label.environment))
}

case core.ValueVar(id, tpe) =>
pure(Variable(transform(id), transform(tpe)))
case _ =>
transformNamed(Variable(freshName("x"), transform(expr.tpe)), expr)
}

/**
Must not be called on an expression that is a variable.
*/
def transformNamed(variable: Variable, expr: core.Expr)(using BC: BlocksParamsContext, DC: DeclarationContext, E: ErrorReporter): Binding[Variable] = expr match {

case core.ValueVar(id, tpe) =>
ErrorReporter.panic(s"Must not be called on an expression that is a variable $expr.")

case core.Literal((), _) =>
val variable = Variable(freshName("unitLiteral"), Positive());
shift { k =>
Construct(variable, builtins.Unit, List(), k(variable))
}

case core.Literal(value: Long, _) =>
val variable = Variable(freshName("longLiteral"), Type.Int());
shift { k =>
LiteralInt(variable, value, k(variable))
}

// for characters
case core.Literal(value: Int, _) =>
val variable = Variable(freshName("intLiteral"), Type.Int());
shift { k =>
LiteralInt(variable, value, k(variable))
}

case core.Literal(value: Boolean, _) =>
val variable = Variable(freshName("booleanLiteral"), Positive())
shift { k =>
Construct(variable, if (value) builtins.True else builtins.False, List(), k(variable))
}

case core.Literal(v: Double, _) =>
val literal_binding = Variable(freshName("doubleLiteral"), Type.Double());
shift { k =>
LiteralDouble(literal_binding, v, k(literal_binding))
LiteralDouble(variable, v, k(variable))
}

case core.Literal(javastring: String, _) =>
val literal_binding = Variable(freshName("utf8StringLiteral"), builtins.StringType);
shift { k =>
LiteralUTF8String(literal_binding, javastring.getBytes("utf-8"), k(literal_binding))
LiteralUTF8String(variable, javastring.getBytes("utf-8"), k(variable))
}

case core.PureApp(core.BlockVar(blockName, tpe: core.BlockType.Function, _), List(), List(arg)) if blockName.name.name.startsWith("@coerce") =>
val variable = Variable(freshName("coerceApp"), transform(tpe.result))
transform(arg).flatMap { value =>
shift { k =>
Coerce(variable, value, k(variable))
Expand All @@ -429,7 +433,6 @@ object Transformer {
case core.PureApp(core.BlockVar(blockName: symbols.ExternFunction, tpe: core.BlockType.Function, capt), targs, vargs) =>
if (targs.exists(requiresBoxing)) { ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") }

val variable = Variable(freshName("pureApp"), transform(tpe.result))
transform(vargs, Nil).flatMap { (values, blocks) =>
shift { k =>
ForeignCall(variable, transform(blockName), values ++ blocks, k(variable))
Expand All @@ -439,7 +442,6 @@ object Transformer {
case core.Make(data, constructor, targs, vargs) =>
if (targs.exists(requiresBoxing)) { ErrorReporter.abort(s"Types ${targs} are used as type parameters but would require boxing.") }

val variable = Variable(freshName("make"), transform(data));
val tag = DeclarationContext.getConstructorTag(constructor)

transform(vargs, Nil).flatMap { (values, blocks) =>
Expand All @@ -451,8 +453,7 @@ object Transformer {
case core.Box(block, annot) =>
transformBlockArg(block).flatMap { unboxed =>
shift { k =>
val boxed = Variable(freshName(unboxed.name), Type.Positive())
Coerce(boxed, unboxed, k(boxed))
Coerce(variable, unboxed, k(variable))
}
}

Expand Down Expand Up @@ -543,10 +544,9 @@ object Transformer {
/**
* Extra info in context
*/

class BlocksParamsContext() {
var info: Map[Symbol, BlockInfo] = Map.empty
var globals: Map[Id, Label] = Map.empty
var info: Map[Symbol, BlockInfo] = Map()
var globals: Map[Id, Label] = Map()
var definitions: List[Definition] = List.empty
}

Expand Down
20 changes: 3 additions & 17 deletions effekt/shared/src/main/scala/effekt/machine/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,8 @@ type Environment = List[Variable]
* Here l is the label and [[environment]] is the list of free variables of s1.
* It thus can be understood as the type of the label.
*/
case class Label(name: String, environment: Environment)
case class Label(name: String, environment: Environment) // TODO delete environment

/**
* Applying a substitution
*
* List(x -> y)
*
* will replace all occurrences of x by y. Here x is a binding occurrence
* and y is a bound occurrence.
*/
type Substitution = List[(Variable, Variable)]

type Tag = Int

Expand Down Expand Up @@ -114,14 +105,9 @@ case class Definition(label: Label, body: Statement)
enum Statement {

/**
* e.g. jump l
*/
case Jump(label: Label)

/**
* e.g. s[x1 -> v1, ...]
* e.g. jump l (v1, ...)
*/
case Substitute(bindings: Substitution, rest: Statement)
case Jump(label: Label, arguments: Environment)

/**
* e.g. let x = make C(v1, ...); s
Expand Down
Loading