Skip to content

Commit 4f3ab7e

Browse files
authored
Transform machine to direct style, if possible (#826)
Machine doesn't receive as much attention as it should. One reason is that I cannot read it. This PR tries to fix this. On top, as a first improvement it also adds a phase that performs contification (transforming functions to continuations)
1 parent f7d7276 commit 4f3ab7e

File tree

3 files changed

+139
-19
lines changed

3 files changed

+139
-19
lines changed

effekt/shared/src/main/scala/effekt/Compiler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ trait Compiler[Executable] {
304304
lazy val Machine = Phase("machine") {
305305
case CoreTransformed(source, tree, mod, core) =>
306306
val main = Context.checkMain(mod)
307-
(mod, main, machine.Transformer.transform(main, core))
307+
val program = machine.Transformer.transform(main, core)
308+
val direct = machine.DirectStyle.rewrite(program)
309+
(mod, main, direct)
308310
}
309311

310312
// Helpers
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package effekt
2+
package machine
3+
4+
object DirectStyle {
5+
6+
private var last = 0
7+
private def fresh() = { last +=1; s"l_${last}" }
8+
9+
def rewrite(p: Program): Program =
10+
val Program(decl, stmt) = p
11+
Program(decl, rewrite(stmt))
12+
13+
def rewrite(s: Statement): Statement = s match {
14+
case Statement.PushFrame(Clause(parameters, body), rest) =>
15+
val rewrittenBody = rewrite(body)
16+
val rewrittenRest = rewrite(rest)
17+
18+
if canBeDirect(rewrittenRest) then
19+
val env: Environment = analysis.freeVariables(rewrittenBody).filterNot { v => parameters.contains(v) }.toList
20+
val l = Label(fresh(), env ++ parameters)
21+
Def(l, rewrittenBody, toDirectStyle(rest, env, parameters, l))
22+
else
23+
PushFrame(Clause(parameters, rewrittenBody), rewrittenRest)
24+
25+
// Congruences
26+
case Statement.Def(label, body, rest) => Def(label, rewrite(body), rewrite(rest))
27+
case Statement.Jump(label) => Jump(label)
28+
case Statement.Substitute(bindings, rest) => Substitute(bindings, rewrite(rest))
29+
case Statement.Construct(name, tag, arguments, rest) => Construct(name, tag, arguments, rewrite(rest))
30+
case Statement.Switch(scrutinee, clauses, default) =>
31+
Switch(scrutinee,
32+
clauses.map { case (idx, Clause(env, body)) => (idx, Clause(env, rewrite(body))) },
33+
default.map { case Clause(env, body) => Clause(env, rewrite(body)) })
34+
case Statement.New(name, operations, rest) => New(name, operations, rewrite(rest))
35+
case Statement.Invoke(receiver, tag, arguments) => Invoke(receiver, tag, arguments)
36+
case Statement.Var(name, init, returnType, rest) => Var(name, init, returnType, rewrite(rest))
37+
case Statement.LoadVar(name, ref, rest) => LoadVar(name, ref, rewrite(rest))
38+
case Statement.StoreVar(ref, value, rest) => StoreVar(ref, value, rewrite(rest))
39+
case Statement.Return(arguments) => Return(arguments)
40+
case Statement.Reset(name, frame, rest) => Reset(name, frame, rewrite(rest))
41+
case Statement.Resume(stack, rest) => Resume(stack, rewrite(rest))
42+
case Statement.Shift(name, prompt, rest) => Shift(name, prompt, rewrite(rest))
43+
case Statement.ForeignCall(name, builtin, arguments, rest) => ForeignCall(name, builtin, arguments, rewrite(rest))
44+
case Statement.LiteralInt(name, value, rest) => LiteralInt(name, value, rewrite(rest))
45+
case Statement.LiteralDouble(name, value, rest) => LiteralDouble(name, value, rewrite(rest))
46+
case Statement.LiteralUTF8String(name, utf8, rest) => LiteralUTF8String(name, utf8, rewrite(rest))
47+
case Statement.Hole => Hole
48+
}
49+
50+
def toDirectStyle(stmt: Statement, env: Environment, params: Environment, label: Label): Statement = stmt match {
51+
case Statement.Return(arguments) => Substitute(env.map(x => x -> x) ++ params.zip(arguments), Jump(label))
52+
case Statement.Hole => stmt
53+
case Statement.Jump(label) => stmt
54+
case Statement.Invoke(receiver, tag, arguments) => stmt
55+
56+
case Statement.PushFrame(frame, rest) => stmt
57+
58+
case Statement.Reset(name, frame, rest) => Reset(name, frame, rewrite(rest))
59+
case Statement.Resume(stack, rest) => Resume(stack, rewrite(rest))
60+
case Statement.Shift(name, prompt, rest) => Shift(name, prompt, rewrite(rest))
61+
62+
// Congruences
63+
case Statement.Def(label2, body, rest) => Def(label2, body, toDirectStyle(rest, env, params, label))
64+
case Statement.Substitute(bindings, rest) => Substitute(bindings, toDirectStyle(rest, env, params, label))
65+
case Statement.Construct(name, tag, arguments, rest) => Construct(name, tag, arguments, toDirectStyle(rest, env, params, label))
66+
case Statement.Switch(scrutinee, clauses, default) =>
67+
Switch(scrutinee,
68+
clauses.map { case (idx, Clause(env, body)) => (idx, Clause(env, toDirectStyle(body, env, params, label))) },
69+
default.map { case Clause(env, body) => Clause(env, toDirectStyle(body, env, params, label)) })
70+
case Statement.New(name, operations, rest) => New(name, operations, toDirectStyle(rest, env, params, label))
71+
case Statement.Var(name, init, returnType, rest) => Var(name, init, returnType, toDirectStyle(rest, env, params, label))
72+
case Statement.LoadVar(name, ref, rest) => LoadVar(name, ref, toDirectStyle(rest, env, params, label))
73+
case Statement.StoreVar(ref, value, rest) => StoreVar(ref, value, toDirectStyle(rest, env, params, label))
74+
case Statement.ForeignCall(name, builtin, arguments, rest) => ForeignCall(name, builtin, arguments, toDirectStyle(rest, env, params, label))
75+
case Statement.LiteralInt(name, value, rest) => LiteralInt(name, value, toDirectStyle(rest, env, params, label))
76+
case Statement.LiteralDouble(name, value, rest) => LiteralDouble(name, value, toDirectStyle(rest, env, params, label))
77+
case Statement.LiteralUTF8String(name, utf8, rest) => LiteralUTF8String(name, utf8, toDirectStyle(rest, env, params, label))
78+
}
79+
80+
def canBeDirect(stmt: Statement): Boolean = stmt match {
81+
case Statement.Return(arguments) => true
82+
case Statement.Hole => true
83+
84+
case Statement.Jump(label) => false
85+
case Statement.Invoke(receiver, tag, arguments) => false
86+
case Statement.PushFrame(frame, rest) => false
87+
88+
case Statement.Reset(name, frame, rest) => false // canBeDirect(rest) // ???
89+
case Statement.Resume(stack, rest) => false // canBeDirect(rest) // ???
90+
case Statement.Shift(name, prompt, rest) => false // canBeDirect(rest) // ???
91+
92+
// Congruences
93+
case Statement.Def(label, body, rest) => canBeDirect(rest)
94+
case Statement.Substitute(bindings, rest) => canBeDirect(rest)
95+
case Statement.Construct(name, tag, arguments, rest) => canBeDirect(rest)
96+
case Statement.Switch(scrutinee, clauses, default) =>
97+
clauses.forall { case (idx, Clause(_, body)) => canBeDirect(body) } && default.forall { case Clause(_, body) => canBeDirect(body) }
98+
case Statement.New(name, operations, rest) => canBeDirect(rest)
99+
case Statement.Var(name, init, returnType, rest) => canBeDirect(rest)
100+
case Statement.LoadVar(name, ref, rest) => canBeDirect(rest)
101+
case Statement.StoreVar(ref, value, rest) => canBeDirect(rest)
102+
case Statement.ForeignCall(name, builtin, arguments, rest) => canBeDirect(rest)
103+
case Statement.LiteralInt(name, value, rest) => canBeDirect(rest)
104+
case Statement.LiteralDouble(name, value, rest) => canBeDirect(rest)
105+
case Statement.LiteralUTF8String(name, utf8, rest) => canBeDirect(rest)
106+
}
107+
}

effekt/shared/src/main/scala/effekt/machine/PrettyPrinter.scala

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object PrettyPrinter extends ParenPrettyPrinter {
1717
implicit def toDoc(v: Label): Doc = string(v.name)
1818

1919
def toDoc(clause: Clause): Doc = clause match {
20-
case Clause(parameters, body) => braces(space <> toDoc(parameters) <+> "=>" <> group(nest(line <> toDoc(body)) <> line))
20+
case Clause(parameters, body) => braces(space <> toDoc(parameters) <+> "=>" <> group(nest(line <> toDocStmts(body)) <> line))
2121
}
2222

2323
def toDoc(e: Environment): Doc = parens(e map {
@@ -35,65 +35,74 @@ object PrettyPrinter extends ParenPrettyPrinter {
3535
case Type.Reference(tpe) => toDoc(tpe) <> "*"
3636
}
3737

38-
def toDoc(stmt: Statement): Doc = stmt match {
38+
def toDoc(stmt: Statement): Doc =
39+
stmt match {
40+
// terminators that do not require a block to be readable:
41+
case _ : (Return | Jump | Invoke | Switch | Reset | Shift) => toDocStmts(stmt)
42+
case other => block(toDocStmts(stmt))
43+
}
44+
45+
46+
def toDocStmts(stmt: Statement): Doc = stmt match {
3947
case Def(label, body, rest) =>
40-
"def" <+> label <+> "=" <+> block(toDoc(body)) <> ";" <> line <> toDoc(rest)
48+
"def" <+> label.name <> toDoc(label.environment) <+> "=" <+> block(toDocStmts(body)) <> ";" <> line <> toDocStmts(rest)
4149

4250
case Jump(label) =>
4351
"jump" <+> label
4452

4553
case Substitute(bindings, rest) =>
46-
"subst" <+> brackets(bindings map { case (left, right) => left <+> "!->" <+> right }) <> ";" <> line <> toDoc(rest)
54+
hsep(bindings map { case (left, right) => left <+> ":=" <+> right }, comma) <> ";" <> line <> toDocStmts(rest)
4755

4856
case Construct(name, tag, arguments, rest) =>
49-
"let" <+> name <+> "=" <+> tag.toString <> parens(arguments map toDoc) <> ";" <> line <> toDoc(rest)
57+
"let" <+> name <+> "=" <+> tag.toString <> parens(arguments map toDoc) <> ";" <> line <> toDocStmts(rest)
5058

5159
case Switch(scrutinee, clauses, default) =>
5260
val cls = clauses.map { case (idx, cl) => idx.toString <+> ":" <+> toDoc(cl) }
5361
val d = default.map(d => space <> "else" <+> toDoc(d)).getOrElse(emptyDoc)
5462
"switch" <+> scrutinee <+> line <> indent(vcat(cls)) <> d
5563

5664
case New(name, operations, rest) =>
57-
"let" <+> name <+> "=" <+> "new" <+> block(operations map toDoc) <> ";" <> line <> toDoc(rest)
65+
"let" <+> name <+> "=" <+> "new" <+> block(operations map toDoc) <> ";" <> line <> toDocStmts(rest)
5866

5967
case Invoke(receiver, tag, arguments) =>
6068
"invoke" <+> receiver <> "." <> tag.toString <> parens(arguments map toDoc)
6169

6270
case Var(name, init, _, rest) =>
63-
"var" <+> name <+> "=" <+> toDoc(init) <> ";" <> line <> toDoc(rest)
71+
"var" <+> name <+> "=" <+> toDoc(init) <> ";" <> line <> toDocStmts(rest)
6472

6573
case LoadVar(name, reference, rest) =>
66-
"let" <+> name <+> "=" <+> "loadVar" <> parens(toDoc(reference)) <> ";" <> line <> toDoc(rest)
74+
"let" <+> name <+> "=" <+> "loadVar" <> parens(toDoc(reference)) <> ";" <> line <> toDocStmts(rest)
6775

6876
case StoreVar(reference, value, rest) =>
69-
"storeVar" <> parens(List(reference, value) map toDoc) <> ";" <> line <> toDoc(rest)
77+
"storeVar" <> parens(List(reference, value) map toDoc) <> ";" <> line <> toDocStmts(rest)
7078

71-
case PushFrame(frame, rest) =>
72-
"push" <+> toDoc(frame) <> ";" <> line <> toDoc(rest)
79+
case PushFrame(Clause(parameters, body), rest) =>
80+
"val" <+> toDoc(parameters) <+> "=" <+> toDoc(rest) <> ";" <> line <>
81+
toDocStmts(body)
7382

7483
case Return(arguments) =>
7584
"return" <+> hsep(arguments map toDoc, ",")
7685

7786
case Reset(prompt, frame, rest) =>
78-
"let" <+> prompt <+> "=" <+> "reset" <+> toDoc(frame) <> ";" <> line <> toDoc(rest)
87+
"reset" <+> braces(prompt <+> "=>" <+> nest(line <> toDocStmts(rest)))
7988

8089
case Resume(stack, rest) =>
81-
"resume" <+> stack <> ";" <> line <> toDoc(rest)
90+
"resume" <+> stack <> ";" <> line <> toDocStmts(rest)
8291

8392
case Shift(name, prompt, rest) =>
84-
"let" <+> name <+> "=" <+> "shift0p" <+> prompt <> ";" <> line <> toDoc(rest)
93+
"shift" <+> prompt <+> braces(name <+> "=>" <+> nest(line <> toDocStmts(rest)))
8594

8695
case ForeignCall(name, builtin, arguments, rest) =>
87-
"let" <+> name <+> "=" <+> builtin <> parens(arguments map toDoc) <> ";" <> line <> toDoc(rest)
96+
"let" <+> name <+> "=" <+> builtin <> parens(arguments map toDoc) <> ";" <> line <> toDocStmts(rest)
8897

8998
case LiteralInt(name, value, rest) =>
90-
"let" <+> name <+> "=" <+> value.toString <> ";" <> line <> toDoc(rest)
99+
"let" <+> name <+> "=" <+> value.toString <> ";" <> line <> toDocStmts(rest)
91100

92101
case LiteralDouble(name, value, rest) =>
93-
"let" <+> name <+> "=" <+> value.toString <> ";" <> line <> toDoc(rest)
102+
"let" <+> name <+> "=" <+> value.toString <> ";" <> line <> toDocStmts(rest)
94103

95104
case LiteralUTF8String(name, utf8, rest) =>
96-
"let" <+> name <+> "=" <+> ("\"" + (utf8.map { b => "\\" + f"$b%02x" }).mkString + "\"") <> ";" <> line <> toDoc(rest)
105+
"let" <+> name <+> "=" <+> ("\"" + (utf8.map { b => "\\" + f"$b%02x" }).mkString + "\"") <> ";" <> line <> toDocStmts(rest)
97106

98107
case Hole => "<>"
99108
}
@@ -104,6 +113,8 @@ object PrettyPrinter extends ParenPrettyPrinter {
104113

105114
def brackets(docs: List[Doc]): Doc = brackets(hsep(docs, comma))
106115

116+
def braces(docs: List[Doc]): Doc = braces(hsep(docs, comma))
117+
107118
def block(content: Doc): Doc = braces(nest(line <> content) <> line)
108119

109120
def block(docs: List[Doc]): Doc = block(vsep(docs, line))

0 commit comments

Comments
 (0)