Skip to content

Commit ed31425

Browse files
Go via trampoline in machine transformer (#917)
Closes #855 --------- Co-authored-by: Marvin Borner <[email protected]>
1 parent e40eee0 commit ed31425

File tree

2 files changed

+35
-19
lines changed

2 files changed

+35
-19
lines changed

.github/actions/run-effekt-tests/action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ runs:
3434
timeout_minutes: ${{ inputs.retry-timeout }}
3535
max_attempts: ${{ inputs.retry-max-attempts }}
3636
retry_on: error
37-
command: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt -J-Xss1G clean test
37+
command: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt clean test
3838
new_command_on_retry: sbt testQuick
3939

4040
- name: Run full test suite without retry
4141
if: ${{ inputs.full-test == 'true' && runner.os != 'Windows' && inputs.use-retry != 'true' }}
42-
run: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt -J-Xss1G clean test
42+
run: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt clean test
4343
shell: bash
4444

4545
- name: Assemble fully optimized js file

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

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import effekt.symbols.{ Symbol, TermSymbol }
77
import effekt.symbols.builtins.TState
88
import effekt.util.messages.ErrorReporter
99
import effekt.symbols.ErrorMessageInterpolator
10+
import scala.annotation.tailrec
1011

1112

1213
object Transformer {
@@ -315,15 +316,15 @@ object Transformer {
315316
def transformBlockArg(block: core.Block)(using BPC: BlocksParamsContext, DC: DeclarationContext, E: ErrorReporter): Binding[Variable] = block match {
316317
case core.BlockVar(id, tpe, _) if BPC.globals contains id =>
317318
val variable = Variable(transform(id), transform(tpe))
318-
Binding { k =>
319+
shift { k =>
319320
PushFrame(Clause(List(variable), k(variable)), Jump(BPC.globals(id)))
320321
}
321322
case core.BlockVar(id, tpe, capt) => getBlockInfo(id) match {
322323
case BlockInfo.Definition(_, parameters) =>
323324
// Passing a top-level function directly, so we need to eta-expand turning it into a closure
324325
// TODO cache the closure somehow to prevent it from being created on every call
325326
val variable = Variable(freshName(id.name.name ++ "$closure"), Negative())
326-
Binding { k =>
327+
shift { k =>
327328
New(variable, List(Clause(parameters,
328329
// conceptually: Substitute(parameters zip parameters, Jump(...)) but the Substitute is a no-op here
329330
Jump(transformLabel(id))
@@ -337,13 +338,13 @@ object Transformer {
337338
noteParameters(bparams)
338339
val parameters = vparams.map(transform) ++ bparams.map(transform);
339340
val variable = Variable(freshName("blockLit"), Negative())
340-
Binding { k =>
341+
shift { k =>
341342
New(variable, List(Clause(parameters, transform(body))), k(variable))
342343
}
343344

344345
case core.New(impl) =>
345346
val variable = Variable(freshName("new"), Negative())
346-
Binding { k =>
347+
shift { k =>
347348
New(variable, transform(impl), k(variable))
348349
}
349350

@@ -355,7 +356,7 @@ object Transformer {
355356

356357
case core.ValueVar(id, tpe) if BC.globals contains id =>
357358
val variable = Variable(freshName("run"), transform(tpe))
358-
Binding { k =>
359+
shift { k =>
359360
// TODO this might introduce too many pushes.
360361
PushFrame(Clause(List(variable), k(variable)),
361362
Substitute(Nil, Jump(BC.globals(id))))
@@ -366,38 +367,38 @@ object Transformer {
366367

367368
case core.Literal((), _) =>
368369
val variable = Variable(freshName("unitLiteral"), Positive());
369-
Binding { k =>
370+
shift { k =>
370371
Construct(variable, builtins.Unit, List(), k(variable))
371372
}
372373

373374
case core.Literal(value: Long, _) =>
374375
val variable = Variable(freshName("longLiteral"), Type.Int());
375-
Binding { k =>
376+
shift { k =>
376377
LiteralInt(variable, value, k(variable))
377378
}
378379

379380
// for characters
380381
case core.Literal(value: Int, _) =>
381382
val variable = Variable(freshName("intLiteral"), Type.Int());
382-
Binding { k =>
383+
shift { k =>
383384
LiteralInt(variable, value, k(variable))
384385
}
385386

386387
case core.Literal(value: Boolean, _) =>
387388
val variable = Variable(freshName("booleanLiteral"), Positive())
388-
Binding { k =>
389+
shift { k =>
389390
Construct(variable, if (value) builtins.True else builtins.False, List(), k(variable))
390391
}
391392

392393
case core.Literal(v: Double, _) =>
393394
val literal_binding = Variable(freshName("doubleLiteral"), Type.Double());
394-
Binding { k =>
395+
shift { k =>
395396
LiteralDouble(literal_binding, v, k(literal_binding))
396397
}
397398

398399
case core.Literal(javastring: String, _) =>
399400
val literal_binding = Variable(freshName("utf8StringLiteral"), builtins.StringType);
400-
Binding { k =>
401+
shift { k =>
401402
LiteralUTF8String(literal_binding, javastring.getBytes("utf-8"), k(literal_binding))
402403
}
403404

@@ -406,7 +407,7 @@ object Transformer {
406407

407408
val variable = Variable(freshName("pureApp"), transform(tpe.result))
408409
transform(vargs, Nil).flatMap { (values, blocks) =>
409-
Binding { k =>
410+
shift { k =>
410411
ForeignCall(variable, transform(blockName), values ++ blocks, k(variable))
411412
}
412413
}
@@ -416,7 +417,7 @@ object Transformer {
416417

417418
val variable = Variable(freshName("pureApp"), transform(tpe.result))
418419
transform(vargs, bargs).flatMap { (values, blocks) =>
419-
Binding { k =>
420+
shift { k =>
420421
ForeignCall(variable, transform(blockName), values ++ blocks, k(variable))
421422
}
422423
}
@@ -428,14 +429,14 @@ object Transformer {
428429
val tag = DeclarationContext.getConstructorTag(constructor)
429430

430431
transform(vargs, Nil).flatMap { (values, blocks) =>
431-
Binding { k =>
432+
shift { k =>
432433
Construct(variable, tag, values ++ blocks, k(variable))
433434
}
434435
}
435436

436437
case core.Box(block, annot) =>
437438
transformBlockArg(block).flatMap { unboxed =>
438-
Binding { k =>
439+
shift { k =>
439440
val boxed = Variable(freshName(unboxed.name), Type.Positive())
440441
ForeignCall(boxed, "box", List(unboxed), k(boxed))
441442
}
@@ -564,13 +565,28 @@ object Transformer {
564565
def getBlockInfo(id: Id)(using BPC: BlocksParamsContext): BlockInfo =
565566
BPC.info.getOrElse(id, sys error s"No block info for ${util.show(id)}")
566567

567-
case class Binding[A](run: (A => Statement) => Statement) {
568+
def shift[A](body: (A => Statement) => Statement): Binding[A] =
569+
Binding { k => Trampoline.Done(body { x => trampoline(k(x)) }) }
570+
571+
case class Binding[A](body: (A => Trampoline[Statement]) => Trampoline[Statement]) {
568572
def flatMap[B](rest: A => Binding[B]): Binding[B] = {
569-
Binding(k => run(a => rest(a).run(k)))
573+
Binding(k => Trampoline.More { () => body(a => Trampoline.More { () => rest(a).body(k) }) })
570574
}
575+
def run(k: A => Statement): Statement = trampoline(body { x => Trampoline.Done(k(x)) })
571576
def map[B](f: A => B): Binding[B] = flatMap { a => pure(f(a)) }
572577
}
573578

579+
enum Trampoline[A] {
580+
case Done(value: A)
581+
case More(thunk: () => Trampoline[A])
582+
}
583+
584+
@tailrec
585+
def trampoline[A](body: Trampoline[A]): A = body match {
586+
case Trampoline.Done(value) => value
587+
case Trampoline.More(thunk) => trampoline(thunk())
588+
}
589+
574590
def traverse[S, T](l: List[S])(f: S => Binding[T]): Binding[List[T]] =
575591
l match {
576592
case Nil => pure(Nil)

0 commit comments

Comments
 (0)