diff --git a/effekt/jvm/src/test/scala/effekt/core/OptimizerTests.scala b/effekt/jvm/src/test/scala/effekt/core/OptimizerTests.scala index dc91897d9..fe5f2381a 100644 --- a/effekt/jvm/src/test/scala/effekt/core/OptimizerTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/OptimizerTests.scala @@ -36,7 +36,7 @@ class OptimizerTests extends CoreTests { def normalize(input: String, expected: String)(using munit.Location) = assertTransformsTo(input, expected) { tree => val anfed = BindSubexpressions.transform(tree) - val normalized = Normalizer.normalize(Set(mainSymbol), anfed, 50) + val normalized = Normalizer.normalize(Set(mainSymbol), anfed, 50, false) Deadcode.remove(mainSymbol, normalized) } diff --git a/effekt/jvm/src/test/scala/effekt/core/VMTests.scala b/effekt/jvm/src/test/scala/effekt/core/VMTests.scala index 8d9a1f1b2..a69bb8d10 100644 --- a/effekt/jvm/src/test/scala/effekt/core/VMTests.scala +++ b/effekt/jvm/src/test/scala/effekt/core/VMTests.scala @@ -533,7 +533,7 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 0, patternMatches = 400, branches = 1487, - pushedFrames = 1352, + pushedFrames = 1185, poppedFrames = 1409, allocations = 54, closures = 0, @@ -549,7 +549,7 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 0, patternMatches = 0, branches = 210, - pushedFrames = 379, + pushedFrames = 378, poppedFrames = 377, allocations = 0, closures = 0, @@ -613,7 +613,7 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 0, patternMatches = 4, branches = 701, - pushedFrames = 874, + pushedFrames = 702, poppedFrames = 880, allocations = 4, closures = 0, @@ -660,13 +660,13 @@ class VMTests extends munit.FunSuite { examplesDir / "casestudies" / "scheduler.effekt.md" -> Some(Summary( staticDispatches = 60, - dynamicDispatches = 8, + dynamicDispatches = 7, patternMatches = 95, branches = 41, pushedFrames = 106, poppedFrames = 106, allocations = 73, - closures = 8, + closures = 7, variableReads = 29, variableWrites = 18, resets = 1, @@ -695,15 +695,15 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 783, patternMatches = 13502, branches = 14892, - pushedFrames = 28523, - poppedFrames = 28499, + pushedFrames = 28210, + poppedFrames = 28186, allocations = 7923, closures = 521, variableReads = 6742, variableWrites = 1901, - resets = 806, - shifts = 855, - resumes = 839 + resets = 778, + shifts = 229, + resumes = 213 )), examplesDir / "casestudies" / "anf.effekt.md" -> Some(Summary( @@ -711,15 +711,15 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 443, patternMatches = 7272, branches = 8110, - pushedFrames = 16275, - poppedFrames = 16260, + pushedFrames = 16101, + poppedFrames = 16088, allocations = 4317, closures = 358, variableReads = 4080, variableWrites = 1343, - resets = 481, - shifts = 660, - resumes = 644 + resets = 458, + shifts = 322, + resumes = 306 )), examplesDir / "casestudies" / "inference.effekt.md" -> Some(Summary( @@ -727,8 +727,8 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 3201452, patternMatches = 1474290, branches = 303298, - pushedFrames = 7574480, - poppedFrames = 6709185, + pushedFrames = 7574476, + poppedFrames = 6709181, allocations = 4626007, closures = 865541, variableReads = 2908620, @@ -741,11 +741,11 @@ class VMTests extends munit.FunSuite { examplesDir / "pos" / "raytracer.effekt" -> Some(Summary( staticDispatches = 79696, dynamicDispatches = 0, - patternMatches = 1014772, + patternMatches = 795964, branches = 71995, pushedFrames = 223269, poppedFrames = 223269, - allocations = 127533, + allocations = 103221, closures = 0, variableReads = 77886, variableWrites = 26904, @@ -761,26 +761,26 @@ class VMTests extends munit.FunSuite { dynamicDispatches = 0, patternMatches = 0, branches = 11, - pushedFrames = 102, - poppedFrames = 102, + pushedFrames = 92, + poppedFrames = 92, allocations = 0, closures = 0, variableReads = 61, variableWrites = 30, - resets = 1, - shifts = 10, - resumes = 10 + resets = 0, + shifts = 0, + resumes = 0 )), examplesDir / "benchmarks" / "other" / "church_exponentiation.effekt" -> Some(Summary( staticDispatches = 7, - dynamicDispatches = 1062912, + dynamicDispatches = 797188, patternMatches = 0, branches = 5, pushedFrames = 531467, poppedFrames = 531467, allocations = 0, - closures = 265750, + closures = 26, variableReads = 0, variableWrites = 0, resets = 0, diff --git a/effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala b/effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala index 1eaf14c36..ac7e96f7c 100644 --- a/effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala +++ b/effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala @@ -35,7 +35,8 @@ object Normalizer { normal => exprs: Map[Id, Expr], decls: DeclarationContext, // for field selection usage: mutable.Map[Id, Usage], // mutable in order to add new information after renaming - maxInlineSize: Int // to control inlining and avoid code bloat + maxInlineSize: Int, // to control inlining and avoid code bloat + preserveBoxing: Boolean // for LLVM, prevents some optimizations ) { def bind(id: Id, expr: Expr): Context = copy(exprs = exprs + (id -> expr)) def bind(id: Id, block: Block): Context = copy(blocks = blocks + (id -> block)) @@ -65,14 +66,14 @@ object Normalizer { normal => case None => false } - def normalize(entrypoints: Set[Id], m: ModuleDecl, maxInlineSize: Int): ModuleDecl = { + def normalize(entrypoints: Set[Id], m: ModuleDecl, maxInlineSize: Int, preserveBoxing: Boolean): ModuleDecl = { // usage information is used to detect recursive functions (and not inline them) val usage = Reachable(entrypoints, m) val defs = m.definitions.collect { case Toplevel.Def(id, block) => id -> block }.toMap - val context = Context(defs, Map.empty, DeclarationContext(m.declarations, m.externs), mutable.Map.from(usage), maxInlineSize) + val context = Context(defs, Map.empty, DeclarationContext(m.declarations, m.externs), mutable.Map.from(usage), maxInlineSize, preserveBoxing) val (normalizedDefs, _) = normalizeToplevel(m.definitions)(using context) m.copy(definitions = normalizedDefs) @@ -213,6 +214,9 @@ object Normalizer { normal => def normalizeVal(id: Id, tpe: ValueType, binding: Stmt, body: Stmt): Stmt = normalize(binding) match { + // [[ val x = ABORT; body ]] = ABORT + case s if !C.preserveBoxing && s.tpe == Type.TBottom => s + // [[ val x = sc match { case id(ps) => body2 }; body ]] = sc match { case id(ps) => val x = body2; body } case Stmt.Match(sc, List((id2, BlockLit(tparams2, cparams2, vparams2, bparams2, body2))), None) => Stmt.Match(sc, List((id2, BlockLit(tparams2, cparams2, vparams2, bparams2, diff --git a/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala b/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala index 6dcaf78c9..3dbc7238a 100644 --- a/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala +++ b/effekt/shared/src/main/scala/effekt/core/optimizer/Optimizer.scala @@ -21,6 +21,8 @@ object Optimizer extends Phase[CoreTransformed, CoreTransformed] { def optimize(source: Source, mainSymbol: symbols.Symbol, core: ModuleDecl)(using Context): ModuleDecl = + val isLLVM = Context.config.backend().name == "llvm" + var tree = core // (1) first thing we do is simply remove unused definitions (this speeds up all following analysis and rewrites) @@ -37,9 +39,10 @@ object Optimizer extends Phase[CoreTransformed, CoreTransformed] { def normalize(m: ModuleDecl) = { val anfed = BindSubexpressions.transform(m) - val normalized = Normalizer.normalize(Set(mainSymbol), anfed, Context.config.maxInlineSize().toInt) + val normalized = Normalizer.normalize(Set(mainSymbol), anfed, Context.config.maxInlineSize().toInt, isLLVM) val live = Deadcode.remove(mainSymbol, normalized) - RemoveTailResumptions(live) + val tailRemoved = RemoveTailResumptions(live) + tailRemoved } // (3) normalize a few times (since tail resumptions might only surface after normalization and leave dead Resets) diff --git a/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala b/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala index 2174f7acd..c290ff9bd 100644 --- a/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala +++ b/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala @@ -38,7 +38,7 @@ object RemoveTailResumptions { case Stmt.Get(id, annotatedCapt, annotatedTpe) => false case Stmt.Put(id, annotatedCapt, value) => false case Stmt.Reset(BlockLit(tparams, cparams, vparams, bparams, body)) => tailResumptive(k, body) // is this correct? - case Stmt.Shift(prompt, body) => false + case Stmt.Shift(prompt, body) => stmt.tpe == Type.TBottom case Stmt.Resume(k2, body) => k2.id == k // what if k is free in body? case Stmt.Hole() => true } diff --git a/effekt/shared/src/main/scala/effekt/cps/Transformer.scala b/effekt/shared/src/main/scala/effekt/cps/Transformer.scala index 95ac72ae9..76cea12b8 100644 --- a/effekt/shared/src/main/scala/effekt/cps/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/cps/Transformer.scala @@ -81,6 +81,9 @@ object Transformer { case core.Stmt.App(callee, targs, vargs, bargs) => App(transform(callee), vargs.map(transform), bargs.map(transform), MetaCont(ks), k.reify) + case core.Stmt.Invoke(callee, method, tpe, targs, vargs, bargs) if stmt.tpe == core.Type.TBottom => + Invoke(transform(callee), method, vargs.map(transform), bargs.map(transform), MetaCont(ks), Continuation.Empty) + case core.Stmt.Invoke(callee, method, tpe, targs, vargs, bargs) => Invoke(transform(callee), method, vargs.map(transform), bargs.map(transform), MetaCont(ks), k.reify) @@ -119,7 +122,10 @@ object Transformer { val translatedBody: BlockLit = BlockLit(vparams.map { p => p.id }, List(resume.id), ks2, k2, transform(body, ks2, Continuation.Dynamic(k2))) - Shift(prompt.id, translatedBody, MetaCont(ks), k.reify) + // if the answer type is Nothing, then the continuation is discarded anyways... + val continuation = if (stmt.tpe == core.Type.TBottom) Continuation.Empty else k.reify + + Shift(prompt.id, translatedBody, MetaCont(ks), continuation) case core.Stmt.Shift(prompt, body) => sys error "Shouldn't happen" @@ -234,4 +240,9 @@ enum Continuation { object Continuation { def Static(hint: Id)(k: (Pure, Id) => Stmt): Continuation.Static = Continuation.Static(hint, k) + val Empty: Cont = { + val a = Id("a") + val ks = Id("ks") + cps.Cont.ContLam(a, ks, cps.Hole()) + } }