From 8b73a24e9c44b3fde9e23a81cf29c166dfa89389 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 13 Mar 2024 17:50:28 +0800 Subject: [PATCH 01/59] move changes from tailrec-opt to new branch --- .../compiler/optimizer/TailRecOpt.scala | 102 ++++++++++++++++++ .../test/scala/mlscript/compiler/TestIR.scala | 11 +- .../src/test/scala/mlscript/DiffTests.scala | 3 + 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala new file mode 100644 index 0000000000..83ee7d917d --- /dev/null +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -0,0 +1,102 @@ +package mlscript.compiler.optimizer + +import mlscript.compiler.ir.Program +import mlscript.compiler.ir.Defn +import mlscript.compiler.ir.Node +import mlscript.compiler.ir.Node._ +import scala.annotation.tailrec + +class TailRecOpt { + private type DefnGraph = Set[DefnNode] + + private def findTailCalls(node: Node)(implicit nodeMap: Map[Defn, DefnNode]): List[DefnNode] = node match + case Result(res) => Nil + case Jump(defn, args) => nodeMap(defn.expectDefn) :: Nil + case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) + case LetExpr(name, expr, body) => findTailCalls(body) + case LetCall(names, defn, args, body) => findTailCalls(body) + + // Partions a tail recursive call graph into strongly connected components + // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component + + // Implements Tarjan's algorithm. + // Wikipedia: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + // Implementation Reference: https://www.baeldung.com/cs/scc-tarjans-algorithm + + private class DefnNode(val defn: Defn) { + override def hashCode(): Int = defn.hashCode + + var num: Int = Int.MaxValue + var lowest: Int = Int.MaxValue + var visited: Boolean = false + var processed: Boolean = false + } + + // TODO: this is untested. test this. + private def partitionNodes(defns: DefnGraph)(implicit nodeMap: Map[Defn, DefnNode]): List[DefnGraph] = { + var ctr = 0 + var stack: List[DefnNode] = Nil + var sccs: List[DefnGraph] = Nil + + def dfs(src: DefnNode): Unit = { + src.num = ctr + src.lowest = ctr + ctr += 1 + src.visited = true + stack = src :: stack + + for (u <- findTailCalls(src.defn.body)) do { + if (u.visited) + if (!u.processed) + src.lowest = u.num.min(src.lowest) + else + dfs(u) + src.lowest = u.lowest.min(src.lowest) + } + + src.processed = true + + if (src.num == src.lowest) { + var scc: DefnGraph = Set() + + def pop(): DefnNode = { + val ret = stack.head + stack = stack.tail + ret + } + + var vertex = pop() + + while (vertex != src) { + scc = scc + vertex + vertex = pop() + } + + scc = scc + vertex + + sccs = scc :: sccs + } + } + + for (v <- defns) { + if (!v.visited) + dfs(v) + } + + sccs + } + + def partition(defns: Set[Defn]) = { + val nodeMap = defns.foldLeft[Map[Defn, DefnNode]](Map())((m, d) => m + (d -> DefnNode(d))) + partitionNodes(nodeMap.values.toSet)(nodeMap).map(g => g.map(d => d.defn)) + } + + def apply(p: Program) = run(p) + + def run(p: Program): Program = { + val defnMap = p.defs.foldLeft[Map[Int, Defn]](Map())((m, d) => m + (d.id -> d)) + + // TODO + p + } +} \ No newline at end of file diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 1d6ac40f10..151ebda156 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -6,6 +6,7 @@ import mlscript.compiler.ir._ import scala.collection.mutable.StringBuilder import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.ir.{Interpreter, Fresh, FreshInt, Builder} +import mlscript.compiler.optimizer.TailRecOpt class IRDiffTestCompiler extends DiffTests { import IRDiffTestCompiler.* @@ -16,7 +17,15 @@ class IRDiffTestCompiler extends DiffTests { try output("\n\nIR:") val gb = Builder(Fresh(), FreshInt(), FreshInt(), FreshInt()) - val graph = gb.buildGraph(unit) + val graph_ = gb.buildGraph(unit) + output(graph_.toString()) + + val graph = if (!mode.noTailrecOpt) { + val tailRecOpt = new TailRecOpt + tailRecOpt(graph_) + } else { + graph_ + } output(graph.toString()) output("\nPromoted:") output(graph.toString()) diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index e9b87fc690..0ffbd433cc 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -43,6 +43,7 @@ abstract class ModeType { def allowEscape: Bool def mono: Bool def useIR: Bool + def noTailrecOpt: Bool def interpIR: Bool def irVerbose: Bool def lift: Bool @@ -178,6 +179,7 @@ class DiffTests lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, + noTailrecOpt: Bool = false, useIR: Bool = false, interpIR: Bool = false, irVerbose: Bool = false, @@ -286,6 +288,7 @@ class DiffTests case "escape" => mode.copy(allowEscape = true) case "mono" => {mode.copy(mono = true)} case "lift" => {mode.copy(lift = true)} + case "noTailrec" => mode.copy(noTailrecOpt = true) case "nolift" => {mode.copy(nolift = true)} case "exit" => out.println(exitMarker) From 93bf78fa034dd9643b80cd59113c876f9171f785 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 23 Mar 2024 18:51:07 +0800 Subject: [PATCH 02/59] Optimize strongly connected components --- .../compiler/optimizer/TailRecOpt.scala | 107 ++- compiler/shared/test/diff-ir/IR.mls | 273 ++++++ compiler/shared/test/diff-ir/IRComplex.mls | 239 +++++ compiler/shared/test/diff-ir/IRRec.mls | 900 +++++++++++++++++- .../test/scala/mlscript/compiler/TestIR.scala | 7 +- 5 files changed, 1482 insertions(+), 44 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 83ee7d917d..44813a4af8 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -5,13 +5,20 @@ import mlscript.compiler.ir.Defn import mlscript.compiler.ir.Node import mlscript.compiler.ir.Node._ import scala.annotation.tailrec - -class TailRecOpt { +import mlscript.compiler.ir.FreshInt +import mlscript.compiler.ir.Name +import mlscript.compiler.ir.ClassInfo +import mlscript.compiler.ir.DefnRef +import mlscript.compiler.ir.Expr +import mlscript.IntLit + +// fnUid should be the same FreshInt that was used to build the graph being passed into this class +class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] private def findTailCalls(node: Node)(implicit nodeMap: Map[Defn, DefnNode]): List[DefnNode] = node match case Result(res) => Nil - case Jump(defn, args) => nodeMap(defn.expectDefn) :: Nil + case Jump(defn, args) => nodeMap(defn.expectDefn) :: Nil // assume that all definition references are resolved case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) case LetCall(names, defn, args, body) => findTailCalls(body) @@ -48,7 +55,7 @@ class TailRecOpt { for (u <- findTailCalls(src.defn.body)) do { if (u.visited) if (!u.processed) - src.lowest = u.num.min(src.lowest) + src.lowest = u.num.min(src.lowest) else dfs(u) src.lowest = u.lowest.min(src.lowest) @@ -74,7 +81,7 @@ class TailRecOpt { scc = scc + vertex - sccs = scc :: sccs + sccs = scc :: sccs } } @@ -86,7 +93,93 @@ class TailRecOpt { sccs } - def partition(defns: Set[Defn]) = { + private case class DefnInfo(defn: Defn, stackFrameIdx: Int) + + // Returns a set containing the original set of functions pointing to an optimized function + // and the optimized function. + // TODO: Currently untested + def optimize(defns: Set[Defn], classes: Set[ClassInfo]): Set[Defn] = { + + def asLit(x: Int) = Expr.Literal(IntLit(x)) + + // To build the case block, we need to compare integers and check if the result is "True" + val trueClass = classes.find(c => c.ident == "True").get + val falseClass = classes.find(c => c.ident == "False").get + + // currently, single tail recursive functions are already optimised + if (defns.size <= 1) + return defns + + // concretely order the functions as soon as possible, since the order of the functions matter + val defnsList = defns.toList + + // TODO: make sure that name clashes aren't a problem + val trName = Name("tailrecBranch"); + val stackFrame = trName :: defnsList.flatMap(_.params) // take union of stack frames + + val stackFrameIdxes = defnsList.foldRight(1 :: Nil)((defn, ls) => defn.params.size + ls.head :: ls) + + val defnInfoMap: Map[Defn, DefnInfo] = (defnsList zip stackFrameIdxes.drop(1)) + .foldLeft(Map.empty)((map, item) => map + (item._1 -> DefnInfo(item._1, item._2))) + + // TODO: This works fine for now, but ideally should find a way to guarantee the new + // name is unique + val newName = defns.foldLeft("")(_ + "_" + _.name) + "opt" + + val newDefnRef = DefnRef(Right(newName)) + + // Build the node. + def transformNode(node: Node)(implicit info: DefnInfo): Node = node match + case Jump(defn, args) => + // transform the stack frame + val start = stackFrame.take(info.stackFrameIdx).drop(1).map(n => Expr.Ref(n)) // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + args.size).map(n => Expr.Ref(n)) + val concated = asLit(info.defn.id) :: start ::: args ::: end + Jump(newDefnRef, concated) + + case Result(_) => node + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) + case LetCall(names, defn, args, body) => LetCall(names, defn, args, transformNode(body)) + + // Tail calls to another function in the component will be replaced with a tail call + // to the merged function + def transformDefn(defn: Defn): Defn = Defn( + defn.id, + defn.name, + defn.params, + defn.resultNum, + Jump(newDefnRef, asLit(defn.id) :: stackFrame.map(n => Expr.Ref(n)).drop(1)).attachTag(tag) + ) + + // given expressions value, e1, e2, transform it into + // let scrut = tailrecBranch == value + // in case scrut of True -> e1 + // False -> e2 + // + def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = + val name = Name("scrut") + val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) + LetExpr( + name, + Expr.BasicOp("==", List(asLit(value), Expr.Ref(trName))), + cases + ).attachTag(tag) + + val first = defnsList.head; + val newNode = defnsList.tail.foldLeft(transformNode(first.body)(defnInfoMap(first)))( + (elz, defn) => makeCaseBranch(defn.id, transformNode(defn.body)(defnInfoMap(defn)), elz) + ).attachTag(tag) + + // TODO: What is resultNum? It's only ever set to 1 elsewhere + val newDefn = Defn(fnUid.make, newName, stackFrame, 1, newNode) + + newDefnRef.defn = Left(newDefn) + + defns.map(d => transformDefn(d)) + newDefn + } + + def partition(defns: Set[Defn]): List[Set[Defn]] = { val nodeMap = defns.foldLeft[Map[Defn, DefnNode]](Map())((m, d) => m + (d -> DefnNode(d))) partitionNodes(nodeMap.values.toSet)(nodeMap).map(g => g.map(d => d.defn)) } @@ -99,4 +192,4 @@ class TailRecOpt { // TODO p } -} \ No newline at end of file +} diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 1900d8e732..7d6c4913af 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -33,6 +33,25 @@ foo() //│ }, //│ let* (x$5) = foo() in -- #26 //│ x$5 -- #25) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, mktup2, [x$0,y$0], +//│ 1, +//│ let* (x$1) = mktup(x$0,y$0) in -- #7 +//│ x$1 -- #6 +//│ ) +//│ Def(1, mktup, [x$2,y$1], +//│ 1, +//│ let x$3 = Pair(x$2,y$1) in -- #14 +//│ x$3 -- #13 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (x$4) = mktup2(1,2) in -- #22 +//│ x$4 -- #21 +//│ ) +//│ }, +//│ let* (x$5) = foo() in -- #26 +//│ x$5 -- #25) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -94,6 +113,29 @@ bar() //│ }, //│ let* (x$6) = bar() in -- #32 //│ x$6 -- #31) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, foo, [pair$0], +//│ 1, +//│ case pair$0 of -- #16 +//│ Pair => +//│ let x$1 = pair$0.y in -- #15 +//│ let x$2 = pair$0.x in -- #14 +//│ let x$3 = Pair(x$2,x$1) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, bar, [], +//│ 1, +//│ let x$4 = Pair(1,2) in -- #28 +//│ let* (x$5) = foo(x$4) in -- #27 +//│ x$5 -- #26 +//│ ) +//│ }, +//│ let* (x$6) = bar() in -- #32 +//│ x$6 -- #31) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -177,6 +219,39 @@ foo() //│ }, //│ let* (x$11) = foo() in -- #47 //│ x$11 -- #46) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, silly, [pair$0], +//│ 1, +//│ let x$0 = 0 in -- #29 +//│ case pair$0 of -- #28 +//│ Pair => +//│ let x$3 = pair$0.y in -- #27 +//│ let x$4 = pair$0.x in -- #26 +//│ case pair$0 of -- #25 +//│ Pair => +//│ let x$6 = pair$0.y in -- #24 +//│ let x$7 = pair$0.x in -- #23 +//│ let x$8 = +(x$7,1) in -- #22 +//│ jump j$1(x$8) -- #21 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ let x$2 = +(x$1,1) in -- #6 +//│ x$2 -- #5 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #13 +//│ ) +//│ Def(3, foo, [], +//│ 1, +//│ let x$9 = Pair(0,1) in -- #43 +//│ let* (x$10) = silly(x$9) in -- #42 +//│ x$10 -- #41 +//│ ) +//│ }, +//│ let* (x$11) = foo() in -- #47 +//│ x$11 -- #46) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -259,6 +334,30 @@ foo() //│ }, //│ let* (x$7) = foo() in -- #33 //│ x$7 -- #32) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, inc_fst, [pair$0], +//│ 1, +//│ let x$0 = 2 in -- #15 +//│ case pair$0 of -- #14 +//│ Pair => +//│ let x$2 = pair$0.y in -- #13 +//│ let x$3 = pair$0.x in -- #12 +//│ let x$4 = +(x$3,x$0) in -- #11 +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #2 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let x$5 = Pair(0,1) in -- #29 +//│ let* (x$6) = inc_fst(x$5) in -- #28 +//│ x$6 -- #27 +//│ ) +//│ }, +//│ let* (x$7) = foo() in -- #33 +//│ x$7 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -328,6 +427,30 @@ foo() //│ }, //│ let* (x$7) = foo() in -- #32 //│ x$7 -- #31) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, inc_fst, [pair$0], +//│ 1, +//│ let x$0 = 0 in -- #15 +//│ case pair$0 of -- #14 +//│ Pair => +//│ let x$2 = pair$0.y in -- #13 +//│ let x$3 = pair$0.x in -- #12 +//│ let x$4 = +(x$2,1) in -- #11 +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #2 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let x$5 = Pair(0,1) in -- #28 +//│ let* (x$6) = inc_fst(x$5) in -- #27 +//│ x$6 -- #26 +//│ ) +//│ }, +//│ let* (x$7) = foo() in -- #32 +//│ x$7 -- #31) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -413,6 +536,43 @@ bar() //│ }, //│ let* (x$11) = bar() in -- #52 //│ x$11 -- #51) +//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { +//│ Def(0, foo, [a$0,b$0], +//│ 1, +//│ case a$0 of -- #36 +//│ Left => +//│ let x$4 = a$0.x in -- #26 +//│ let x$5 = +(x$4,1) in -- #25 +//│ let x$6 = Left(x$5) in -- #24 +//│ jump j$0(x$6) -- #23 +//│ Right => +//│ let x$7 = a$0.y in -- #35 +//│ let x$8 = Right(b$0) in -- #34 +//│ jump j$0(x$8) -- #33 +//│ ) +//│ Def(1, j$1, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, j$0, [x$0], +//│ 1, +//│ case x$0 of -- #14 +//│ Left => +//│ let x$2 = x$0.x in -- #8 +//│ jump j$1(x$2) -- #7 +//│ Right => +//│ let x$3 = x$0.y in -- #13 +//│ jump j$1(x$3) -- #12 +//│ ) +//│ Def(3, bar, [], +//│ 1, +//│ let x$9 = Right(2) in -- #48 +//│ let* (x$10) = foo(x$9,2) in -- #47 +//│ x$10 -- #46 +//│ ) +//│ }, +//│ let* (x$11) = bar() in -- #52 +//│ x$11 -- #51) //│ //│ Promoted: //│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { @@ -486,6 +646,23 @@ bar() //│ }, //│ let* (x$5) = bar() in -- #23 //│ x$5 -- #22) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { +//│ Def(0, foo, [a$0], +//│ 1, +//│ let x$0 = a$0.x in -- #7 +//│ let x$1 = a$0.y in -- #6 +//│ let x$2 = +(x$0,x$1) in -- #5 +//│ x$2 -- #4 +//│ ) +//│ Def(1, bar, [], +//│ 1, +//│ let x$3 = Pair(1,0) in -- #19 +//│ let* (x$4) = foo(x$3) in -- #18 +//│ x$4 -- #17 +//│ ) +//│ }, +//│ let* (x$5) = bar() in -- #23 +//│ x$5 -- #22) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { @@ -549,6 +726,31 @@ bar() //│ }, //│ let* (x$6) = bar() in -- #31 //│ x$6 -- #30) +//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { +//│ Def(0, foo, [a$0], +//│ 1, +//│ case a$0 of -- #15 +//│ C1 => +//│ let x$1 = a$0.y in -- #9 +//│ let x$2 = a$0.x in -- #8 +//│ jump j$0(x$2) -- #7 +//│ C2 => +//│ let x$3 = a$0.z in -- #14 +//│ jump j$0(x$3) -- #13 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, bar, [], +//│ 1, +//│ let x$4 = C1(0,1) in -- #27 +//│ let* (x$5) = foo(x$4) in -- #26 +//│ x$5 -- #25 +//│ ) +//│ }, +//│ let* (x$6) = bar() in -- #31 +//│ x$6 -- #30) //│ //│ Promoted: //│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { @@ -632,6 +834,38 @@ baz() //│ }, //│ let* (x$16) = baz() in -- #85 //│ x$16 -- #84) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, foo, [a$0,b$0], +//│ 1, +//│ let x$0 = a$0.x in -- #21 +//│ let x$1 = a$0.y in -- #20 +//│ let x$2 = b$0.x in -- #19 +//│ let x$3 = b$0.y in -- #18 +//│ let x$4 = +(x$0,x$1) in -- #17 +//│ let x$5 = +(x$4,x$2) in -- #16 +//│ let x$6 = +(x$5,x$3) in -- #15 +//│ x$6 -- #14 +//│ ) +//│ Def(1, bar, [c$0], +//│ 1, +//│ let x$7 = Pair(0,1) in -- #69 +//│ let* (x$8) = foo(x$7,c$0) in -- #68 +//│ let x$9 = Pair(2,3) in -- #67 +//│ let* (x$10) = foo(c$0,x$9) in -- #66 +//│ let x$11 = Pair(0,1) in -- #65 +//│ let x$12 = Pair(2,3) in -- #64 +//│ let* (x$13) = foo(x$11,x$12) in -- #63 +//│ x$13 -- #62 +//│ ) +//│ Def(2, baz, [], +//│ 1, +//│ let x$14 = Pair(4,5) in -- #81 +//│ let* (x$15) = bar(x$14) in -- #80 +//│ x$15 -- #79 +//│ ) +//│ }, +//│ let* (x$16) = baz() in -- #85 +//│ x$16 -- #84) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -692,6 +926,16 @@ foo() //│ }, //│ let* (x$2) = foo() in -- #14 //│ x$2 -- #13) +//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let x$0 = Pair(0,1) in -- #10 +//│ let x$1 = x$0.x in -- #9 +//│ x$1 -- #8 +//│ ) +//│ }, +//│ let* (x$2) = foo() in -- #14 +//│ x$2 -- #13) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -754,6 +998,35 @@ foo() //│ }, //│ let* (x$8) = foo() in -- #30 //│ x$8 -- #29) +//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let x$0 = O() in -- #10 +//│ let x$1 = S(x$0) in -- #9 +//│ let* (x$2) = bar(x$1) in -- #8 +//│ x$2 -- #7 +//│ ) +//│ Def(1, bar, [x$3], +//│ 1, +//│ let* (x$4) = baz(x$3) in -- #16 +//│ x$4 -- #15 +//│ ) +//│ Def(2, baz, [x$5], +//│ 1, +//│ case x$5 of -- #26 +//│ S => +//│ let x$7 = x$5.s in -- #23 +//│ jump j$0(x$7) -- #22 +//│ O => +//│ jump j$0(x$5) -- #25 +//│ ) +//│ Def(3, j$0, [x$6], +//│ 1, +//│ x$6 -- #18 +//│ ) +//│ }, +//│ let* (x$8) = foo() in -- #30 +//│ x$8 -- #29) //│ //│ Promoted: //│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index 5e8d6ebbbe..fa0a44dad8 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -70,6 +70,53 @@ bar() //│ }, //│ let* (x$21) = bar() in -- #93 //│ x$21 -- #92) +//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { +//│ Def(0, complex_foo, [t$0], +//│ 1, +//│ case t$0 of -- #63 +//│ A => +//│ let x$9 = t$0.z in -- #51 +//│ let x$10 = t$0.y in -- #50 +//│ let x$11 = t$0.x in -- #49 +//│ let x$12 = *(x$10,x$9) in -- #48 +//│ let x$13 = +(x$11,x$12) in -- #47 +//│ jump j$0(x$13) -- #46 +//│ B => +//│ let x$14 = t$0.n in -- #62 +//│ let x$15 = t$0.m in -- #61 +//│ let x$16 = -(x$15,x$14) in -- #60 +//│ jump j$0(x$16) -- #59 +//│ ) +//│ Def(1, j$1, [x$2,x$0], +//│ 1, +//│ let x$3 = +(x$0,x$2) in -- #13 +//│ x$3 -- #12 +//│ ) +//│ Def(2, j$0, [x$0], +//│ 1, +//│ let x$1 = B(1,2) in -- #34 +//│ case x$1 of -- #33 +//│ A => +//│ let x$4 = x$1.z in -- #24 +//│ let x$5 = x$1.y in -- #23 +//│ let x$6 = x$1.x in -- #22 +//│ jump j$1(3,x$0) -- #21 +//│ B => +//│ let x$7 = x$1.n in -- #32 +//│ let x$8 = x$1.m in -- #31 +//│ jump j$1(4,x$0) -- #30 +//│ ) +//│ Def(3, bar, [], +//│ 1, +//│ let x$17 = A(6,7,8) in -- #89 +//│ let* (x$18) = complex_foo(x$17) in -- #88 +//│ let x$19 = B(9,10) in -- #87 +//│ let* (x$20) = complex_foo(x$19) in -- #86 +//│ x$20 -- #85 +//│ ) +//│ }, +//│ let* (x$21) = bar() in -- #93 +//│ x$21 -- #92) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { @@ -256,6 +303,101 @@ bar() //│ }, //│ let* (x$38) = bar() in -- #166 //│ x$38 -- #165) +//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Def(0, complex_foo, [t$0], +//│ 1, +//│ let x$0 = +(1,2) in -- #140 +//│ let x$1 = *(1,2) in -- #139 +//│ case t$0 of -- #138 +//│ A => +//│ let x$27 = t$0.x in -- #116 +//│ let x$28 = t$0.w in -- #115 +//│ jump j$0(x$27,x$0,x$1) -- #114 +//│ B => +//│ let x$29 = t$0.y in -- #128 +//│ let x$30 = +(x$29,x$1) in -- #127 +//│ let x$31 = B(x$30) in -- #126 +//│ jump j$0(x$31,x$0,x$1) -- #125 +//│ C => +//│ let x$32 = t$0.z in -- #137 +//│ let x$33 = C(0) in -- #136 +//│ jump j$0(x$33,x$0,x$1) -- #135 +//│ ) +//│ Def(1, j$2, [x$6], +//│ 1, +//│ x$6 -- #21 +//│ ) +//│ Def(2, j$3, [x$11], +//│ 1, +//│ jump j$2(x$11) -- #39 +//│ ) +//│ Def(3, j$1, [x$5,x$3,x$4], +//│ 1, +//│ case x$3 of -- #60 +//│ A => +//│ let x$7 = x$3.x in -- #29 +//│ let x$8 = x$3.w in -- #28 +//│ jump j$2(x$8) -- #27 +//│ B => +//│ let x$9 = x$3.y in -- #34 +//│ jump j$2(4) -- #33 +//│ C => +//│ let x$10 = x$3.z in -- #59 +//│ case x$4 of -- #58 +//│ A => +//│ let x$12 = x$4.x in -- #47 +//│ let x$13 = x$4.w in -- #46 +//│ jump j$3(x$13) -- #45 +//│ B => +//│ let x$14 = x$4.y in -- #52 +//│ jump j$3(7) -- #51 +//│ C => +//│ let x$15 = x$4.z in -- #57 +//│ jump j$3(8) -- #56 +//│ ) +//│ Def(4, j$4, [x$20,x$3,x$4], +//│ 1, +//│ jump j$1(x$20,x$3,x$4) -- #72 +//│ ) +//│ Def(5, j$0, [x$2,x$0,x$1], +//│ 1, +//│ let x$3 = A(5,x$2) in -- #108 +//│ let x$4 = B(6) in -- #107 +//│ case x$2 of -- #106 +//│ A => +//│ let x$16 = x$2.x in -- #95 +//│ let x$17 = x$2.w in -- #94 +//│ let x$18 = +(x$17,x$0) in -- #93 +//│ let x$19 = +(x$18,x$1) in -- #92 +//│ case x$16 of -- #91 +//│ A => +//│ let x$21 = x$16.x in -- #80 +//│ let x$22 = x$16.w in -- #79 +//│ jump j$4(x$22,x$3,x$4) -- #78 +//│ B => +//│ let x$23 = x$16.y in -- #85 +//│ jump j$4(x$19,x$3,x$4) -- #84 +//│ C => +//│ let x$24 = x$16.z in -- #90 +//│ jump j$4(0,x$3,x$4) -- #89 +//│ B => +//│ let x$25 = x$2.y in -- #100 +//│ jump j$1(2,x$3,x$4) -- #99 +//│ C => +//│ let x$26 = x$2.z in -- #105 +//│ jump j$1(3,x$3,x$4) -- #104 +//│ ) +//│ Def(6, bar, [], +//│ 1, +//│ let x$34 = B(10) in -- #162 +//│ let x$35 = A(9,x$34) in -- #161 +//│ let x$36 = A(10,x$35) in -- #160 +//│ let* (x$37) = complex_foo(x$36) in -- #159 +//│ x$37 -- #158 +//│ ) +//│ }, +//│ let* (x$38) = bar() in -- #166 +//│ x$38 -- #165) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { @@ -492,6 +634,103 @@ bar() //│ }, //│ let* (x$40) = bar() in -- #176 //│ x$40 -- #175) +//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Def(0, complex_foo, [t$0], +//│ 1, +//│ let x$0 = +(1,2) in -- #150 +//│ let x$1 = *(1,2) in -- #149 +//│ case t$0 of -- #148 +//│ A => +//│ let x$27 = t$0.x in -- #126 +//│ let x$28 = t$0.w in -- #125 +//│ let x$29 = C(0) in -- #124 +//│ let x$30 = A(x$28,x$29) in -- #123 +//│ jump j$0(x$30,x$0,x$1) -- #122 +//│ B => +//│ let x$31 = t$0.y in -- #138 +//│ let x$32 = +(x$31,x$1) in -- #137 +//│ let x$33 = B(x$32) in -- #136 +//│ jump j$0(x$33,x$0,x$1) -- #135 +//│ C => +//│ let x$34 = t$0.z in -- #147 +//│ let x$35 = C(0) in -- #146 +//│ jump j$0(x$35,x$0,x$1) -- #145 +//│ ) +//│ Def(1, j$2, [x$6], +//│ 1, +//│ x$6 -- #21 +//│ ) +//│ Def(2, j$3, [x$11], +//│ 1, +//│ jump j$2(x$11) -- #39 +//│ ) +//│ Def(3, j$1, [x$5,x$3,x$4], +//│ 1, +//│ case x$3 of -- #60 +//│ A => +//│ let x$7 = x$3.x in -- #29 +//│ let x$8 = x$3.w in -- #28 +//│ jump j$2(x$8) -- #27 +//│ B => +//│ let x$9 = x$3.y in -- #34 +//│ jump j$2(4) -- #33 +//│ C => +//│ let x$10 = x$3.z in -- #59 +//│ case x$4 of -- #58 +//│ A => +//│ let x$12 = x$4.x in -- #47 +//│ let x$13 = x$4.w in -- #46 +//│ jump j$3(x$13) -- #45 +//│ B => +//│ let x$14 = x$4.y in -- #52 +//│ jump j$3(7) -- #51 +//│ C => +//│ let x$15 = x$4.z in -- #57 +//│ jump j$3(8) -- #56 +//│ ) +//│ Def(4, j$4, [x$20,x$3,x$4], +//│ 1, +//│ jump j$1(x$20,x$3,x$4) -- #72 +//│ ) +//│ Def(5, j$0, [x$2,x$0,x$1], +//│ 1, +//│ let x$3 = A(5,x$2) in -- #108 +//│ let x$4 = B(6) in -- #107 +//│ case x$2 of -- #106 +//│ A => +//│ let x$16 = x$2.x in -- #95 +//│ let x$17 = x$2.w in -- #94 +//│ let x$18 = +(x$17,x$0) in -- #93 +//│ let x$19 = +(x$18,x$1) in -- #92 +//│ case x$16 of -- #91 +//│ A => +//│ let x$21 = x$16.x in -- #80 +//│ let x$22 = x$16.w in -- #79 +//│ jump j$4(x$22,x$3,x$4) -- #78 +//│ B => +//│ let x$23 = x$16.y in -- #85 +//│ jump j$4(x$19,x$3,x$4) -- #84 +//│ C => +//│ let x$24 = x$16.z in -- #90 +//│ jump j$4(0,x$3,x$4) -- #89 +//│ B => +//│ let x$25 = x$2.y in -- #100 +//│ jump j$1(2,x$3,x$4) -- #99 +//│ C => +//│ let x$26 = x$2.z in -- #105 +//│ jump j$1(3,x$3,x$4) -- #104 +//│ ) +//│ Def(6, bar, [], +//│ 1, +//│ let x$36 = B(10) in -- #172 +//│ let x$37 = A(9,x$36) in -- #171 +//│ let x$38 = A(10,x$37) in -- #170 +//│ let* (x$39) = complex_foo(x$38) in -- #169 +//│ x$39 -- #168 +//│ ) +//│ }, +//│ let* (x$40) = bar() in -- #176 +//│ x$40 -- #175) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index 0275612d6a..cb3f365216 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -34,6 +34,28 @@ fib(20) //│ }, //│ let* (x$7) = fib(20) in -- #34 //│ x$7 -- #33) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fib, [n$0], +//│ 1, +//│ let x$0 = <(n$0,2) in -- #28 +//│ if x$0 -- #27 +//│ true => +//│ jump j$0(n$0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #26 +//│ let* (x$3) = fib(x$2) in -- #25 +//│ let x$4 = -(n$0,2) in -- #24 +//│ let* (x$5) = fib(x$4) in -- #23 +//│ let x$6 = +(x$3,x$5) in -- #22 +//│ jump j$0(x$6) -- #21 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$7) = fib(20) in -- #34 +//│ x$7 -- #33) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { @@ -115,6 +137,47 @@ foo() //│ }, //│ let* (x$13) = foo() in -- #47 //│ x$13 -- #46) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, odd, [x$0], +//│ 1, +//│ let x$1 = ==(x$0,0) in -- #18 +//│ if x$1 -- #17 +//│ true => +//│ let x$3 = False() in -- #6 +//│ jump j$0(x$3) -- #5 +//│ false => +//│ let x$4 = -(x$0,1) in -- #16 +//│ let* (x$5) = even(x$4) in -- #15 +//│ jump j$0(x$5) -- #14 +//│ ) +//│ Def(1, j$0, [x$2], +//│ 1, +//│ x$2 -- #3 +//│ ) +//│ Def(2, even, [x$6], +//│ 1, +//│ let x$7 = ==(x$6,0) in -- #37 +//│ if x$7 -- #36 +//│ true => +//│ let x$9 = True() in -- #25 +//│ jump j$1(x$9) -- #24 +//│ false => +//│ let x$10 = -(x$6,1) in -- #35 +//│ let* (x$11) = odd(x$10) in -- #34 +//│ jump j$1(x$11) -- #33 +//│ ) +//│ Def(3, j$1, [x$8], +//│ 1, +//│ x$8 -- #22 +//│ ) +//│ Def(4, foo, [], +//│ 1, +//│ let* (x$12) = odd(10) in -- #43 +//│ x$12 -- #42 +//│ ) +//│ }, +//│ let* (x$13) = foo() in -- #47 +//│ x$13 -- #46) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { @@ -219,6 +282,46 @@ main() //│ }, //│ let* (x$12) = main() in -- #41 //│ x$12 -- #40) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Def(0, not, [x$0], +//│ 1, +//│ if x$0 -- #8 +//│ true => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ false => +//│ let x$3 = True() in -- #7 +//│ jump j$0(x$3) -- #6 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, foo, [x$4], +//│ 1, +//│ if x$4 -- #30 +//│ true => +//│ let x$6 = A() in -- #13 +//│ jump j$1(x$6) -- #12 +//│ false => +//│ let* (x$7) = not(x$4) in -- #29 +//│ let* (x$8) = foo(x$7) in -- #28 +//│ let x$9 = B(x$8) in -- #27 +//│ jump j$1(x$9) -- #26 +//│ ) +//│ Def(3, j$1, [x$5], +//│ 1, +//│ x$5 -- #10 +//│ ) +//│ Def(4, main, [], +//│ 1, +//│ let x$10 = False() in -- #37 +//│ let* (x$11) = foo(x$10) in -- #36 +//│ x$11 -- #35 +//│ ) +//│ }, +//│ let* (x$12) = main() in -- #41 +//│ x$12 -- #40) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { @@ -364,6 +467,75 @@ main() //│ }, //│ let* (x$26) = main() in -- #86 //│ x$26 -- #85) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Def(0, aaa, [], +//│ 1, +//│ let x$0 = 1 in -- #17 +//│ let x$1 = 2 in -- #16 +//│ let x$2 = 3 in -- #15 +//│ let x$3 = 4 in -- #14 +//│ let x$4 = +(x$0,x$1) in -- #13 +//│ let x$5 = -(x$4,x$2) in -- #12 +//│ let x$6 = +(x$5,x$3) in -- #11 +//│ x$6 -- #10 +//│ ) +//│ Def(1, bbb, [], +//│ 1, +//│ let* (x$7) = aaa() in -- #28 +//│ let x$8 = *(x$7,100) in -- #27 +//│ let x$9 = +(x$8,4) in -- #26 +//│ x$9 -- #25 +//│ ) +//│ Def(2, not, [x$10], +//│ 1, +//│ if x$10 -- #37 +//│ true => +//│ let x$12 = False() in -- #33 +//│ jump j$0(x$12) -- #32 +//│ false => +//│ let x$13 = True() in -- #36 +//│ jump j$0(x$13) -- #35 +//│ ) +//│ Def(3, j$0, [x$11], +//│ 1, +//│ x$11 -- #30 +//│ ) +//│ Def(4, foo, [x$14], +//│ 1, +//│ if x$14 -- #59 +//│ true => +//│ let x$16 = A() in -- #42 +//│ jump j$1(x$16) -- #41 +//│ false => +//│ let* (x$17) = not(x$14) in -- #58 +//│ let* (x$18) = foo(x$17) in -- #57 +//│ let x$19 = B(x$18) in -- #56 +//│ jump j$1(x$19) -- #55 +//│ ) +//│ Def(5, j$1, [x$15], +//│ 1, +//│ x$15 -- #39 +//│ ) +//│ Def(6, main, [], +//│ 1, +//│ let x$20 = False() in -- #82 +//│ let* (x$21) = foo(x$20) in -- #81 +//│ case x$21 of -- #80 +//│ A => +//│ let* (x$23) = aaa() in -- #71 +//│ jump j$2(x$23) -- #70 +//│ B => +//│ let x$24 = x$21.b in -- #79 +//│ let* (x$25) = bbb() in -- #78 +//│ jump j$2(x$25) -- #77 +//│ ) +//│ Def(7, j$2, [x$22], +//│ 1, +//│ x$22 -- #66 +//│ ) +//│ }, +//│ let* (x$26) = main() in -- #86 +//│ x$26 -- #85) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { @@ -503,6 +675,49 @@ foo() //│ }, //│ let* (x$15) = foo() in -- #54 //│ x$15 -- #53) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Def(0, odd, [x$0], +//│ 1, +//│ case x$0 of -- #15 +//│ O => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ S => +//│ let x$3 = x$0.s in -- #14 +//│ let* (x$4) = even(x$3) in -- #13 +//│ jump j$0(x$4) -- #12 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, even, [x$5], +//│ 1, +//│ case x$5 of -- #31 +//│ O => +//│ let x$7 = True() in -- #20 +//│ jump j$1(x$7) -- #19 +//│ S => +//│ let x$8 = x$5.s in -- #30 +//│ let* (x$9) = odd(x$8) in -- #29 +//│ jump j$1(x$9) -- #28 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #17 +//│ ) +//│ Def(4, foo, [], +//│ 1, +//│ let x$10 = O() in -- #50 +//│ let x$11 = S(x$10) in -- #49 +//│ let x$12 = S(x$11) in -- #48 +//│ let x$13 = S(x$12) in -- #47 +//│ let* (x$14) = odd(x$13) in -- #46 +//│ x$14 -- #45 +//│ ) +//│ }, +//│ let* (x$15) = foo() in -- #54 +//│ x$15 -- #53) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { @@ -630,8 +845,6 @@ foo() //│ }, //│ let* (x$18) = foo() in -- #69 //│ x$18 -- #68) -//│ -//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, @@ -690,30 +903,7 @@ foo() //│ let* (x$18) = foo() in -- #69 //│ x$18 -- #68) //│ -//│ Interpreted: -//│ False() - -:interpIR -class True -class False -class S(s) -class O -fun odd(x) = - if x is - O then False - S(s) then even(s) -fun even(x) = - if x is - O then True - S(s) then odd(s) -fun mk(n) = if n > 0 then S(mk(n - 1)) else O -fun foo() = odd(S(S(mk(10)))) -foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} -//│ -//│ -//│ IR: +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, @@ -764,18 +954,159 @@ foo() //│ ) //│ Def(6, foo, [], //│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 +//│ let* (x$16) = mk(10) in -- #65 +//│ let* (x$17) = odd(x$16) in -- #64 +//│ x$17 -- #63 //│ ) //│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) +//│ let* (x$18) = foo() in -- #69 +//│ x$18 -- #68) //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Interpreted: +//│ False() + +:interpIR +class True +class False +class S(s) +class O +fun odd(x) = + if x is + O then False + S(s) then even(s) +fun even(x) = + if x is + O then True + S(s) then odd(s) +fun mk(n) = if n > 0 then S(mk(n - 1)) else O +fun foo() = odd(S(S(mk(10)))) +foo() +//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| +//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Def(0, odd, [x$0], +//│ 1, +//│ case x$0 of -- #15 +//│ O => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ S => +//│ let x$3 = x$0.s in -- #14 +//│ let* (x$4) = even(x$3) in -- #13 +//│ jump j$0(x$4) -- #12 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, even, [x$5], +//│ 1, +//│ case x$5 of -- #31 +//│ O => +//│ let x$7 = True() in -- #20 +//│ jump j$1(x$7) -- #19 +//│ S => +//│ let x$8 = x$5.s in -- #30 +//│ let* (x$9) = odd(x$8) in -- #29 +//│ jump j$1(x$9) -- #28 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #17 +//│ ) +//│ Def(4, mk, [n$0], +//│ 1, +//│ let x$10 = >(n$0,0) in -- #54 +//│ if x$10 -- #53 +//│ true => +//│ let x$12 = -(n$0,1) in -- #49 +//│ let* (x$13) = mk(x$12) in -- #48 +//│ let x$14 = S(x$13) in -- #47 +//│ jump j$2(x$14) -- #46 +//│ false => +//│ let x$15 = O() in -- #52 +//│ jump j$2(x$15) -- #51 +//│ ) +//│ Def(5, j$2, [x$11], +//│ 1, +//│ x$11 -- #35 +//│ ) +//│ Def(6, foo, [], +//│ 1, +//│ let* (x$16) = mk(10) in -- #73 +//│ let x$17 = S(x$16) in -- #72 +//│ let x$18 = S(x$17) in -- #71 +//│ let* (x$19) = odd(x$18) in -- #70 +//│ x$19 -- #69 +//│ ) +//│ }, +//│ let* (x$20) = foo() in -- #77 +//│ x$20 -- #76) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Def(0, odd, [x$0], +//│ 1, +//│ case x$0 of -- #15 +//│ O => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ S => +//│ let x$3 = x$0.s in -- #14 +//│ let* (x$4) = even(x$3) in -- #13 +//│ jump j$0(x$4) -- #12 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, even, [x$5], +//│ 1, +//│ case x$5 of -- #31 +//│ O => +//│ let x$7 = True() in -- #20 +//│ jump j$1(x$7) -- #19 +//│ S => +//│ let x$8 = x$5.s in -- #30 +//│ let* (x$9) = odd(x$8) in -- #29 +//│ jump j$1(x$9) -- #28 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #17 +//│ ) +//│ Def(4, mk, [n$0], +//│ 1, +//│ let x$10 = >(n$0,0) in -- #54 +//│ if x$10 -- #53 +//│ true => +//│ let x$12 = -(n$0,1) in -- #49 +//│ let* (x$13) = mk(x$12) in -- #48 +//│ let x$14 = S(x$13) in -- #47 +//│ jump j$2(x$14) -- #46 +//│ false => +//│ let x$15 = O() in -- #52 +//│ jump j$2(x$15) -- #51 +//│ ) +//│ Def(5, j$2, [x$11], +//│ 1, +//│ x$11 -- #35 +//│ ) +//│ Def(6, foo, [], +//│ 1, +//│ let* (x$16) = mk(10) in -- #73 +//│ let x$17 = S(x$16) in -- #72 +//│ let x$18 = S(x$17) in -- #71 +//│ let* (x$19) = odd(x$18) in -- #70 +//│ x$19 -- #69 +//│ ) +//│ }, +//│ let* (x$20) = foo() in -- #77 +//│ x$20 -- #76) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -937,6 +1268,81 @@ main() //│ }, //│ let* (x$25) = main() in -- #90 //│ x$25 -- #89) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Def(0, odd, [x$0], +//│ 1, +//│ case x$0 of -- #15 +//│ O => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ S => +//│ let x$3 = x$0.s in -- #14 +//│ let* (x$4) = even(x$3) in -- #13 +//│ jump j$0(x$4) -- #12 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, even, [x$5], +//│ 1, +//│ case x$5 of -- #31 +//│ O => +//│ let x$7 = True() in -- #20 +//│ jump j$1(x$7) -- #19 +//│ S => +//│ let x$8 = x$5.s in -- #30 +//│ let* (x$9) = odd(x$8) in -- #29 +//│ jump j$1(x$9) -- #28 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #17 +//│ ) +//│ Def(4, foo, [], +//│ 1, +//│ let x$10 = >(10,0) in -- #52 +//│ if x$10 -- #51 +//│ true => +//│ let x$13 = O() in -- #47 +//│ let x$14 = S(x$13) in -- #46 +//│ jump j$2(x$14) -- #45 +//│ false => +//│ let x$15 = O() in -- #50 +//│ jump j$2(x$15) -- #49 +//│ ) +//│ Def(5, j$2, [x$11], +//│ 1, +//│ let* (x$12) = odd(x$11) in -- #40 +//│ x$12 -- #39 +//│ ) +//│ Def(6, bar, [], +//│ 1, +//│ let x$16 = >(10,0) in -- #78 +//│ if x$16 -- #77 +//│ true => +//│ let x$18 = O() in -- #68 +//│ let x$19 = S(x$18) in -- #67 +//│ let* (x$20) = odd(x$19) in -- #66 +//│ jump j$3(x$20) -- #65 +//│ false => +//│ let x$21 = O() in -- #76 +//│ let* (x$22) = odd(x$21) in -- #75 +//│ jump j$3(x$22) -- #74 +//│ ) +//│ Def(7, j$3, [x$17], +//│ 1, +//│ x$17 -- #56 +//│ ) +//│ Def(8, main, [], +//│ 1, +//│ let* (x$23) = foo() in -- #86 +//│ let* (x$24) = bar() in -- #85 +//│ x$24 -- #84 +//│ ) +//│ }, +//│ let* (x$25) = main() in -- #90 +//│ x$25 -- #89) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { @@ -1116,6 +1522,75 @@ main(False) //│ let x$25 = False() in -- #88 //│ let* (x$26) = main(x$25) in -- #87 //│ x$26 -- #86) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Def(0, aaa, [], +//│ 1, +//│ let x$0 = 1 in -- #17 +//│ let x$1 = 2 in -- #16 +//│ let x$2 = 3 in -- #15 +//│ let x$3 = 4 in -- #14 +//│ let x$4 = +(x$0,x$1) in -- #13 +//│ let x$5 = -(x$4,x$2) in -- #12 +//│ let x$6 = +(x$5,x$3) in -- #11 +//│ x$6 -- #10 +//│ ) +//│ Def(1, bbb, [], +//│ 1, +//│ let* (x$7) = aaa() in -- #28 +//│ let x$8 = *(x$7,100) in -- #27 +//│ let x$9 = +(x$8,4) in -- #26 +//│ x$9 -- #25 +//│ ) +//│ Def(2, not, [x$10], +//│ 1, +//│ if x$10 -- #37 +//│ true => +//│ let x$12 = False() in -- #33 +//│ jump j$0(x$12) -- #32 +//│ false => +//│ let x$13 = True() in -- #36 +//│ jump j$0(x$13) -- #35 +//│ ) +//│ Def(3, j$0, [x$11], +//│ 1, +//│ x$11 -- #30 +//│ ) +//│ Def(4, foo, [x$14], +//│ 1, +//│ if x$14 -- #59 +//│ true => +//│ let x$16 = A() in -- #42 +//│ jump j$1(x$16) -- #41 +//│ false => +//│ let* (x$17) = not(x$14) in -- #58 +//│ let* (x$18) = foo(x$17) in -- #57 +//│ let x$19 = B(x$18) in -- #56 +//│ jump j$1(x$19) -- #55 +//│ ) +//│ Def(5, j$1, [x$15], +//│ 1, +//│ x$15 -- #39 +//│ ) +//│ Def(6, main, [flag$0], +//│ 1, +//│ let* (x$20) = foo(flag$0) in -- #81 +//│ case x$20 of -- #80 +//│ A => +//│ let* (x$22) = aaa() in -- #71 +//│ jump j$2(x$22) -- #70 +//│ B => +//│ let x$23 = x$20.b in -- #79 +//│ let* (x$24) = bbb() in -- #78 +//│ jump j$2(x$24) -- #77 +//│ ) +//│ Def(7, j$2, [x$21], +//│ 1, +//│ x$21 -- #66 +//│ ) +//│ }, +//│ let x$25 = False() in -- #88 +//│ let* (x$26) = main(x$25) in -- #87 +//│ x$26 -- #86) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { @@ -1266,6 +1741,55 @@ main() //│ }, //│ let* (x$15) = main() in -- #63 //│ x$15 -- #62) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, head_opt, [l$0], +//│ 1, +//│ case l$0 of -- #17 +//│ Nil => +//│ let x$1 = None() in -- #4 +//│ jump j$0(x$1) -- #3 +//│ Cons => +//│ let x$2 = l$0.t in -- #16 +//│ let x$3 = l$0.h in -- #15 +//│ let x$4 = Some(x$3) in -- #14 +//│ jump j$0(x$4) -- #13 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, is_none, [o$0], +//│ 1, +//│ case o$0 of -- #29 +//│ None => +//│ let x$6 = True() in -- #22 +//│ jump j$1(x$6) -- #21 +//│ Some => +//│ let x$7 = o$0.x in -- #28 +//│ let x$8 = False() in -- #27 +//│ jump j$1(x$8) -- #26 +//│ ) +//│ Def(3, j$1, [x$5], +//│ 1, +//│ x$5 -- #19 +//│ ) +//│ Def(4, is_empty, [l$1], +//│ 1, +//│ let* (x$9) = head_opt(l$1) in -- #40 +//│ let* (x$10) = is_none(x$9) in -- #39 +//│ x$10 -- #38 +//│ ) +//│ Def(5, main, [], +//│ 1, +//│ let x$11 = Nil() in -- #59 +//│ let x$12 = Cons(2,x$11) in -- #58 +//│ let x$13 = Cons(1,x$12) in -- #57 +//│ let* (x$14) = is_empty(x$13) in -- #56 +//│ x$14 -- #55 +//│ ) +//│ }, +//│ let* (x$15) = main() in -- #63 +//│ x$15 -- #62) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -1412,6 +1936,70 @@ main() //│ }, //│ let* (x$19) = main() in -- #80 //│ x$19 -- #79) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, mk_list, [n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #24 +//│ if x$0 -- #23 +//│ true => +//│ let x$2 = Nil() in -- #6 +//│ jump j$0(x$2) -- #5 +//│ false => +//│ let x$3 = -(n$0,1) in -- #22 +//│ let* (x$4) = mk_list(x$3) in -- #21 +//│ let x$5 = Cons(n$0,x$4) in -- #20 +//│ jump j$0(x$5) -- #19 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, head_opt, [l$0], +//│ 1, +//│ case l$0 of -- #42 +//│ Nil => +//│ let x$7 = None() in -- #29 +//│ jump j$1(x$7) -- #28 +//│ Cons => +//│ let x$8 = l$0.t in -- #41 +//│ let x$9 = l$0.h in -- #40 +//│ let x$10 = Some(x$9) in -- #39 +//│ jump j$1(x$10) -- #38 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #26 +//│ ) +//│ Def(4, is_none, [o$0], +//│ 1, +//│ case o$0 of -- #54 +//│ None => +//│ let x$12 = True() in -- #47 +//│ jump j$2(x$12) -- #46 +//│ Some => +//│ let x$13 = o$0.x in -- #53 +//│ let x$14 = False() in -- #52 +//│ jump j$2(x$14) -- #51 +//│ ) +//│ Def(5, j$2, [x$11], +//│ 1, +//│ x$11 -- #44 +//│ ) +//│ Def(6, is_empty, [l$1], +//│ 1, +//│ let* (x$15) = head_opt(l$1) in -- #65 +//│ let* (x$16) = is_none(x$15) in -- #64 +//│ x$16 -- #63 +//│ ) +//│ Def(7, main, [], +//│ 1, +//│ let* (x$17) = mk_list(10) in -- #76 +//│ let* (x$18) = is_empty(x$17) in -- #75 +//│ x$18 -- #74 +//│ ) +//│ }, +//│ let* (x$19) = main() in -- #80 +//│ x$19 -- #79) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -1560,6 +2148,60 @@ main() //│ }, //│ let* (x$17) = main() in -- #74 //│ x$17 -- #73) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, mk_list, [n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #24 +//│ if x$0 -- #23 +//│ true => +//│ let x$2 = Nil() in -- #6 +//│ jump j$0(x$2) -- #5 +//│ false => +//│ let x$3 = -(n$0,1) in -- #22 +//│ let* (x$4) = mk_list(x$3) in -- #21 +//│ let x$5 = Cons(n$0,x$4) in -- #20 +//│ jump j$0(x$5) -- #19 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, last_opt, [l$0], +//│ 1, +//│ case l$0 of -- #59 +//│ Nil => +//│ let x$7 = None() in -- #29 +//│ jump j$1(x$7) -- #28 +//│ Cons => +//│ let x$8 = l$0.t in -- #58 +//│ let x$9 = l$0.h in -- #57 +//│ case x$8 of -- #56 +//│ Nil => +//│ let x$11 = Some(x$9) in -- #42 +//│ jump j$2(x$11) -- #41 +//│ Cons => +//│ let x$12 = x$8.t in -- #55 +//│ let x$13 = x$8.h in -- #54 +//│ let* (x$14) = last_opt(x$8) in -- #53 +//│ jump j$2(x$14) -- #52 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #26 +//│ ) +//│ Def(4, j$2, [x$10], +//│ 1, +//│ jump j$1(x$10) -- #36 +//│ ) +//│ Def(5, main, [], +//│ 1, +//│ let* (x$15) = mk_list(10) in -- #70 +//│ let* (x$16) = last_opt(x$15) in -- #69 +//│ x$16 -- #68 +//│ ) +//│ }, +//│ let* (x$17) = main() in -- #74 +//│ x$17 -- #73) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -1748,6 +2390,96 @@ main() //│ }, //│ let* (x$39) = main() in -- #140 //│ x$39 -- #139) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, is_some, [o$0], +//│ 1, +//│ case o$0 of -- #11 +//│ Some => +//│ let x$1 = o$0.x in -- #7 +//│ let x$2 = True() in -- #6 +//│ jump j$0(x$2) -- #5 +//│ None => +//│ let x$3 = False() in -- #10 +//│ jump j$0(x$3) -- #9 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, e0, [w$0], +//│ 1, +//│ let x$4 = +(w$0,8) in -- #21 +//│ let x$5 = +(x$4,9) in -- #20 +//│ let x$6 = +(x$5,10) in -- #19 +//│ x$6 -- #18 +//│ ) +//│ Def(3, e1, [a$0,c$0], +//│ 1, +//│ let x$7 = +(a$0,1) in -- #34 +//│ let x$8 = +(x$7,2) in -- #33 +//│ let x$9 = +(x$8,3) in -- #32 +//│ let x$10 = +(x$9,4) in -- #31 +//│ x$10 -- #30 +//│ ) +//│ Def(4, e3, [c$1], +//│ 1, +//│ let x$11 = 4 in -- #67 +//│ let x$12 = 5 in -- #66 +//│ let x$13 = 6 in -- #65 +//│ let x$14 = 7 in -- #64 +//│ if c$1 -- #63 +//│ true => +//│ let x$16 = +(x$11,x$12) in -- #51 +//│ let x$17 = +(x$16,x$13) in -- #50 +//│ let x$18 = +(x$17,x$14) in -- #49 +//│ jump j$1(x$18) -- #48 +//│ false => +//│ let x$19 = +(x$11,x$12) in -- #62 +//│ let x$20 = -(x$19,x$13) in -- #61 +//│ let x$21 = +(x$20,x$14) in -- #60 +//│ jump j$1(x$21) -- #59 +//│ ) +//│ Def(5, j$1, [x$15], +//│ 1, +//│ x$15 -- #40 +//│ ) +//│ Def(6, e2, [x$22], +//│ 1, +//│ let x$23 = +(x$22,12) in -- #77 +//│ let x$24 = +(x$23,13) in -- #76 +//│ let x$25 = +(x$24,14) in -- #75 +//│ x$25 -- #74 +//│ ) +//│ Def(7, f, [x$26], +//│ 1, +//│ let* (x$27) = is_some(x$26) in -- #117 +//│ let* (x$28) = e3(x$27) in -- #116 +//│ case x$26 of -- #115 +//│ Some => +//│ let x$31 = x$26.x in -- #107 +//│ let* (x$32) = e1(x$31,x$28) in -- #106 +//│ jump j$2(x$32) -- #105 +//│ None => +//│ let* (x$33) = e2(x$28) in -- #114 +//│ jump j$2(x$33) -- #113 +//│ ) +//│ Def(8, j$2, [x$29], +//│ 1, +//│ let* (x$30) = e0(x$29) in -- #95 +//│ x$30 -- #94 +//│ ) +//│ Def(9, main, [], +//│ 1, +//│ let x$34 = Some(2) in -- #136 +//│ let* (x$35) = f(x$34) in -- #135 +//│ let x$36 = None() in -- #134 +//│ let* (x$37) = f(x$36) in -- #133 +//│ let x$38 = +(x$35,x$37) in -- #132 +//│ x$38 -- #131 +//│ ) +//│ }, +//│ let* (x$39) = main() in -- #140 +//│ x$39 -- #139) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -1980,6 +2712,104 @@ main() //│ }, //│ let* (x$40) = main() in -- #149 //│ x$40 -- #148) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, is_some, [o$0], +//│ 1, +//│ case o$0 of -- #11 +//│ Some => +//│ let x$1 = o$0.x in -- #7 +//│ let x$2 = True() in -- #6 +//│ jump j$0(x$2) -- #5 +//│ None => +//│ let x$3 = False() in -- #10 +//│ jump j$0(x$3) -- #9 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, e0, [w$0], +//│ 1, +//│ let x$4 = +(w$0,8) in -- #21 +//│ let x$5 = +(x$4,9) in -- #20 +//│ let x$6 = +(x$5,10) in -- #19 +//│ x$6 -- #18 +//│ ) +//│ Def(3, e1, [a$0,z$0], +//│ 1, +//│ let x$7 = >(a$0,0) in -- #43 +//│ if x$7 -- #42 +//│ true => +//│ let x$9 = -(a$0,1) in -- #39 +//│ let x$10 = Some(x$9) in -- #38 +//│ let* (x$11) = f(x$10) in -- #37 +//│ jump j$1(x$11) -- #36 +//│ false => +//│ jump j$1(z$0) -- #41 +//│ ) +//│ Def(4, j$1, [x$8], +//│ 1, +//│ x$8 -- #25 +//│ ) +//│ Def(5, e3, [c$0], +//│ 1, +//│ let x$12 = 4 in -- #76 +//│ let x$13 = 5 in -- #75 +//│ let x$14 = 6 in -- #74 +//│ let x$15 = 7 in -- #73 +//│ if c$0 -- #72 +//│ true => +//│ let x$17 = +(x$12,x$13) in -- #60 +//│ let x$18 = +(x$17,x$14) in -- #59 +//│ let x$19 = +(x$18,x$15) in -- #58 +//│ jump j$2(x$19) -- #57 +//│ false => +//│ let x$20 = +(x$12,x$13) in -- #71 +//│ let x$21 = -(x$20,x$14) in -- #70 +//│ let x$22 = +(x$21,x$15) in -- #69 +//│ jump j$2(x$22) -- #68 +//│ ) +//│ Def(6, j$2, [x$16], +//│ 1, +//│ x$16 -- #49 +//│ ) +//│ Def(7, e2, [x$23], +//│ 1, +//│ let x$24 = +(x$23,12) in -- #86 +//│ let x$25 = +(x$24,13) in -- #85 +//│ let x$26 = +(x$25,14) in -- #84 +//│ x$26 -- #83 +//│ ) +//│ Def(8, f, [x$27], +//│ 1, +//│ let* (x$28) = is_some(x$27) in -- #126 +//│ let* (x$29) = e3(x$28) in -- #125 +//│ case x$27 of -- #124 +//│ Some => +//│ let x$32 = x$27.x in -- #116 +//│ let* (x$33) = e1(x$32,x$29) in -- #115 +//│ jump j$3(x$33) -- #114 +//│ None => +//│ let* (x$34) = e2(x$29) in -- #123 +//│ jump j$3(x$34) -- #122 +//│ ) +//│ Def(9, j$3, [x$30], +//│ 1, +//│ let* (x$31) = e0(x$30) in -- #104 +//│ x$31 -- #103 +//│ ) +//│ Def(10, main, [], +//│ 1, +//│ let x$35 = Some(2) in -- #145 +//│ let* (x$36) = f(x$35) in -- #144 +//│ let x$37 = None() in -- #143 +//│ let* (x$38) = f(x$37) in -- #142 +//│ let x$39 = +(x$36,x$38) in -- #141 +//│ x$39 -- #140 +//│ ) +//│ }, +//│ let* (x$40) = main() in -- #149 +//│ x$40 -- #148) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 151ebda156..67d2956428 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -16,12 +16,15 @@ class IRDiffTestCompiler extends DiffTests { if (mode.useIR || mode.irVerbose) try output("\n\nIR:") - val gb = Builder(Fresh(), FreshInt(), FreshInt(), FreshInt()) + val fnUid = FreshInt() + val tag = FreshInt() + + val gb = Builder(Fresh(), fnUid, FreshInt(), tag) val graph_ = gb.buildGraph(unit) output(graph_.toString()) val graph = if (!mode.noTailrecOpt) { - val tailRecOpt = new TailRecOpt + val tailRecOpt = new TailRecOpt(fnUid, tag) tailRecOpt(graph_) } else { graph_ From 9b65640c016bed3b11b952422bc1754eb7fe0d17 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 24 Mar 2024 15:17:16 +0800 Subject: [PATCH 03/59] update map braces --- .../mlscript/compiler/optimizer/TailRecOpt.scala | 14 ++++++++------ compiler/shared/test/diff-ir/NuScratch.mls | 9 +++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 compiler/shared/test/diff-ir/NuScratch.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 44813a4af8..80e5929743 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -132,8 +132,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { def transformNode(node: Node)(implicit info: DefnInfo): Node = node match case Jump(defn, args) => // transform the stack frame - val start = stackFrame.take(info.stackFrameIdx).drop(1).map(n => Expr.Ref(n)) // we drop tailrecBranch and replace it with the defn id - val end = stackFrame.drop(info.stackFrameIdx + args.size).map(n => Expr.Ref(n)) + val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } val concated = asLit(info.defn.id) :: start ::: args ::: end Jump(newDefnRef, concated) @@ -167,16 +167,18 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { ).attachTag(tag) val first = defnsList.head; - val newNode = defnsList.tail.foldLeft(transformNode(first.body)(defnInfoMap(first)))( - (elz, defn) => makeCaseBranch(defn.id, transformNode(defn.body)(defnInfoMap(defn)), elz) - ).attachTag(tag) + val newNode = defnsList.tail + .foldLeft(transformNode(first.body)(defnInfoMap(first)))((elz, defn) => + makeCaseBranch(defn.id, transformNode(defn.body)(defnInfoMap(defn)), elz) + ) + .attachTag(tag) // TODO: What is resultNum? It's only ever set to 1 elsewhere val newDefn = Defn(fnUid.make, newName, stackFrame, 1, newNode) newDefnRef.defn = Left(newDefn) - defns.map(d => transformDefn(d)) + newDefn + defns.map { d => transformDefn(d) } + newDefn } def partition(defns: Set[Defn]): List[Set[Defn]] = { diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls new file mode 100644 index 0000000000..99f3c510db --- /dev/null +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -0,0 +1,9 @@ +:NewParser +:ParseOnly +:UseIR + +:interpIR +if 2 is + 2 then 0 + 3 then 0 + else 0 \ No newline at end of file From 915891dd6b77de123c2076435a36ddb06f7ee5a8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 24 Mar 2024 15:39:49 +0800 Subject: [PATCH 04/59] small refactor --- .../compiler/optimizer/TailRecOpt.scala | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 80e5929743..6a5418ac16 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -11,6 +11,7 @@ import mlscript.compiler.ir.ClassInfo import mlscript.compiler.ir.DefnRef import mlscript.compiler.ir.Expr import mlscript.IntLit +import mlscript.compiler.ir.resolveDefnRef // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { @@ -40,7 +41,9 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { } // TODO: this is untested. test this. - private def partitionNodes(defns: DefnGraph)(implicit nodeMap: Map[Defn, DefnNode]): List[DefnGraph] = { + private def partitionNodes(implicit nodeMap: Map[Defn, DefnNode]): List[DefnGraph] = { + val defns = nodeMap.values.toSet + var ctr = 0 var stack: List[DefnNode] = Nil var sccs: List[DefnGraph] = Nil @@ -95,8 +98,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private case class DefnInfo(defn: Defn, stackFrameIdx: Int) - // Returns a set containing the original set of functions pointing to an optimized function - // and the optimized function. + // Returns a set containing the optimized function and the + // original functions pointing to an optimized function. // TODO: Currently untested def optimize(defns: Set[Defn], classes: Set[ClassInfo]): Set[Defn] = { @@ -156,7 +159,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // let scrut = tailrecBranch == value // in case scrut of True -> e1 // False -> e2 - // def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = val name = Name("scrut") val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) @@ -167,9 +169,12 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { ).attachTag(tag) val first = defnsList.head; + val firstNode = transformNode(first.body)(defnInfoMap(first)) + val newNode = defnsList.tail - .foldLeft(transformNode(first.body)(defnInfoMap(first)))((elz, defn) => - makeCaseBranch(defn.id, transformNode(defn.body)(defnInfoMap(defn)), elz) + .foldLeft(firstNode)((elz, defn) => + val thisNode = transformNode(defn.body)(defnInfoMap(defn)) + makeCaseBranch(defn.id, thisNode, elz) ) .attachTag(tag) @@ -182,16 +187,20 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { } def partition(defns: Set[Defn]): List[Set[Defn]] = { - val nodeMap = defns.foldLeft[Map[Defn, DefnNode]](Map())((m, d) => m + (d -> DefnNode(d))) - partitionNodes(nodeMap.values.toSet)(nodeMap).map(g => g.map(d => d.defn)) + val nodeMap: Map[Defn, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d -> DefnNode(d))) + partitionNodes(nodeMap).map(g => g.map(d => d.defn)) } def apply(p: Program) = run(p) def run(p: Program): Program = { - val defnMap = p.defs.foldLeft[Map[Int, Defn]](Map())((m, d) => m + (d.id -> d)) + val partitions = partition(p.defs) + val newDefs: Set[Defn] = partitions.flatMap { optimize(_, p.classes) }.toSet + + // update the definition refs + newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } + resolveDefnRef(p.main, newDefs, true) - // TODO - p + Program(p.classes, newDefs, p.main) } } From 6f2ca3da89473b8de7687afb647a20354ec7b16d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 25 Mar 2024 02:29:02 +0800 Subject: [PATCH 05/59] refactor --- .../mlscript/compiler/optimizer/TailRecOpt.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 6a5418ac16..0fd9f592ab 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -147,13 +147,11 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Tail calls to another function in the component will be replaced with a tail call // to the merged function - def transformDefn(defn: Defn): Defn = Defn( - defn.id, - defn.name, - defn.params, - defn.resultNum, - Jump(newDefnRef, asLit(defn.id) :: stackFrame.map(n => Expr.Ref(n)).drop(1)).attachTag(tag) - ) + def transformDefn(defn: Defn): Defn = { + val args = asLit(defn.id) :: stackFrame.drop(1).map { Expr.Ref(_) } + val jmp = Jump(newDefnRef, args).attachTag(tag) + Defn(defn.id, defn.name, defn.params, defn.resultNum, jmp) + } // given expressions value, e1, e2, transform it into // let scrut = tailrecBranch == value From f987e029239f7645142eb7d0406df6a7392464e3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 29 Mar 2024 16:10:52 +0800 Subject: [PATCH 06/59] Update test infrastructure, fix code, add basic test for tailrec. --- .../compiler/optimizer/TailRecOpt.scala | 42 +++++++-- compiler/shared/test/diff-ir/IR.mls | 1 + compiler/shared/test/diff-ir/IRComplex.mls | 1 + compiler/shared/test/diff-ir/IRRec.mls | 1 + compiler/shared/test/diff-ir/IRTailRec.mls | 87 +++++++++++++++++++ compiler/shared/test/diff-ir/NuScratch.mls | 9 -- .../test/scala/mlscript/compiler/TestIR.scala | 7 +- .../src/test/scala/mlscript/DiffTests.scala | 12 ++- 8 files changed, 137 insertions(+), 23 deletions(-) create mode 100644 compiler/shared/test/diff-ir/IRTailRec.mls delete mode 100644 compiler/shared/test/diff-ir/NuScratch.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 0fd9f592ab..acff742102 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -17,9 +17,27 @@ import mlscript.compiler.ir.resolveDefnRef class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] - private def findTailCalls(node: Node)(implicit nodeMap: Map[Defn, DefnNode]): List[DefnNode] = node match + // Rewrites nodes of the form + // let x = g(params) in x + // as Jump(g, params) + private def rewriteTailCalls(node: Node): Node = node match + case Case(scrut, cases) => Case(scrut, cases.map((ci, node) => (ci, rewriteTailCalls(node)))) + case LetExpr(name, expr, body) => LetExpr(name, expr, rewriteTailCalls(body)) + case LetCall(names, defn, args, body) => body match + case Result(res) => + val results = res.collect { case Expr.Ref(name) => name } + if (names == results) then + Jump(defn, args) + else + LetCall(names, defn, args, rewriteTailCalls(body)) + + case _ => LetCall(names, defn, args, rewriteTailCalls(body)) + case _ => node + + + private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match case Result(res) => Nil - case Jump(defn, args) => nodeMap(defn.expectDefn) :: Nil // assume that all definition references are resolved + case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) case LetCall(names, defn, args, body) => findTailCalls(body) @@ -41,7 +59,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { } // TODO: this is untested. test this. - private def partitionNodes(implicit nodeMap: Map[Defn, DefnNode]): List[DefnGraph] = { + private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = { val defns = nodeMap.values.toSet var ctr = 0 @@ -56,7 +74,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { stack = src :: stack for (u <- findTailCalls(src.defn.body)) do { - if (u.visited) + if (u.visited) then if (!u.processed) src.lowest = u.num.min(src.lowest) else @@ -118,6 +136,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // TODO: make sure that name clashes aren't a problem val trName = Name("tailrecBranch"); + + // TODO: Handle clashing variable names in different functions. val stackFrame = trName :: defnsList.flatMap(_.params) // take union of stack frames val stackFrameIdxes = defnsList.foldRight(1 :: Nil)((defn, ls) => defn.params.size + ls.head :: ls) @@ -148,6 +168,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Tail calls to another function in the component will be replaced with a tail call // to the merged function def transformDefn(defn: Defn): Defn = { + // TODO: Figure out how to substitute variables with dummy variables. val args = asLit(defn.id) :: stackFrame.drop(1).map { Expr.Ref(_) } val jmp = Jump(newDefnRef, args).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, jmp) @@ -185,20 +206,25 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { } def partition(defns: Set[Defn]): List[Set[Defn]] = { - val nodeMap: Map[Defn, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d -> DefnNode(d))) + val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(g => g.map(d => d.defn)) } def apply(p: Program) = run(p) - def run(p: Program): Program = { - val partitions = partition(p.defs) + def run_debug(p: Program): (Program, List[Set[String]]) = { + val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) + val partitions = partition(rewritten) val newDefs: Set[Defn] = partitions.flatMap { optimize(_, p.classes) }.toSet // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } resolveDefnRef(p.main, newDefs, true) - Program(p.classes, newDefs, p.main) + (Program(p.classes, newDefs, p.main), partitions.map(t => t.map(f => f.name))) + } + + def run(p: Program): Program = { + run_debug(p)._1 } } diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 7d6c4913af..4061b25e99 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class Pair(x, y) diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index fa0a44dad8..1de0cf2d73 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class A(x, y, z) diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index cb3f365216..cf8ab42e06 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -1,6 +1,7 @@ :NewParser :ParseOnly :UseIR +:NoTailRec :interpIR class True diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls new file mode 100644 index 0000000000..cc9857dbea --- /dev/null +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -0,0 +1,87 @@ +:NewParser +:ParseOnly +:UseIR + +// :interpIR +class True // remark: this really should be changed so we don't need to add `class True` every time we want to use conditionals +class False +fun f(n) = g(n) +fun g(m) = f(m) +fun h() = h() +2 +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |g|(|n|)|↵|#fun| |g|(|m|)| |#=| |f|(|m|)|↵|#fun| |h|(||)| |#=| |h|(||)|↵|2| +//│ Parsed: {class True {}; class False {}; fun f = (n,) => g(n,); fun g = (m,) => f(m,); fun h = () => h(); 2} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ let* (x$0) = g(n$0) in -- #5 +//│ x$0 -- #4 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ let* (x$1) = f(m$0) in -- #11 +//│ x$1 -- #10 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 +//│ ) +//│ }, +//│ 2 -- #16) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(h), Set(g, f)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ jump _g_fopt(0,m$0,n$0) -- #21 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ jump _g_fopt(1,m$0,n$0) -- #20 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ jump h() -- #x +//│ ) +//│ Def(3, _g_fopt, [tailrecBranch,m$0,n$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #19 +//│ if scrut -- #17 +//│ true => +//│ jump _g_fopt(0,n$0,n$0) -- #x +//│ false => +//│ jump _g_fopt(1,m$0,m$0) -- #x +//│ ) +//│ }, +//│ 2 -- #16) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ jump _g_fopt(0,m$0,n$0) -- #21 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ jump _g_fopt(1,m$0,n$0) -- #20 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ jump h() -- #x +//│ ) +//│ Def(3, _g_fopt, [tailrecBranch,m$0,n$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #19 +//│ if scrut -- #17 +//│ true => +//│ jump _g_fopt(0,n$0,n$0) -- #x +//│ false => +//│ jump _g_fopt(1,m$0,m$0) -- #x +//│ ) +//│ }, +//│ 2 -- #16) diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls deleted file mode 100644 index 99f3c510db..0000000000 --- a/compiler/shared/test/diff-ir/NuScratch.mls +++ /dev/null @@ -1,9 +0,0 @@ -:NewParser -:ParseOnly -:UseIR - -:interpIR -if 2 is - 2 then 0 - 3 then 0 - else 0 \ No newline at end of file diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 67d2956428..58c6b81401 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -23,9 +23,12 @@ class IRDiffTestCompiler extends DiffTests { val graph_ = gb.buildGraph(unit) output(graph_.toString()) - val graph = if (!mode.noTailrecOpt) { + val graph = if (!mode.noTailRecOpt) { val tailRecOpt = new TailRecOpt(fnUid, tag) - tailRecOpt(graph_) + val (g, comps) = tailRecOpt.run_debug(graph_) + output("\nStrongly Connected Tail Calls:") + output(comps.toString) + g } else { graph_ } diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 0ffbd433cc..6bdc86e876 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -43,7 +43,7 @@ abstract class ModeType { def allowEscape: Bool def mono: Bool def useIR: Bool - def noTailrecOpt: Bool + def noTailRecOpt: Bool def interpIR: Bool def irVerbose: Bool def lift: Bool @@ -179,7 +179,7 @@ class DiffTests lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, - noTailrecOpt: Bool = false, + noTailRecOpt: Bool = false, useIR: Bool = false, interpIR: Bool = false, irVerbose: Bool = false, @@ -206,6 +206,7 @@ class DiffTests var irregularTypes = false var prettyPrintQQ = false var useIR = false + var noTailRec = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. @@ -250,6 +251,7 @@ class DiffTests case "NewParser" => newParser = true; mode case "NewDefs" => newParser = true; newDefs = true; mode case "NoJS" => noJavaScript = true; mode + case "NoTailRec" => noTailRec = true; mode case "NoProvs" => noProvs = true; mode case "GeneralizeCurriedFunctions" => generalizeCurriedFunctions = true; mode case "DontGeneralizeCurriedFunctions" => generalizeCurriedFunctions = false; mode @@ -288,7 +290,7 @@ class DiffTests case "escape" => mode.copy(allowEscape = true) case "mono" => {mode.copy(mono = true)} case "lift" => {mode.copy(lift = true)} - case "noTailrec" => mode.copy(noTailrecOpt = true) + case "noTailRec" => mode.copy(noTailRecOpt = true) case "nolift" => {mode.copy(nolift = true)} case "exit" => out.println(exitMarker) @@ -463,7 +465,9 @@ class DiffTests output(s"AST: $res") val newMode = if (useIR) { mode.copy(useIR = true) } else mode - val (postLines, nuRes) = postProcess(newMode, basePath, testName, res, output) + val newNewMode = if (noTailRec) { newMode.copy(noTailRecOpt = true) } else newMode + + val (postLines, nuRes) = postProcess(newNewMode, basePath, testName, res, output) postLines.foreach(output) if (parseOnly) From 184a5de45314c2ecce79fa9297ab9500d5ff1ac8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 29 Mar 2024 16:23:28 +0800 Subject: [PATCH 07/59] Update test --- compiler/shared/test/diff-ir/IRTailRec.mls | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index cc9857dbea..9c7a46a4c9 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -2,8 +2,9 @@ :ParseOnly :UseIR +// remark: this really should be changed so we don't need to add `class True` every time we want to use conditionals // :interpIR -class True // remark: this really should be changed so we don't need to add `class True` every time we want to use conditionals +class True class False fun f(n) = g(n) fun g(m) = f(m) From 3066c7f53cf8c43706ad8acd8143682859af869f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 29 Mar 2024 16:29:38 +0800 Subject: [PATCH 08/59] remove todos --- .../main/scala/mlscript/compiler/optimizer/TailRecOpt.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index acff742102..9e711b6c9d 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -58,7 +58,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { var processed: Boolean = false } - // TODO: this is untested. test this. private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = { val defns = nodeMap.values.toSet @@ -118,7 +117,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Returns a set containing the optimized function and the // original functions pointing to an optimized function. - // TODO: Currently untested def optimize(defns: Set[Defn], classes: Set[ClassInfo]): Set[Defn] = { def asLit(x: Int) = Expr.Literal(IntLit(x)) From d7b4bb31c03b9445bd1a222e595ab3d1b9a343be Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 8 Apr 2024 17:10:10 +0800 Subject: [PATCH 09/59] Add field assignment to IR --- .../compiler/ir/DefnRefResolver.scala | 1 + .../main/scala/mlscript/compiler/ir/IR.scala | 19 +++++++++++++++++-- .../scala/mlscript/compiler/ir/Interp.scala | 1 + .../mlscript/compiler/ir/Validator.scala | 1 + .../compiler/optimizer/Analysis.scala | 5 +++++ .../compiler/optimizer/TailRecOpt.scala | 10 ++++++---- compiler/shared/test/diff-ir/NuScratch.mls | 3 +++ 7 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 compiler/shared/test/diff-ir/NuScratch.mls diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index d49e1fb38a..c0219cc73a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -23,6 +23,7 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case None => if (!allowInlineJp) throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") + case AssignField(assignee, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(node: Defn) = f(node.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index e699379d24..91abf0d7a9 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -138,6 +138,7 @@ enum Node: // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node) + case AssignField(assignee: Name, fieldName: Str, value: TrivialExpr, body: Node) var tag = DefnTag(-1) @@ -160,7 +161,9 @@ enum Node: case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case LetCall(names, defn, args, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) + case LetCall(names, defn, args, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) + case AssignField(assignee, fieldName, value, body) => + AssignField(f(assignee), fieldName, value.mapNameOfTrivialExpr(f), body.mapName(f)) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) @@ -172,6 +175,8 @@ enum Node: case LetCall(names, defn, args, body) => val names_copy = names.map(_.copy) LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) + case AssignField(assignee, fieldName, value, body) => + AssignField(assignee.trySubst(ctx), fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) private def toDocument: Document = this match case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") @@ -217,7 +222,15 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - + case AssignField(assignee, fieldName, value, body) => + stack( + raw("assign") + <:> raw(assignee.toString + "." + fieldName) + <:> raw(":=") + <:> value.toDocument + <:> raw("in") + <:> raw(s"-- $tag"), + body.toDocument) def locMarker: LocMarker = val marker = this match case Result(res) => LocMarker.MResult(res.map(_.toExpr.locMarker)) @@ -225,6 +238,7 @@ enum Node: case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) case LetCall(names, defn, args, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) + case AssignField(assignee, field, value, _) => LocMarker.MAssignField(assignee.toString, field, value.toExpr.locMarker) marker.tag = this.tag marker @@ -252,6 +266,7 @@ enum LocMarker: case MCase(scrut: Str, cases: Ls[ClassInfo]) case MLetExpr(name: Str, expr: LocMarker) case MLetCall(names: Ls[Str], defn: Str, args: Ls[LocMarker]) + case MAssignField(assignee: Str, field: Str, value: LocMarker) var tag = DefnTag(-1) def toDocument: Document = this match diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index fb4bce250b..9b3a15b322 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -155,6 +155,7 @@ class Interpreter(verbose: Bool): case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) case INode.LetCall(xs, defnref, args, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) + case INode.AssignField(_, _, _, _) => ??? // TODO: Interpret field assignments private def convert(defn: IDefn): Defn = Defn(defn.name, defn.params, defn.body |> convert) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index 8977a2ebd4..1a095f7ad0 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -17,6 +17,7 @@ private final class DefnRefInSet(defs: Set[Defn]): case _ => } f(body) + case AssignField(assignee, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(defn: Defn) = f(defn.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index e277196e98..60fb490fae 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -46,6 +46,7 @@ class UsefulnessAnalysis(verbose: Bool = false): case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, body) => args.foreach(f); names.foreach(addDef); f(body) + case AssignField(assignee, fieldName, value, body) => f(value); f(body) def run(x: Defn) = x.params.foreach(addDef) @@ -94,5 +95,9 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) f(using defined2)(body, fv2) + case AssignField(assignee, fieldName, value, body) => + val fv2 = if (defined.contains(assignee.str)) fv else fv + assignee.str + val fv3 = f(using defined)(value.toExpr, fv2) + f(using defined)(body, fv3) def run(node: Node) = f(using Set.empty)(node, Set.empty) def run_with(node: Node, defined: Set[Str]) = f(using defined)(node, Set.empty) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 9e711b6c9d..f7d4e1df4a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -41,6 +41,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) case LetCall(names, defn, args, body) => findTailCalls(body) + case AssignField(_, _, _, body) => findTailCalls(body) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -158,10 +159,11 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val concated = asLit(info.defn.id) :: start ::: args ::: end Jump(newDefnRef, concated) - case Result(_) => node - case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) - case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) - case LetCall(names, defn, args, body) => LetCall(names, defn, args, transformNode(body)) + case Result(_) => node + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) + case LetCall(names, defn, args, body) => LetCall(names, defn, args, transformNode(body)) + case AssignField(assignee, field, value, body) => AssignField(assignee, field, value, transformNode(body)) // Tail calls to another function in the component will be replaced with a tail call // to the merged function diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls new file mode 100644 index 0000000000..f86f327c94 --- /dev/null +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -0,0 +1,3 @@ +:NewParser +:ParseOnly +:UseIR \ No newline at end of file From 058a929fb8b1a5b7cc6cb9d460aefc2c1d7f3a89 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 14 Apr 2024 14:24:20 +0800 Subject: [PATCH 10/59] Prevent unnecessary inlining for mutually tail recursive funcs, handle variable name clash --- .../compiler/optimizer/TailRecOpt.scala | 103 +++++++++++---- compiler/shared/test/diff-ir/IRTailRec.mls | 124 +++++++++++++++--- 2 files changed, 184 insertions(+), 43 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index f7d4e1df4a..f48e0c7429 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -12,6 +12,7 @@ import mlscript.compiler.ir.DefnRef import mlscript.compiler.ir.Expr import mlscript.IntLit import mlscript.compiler.ir.resolveDefnRef +import mlscript.compiler.ir.TrivialExpr // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { @@ -27,21 +28,30 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case Result(res) => val results = res.collect { case Expr.Ref(name) => name } if (names == results) then - Jump(defn, args) + Jump(defn, args).attachTag(tag) else LetCall(names, defn, args, rewriteTailCalls(body)) case _ => LetCall(names, defn, args, rewriteTailCalls(body)) case _ => node - + private def isTailCall(node: Node): Boolean = node match + case LetCall(names, defn, args, body) => body match + case Result(res) => + val results = res.collect { case Expr.Ref(name) => name } + return names == results + case _ => false + case _ => false + private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match case Result(res) => Nil case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) - case LetCall(names, defn, args, body) => findTailCalls(body) case AssignField(_, _, _, body) => findTailCalls(body) + case LetCall(names, defn, args, body) => + if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil + else findTailCalls(body) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -116,7 +126,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private case class DefnInfo(defn: Defn, stackFrameIdx: Int) - // Returns a set containing the optimized function and the + // Given a strongly connected component `defns`, + // returns a set containing the optimized function and the // original functions pointing to an optimized function. def optimize(defns: Set[Defn], classes: Set[ClassInfo]): Set[Defn] = { @@ -133,45 +144,70 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // concretely order the functions as soon as possible, since the order of the functions matter val defnsList = defns.toList + // assume all defns have the same number of results + // in fact, they should theoretically have the same return type if the program type checked + val resultNum = defnsList.head.resultNum + // TODO: make sure that name clashes aren't a problem val trName = Name("tailrecBranch"); - // TODO: Handle clashing variable names in different functions. - val stackFrame = trName :: defnsList.flatMap(_.params) // take union of stack frames + // To be used to replace variable names inside a definition to avoid variable name clashes + val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map( + defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap + ).toMap val stackFrameIdxes = defnsList.foldRight(1 :: Nil)((defn, ls) => defn.params.size + ls.head :: ls) - val defnInfoMap: Map[Defn, DefnInfo] = (defnsList zip stackFrameIdxes.drop(1)) - .foldLeft(Map.empty)((map, item) => map + (item._1 -> DefnInfo(item._1, item._2))) + val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes.drop(1)) + .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) + + val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames // TODO: This works fine for now, but ideally should find a way to guarantee the new // name is unique - val newName = defns.foldLeft("")(_ + "_" + _.name) + "opt" + val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt" + val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp" val newDefnRef = DefnRef(Right(newName)) + val jpDefnRef = DefnRef(Right(jpName)) + + def transformStackFrame(args: List[TrivialExpr])(implicit info: DefnInfo) = + val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } + asLit(info.defn.id) :: start ::: args ::: end - // Build the node. + // Build the node which will be contained inside the jump point. def transformNode(node: Node)(implicit info: DefnInfo): Node = node match case Jump(defn, args) => - // transform the stack frame - val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id - val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } - val concated = asLit(info.defn.id) :: start ::: args ::: end - Jump(newDefnRef, concated) + Jump(jpDefnRef, transformStackFrame(args)).attachTag(tag) case Result(_) => node case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) - case LetCall(names, defn, args, body) => LetCall(names, defn, args, transformNode(body)) + case LetCall(names, defn, args, body) => + if isTailCall(node) then + Jump(jpDefnRef, transformStackFrame(args)).attachTag(tag) + else + LetCall(names, defn, args, transformNode(body)) case AssignField(assignee, field, value, body) => AssignField(assignee, field, value, transformNode(body)) // Tail calls to another function in the component will be replaced with a tail call // to the merged function def transformDefn(defn: Defn): Defn = { // TODO: Figure out how to substitute variables with dummy variables. - val args = asLit(defn.id) :: stackFrame.drop(1).map { Expr.Ref(_) } - val jmp = Jump(newDefnRef, args).attachTag(tag) - Defn(defn.id, defn.name, defn.params, defn.resultNum, jmp) + val info = defnInfoMap(defn.id) + + val start = stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => Expr.Literal(IntLit(0)) } + val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end + + // We use a let call instead of a jump to avoid newDefn from being turned into a join point, + // which would cause it to be inlined and result in code duplication. + val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList + val namesExpr = names.map(Expr.Ref(_)) + val res = Result(namesExpr).attachTag(tag) + val call = LetCall(names, newDefnRef, args, res).attachTag(tag) + Defn(defn.id, defn.name, defn.params, defn.resultNum, call) } // given expressions value, e1, e2, transform it into @@ -187,22 +223,37 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { cases ).attachTag(tag) + def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match + case None => key + case Some(value) => value + + val first = defnsList.head; - val firstNode = transformNode(first.body)(defnInfoMap(first)) + val firstMap = nameMaps(first.id) + val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) + val firstNode = transformNode(firstBodyRenamed)(defnInfoMap(first.id)) val newNode = defnsList.tail .foldLeft(firstNode)((elz, defn) => - val thisNode = transformNode(defn.body)(defnInfoMap(defn)) + val nmeNap = nameMaps(defn.id) + val renamed = defn.body.mapName(getOrKey(nmeNap)) + val thisNode = transformNode(renamed)(defnInfoMap(defn.id)) makeCaseBranch(defn.id, thisNode, elz) ) .attachTag(tag) - // TODO: What is resultNum? It's only ever set to 1 elsewhere - val newDefn = Defn(fnUid.make, newName, stackFrame, 1, newNode) + val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode) + + val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) + val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp) + + // This is the definition that will be called + // val createIntermidDefn = + jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) - defns.map { d => transformDefn(d) } + newDefn + defns.map { d => transformDefn(d) } + newDefn + jpDefn } def partition(defns: Set[Defn]): List[Set[Defn]] = { @@ -213,8 +264,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = { - val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) - val partitions = partition(rewritten) + // val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) + val partitions = partition(p.defs) val newDefs: Set[Defn] = partitions.flatMap { optimize(_, p.classes) }.toSet // update the definition refs diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 9c7a46a4c9..6bed403506 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -2,8 +2,81 @@ :ParseOnly :UseIR +:noTailRec +:interpIR +class True +class False +fun f(n) = g(n) +fun g(m) = f(m) +fun h() = h() +2 +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |g|(|n|)|↵|#fun| |g|(|m|)| |#=| |f|(|m|)|↵|#fun| |h|(||)| |#=| |h|(||)|↵|2| +//│ Parsed: {class True {}; class False {}; fun f = (n,) => g(n,); fun g = (m,) => f(m,); fun h = () => h(); 2} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ let* (x$0) = g(n$0) in -- #5 +//│ x$0 -- #4 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ let* (x$1) = f(m$0) in -- #11 +//│ x$1 -- #10 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 +//│ ) +//│ }, +//│ 2 -- #16) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ let* (x$0) = g(n$0) in -- #5 +//│ x$0 -- #4 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ let* (x$1) = f(m$0) in -- #11 +//│ x$1 -- #10 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 +//│ ) +//│ }, +//│ 2 -- #16) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [n$0], +//│ 1, +//│ let* (x$0) = g(n$0) in -- #5 +//│ x$0 -- #4 +//│ ) +//│ Def(1, g, [m$0], +//│ 1, +//│ let* (x$1) = f(m$0) in -- #11 +//│ x$1 -- #10 +//│ ) +//│ Def(2, h, [], +//│ 1, +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 +//│ ) +//│ }, +//│ 2 -- #16) +//│ +//│ Interpreted: +//│ 2 + // remark: this really should be changed so we don't need to add `class True` every time we want to use conditionals -// :interpIR +:interpIR class True class False fun f(n) = g(n) @@ -39,24 +112,31 @@ fun h() = h() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ jump _g_fopt(0,m$0,n$0) -- #21 +//│ let* (r0) = _g_f_opt(0,n$0,0) in -- #26 +//│ r0 -- #25 //│ ) //│ Def(1, g, [m$0], //│ 1, -//│ jump _g_fopt(1,m$0,n$0) -- #20 +//│ let* (r0) = _g_f_opt(1,0,m$0) in -- #24 +//│ r0 -- #23 //│ ) //│ Def(2, h, [], //│ 1, -//│ jump h() -- #x +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 //│ ) -//│ Def(3, _g_fopt, [tailrecBranch,m$0,n$0], +//│ Def(3, _g_f_opt_jp, [tailrecBranch,g_m$0,f_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #19 -//│ if scrut -- #17 +//│ let scrut = ==(0,tailrecBranch) in -- #21 +//│ if scrut -- #19 //│ true => -//│ jump _g_fopt(0,n$0,n$0) -- #x +//│ jump _g_f_opt_jp(0,f_n$0,f_n$0) -- #18 //│ false => -//│ jump _g_fopt(1,m$0,m$0) -- #x +//│ jump _g_f_opt_jp(1,g_m$0,g_m$0) -- #17 +//│ ) +//│ Def(4, _g_f_opt, [tailrecBranch,g_m$0,f_n$0], +//│ 1, +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,f_n$0) -- #22 //│ ) //│ }, //│ 2 -- #16) @@ -65,24 +145,34 @@ fun h() = h() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ jump _g_fopt(0,m$0,n$0) -- #21 +//│ let* (r0) = _g_f_opt(0,n$0,0) in -- #26 +//│ r0 -- #25 //│ ) //│ Def(1, g, [m$0], //│ 1, -//│ jump _g_fopt(1,m$0,n$0) -- #20 +//│ let* (r0) = _g_f_opt(1,0,m$0) in -- #24 +//│ r0 -- #23 //│ ) //│ Def(2, h, [], //│ 1, -//│ jump h() -- #x +//│ let* (x$2) = h() in -- #15 +//│ x$2 -- #14 //│ ) -//│ Def(3, _g_fopt, [tailrecBranch,m$0,n$0], +//│ Def(3, _g_f_opt_jp, [tailrecBranch,g_m$0,f_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #19 -//│ if scrut -- #17 +//│ let scrut = ==(0,tailrecBranch) in -- #21 +//│ if scrut -- #19 //│ true => -//│ jump _g_fopt(0,n$0,n$0) -- #x +//│ jump _g_f_opt_jp(0,f_n$0,f_n$0) -- #18 //│ false => -//│ jump _g_fopt(1,m$0,m$0) -- #x +//│ jump _g_f_opt_jp(1,g_m$0,g_m$0) -- #17 +//│ ) +//│ Def(4, _g_f_opt, [tailrecBranch,g_m$0,f_n$0], +//│ 1, +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,f_n$0) -- #22 //│ ) //│ }, //│ 2 -- #16) +//│ +//│ Interpreted: +//│ 2 From 0e3a8755d653bf38cb22d31b6f9025407cafbee8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 14 Apr 2024 14:28:43 +0800 Subject: [PATCH 11/59] Update test --- compiler/shared/test/diff-ir/NuScratch.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls index f86f327c94..907012746a 100644 --- a/compiler/shared/test/diff-ir/NuScratch.mls +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -1,3 +1,3 @@ :NewParser :ParseOnly -:UseIR \ No newline at end of file +:UseIR From dd50c7a1614869737684016dd7d21200b196c66b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 14 Apr 2024 16:15:28 +0800 Subject: [PATCH 12/59] Improved tests --- compiler/shared/test/diff-ir/IRTailRec.mls | 243 +++++++++++++-------- 1 file changed, 155 insertions(+), 88 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 6bed403506..1041cc7694 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -6,173 +6,240 @@ :interpIR class True class False -fun f(n) = g(n) -fun g(m) = f(m) -fun h() = h() -2 -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |g|(|n|)|↵|#fun| |g|(|m|)| |#=| |f|(|m|)|↵|#fun| |h|(||)| |#=| |h|(||)|↵|2| -//│ Parsed: {class True {}; class False {}; fun f = (n,) => g(n,); fun g = (m,) => f(m,); fun h = () => h(); 2} +fun f(n) = if n == 0 then 0 else g(n - 1) +fun g(m) = if m == 0 then 1 else f(m - 1) +f(4) +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |#if| |n| |==| |0| |#then| |0| |#else| |g|(|n| |-| |1|)|↵|#fun| |g|(|m|)| |#=| |#if| |m| |==| |0| |#then| |1| |#else| |f|(|m| |-| |1|)|↵|f|(|4|)| +//│ Parsed: {class True {}; class False {}; fun f = (n,) => if (==(n,)(0,)) then 0 else g(-(n,)(1,),); fun g = (m,) => if (==(m,)(0,)) then 1 else f(-(m,)(1,),); f(4,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (x$0) = g(n$0) in -- #5 -//│ x$0 -- #4 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 //│ ) -//│ Def(1, g, [m$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let* (x$1) = f(m$0) in -- #11 -//│ x$1 -- #10 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 +//│ true => +//│ jump j$1(1) -- #23 +//│ false => +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(2, h, [], +//│ Def(3, j$1, [x$5], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (x$0) = g(n$0) in -- #5 -//│ x$0 -- #4 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 //│ ) -//│ Def(1, g, [m$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let* (x$1) = f(m$0) in -- #11 -//│ x$1 -- #10 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 +//│ true => +//│ jump j$1(1) -- #23 +//│ false => +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(2, h, [], +//│ Def(3, j$1, [x$5], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (x$0) = g(n$0) in -- #5 -//│ x$0 -- #4 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 //│ ) -//│ Def(1, g, [m$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let* (x$1) = f(m$0) in -- #11 -//│ x$1 -- #10 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 +//│ true => +//│ jump j$1(1) -- #23 +//│ false => +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(2, h, [], +//│ Def(3, j$1, [x$5], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ //│ Interpreted: -//│ 2 +//│ 0 -// remark: this really should be changed so we don't need to add `class True` every time we want to use conditionals :interpIR class True class False -fun f(n) = g(n) -fun g(m) = f(m) -fun h() = h() -2 -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |g|(|n|)|↵|#fun| |g|(|m|)| |#=| |f|(|m|)|↵|#fun| |h|(||)| |#=| |h|(||)|↵|2| -//│ Parsed: {class True {}; class False {}; fun f = (n,) => g(n,); fun g = (m,) => f(m,); fun h = () => h(); 2} +fun f(n) = if n == 0 then 0 else g(n - 1) +fun g(m) = if m == 0 then 1 else f(m - 1) +f(4) +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |#if| |n| |==| |0| |#then| |0| |#else| |g|(|n| |-| |1|)|↵|#fun| |g|(|m|)| |#=| |#if| |m| |==| |0| |#then| |1| |#else| |f|(|m| |-| |1|)|↵|f|(|4|)| +//│ Parsed: {class True {}; class False {}; fun f = (n,) => if (==(n,)(0,)) then 0 else g(-(n,)(1,),); fun g = (m,) => if (==(m,)(0,)) then 1 else f(-(m,)(1,),); f(4,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (x$0) = g(n$0) in -- #5 -//│ x$0 -- #4 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 //│ ) -//│ Def(1, g, [m$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let* (x$1) = f(m$0) in -- #11 -//│ x$1 -- #10 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 +//│ true => +//│ jump j$1(1) -- #23 +//│ false => +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(2, h, [], +//│ Def(3, j$1, [x$5], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ //│ Strongly Connected Tail Calls: -//│ List(Set(h), Set(g, f)) +//│ List(Set(g), Set(j$1), Set(f), Set(j$0)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (r0) = _g_f_opt(0,n$0,0) in -- #26 -//│ r0 -- #25 -//│ ) -//│ Def(1, g, [m$0], -//│ 1, -//│ let* (r0) = _g_f_opt(1,0,m$0) in -- #24 -//│ r0 -- #23 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 //│ ) -//│ Def(2, h, [], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$1 -- #3 //│ ) -//│ Def(3, _g_f_opt_jp, [tailrecBranch,g_m$0,f_n$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #21 -//│ if scrut -- #19 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 //│ true => -//│ jump _g_f_opt_jp(0,f_n$0,f_n$0) -- #18 +//│ jump j$1(1) -- #23 //│ false => -//│ jump _g_f_opt_jp(1,g_m$0,g_m$0) -- #17 +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(4, _g_f_opt, [tailrecBranch,g_m$0,f_n$0], +//│ Def(3, j$1, [x$5], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,f_n$0) -- #22 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [n$0], //│ 1, -//│ let* (r0) = _g_f_opt(0,n$0,0) in -- #26 -//│ r0 -- #25 -//│ ) -//│ Def(1, g, [m$0], -//│ 1, -//│ let* (r0) = _g_f_opt(1,0,m$0) in -- #24 -//│ r0 -- #23 +//│ let x$0 = ==(n$0,0) in -- #17 +//│ if x$0 -- #16 +//│ true => +//│ jump j$0(0) -- #5 +//│ false => +//│ let x$2 = -(n$0,1) in -- #15 +//│ let* (x$3) = g(x$2) in -- #14 +//│ jump j$0(x$3) -- #13 //│ ) -//│ Def(2, h, [], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let* (x$2) = h() in -- #15 -//│ x$2 -- #14 +//│ x$1 -- #3 //│ ) -//│ Def(3, _g_f_opt_jp, [tailrecBranch,g_m$0,f_n$0], +//│ Def(2, g, [m$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #21 -//│ if scrut -- #19 +//│ let x$4 = ==(m$0,0) in -- #35 +//│ if x$4 -- #34 //│ true => -//│ jump _g_f_opt_jp(0,f_n$0,f_n$0) -- #18 +//│ jump j$1(1) -- #23 //│ false => -//│ jump _g_f_opt_jp(1,g_m$0,g_m$0) -- #17 +//│ let x$6 = -(m$0,1) in -- #33 +//│ let* (x$7) = f(x$6) in -- #32 +//│ jump j$1(x$7) -- #31 //│ ) -//│ Def(4, _g_f_opt, [tailrecBranch,g_m$0,f_n$0], +//│ Def(3, j$1, [x$5], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,f_n$0) -- #22 +//│ x$5 -- #21 //│ ) //│ }, -//│ 2 -- #16) +//│ let* (x$8) = f(4) in -- #41 +//│ x$8 -- #40) //│ //│ Interpreted: -//│ 2 +//│ 0 From fc282e7208db4751d17ada8d06ed5ac637d51e42 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 15 Apr 2024 01:11:48 +0800 Subject: [PATCH 13/59] Fix bugs and cases --- .../compiler/optimizer/TailRecOpt.scala | 58 ++- compiler/shared/test/diff-ir/IRTailRec.mls | 401 ++++++++++++------ 2 files changed, 301 insertions(+), 158 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index f48e0c7429..04ba805231 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -13,45 +13,36 @@ import mlscript.compiler.ir.Expr import mlscript.IntLit import mlscript.compiler.ir.resolveDefnRef import mlscript.compiler.ir.TrivialExpr +import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] - // Rewrites nodes of the form - // let x = g(params) in x - // as Jump(g, params) - private def rewriteTailCalls(node: Node): Node = node match - case Case(scrut, cases) => Case(scrut, cases.map((ci, node) => (ci, rewriteTailCalls(node)))) - case LetExpr(name, expr, body) => LetExpr(name, expr, rewriteTailCalls(body)) - case LetCall(names, defn, args, body) => body match - case Result(res) => - val results = res.collect { case Expr.Ref(name) => name } - if (names == results) then - Jump(defn, args).attachTag(tag) - else - LetCall(names, defn, args, rewriteTailCalls(body)) - - case _ => LetCall(names, defn, args, rewriteTailCalls(body)) - case _ => node + // checks whether a list of names is equal to a list of trivial expressions referencing those names + private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = + val results = exprs.collect { case Expr.Ref(name) => name } + names == results + private def isIdentityJp(d: Defn): Bool = true + private def isTailCall(node: Node): Boolean = node match case LetCall(names, defn, args, body) => body match - case Result(res) => - val results = res.collect { case Expr.Ref(name) => name } - return names == results + case Result(res) => argsListEqual(names, res) + case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) case _ => false case _ => false private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match + case LetCall(names, defn, args, body) => + if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil + else findTailCalls(body) case Result(res) => Nil - case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved + // case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved + case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) case AssignField(_, _, _, body) => findTailCalls(body) - case LetCall(names, defn, args, body) => - if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil - else findTailCalls(body) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -156,9 +147,12 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap ).toMap - val stackFrameIdxes = defnsList.foldRight(1 :: Nil)((defn, ls) => defn.params.size + ls.head :: ls) + val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse - val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes.drop(1)) + println(defnsList.map(_.name)) + println(stackFrameIdxes) + + val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames @@ -171,22 +165,20 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val newDefnRef = DefnRef(Right(newName)) val jpDefnRef = DefnRef(Right(jpName)) - def transformStackFrame(args: List[TrivialExpr])(implicit info: DefnInfo) = + def transformStackFrame(args: List[TrivialExpr], info: DefnInfo) = val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } asLit(info.defn.id) :: start ::: args ::: end // Build the node which will be contained inside the jump point. - def transformNode(node: Node)(implicit info: DefnInfo): Node = node match - case Jump(defn, args) => - Jump(jpDefnRef, transformStackFrame(args)).attachTag(tag) - + def transformNode(node: Node): Node = node match + case Jump(_, _) => node case Result(_) => node case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) case LetCall(names, defn, args, body) => if isTailCall(node) then - Jump(jpDefnRef, transformStackFrame(args)).attachTag(tag) + Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, transformNode(body)) case AssignField(assignee, field, value, body) => AssignField(assignee, field, value, transformNode(body)) @@ -231,13 +223,13 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val first = defnsList.head; val firstMap = nameMaps(first.id) val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) - val firstNode = transformNode(firstBodyRenamed)(defnInfoMap(first.id)) + val firstNode = transformNode(firstBodyRenamed) val newNode = defnsList.tail .foldLeft(firstNode)((elz, defn) => val nmeNap = nameMaps(defn.id) val renamed = defn.body.mapName(getOrKey(nmeNap)) - val thisNode = transformNode(renamed)(defnInfoMap(defn.id)) + val thisNode = transformNode(renamed) makeCaseBranch(defn.id, thisNode, elz) ) .attachTag(tag) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 1041cc7694..2913901f2e 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -6,240 +6,391 @@ :interpIR class True class False -fun f(n) = if n == 0 then 0 else g(n - 1) -fun g(m) = if m == 0 then 1 else f(m - 1) -f(4) -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |#if| |n| |==| |0| |#then| |0| |#else| |g|(|n| |-| |1|)|↵|#fun| |g|(|m|)| |#=| |#if| |m| |==| |0| |#then| |1| |#else| |f|(|m| |-| |1|)|↵|f|(|4|)| -//│ Parsed: {class True {}; class False {}; fun f = (n,) => if (==(n,)(0,)) then 0 else g(-(n,)(1,),); fun g = (m,) => if (==(m,)(0,)) then 1 else f(-(m,)(1,),); f(4,)} +fun f(n, acc) = if n == 0 then acc else g(n - 1, acc + 1) +fun g(m, acc) = if m == 0 then -acc else f(m - 1, acc + 1) +g(5, 0) +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |acc| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|acc| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun f = (n, acc,) => if (==(n,)(0,)) then acc else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(acc,) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(0) -- #5 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let x$2 = -(n$0,1) in -- #20 +//│ let x$3 = +(acc$0,1) in -- #19 +//│ let* (x$4) = g(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ let x$5 = ==(m$0,0) in -- #48 +//│ if x$5 -- #47 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$7 = -(0,acc$1) in -- #31 +//│ jump j$1(x$7) -- #30 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$8 = -(m$0,1) in -- #46 +//│ let x$9 = +(acc$1,1) in -- #45 +//│ let* (x$10) = f(x$8,x$9) in -- #44 +//│ jump j$1(x$10) -- #43 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(3, j$1, [x$6], //│ 1, -//│ x$5 -- #21 +//│ x$6 -- #26 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(0) -- #5 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let x$2 = -(n$0,1) in -- #20 +//│ let x$3 = +(acc$0,1) in -- #19 +//│ let* (x$4) = g(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ let x$5 = ==(m$0,0) in -- #48 +//│ if x$5 -- #47 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$7 = -(0,acc$1) in -- #31 +//│ jump j$1(x$7) -- #30 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$8 = -(m$0,1) in -- #46 +//│ let x$9 = +(acc$1,1) in -- #45 +//│ let* (x$10) = f(x$8,x$9) in -- #44 +//│ jump j$1(x$10) -- #43 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(3, j$1, [x$6], //│ 1, -//│ x$5 -- #21 +//│ x$6 -- #26 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(0) -- #5 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let x$2 = -(n$0,1) in -- #20 +//│ let x$3 = +(acc$0,1) in -- #19 +//│ let* (x$4) = g(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ let x$5 = ==(m$0,0) in -- #48 +//│ if x$5 -- #47 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$7 = -(0,acc$1) in -- #31 +//│ jump j$1(x$7) -- #30 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$8 = -(m$0,1) in -- #46 +//│ let x$9 = +(acc$1,1) in -- #45 +//│ let* (x$10) = f(x$8,x$9) in -- #44 +//│ jump j$1(x$10) -- #43 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(3, j$1, [x$6], //│ 1, -//│ x$5 -- #21 +//│ x$6 -- #26 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ //│ Interpreted: -//│ 0 +//│ 5 :interpIR class True class False -fun f(n) = if n == 0 then 0 else g(n - 1) -fun g(m) = if m == 0 then 1 else f(m - 1) -f(4) -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|)| |#=| |#if| |n| |==| |0| |#then| |0| |#else| |g|(|n| |-| |1|)|↵|#fun| |g|(|m|)| |#=| |#if| |m| |==| |0| |#then| |1| |#else| |f|(|m| |-| |1|)|↵|f|(|4|)| -//│ Parsed: {class True {}; class False {}; fun f = (n,) => if (==(n,)(0,)) then 0 else g(-(n,)(1,),); fun g = (m,) => if (==(m,)(0,)) then 1 else f(-(m,)(1,),); f(4,)} +fun f(n, acc) = if n == 0 then acc else g(n - 1, acc + 1) +fun g(m, acc) = if m == 0 then -acc else f(m - 1, acc + 1) +g(5, 0) +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |acc| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|acc| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun f = (n, acc,) => if (==(n,)(0,)) then acc else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(acc,) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(0) -- #5 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let x$2 = -(n$0,1) in -- #20 +//│ let x$3 = +(acc$0,1) in -- #19 +//│ let* (x$4) = g(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ let x$5 = ==(m$0,0) in -- #48 +//│ if x$5 -- #47 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$7 = -(0,acc$1) in -- #31 +//│ jump j$1(x$7) -- #30 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$8 = -(m$0,1) in -- #46 +//│ let x$9 = +(acc$1,1) in -- #45 +//│ let* (x$10) = f(x$8,x$9) in -- #44 +//│ jump j$1(x$10) -- #43 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(3, j$1, [x$6], //│ 1, -//│ x$5 -- #21 +//│ x$6 -- #26 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ //│ Strongly Connected Tail Calls: -//│ List(Set(g), Set(j$1), Set(f), Set(j$0)) +//│ List(Set(j$1), Set(j$0), Set(g, f)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 -//│ true => -//│ jump j$0(0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let* (r0) = _g_f_opt(0,0,0,n$0,acc$0) in -- #66 +//│ r0 -- #65 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], +//│ 1, +//│ let* (r0) = _g_f_opt(2,m$0,acc$1,0,0) in -- #64 +//│ r0 -- #63 +//│ ) +//│ Def(3, j$1, [x$6], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ x$6 -- #26 +//│ ) +//│ Def(4, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #61 +//│ if scrut -- #59 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$0 = ==(f_n$0,0) in -- #x +//│ if x$0 -- #x +//│ true => +//│ jump j$0(f_acc$0) -- #x +//│ false => +//│ let x$2 = -(f_n$0,1) in -- #x +//│ let x$3 = +(f_acc$0,1) in -- #x +//│ jump _g_f_opt_jp(2,x$2,x$3,f_n$0,f_acc$0) -- #58 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$5 = ==(g_m$0,0) in -- #x +//│ if x$5 -- #x +//│ true => +//│ let x$7 = -(0,g_acc$1) in -- #x +//│ jump j$1(x$7) -- #x +//│ false => +//│ let x$8 = -(g_m$0,1) in -- #x +//│ let x$9 = +(g_acc$1,1) in -- #x +//│ jump _g_f_opt_jp(0,g_m$0,g_acc$1,x$8,x$9) -- #57 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(5, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ x$5 -- #21 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #62 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0], +//│ Def(0, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #17 -//│ if x$0 -- #16 -//│ true => -//│ jump j$0(0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #15 -//│ let* (x$3) = g(x$2) in -- #14 -//│ jump j$0(x$3) -- #13 +//│ let* (r0) = _g_f_opt(0,0,0,n$0,acc$0) in -- #66 +//│ r0 -- #65 //│ ) //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 //│ ) -//│ Def(2, g, [m$0], +//│ Def(2, g, [m$0,acc$1], //│ 1, -//│ let x$4 = ==(m$0,0) in -- #35 -//│ if x$4 -- #34 +//│ let* (r0) = _g_f_opt(2,m$0,acc$1,0,0) in -- #64 +//│ r0 -- #63 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #26 +//│ ) +//│ Def(4, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #61 +//│ if scrut -- #59 //│ true => -//│ jump j$1(1) -- #23 +//│ let x$0 = ==(f_n$0,0) in -- #x +//│ if x$0 -- #x +//│ true => +//│ jump j$0(f_acc$0) -- #x +//│ false => +//│ let x$2 = -(f_n$0,1) in -- #x +//│ let x$3 = +(f_acc$0,1) in -- #x +//│ jump _g_f_opt_jp(2,x$2,x$3,f_n$0,f_acc$0) -- #58 //│ false => -//│ let x$6 = -(m$0,1) in -- #33 -//│ let* (x$7) = f(x$6) in -- #32 -//│ jump j$1(x$7) -- #31 +//│ let x$5 = ==(g_m$0,0) in -- #x +//│ if x$5 -- #x +//│ true => +//│ let x$7 = -(0,g_acc$1) in -- #x +//│ jump j$1(x$7) -- #x +//│ false => +//│ let x$8 = -(g_m$0,1) in -- #x +//│ let x$9 = +(g_acc$1,1) in -- #x +//│ jump _g_f_opt_jp(0,g_m$0,g_acc$1,x$8,x$9) -- #57 //│ ) -//│ Def(3, j$1, [x$5], +//│ Def(5, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ x$5 -- #21 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #62 //│ ) //│ }, -//│ let* (x$8) = f(4) in -- #41 -//│ x$8 -- #40) +//│ let* (x$11) = g(5,0) in -- #56 +//│ x$11 -- #55) //│ //│ Interpreted: -//│ 0 +//│ 5 + +class True +class False +fun f(a, b, c) = g(0, 0) +fun g(d, e) = h(0, 0, 0, 0) +fun h(p, q, r, s) = f(0, 0, 0) +2 +//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | +//│ Parsed: {class True {}; class False {}; fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (x$1) = h(0,0,0,0) in -- #19 +//│ x$1 -- #18 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (x$2) = f(0,0,0) in -- #29 +//│ x$2 -- #28 +//│ ) +//│ }, +//│ 2 -- #30) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(h, g, f)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ r0 -- #42 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #38 +//│ if scrut -- #36 +//│ true => +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #35 +//│ false => +//│ let scrut = ==(1,tailrecBranch) in -- #34 +//│ if scrut -- #33 +//│ true => +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #32 +//│ false => +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #31 +//│ ) +//│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp(tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ }, +//│ 2 -- #30) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ r0 -- #42 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #38 +//│ if scrut -- #36 +//│ true => +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #35 +//│ false => +//│ let scrut = ==(1,tailrecBranch) in -- #34 +//│ if scrut -- #33 +//│ true => +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #32 +//│ false => +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #31 +//│ ) +//│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp(tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ }, +//│ 2 -- #30) From edf7c89a7f7f81d43dda7f1f11a9eb5e8c2df653 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 20 Apr 2024 22:43:01 +0800 Subject: [PATCH 14/59] Interpret field assignment --- .../scala/mlscript/compiler/ir/Interp.scala | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 9b3a15b322..6b93231df6 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -39,7 +39,7 @@ class Interpreter(verbose: Bool): private enum Expr: case Ref(name: Name) case Literal(lit: Lit) - case CtorApp(name: ClassInfo, args: Ls[Expr]) + case CtorApp(name: ClassInfo, var args: Ls[Expr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[Expr]) @@ -68,6 +68,7 @@ class Interpreter(verbose: Bool): case LetExpr(name: Name, expr: Expr, body: Node) case LetJoin(joinName: Name, params: Ls[Name], rhs: Node, body: Node) case LetCall(resultNames: Ls[Name], defn: DefnRef, args: Ls[Expr], body: Node) + case AssignField(assignee: Name, fieldName: Str, value: Expr, body: Node) def show: Str = document.print @@ -126,6 +127,16 @@ class Interpreter(verbose: Bool): <#> raw(")"), raw("in") <:> body.document |> indent ) + case AssignField(Name(assignee), fieldName, value, body) => + stack( + raw("assign") + <:> raw(assignee) + <#> raw(".") + <#> raw(fieldName) + <:> raw("=") + <:> value.document, + raw("in") <:> body.document |> indent + ) private class DefnRef(var defn: Either[Defn, Str]): def name = defn match @@ -155,7 +166,7 @@ class Interpreter(verbose: Bool): case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) case INode.LetCall(xs, defnref, args, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) - case INode.AssignField(_, _, _, _) => ??? // TODO: Interpret field assignments + case INode.AssignField(assignee, fieldName, value, body) => AssignField(assignee, fieldName, value |> convert, body |> convert) private def convert(defn: IDefn): Defn = Defn(defn.name, defn.params, defn.body |> convert) @@ -289,6 +300,18 @@ class Interpreter(verbose: Bool): } val ctx2 = ctx ++ xs.map{_.str}.zip(res) eval(using ctx2, clsctx)(body) + case AssignField(Name(assignee), fieldName, expr, body) => + val value = evalMayNotProgress(expr) + val x = ctx.get(assignee) match + case Some(x: CtorApp) => + val CtorApp(cls, args) = x + val idx = cls.fields.indexOf(fieldName) + val newArgs = args.take(idx - 1) ::: value :: args.drop(idx + 1) + x.args = newArgs + case Some(_) => IRInterpreterError("tried to assign a field of a non-ctor") + case None => IRInterpreterError("could not find value " + assignee) + + eval(body) case _ => throw IRInterpreterError("unexpected node") private def interpret(prog: Program): Node = From c08f31bb45d4b390906eb0506b01db05d3651e29 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 25 Apr 2024 20:16:29 +0800 Subject: [PATCH 15/59] add class info to field assignment --- .../compiler/ir/DefnRefResolver.scala | 2 +- .../main/scala/mlscript/compiler/ir/IR.scala | 14 +++++----- .../scala/mlscript/compiler/ir/Interp.scala | 8 +++--- .../mlscript/compiler/ir/Validator.scala | 2 +- .../compiler/optimizer/Analysis.scala | 4 +-- .../compiler/optimizer/TailRecOpt.scala | 27 ++++++++++--------- shared/src/test/diff/nu/NuScratch.mls | 1 - 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index c0219cc73a..50b088a2bf 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -23,7 +23,7 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case None => if (!allowInlineJp) throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") - case AssignField(assignee, fieldName, value, body) => f(body) + case AssignField(assignee, clsInfo, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(node: Defn) = f(node.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 91abf0d7a9..4efdd84771 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -138,7 +138,7 @@ enum Node: // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node) - case AssignField(assignee: Name, fieldName: Str, value: TrivialExpr, body: Node) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr, body: Node) var tag = DefnTag(-1) @@ -162,8 +162,8 @@ enum Node: case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) case LetCall(names, defn, args, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) - case AssignField(assignee, fieldName, value, body) => - AssignField(f(assignee), fieldName, value.mapNameOfTrivialExpr(f), body.mapName(f)) + case AssignField(assignee, fieldName, clsInfo, value, body) => + AssignField(f(assignee), fieldName, clsInfo, value.mapNameOfTrivialExpr(f), body.mapName(f)) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) @@ -175,8 +175,8 @@ enum Node: case LetCall(names, defn, args, body) => val names_copy = names.map(_.copy) LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) - case AssignField(assignee, fieldName, value, body) => - AssignField(assignee.trySubst(ctx), fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) + case AssignField(assignee, clsInfo, fieldName, value, body) => + AssignField(assignee.trySubst(ctx), clsInfo, fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) private def toDocument: Document = this match case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") @@ -222,7 +222,7 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - case AssignField(assignee, fieldName, value, body) => + case AssignField(assignee, clsInfo, fieldName, value, body) => stack( raw("assign") <:> raw(assignee.toString + "." + fieldName) @@ -238,7 +238,7 @@ enum Node: case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) case LetCall(names, defn, args, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) - case AssignField(assignee, field, value, _) => LocMarker.MAssignField(assignee.toString, field, value.toExpr.locMarker) + case AssignField(assignee, clsInfo, field, value, _) => LocMarker.MAssignField(assignee.toString, field, value.toExpr.locMarker) marker.tag = this.tag marker diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 6b93231df6..5ab61ec258 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -68,7 +68,7 @@ class Interpreter(verbose: Bool): case LetExpr(name: Name, expr: Expr, body: Node) case LetJoin(joinName: Name, params: Ls[Name], rhs: Node, body: Node) case LetCall(resultNames: Ls[Name], defn: DefnRef, args: Ls[Expr], body: Node) - case AssignField(assignee: Name, fieldName: Str, value: Expr, body: Node) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: Expr, body: Node) def show: Str = document.print @@ -127,7 +127,7 @@ class Interpreter(verbose: Bool): <#> raw(")"), raw("in") <:> body.document |> indent ) - case AssignField(Name(assignee), fieldName, value, body) => + case AssignField(Name(assignee), clsInfo, fieldName, value, body) => stack( raw("assign") <:> raw(assignee) @@ -166,7 +166,7 @@ class Interpreter(verbose: Bool): case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) case INode.LetCall(xs, defnref, args, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) - case INode.AssignField(assignee, fieldName, value, body) => AssignField(assignee, fieldName, value |> convert, body |> convert) + case INode.AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value |> convert, body |> convert) private def convert(defn: IDefn): Defn = Defn(defn.name, defn.params, defn.body |> convert) @@ -300,7 +300,7 @@ class Interpreter(verbose: Bool): } val ctx2 = ctx ++ xs.map{_.str}.zip(res) eval(using ctx2, clsctx)(body) - case AssignField(Name(assignee), fieldName, expr, body) => + case AssignField(Name(assignee), clsInfo, fieldName, expr, body) => val value = evalMayNotProgress(expr) val x = ctx.get(assignee) match case Some(x: CtorApp) => diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index 1a095f7ad0..edcdbfc24d 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -17,7 +17,7 @@ private final class DefnRefInSet(defs: Set[Defn]): case _ => } f(body) - case AssignField(assignee, fieldName, value, body) => f(body) + case AssignField(assignee, clsInfo, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(defn: Defn) = f(defn.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index 60fb490fae..1848fced78 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -46,7 +46,7 @@ class UsefulnessAnalysis(verbose: Bool = false): case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, body) => args.foreach(f); names.foreach(addDef); f(body) - case AssignField(assignee, fieldName, value, body) => f(value); f(body) + case AssignField(assignee, clsInfo, fieldName, value, body) => f(value); f(body) def run(x: Defn) = x.params.foreach(addDef) @@ -95,7 +95,7 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) f(using defined2)(body, fv2) - case AssignField(assignee, fieldName, value, body) => + case AssignField(assignee, clsInfo, fieldName, value, body) => val fv2 = if (defined.contains(assignee.str)) fv else fv + assignee.str val fv3 = f(using defined)(value.toExpr, fv2) f(using defined)(body, fv3) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 04ba805231..7852357faa 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -1,24 +1,25 @@ package mlscript.compiler.optimizer -import mlscript.compiler.ir.Program -import mlscript.compiler.ir.Defn -import mlscript.compiler.ir.Node +import mlscript.compiler.ir._ import mlscript.compiler.ir.Node._ import scala.annotation.tailrec -import mlscript.compiler.ir.FreshInt -import mlscript.compiler.ir.Name -import mlscript.compiler.ir.ClassInfo -import mlscript.compiler.ir.DefnRef -import mlscript.compiler.ir.Expr import mlscript.IntLit -import mlscript.compiler.ir.resolveDefnRef -import mlscript.compiler.ir.TrivialExpr import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] + class ModConsCall(defn: Defn) + + private def getModConsCall(node: Node, defnAcc: Option[Defn]) = node match + case Result(res) => + case Jump(defn, args) => + case Case(scrut, cases) => + case LetExpr(name, expr, body) => + case LetCall(names, defn, args, body) => + case AssignField(assignee, clsInfo, fieldName, value, body) => + // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = val results = exprs.collect { case Expr.Ref(name) => name } @@ -42,7 +43,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) case LetExpr(name, expr, body) => findTailCalls(body) - case AssignField(_, _, _, body) => findTailCalls(body) + case AssignField(_, _, _, _, body) => findTailCalls(body) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -181,7 +182,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, transformNode(body)) - case AssignField(assignee, field, value, body) => AssignField(assignee, field, value, transformNode(body)) + case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)) // Tail calls to another function in the component will be replaced with a tail call // to the merged function @@ -253,6 +254,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { partitionNodes(nodeMap).map(g => g.map(d => d.defn)) } + + def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = { diff --git a/shared/src/test/diff/nu/NuScratch.mls b/shared/src/test/diff/nu/NuScratch.mls index db2ada0199..34d9bdfef7 100644 --- a/shared/src/test/diff/nu/NuScratch.mls +++ b/shared/src/test/diff/nu/NuScratch.mls @@ -1,2 +1 @@ :NewDefs - From 518ffb4a0e554c025e96123e5d4831c79243b7b1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 27 Apr 2024 19:30:26 +0800 Subject: [PATCH 16/59] propagate tailrec, fix tailrec parsing issue --- .../scala/mlscript/compiler/ir/Builder.scala | 10 +- .../compiler/ir/DefnRefResolver.scala | 2 +- .../main/scala/mlscript/compiler/ir/IR.scala | 14 +-- .../scala/mlscript/compiler/ir/Interp.scala | 2 +- .../mlscript/compiler/ir/Validator.scala | 2 +- .../compiler/optimizer/Analysis.scala | 4 +- .../compiler/optimizer/TailRecOpt.scala | 17 ++- compiler/shared/test/diff-ir/IRTailRec.mls | 102 ++++++++++++++++++ .../src/main/scala/mlscript/NewParser.scala | 5 +- shared/src/main/scala/mlscript/Typer.scala | 5 +- 10 files changed, 136 insertions(+), 27 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 59227ca644..c63c4348b1 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -119,7 +119,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case Result(Ref(f) :: Nil) if ctx.fnCtx.contains(f.str) => buildResultFromTerm(xs) { case Result(args) => val v = fresh.make - LetCall(List(v), DefnRef(Right(f.str)), args, v |> ref |> sresult |> k).attachTag(tag) + LetCall(List(v), DefnRef(Right(f.str)), args, v |> ref |> sresult |> k, false).attachTag(tag) case node @ _ => node |> unexpectedNode } case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { @@ -226,6 +226,14 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres v |> ref |> sresult |> k).attachTag(tag) case node @ _ => node |> unexpectedNode } + + case Ann(term, receiver) => + // TODO: what happens if thetailrec is overridden? + (term, buildResultFromTerm(receiver)(k)) match + case (Var("tailrec"), LetCall(names, defn, args, body, isTailRec)) => + LetCall(names, defn, args, body, true) + case _ => tm |> unexpectedTerm + case tup: Tup => buildResultFromTup(tup)(k) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index 50b088a2bf..765815ae96 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -11,7 +11,7 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case Result(res) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(resultNames, defnref, args, body) => + case LetCall(resultNames, defnref, args, body, _) => defs.find{_.getName == defnref.getName} match case Some(defn) => defnref.defn = Left(defn) case None => throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 4efdd84771..3846ea43c9 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -137,7 +137,7 @@ enum Node: case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) - case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node) + case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node, isTailRec: Bool) case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr, body: Node) var tag = DefnTag(-1) @@ -161,7 +161,7 @@ enum Node: case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case LetCall(names, defn, args, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) + case LetCall(names, defn, args, body, isTailRec) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f), isTailRec) case AssignField(assignee, fieldName, clsInfo, value, body) => AssignField(f(assignee), fieldName, clsInfo, value.mapNameOfTrivialExpr(f), body.mapName(f)) @@ -172,9 +172,9 @@ enum Node: case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) - case LetCall(names, defn, args, body) => + case LetCall(names, defn, args, body, isTailRec) => val names_copy = names.map(_.copy) - LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) + LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x)), isTailRec) case AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee.trySubst(ctx), clsInfo, fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) @@ -208,14 +208,14 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - case LetCall(xs, defn, args, body) => + case LetCall(xs, defn, args, body, isTailRec) => stack( raw("let*") <:> raw("(") <#> raw(xs.map(_.toString).mkString(",")) <#> raw(")") <:> raw("=") - <:> raw(defn.getName) + <:> raw((if isTailRec then "@tailrec " else "") + defn.getName) <#> raw("(") <#> raw(args.map{ x => x.toString }.mkString(",")) <#> raw(")") @@ -237,7 +237,7 @@ enum Node: case Jump(defn, args) => LocMarker.MJump(defn.getName, args.map(_.toExpr.locMarker)) case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) - case LetCall(names, defn, args, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) + case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) case AssignField(assignee, clsInfo, field, value, _) => LocMarker.MAssignField(assignee.toString, field, value.toExpr.locMarker) marker.tag = this.tag marker diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 5ab61ec258..3b4e305a13 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -164,7 +164,7 @@ class Interpreter(verbose: Bool): case INode.Jump(defnref, args) => Jump(DefnRef(Right(defnref.getName)), args |> convertArgs) case INode.Case(scrut, cases) => Case(scrut, cases.map{(cls, node) => (cls, node |> convert)}) case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) - case INode.LetCall(xs, defnref, args, body) => + case INode.LetCall(xs, defnref, args, body, _) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) case INode.AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value |> convert, body |> convert) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index edcdbfc24d..8682cdebb7 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -11,7 +11,7 @@ private final class DefnRefInSet(defs: Set[Defn]): case Jump(defn, args) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(res, defnref, args, body) => + case LetCall(res, defnref, args, body, _) => defnref.getDefn match { case Some(real_defn) => if (!defs.exists(_ eq real_defn)) throw IRError("ref is not in the set") case _ => diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index 1848fced78..aa3c29dbd3 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -45,7 +45,7 @@ class UsefulnessAnalysis(verbose: Bool = false): case Jump(defn, args) => args.foreach(f) case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) - case LetCall(names, defn, args, body) => args.foreach(f); names.foreach(addDef); f(body) + case LetCall(names, defn, args, body, _) => args.foreach(f); names.foreach(addDef); f(body) case AssignField(assignee, clsInfo, fieldName, value, body) => f(value); f(body) def run(x: Defn) = @@ -86,7 +86,7 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val fv2 = f(using defined)(expr, fv) val defined2 = defined + name.str f(using defined2)(body, fv2) - case LetCall(resultNames, defnref, args, body) => + case LetCall(resultNames, defnref, args, body, _) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) if (extended_scope && !visited.contains(defnref.getName)) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 7852357faa..da0de4511c 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -10,14 +10,14 @@ import mlscript.utils.shorthands.Bool class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] - class ModConsCall(defn: Defn) + class ModConsCall(defn: Defn, valName: Name, clsInfo: ClassInfo, fieldName: String) private def getModConsCall(node: Node, defnAcc: Option[Defn]) = node match case Result(res) => case Jump(defn, args) => case Case(scrut, cases) => case LetExpr(name, expr, body) => - case LetCall(names, defn, args, body) => + case LetCall(names, defn, args, body, _) => case AssignField(assignee, clsInfo, fieldName, value, body) => // checks whether a list of names is equal to a list of trivial expressions referencing those names @@ -28,14 +28,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private def isIdentityJp(d: Defn): Bool = true private def isTailCall(node: Node): Boolean = node match - case LetCall(names, defn, args, body) => body match + case LetCall(names, defn, args, body, _) => body match case Result(res) => argsListEqual(names, res) case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) case _ => false case _ => false private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match - case LetCall(names, defn, args, body) => + case LetCall(names, defn, args, body, _) => if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil else findTailCalls(body) case Result(res) => Nil @@ -150,9 +150,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse - println(defnsList.map(_.name)) - println(stackFrameIdxes) - val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) @@ -177,11 +174,11 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case Result(_) => node case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) - case LetCall(names, defn, args, body) => + case LetCall(names, defn, args, body, isTailRec) => if isTailCall(node) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else - LetCall(names, defn, args, transformNode(body)) + LetCall(names, defn, args, transformNode(body), isTailRec) case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)) // Tail calls to another function in the component will be replaced with a tail call @@ -199,7 +196,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList val namesExpr = names.map(Expr.Ref(_)) val res = Result(namesExpr).attachTag(tag) - val call = LetCall(names, newDefnRef, args, res).attachTag(tag) + val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call) } diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 2913901f2e..d9faaea615 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -394,3 +394,105 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ ) //│ }, //│ 2 -- #30) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun addOne(xs) = + if xs is + Cons(h, t) then Cons(h + 1, @tailrec addOne(t)) + Nil then Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailrec| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailrec addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Interpreted: +//│ Cons(2,Cons(3,Cons(4,Nil()))) diff --git a/shared/src/main/scala/mlscript/NewParser.scala b/shared/src/main/scala/mlscript/NewParser.scala index a7837178e4..4188240921 100644 --- a/shared/src/main/scala/mlscript/NewParser.scala +++ b/shared/src/main/scala/mlscript/NewParser.scala @@ -1358,6 +1358,8 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo final def argsOrIf(acc: Ls[Opt[Var] -> (IfBody \/ Fld)], seqAcc: Ls[Statement], allowNewlines: Bool, prec: Int = NoElsePrec) (implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> (IfBody \/ Fld)] = wrap(acc, seqAcc) { l => + + val anns = parseAnnotations(false) cur match { case Nil => @@ -1409,8 +1411,9 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bo S(Var(i.toString).withLoc(S(l0))) case _ => N } + // val e = expr(NoElsePrec) -> argMut.isDefined - val e = exprOrIf(prec).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) + val e = exprOrIf(prec, true, anns).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) def mkSeq = if (seqAcc.isEmpty) argName -> e else e match { case L(_) => ??? diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index c3d03f82b5..86b5e03482 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -301,9 +301,8 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne NuTypeDef(Als, TN("null"), Nil, N, N, S(Literal(UnitLit(false))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Annotation"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Code"), (S(VarianceInfo.co) -> TN("T")) :: (S(VarianceInfo.co) -> TN("C")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), - NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil) - // Not yet implemented, so we do not define it yet - // NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), + NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), + NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), ) val builtinTypes: Ls[TypeDef] = TypeDef(Cls, TN("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations From d00d072e291f28d8b3dcf39aa1d88c82df373096 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 16:10:56 +0800 Subject: [PATCH 17/59] progress, fix bug --- .../scala/mlscript/compiler/ir/Builder.scala | 2 +- .../compiler/optimizer/TailRecOpt.scala | 43 +- compiler/shared/test/diff-ir/IRTailRec.mls | 585 +++++++++++------- 3 files changed, 397 insertions(+), 233 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index c63c4348b1..fad8cea5fc 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -228,7 +228,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres } case Ann(term, receiver) => - // TODO: what happens if thetailrec is overridden? + // TODO: what happens if the tailrec module is overridden? (term, buildResultFromTerm(receiver)(k)) match case (Var("tailrec"), LetCall(names, defn, args, body, isTailRec)) => LetCall(names, defn, args, body, true) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index da0de4511c..9acf876ba6 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -10,15 +10,33 @@ import mlscript.utils.shorthands.Bool class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] - class ModConsCall(defn: Defn, valName: Name, clsInfo: ClassInfo, fieldName: String) - - private def getModConsCall(node: Node, defnAcc: Option[Defn]) = node match + class ModConsCallInfo(defn: Defn, letCallName: Name, letCtorName: Name, fieldName: String, clsInfo: ClassInfo) + + class TailCallInfo(defn: Defn) + + private def getModConsCall( + node: Node, + defnAcc: Option[Defn], + letCallName: Option[Name], + letCtorName: Option[Name], + fieldName: Option[String], + clsInfo: Option[ClassInfo], + hasTailrecAnn: Boolean // has tailrec annotation + ): Option[ModConsCallInfo] = node match case Result(res) => - case Jump(defn, args) => - case Case(scrut, cases) => - case LetExpr(name, expr, body) => - case LetCall(names, defn, args, body, _) => - case AssignField(assignee, clsInfo, fieldName, value, body) => + (defnAcc, letCallName, letCtorName, fieldName, clsInfo) match + case (Some(defn), Some(letCallName), Some(letCtorName), Some(fieldName), Some(clsInfo)) => + Some(ModConsCallInfo(defn, letCallName, letCtorName, fieldName, clsInfo)) + case _ => None + case Jump(_, _) => None + case Case(scrut, cases) => ??? + case LetExpr(name, expr, body) => body match + case Result(res) => ??? + case Jump(defn, args) => ??? + case _ => getModConsCall(body, defnAcc, letCallName, letCtorName, fieldName, clsInfo, hasTailrecAnn) + + case LetCall(names, defn, args, body, _) => ??? + case AssignField(assignee, clsInfo, fieldName, value, body) => ??? // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -35,9 +53,12 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case _ => false private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match - case LetCall(names, defn, args, body, _) => + case LetCall(names, defn, args, body, isTailRec) => if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil - else findTailCalls(body) + else + if isTailRec then + throw IRError("not a tail call") + findTailCalls(body) case Result(res) => Nil // case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them @@ -175,7 +196,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) case LetCall(names, defn, args, body, isTailRec) => - if isTailCall(node) then + if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, transformNode(body), isTailRec) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index d9faaea615..18aa27df30 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -6,287 +6,331 @@ :interpIR class True class False -fun f(n, acc) = if n == 0 then acc else g(n - 1, acc + 1) -fun g(m, acc) = if m == 0 then -acc else f(m - 1, acc + 1) +fun double(x) = x * 2 +fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(5, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |acc| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|acc| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun f = (n, acc,) => if (==(n,)(0,)) then acc else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(acc,) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 //│ false => -//│ let x$2 = -(n$0,1) in -- #20 -//│ let x$3 = +(acc$0,1) in -- #19 -//│ let* (x$4) = g(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ x$1 -- #3 +//│ x$3 -- #7 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$5 = ==(m$0,0) in -- #48 -//│ if x$5 -- #47 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$7 = -(0,acc$1) in -- #31 -//│ jump j$1(x$7) -- #30 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = -(m$0,1) in -- #46 -//│ let x$9 = +(acc$1,1) in -- #45 -//│ let* (x$10) = f(x$8,x$9) in -- #44 -//│ jump j$1(x$10) -- #43 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(4, j$1, [x$9], //│ 1, -//│ x$6 -- #26 +//│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], +//│ 1, +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 //│ false => -//│ let x$2 = -(n$0,1) in -- #20 -//│ let x$3 = +(acc$0,1) in -- #19 -//│ let* (x$4) = g(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ x$1 -- #3 +//│ x$3 -- #7 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$5 = ==(m$0,0) in -- #48 -//│ if x$5 -- #47 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$7 = -(0,acc$1) in -- #31 -//│ jump j$1(x$7) -- #30 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = -(m$0,1) in -- #46 -//│ let x$9 = +(acc$1,1) in -- #45 -//│ let* (x$10) = f(x$8,x$9) in -- #44 -//│ jump j$1(x$10) -- #43 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(4, j$1, [x$9], //│ 1, -//│ x$6 -- #26 +//│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 //│ false => -//│ let x$2 = -(n$0,1) in -- #20 -//│ let x$3 = +(acc$0,1) in -- #19 -//│ let* (x$4) = g(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ x$1 -- #3 +//│ x$3 -- #7 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$5 = ==(m$0,0) in -- #48 -//│ if x$5 -- #47 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$7 = -(0,acc$1) in -- #31 -//│ jump j$1(x$7) -- #30 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = -(m$0,1) in -- #46 -//│ let x$9 = +(acc$1,1) in -- #45 -//│ let* (x$10) = f(x$8,x$9) in -- #44 -//│ jump j$1(x$10) -- #43 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(4, j$1, [x$9], //│ 1, -//│ x$6 -- #26 +//│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ //│ Interpreted: -//│ 5 +//│ 10 :interpIR class True class False -fun f(n, acc) = if n == 0 then acc else g(n - 1, acc + 1) -fun g(m, acc) = if m == 0 then -acc else f(m - 1, acc + 1) +fun double(x) = x * 2 +fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(5, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |acc| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|acc| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun f = (n, acc,) => if (==(n,)(0,)) then acc else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(acc,) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 //│ false => -//│ let x$2 = -(n$0,1) in -- #20 -//│ let x$3 = +(acc$0,1) in -- #19 -//│ let* (x$4) = g(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ x$1 -- #3 +//│ x$3 -- #7 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$5 = ==(m$0,0) in -- #48 -//│ if x$5 -- #47 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$7 = -(0,acc$1) in -- #31 -//│ jump j$1(x$7) -- #30 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = -(m$0,1) in -- #46 -//│ let x$9 = +(acc$1,1) in -- #45 -//│ let* (x$10) = f(x$8,x$9) in -- #44 -//│ jump j$1(x$10) -- #43 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(4, j$1, [x$9], //│ 1, -//│ x$6 -- #26 +//│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ //│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(g, f)) +//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], +//│ 1, +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 +//│ ) +//│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(0,0,0,n$0,acc$0) in -- #66 -//│ r0 -- #65 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #80 +//│ r0 -- #79 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ x$1 -- #3 +//│ x$3 -- #7 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(2,m$0,acc$1,0,0) in -- #64 -//│ r0 -- #63 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #78 +//│ r0 -- #77 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(4, j$1, [x$9], //│ 1, -//│ x$6 -- #26 +//│ x$9 -- #35 //│ ) -//│ Def(4, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #61 -//│ if scrut -- #59 +//│ let scrut = ==(1,tailrecBranch) in -- #75 +//│ if scrut -- #73 //│ true => -//│ let x$0 = ==(f_n$0,0) in -- #x -//│ if x$0 -- #x +//│ let x$2 = ==(f_n$0,0) in -- #x +//│ if x$2 -- #x //│ true => -//│ jump j$0(f_acc$0) -- #x +//│ let* (x$4) = double(f_acc$0) in -- #x +//│ jump j$0(x$4) -- #x //│ false => -//│ let x$2 = -(f_n$0,1) in -- #x -//│ let x$3 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(2,x$2,x$3,f_n$0,f_acc$0) -- #58 +//│ let x$5 = -(f_n$0,1) in -- #x +//│ let x$6 = +(f_acc$0,1) in -- #x +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #72 //│ false => -//│ let x$5 = ==(g_m$0,0) in -- #x -//│ if x$5 -- #x +//│ let x$8 = ==(g_m$0,0) in -- #x +//│ if x$8 -- #x //│ true => -//│ let x$7 = -(0,g_acc$1) in -- #x -//│ jump j$1(x$7) -- #x +//│ let* (x$10) = double(g_acc$1) in -- #x +//│ let x$11 = -(0,x$10) in -- #x +//│ jump j$1(x$11) -- #x //│ false => -//│ let x$8 = -(g_m$0,1) in -- #x -//│ let x$9 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(0,g_m$0,g_acc$1,x$8,x$9) -- #57 +//│ let x$12 = -(g_m$0,1) in -- #x +//│ let x$13 = +(g_acc$1,1) in -- #x +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #71 //│ ) -//│ Def(5, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #62 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [n$0,acc$0], +//│ Def(0, double, [x$0], //│ 1, -//│ let* (r0) = _g_f_opt(0,0,0,n$0,acc$0) in -- #66 -//│ r0 -- #65 +//│ let x$1 = *(x$0,2) in -- #3 +//│ x$1 -- #2 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(1, f, [n$0,acc$0], //│ 1, -//│ x$1 -- #3 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #80 +//│ r0 -- #79 //│ ) -//│ Def(2, g, [m$0,acc$1], +//│ Def(2, j$0, [x$3], //│ 1, -//│ let* (r0) = _g_f_opt(2,m$0,acc$1,0,0) in -- #64 -//│ r0 -- #63 +//│ x$3 -- #7 //│ ) -//│ Def(3, j$1, [x$6], +//│ Def(3, g, [m$0,acc$1], //│ 1, -//│ x$6 -- #26 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #78 +//│ r0 -- #77 //│ ) -//│ Def(4, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(4, j$1, [x$9], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #61 -//│ if scrut -- #59 +//│ x$9 -- #35 +//│ ) +//│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch) in -- #75 +//│ if scrut -- #73 //│ true => -//│ let x$0 = ==(f_n$0,0) in -- #x -//│ if x$0 -- #x +//│ let x$2 = ==(f_n$0,0) in -- #x +//│ if x$2 -- #x //│ true => -//│ jump j$0(f_acc$0) -- #x +//│ let* (x$4) = double(f_acc$0) in -- #x +//│ jump j$0(x$4) -- #x //│ false => -//│ let x$2 = -(f_n$0,1) in -- #x -//│ let x$3 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(2,x$2,x$3,f_n$0,f_acc$0) -- #58 +//│ let x$5 = -(f_n$0,1) in -- #x +//│ let x$6 = +(f_acc$0,1) in -- #x +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #72 //│ false => -//│ let x$5 = ==(g_m$0,0) in -- #x -//│ if x$5 -- #x +//│ let x$8 = ==(g_m$0,0) in -- #x +//│ if x$8 -- #x //│ true => -//│ let x$7 = -(0,g_acc$1) in -- #x -//│ jump j$1(x$7) -- #x +//│ let* (x$10) = double(g_acc$1) in -- #x +//│ let x$11 = -(0,x$10) in -- #x +//│ jump j$1(x$11) -- #x //│ false => -//│ let x$8 = -(g_m$0,1) in -- #x -//│ let x$9 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(0,g_m$0,g_acc$1,x$8,x$9) -- #57 +//│ let x$12 = -(g_m$0,1) in -- #x +//│ let x$13 = +(g_acc$1,1) in -- #x +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #71 //│ ) -//│ Def(5, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #62 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 //│ ) //│ }, -//│ let* (x$11) = g(5,0) in -- #56 -//│ x$11 -- #55) +//│ let* (x$15) = g(5,0) in -- #70 +//│ x$15 -- #69) //│ //│ Interpreted: -//│ 5 +//│ 10 class True class False @@ -395,6 +439,95 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ }, //│ 2 -- #30) +// TODO: should error, but it seems IR errors are not considered errors by the unit test framework +class True +class False +fun hello() = + @tailrec hello() + 2 +hello() +//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailrec| |hello|(||)|↵|2|←|↵|hello|(||)| +//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailrec hello(); 2}; hello()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailrec hello() in -- #x +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) +//│ +//│ IR Processing Failed: not a tail call +//│ +//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:60) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:99) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:134) +//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) +//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) +//│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:135) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:272) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:281) +//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) +//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) +//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) +//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) +//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) +//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) +//│ org.scalatest.Transformer.apply(Transformer.scala:22) +//│ org.scalatest.Transformer.apply(Transformer.scala:20) +//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) +//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) +//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) +//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) +//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) +//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) +//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) +//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) +//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) +//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) +//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) +//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) +//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) +//│ mlscript.DiffTests.runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) +//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) +//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) +//│ mlscript.DiffTests.runTests(DiffTests.scala:53) +//│ org.scalatest.Suite.run(Suite.scala:1114) +//│ org.scalatest.Suite.run$(Suite.scala:1096) +//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) +//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) +//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) +//│ mlscript.DiffTests.run(DiffTests.scala:53) +//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) +//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) +//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) +//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) +//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) +//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) +//│ java.base/java.lang.Thread.run(Thread.java:840) + :interpIR class True class False @@ -437,62 +570,72 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let* (x$11) = addOne(x$10) in -- #48 //│ x$11 -- #47) //│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #27 -//│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #x -//│ let x$5 = Cons(x$3,x$4) in -- #19 -//│ jump j$0(x$5) -- #18 -//│ Nil => -//│ let x$6 = Nil() in -- #26 -//│ jump j$0(x$6) -- #25 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #27 -//│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #x -//│ let x$5 = Cons(x$3,x$4) in -- #19 -//│ jump j$0(x$5) -- #18 -//│ Nil => -//│ let x$6 = Nil() in -- #26 -//│ jump j$0(x$6) -- #25 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ IR Processing Failed: not a tail call //│ -//│ Interpreted: -//│ Cons(2,Cons(3,Cons(4,Nil()))) +//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:60) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls$$anonfun$1(TailRecOpt.scala:65) +//│ scala.collection.immutable.List.flatMap(List.scala:293) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:65) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:99) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:134) +//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) +//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) +//│ scala.collection.immutable.Set$Set2.foreach(Set.scala:201) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:135) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:272) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:281) +//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) +//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) +//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) +//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) +//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) +//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) +//│ org.scalatest.Transformer.apply(Transformer.scala:22) +//│ org.scalatest.Transformer.apply(Transformer.scala:20) +//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) +//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) +//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) +//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) +//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) +//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) +//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) +//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) +//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) +//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) +//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) +//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) +//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) +//│ mlscript.DiffTests.runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) +//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) +//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) +//│ mlscript.DiffTests.runTests(DiffTests.scala:53) +//│ org.scalatest.Suite.run(Suite.scala:1114) +//│ org.scalatest.Suite.run$(Suite.scala:1096) +//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) +//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) +//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) +//│ mlscript.DiffTests.run(DiffTests.scala:53) +//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) +//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) +//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) +//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) +//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) +//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) +//│ java.base/java.lang.Thread.run(Thread.java:840) From e45412b74d135b75ceb837171b1dd2186b4cc902 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 17:56:26 +0800 Subject: [PATCH 18/59] Detect mod cons tail calls --- .../compiler/optimizer/TailRecOpt.scala | 246 +++++++++++++----- compiler/shared/test/diff-ir/IRTailRec.mls | 28 +- 2 files changed, 198 insertions(+), 76 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 9acf876ba6..87e8dbb07a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -10,33 +10,159 @@ import mlscript.utils.shorthands.Bool class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { private type DefnGraph = Set[DefnNode] - class ModConsCallInfo(defn: Defn, letCallName: Name, letCtorName: Name, fieldName: String, clsInfo: ClassInfo) - - class TailCallInfo(defn: Defn) - - private def getModConsCall( - node: Node, - defnAcc: Option[Defn], - letCallName: Option[Name], - letCtorName: Option[Name], - fieldName: Option[String], - clsInfo: Option[ClassInfo], - hasTailrecAnn: Boolean // has tailrec annotation - ): Option[ModConsCallInfo] = node match - case Result(res) => - (defnAcc, letCallName, letCtorName, fieldName, clsInfo) match - case (Some(defn), Some(letCallName), Some(letCtorName), Some(fieldName), Some(clsInfo)) => - Some(ModConsCallInfo(defn, letCallName, letCtorName, fieldName, clsInfo)) - case _ => None - case Jump(_, _) => None - case Case(scrut, cases) => ??? - case LetExpr(name, expr, body) => body match - case Result(res) => ??? - case Jump(defn, args) => ??? - case _ => getModConsCall(body, defnAcc, letCallName, letCtorName, fieldName, clsInfo, hasTailrecAnn) + case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, ctorValName: Name, fieldName: String) + + enum CallInfo: + case TailCallInfo(defn: Defn) extends CallInfo + case ModConsCallInfo(defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + + def getDefn = this match + case TailCallInfo(defn) => defn + case ModConsCallInfo(defn, letCallNode, letCtorNode) => defn - case LetCall(names, defn, args, body, _) => ??? - case AssignField(assignee, clsInfo, fieldName, value, body) => ??? + + import CallInfo._ + + private def getModConsCall(node: Node)(implicit + defn: Option[Defn], + letCallNode: Option[LetCall], + letCtorNode: Option[LetCtorNodeInfo], + ): Either[ModConsCallInfo, List[Node]] = + node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned + case Result(res) => + (defn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + Left(ModConsCallInfo(defn, letCallNode, letCtorName)) + case _ => Right(Nil) + case Jump(_, _) => Right(Nil) + case Case(scrut, cases) => Right(cases.map(_._2)) + case x: LetExpr => + val LetExpr(name, expr, body) = x + expr match + // Check if this let binding references the mod cons call. + case Expr.Ref(name) => + letCallNode match + case None => getModConsCall(body) // OK + case Some(LetCall(names, _, _, _, isTailRec)) => + // for it to be mod cons, other values cannot use the return value from the call. + if names.contains(name) then + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + if isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else + getModConsCall(body) // OK + + case Expr.Literal(lit) => getModConsCall(body) // OK + case y: Expr.CtorApp => + val Expr.CtorApp(clsInfo, ctorArgs) = y + // if expr is a constructor with a call to some function as a parameter + letCallNode match + case None => getModConsCall(body) // OK + case Some(LetCall(letCallNames, _, _, _, isTailRec)) => // there was a previous call + // 1. Check if the ctor application contains this call + val argNames = ctorArgs.collect { case Expr.Ref(name) => name }.toSet + val namesSet = letCallNames.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then + // OK, this constructor does not use the mod cons call + getModConsCall(body) + else + // it does use it, further analyse + letCtorNode match + case None => + // First constructor discovered using this call as a parameter. + // This is OK. Add this discovered information + + // TODO: for now, assume functions return only one value. handling multiple + // values is a bit more complicated + val ctorArgName = inters.head + val ctorArgIndex = ctorArgs.indexWhere { + case Expr.Ref(nme) => nme == ctorArgName + case _ => false + } + + val fieldName = clsInfo.fields(ctorArgIndex) + + // populate required values + getModConsCall(body)(defn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName))) + case Some(_) => + // another constructor is already using the call. Not OK + + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + if isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + + case Expr.Select(name, cls, field) => + letCallNode match + case None => getModConsCall(body) // OK + case Some(LetCall(names, _, _, _, isTailRec)) => + // for it to be mod cons, other values cannot use the return value from the call. + if names.contains(name) then + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + if isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else + getModConsCall(body) // OK + case Expr.BasicOp(name, args) => + letCallNode match + case None => getModConsCall(body) // OK + case Some(LetCall(names, _, _, _, isTailRec)) => + // for it to be mod cons, other values cannot use the return value from the call. + val argNames = args.collect { case Expr.Ref(name) => name }.toSet + val namesSet = names.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then + getModConsCall(body) // OK + else + // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, + // invalidate the discovered call and continue + if isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + case x: LetCall => + val LetCall(names, defn, args, body, isTailRec) = x + letCallNode match + case None => // OK, use this LetCall as the mod cons + getModConsCall(body)(Some(defn.expectDefn), Some(x), None) + case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => + if isTailRecOld && isTailRec then + // 1. If both the old and newly discovered call are marked with tailrec, error + throw IRError("multiple calls in the same branch marked with tailrec") + else if isTailRecOld then + // 2. old call is marked as tailrec so we must continue using it as the mod cons call. + // make sure the newly discovered call does not use the current call as a parameter + val argNames = args.collect { case Expr.Ref(name) => name }.toSet + val namesSet = namesOld.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then getModConsCall(body) // OK + else throw IRError("not a mod cons call") + else + // old call is not tailrec, so we can override it however we want + // we take a lucky guess and mark this as the mod cons call, but the + // user really should mark which calls should be tailrec + getModConsCall(body)(Some(defn.expectDefn), Some(x), None) + + case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => + // make sure `value` is not the mod cons call + letCallNode match + case None => getModConsCall(body) // OK + case Some(LetCall(names, defn, args, body, isTailRec)) => + value match + case Expr.Ref(name) => + if names.contains(name) && isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + case _ => + letCtorNode match + case None => getModConsCall(body) // OK + case Some(LetCtorNodeInfo(_, ctor, name, fieldName)) => + // If this assignment overwrites the mod cons value, forget it + if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") + else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -44,27 +170,27 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { names == results private def isIdentityJp(d: Defn): Bool = true - + private def isTailCall(node: Node): Boolean = node match - case LetCall(names, defn, args, body, _) => body match - case Result(res) => argsListEqual(names, res) - case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) - case _ => false + case LetCall(names, defn, args, body, _) => + body match + case Result(res) => argsListEqual(names, res) + case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) + case _ => false case _ => false - - private def findTailCalls(node: Node)(implicit nodeMap: Map[Int, DefnNode]): List[DefnNode] = node match + + private def findTailCalls(node: Node): List[CallInfo] = node match case LetCall(names, defn, args, body, isTailRec) => - if isTailCall(node) then nodeMap(defn.expectDefn.id) :: Nil - else - if isTailRec then - throw IRError("not a tail call") + if isTailCall(node) then TailCallInfo(defn.expectDefn) :: Nil + else + if isTailRec then throw IRError("not a tail call") findTailCalls(body) - case Result(res) => Nil + case Result(res) => Nil // case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved - case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them - case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) - case LetExpr(name, expr, body) => findTailCalls(body) - case AssignField(_, _, _, _, body) => findTailCalls(body) + case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them + case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) + case LetExpr(name, expr, body) => findTailCalls(body) + case AssignField(_, _, _, _, body) => findTailCalls(body) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -97,12 +223,13 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { stack = src :: stack for (u <- findTailCalls(src.defn.body)) do { - if (u.visited) then - if (!u.processed) - src.lowest = u.num.min(src.lowest) + val neighbour = nodeMap(u.getDefn.id) + if (neighbour.visited) then + if (!neighbour.processed) + src.lowest = neighbour.num.min(src.lowest) else - dfs(u) - src.lowest = u.lowest.min(src.lowest) + dfs(neighbour) + src.lowest = neighbour.lowest.min(src.lowest) } src.processed = true @@ -164,10 +291,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // TODO: make sure that name clashes aren't a problem val trName = Name("tailrecBranch"); - // To be used to replace variable names inside a definition to avoid variable name clashes - val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map( - defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap - ).toMap + // To be used to replace variable names inside a definition to avoid variable name clashes + val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse @@ -191,15 +316,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Build the node which will be contained inside the jump point. def transformNode(node: Node): Node = node match - case Jump(_, _) => node - case Result(_) => node - case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) - case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) + case Jump(_, _) => node + case Result(_) => node + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) case LetCall(names, defn, args, body, isTailRec) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) - else - LetCall(names, defn, args, transformNode(body), isTailRec) + else LetCall(names, defn, args, transformNode(body), isTailRec) case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)) // Tail calls to another function in the component will be replaced with a tail call @@ -208,7 +332,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // TODO: Figure out how to substitute variables with dummy variables. val info = defnInfoMap(defn.id) - val start = stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id + val start = + stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => Expr.Literal(IntLit(0)) } val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end @@ -235,9 +360,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { ).attachTag(tag) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match - case None => key + case None => key case Some(value) => value - val first = defnsList.head; val firstMap = nameMaps(first.id) @@ -272,8 +396,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { partitionNodes(nodeMap).map(g => g.map(d => d.defn)) } - - def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = { diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 18aa27df30..b73c203745 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -464,15 +464,15 @@ hello() //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:60) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:99) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:134) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:186) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:225) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:261) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:135) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:272) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:281) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:262) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:396) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:403) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -573,18 +573,18 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:60) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls$$anonfun$1(TailRecOpt.scala:65) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:186) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls$$anonfun$1(TailRecOpt.scala:191) //│ scala.collection.immutable.List.flatMap(List.scala:293) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:65) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:99) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:134) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:191) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:225) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:261) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set2.foreach(Set.scala:201) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:135) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:272) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:281) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:262) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:396) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:403) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) From fc473a42a5d18108e1bef0165a676db1c458d6f6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 19:23:38 +0800 Subject: [PATCH 19/59] Refactor tail call discovery --- .../compiler/optimizer/TailRecOpt.scala | 194 ++++++++++-------- compiler/shared/test/diff-ir/IRTailRec.mls | 140 ++++++------- 2 files changed, 178 insertions(+), 156 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 87e8dbb07a..7c0586ed84 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -8,33 +8,53 @@ import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { - private type DefnGraph = Set[DefnNode] - case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, ctorValName: Name, fieldName: String) enum CallInfo: - case TailCallInfo(defn: Defn) extends CallInfo - case ModConsCallInfo(defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo + case ModConsCallInfo(src: Defn, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo def getDefn = this match - case TailCallInfo(defn) => defn - case ModConsCallInfo(defn, letCallNode, letCtorNode) => defn + case TailCallInfo(_, defn, _) => defn + case ModConsCallInfo(_, defn, _, _) => defn + private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo]) { + def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges) + } + + private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo]) + import CallInfo._ - private def getModConsCall(node: Node)(implicit - defn: Option[Defn], - letCallNode: Option[LetCall], - letCtorNode: Option[LetCtorNodeInfo], - ): Either[ModConsCallInfo, List[Node]] = + @tailrec + private def getOptimizableCalls(node: Node)(implicit + src: Defn, + calledDefn: Option[Defn], + letCallNode: Option[LetCall], + letCtorNode: Option[LetCtorNodeInfo], + ): Either[CallInfo, List[Node]] = + def returnFailure = letCallNode match + case Some(LetCall(_, _, _, _, isTailRec)) if isTailRec => throw IRError("not a tail call") + case _ => Right(Nil) + node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => - (defn, letCallNode, letCtorNode) match + (calledDefn, letCallNode, letCtorNode) match case (Some(defn), Some(letCallNode), Some(letCtorName)) => - Left(ModConsCallInfo(defn, letCallNode, letCtorName)) - case _ => Right(Nil) - case Jump(_, _) => Right(Nil) + Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + case _ => returnFailure + case Jump(jp, args) => + // different cases + (calledDefn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + val ctorValName = letCtorName.node.name + if argsListEqual(List(ctorValName), args) && isIdentityJp(jp.expectDefn) then + Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + else + returnFailure + case _ => returnFailure + case Case(scrut, cases) => Right(cases.map(_._2)) case x: LetExpr => val LetExpr(name, expr, body) = x @@ -42,23 +62,23 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Check if this let binding references the mod cons call. case Expr.Ref(name) => letCallNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCall(names, _, _, _, isTailRec)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered else - getModConsCall(body) // OK + getOptimizableCalls(body) // OK - case Expr.Literal(lit) => getModConsCall(body) // OK + case Expr.Literal(lit) => getOptimizableCalls(body) // OK case y: Expr.CtorApp => val Expr.CtorApp(clsInfo, ctorArgs) = y // if expr is a constructor with a call to some function as a parameter letCallNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCall(letCallNames, _, _, _, isTailRec)) => // there was a previous call // 1. Check if the ctor application contains this call val argNames = ctorArgs.collect { case Expr.Ref(name) => name }.toSet @@ -67,7 +87,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { if inters.isEmpty then // OK, this constructor does not use the mod cons call - getModConsCall(body) + getOptimizableCalls(body) else // it does use it, further analyse letCtorNode match @@ -86,30 +106,30 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - getModConsCall(body)(defn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName))) + getOptimizableCalls(body)(src, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName))) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered case Expr.Select(name, cls, field) => letCallNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCall(names, _, _, _, isTailRec)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered else - getModConsCall(body) // OK + getOptimizableCalls(body) // OK case Expr.BasicOp(name, args) => letCallNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCall(names, _, _, _, isTailRec)) => // for it to be mod cons, other values cannot use the return value from the call. val argNames = args.collect { case Expr.Ref(name) => name }.toSet @@ -117,57 +137,64 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val inters = argNames.intersect(namesSet) if inters.isEmpty then - getModConsCall(body) // OK + getOptimizableCalls(body) // OK else // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered case x: LetCall => val LetCall(names, defn, args, body, isTailRec) = x - letCallNode match - case None => // OK, use this LetCall as the mod cons - getModConsCall(body)(Some(defn.expectDefn), Some(x), None) - case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => - if isTailRecOld && isTailRec then - // 1. If both the old and newly discovered call are marked with tailrec, error - throw IRError("multiple calls in the same branch marked with tailrec") - else if isTailRecOld then - // 2. old call is marked as tailrec so we must continue using it as the mod cons call. - // make sure the newly discovered call does not use the current call as a parameter - val argNames = args.collect { case Expr.Ref(name) => name }.toSet - val namesSet = namesOld.toSet - val inters = argNames.intersect(namesSet) - - if inters.isEmpty then getModConsCall(body) // OK - else throw IRError("not a mod cons call") - else - // old call is not tailrec, so we can override it however we want - // we take a lucky guess and mark this as the mod cons call, but the - // user really should mark which calls should be tailrec - getModConsCall(body)(Some(defn.expectDefn), Some(x), None) + + if isTailCall(x) then + Left(TailCallInfo(src, defn.expectDefn, x)) + else + letCallNode match + case None => // OK, use this LetCall as the mod cons + getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None) + case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => + if isTailRecOld && isTailRec then + // 1. If both the old and newly discovered call are marked with tailrec, error + throw IRError("multiple calls in the same branch marked with tailrec") + else if isTailRecOld then + // 2. old call is marked as tailrec so we must continue using it as the mod cons call. + // make sure the newly discovered call does not use the current call as a parameter + val argNames = args.collect { case Expr.Ref(name) => name }.toSet + val namesSet = namesOld.toSet + val inters = argNames.intersect(namesSet) + + if inters.isEmpty then getOptimizableCalls(body) // OK + else throw IRError("not a mod cons call") + else + // old call is not tailrec, so we can override it however we want + // we take a lucky guess and mark this as the mod cons call, but the + // user really should mark which calls should be tailrec + getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None) case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => // make sure `value` is not the mod cons call letCallNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCall(names, defn, args, body, isTailRec)) => value match case Expr.Ref(name) => if names.contains(name) && isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered case _ => letCtorNode match - case None => getModConsCall(body) // OK + case None => getOptimizableCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, name, fieldName)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") - else getModConsCall(body)(None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = - val results = exprs.collect { case Expr.Ref(name) => name } - names == results + if names.length == exprs.length then + val results = exprs.collect { case Expr.Ref(name) => name } + names == results + else + false private def isIdentityJp(d: Defn): Bool = true @@ -179,19 +206,11 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case _ => false case _ => false - private def findTailCalls(node: Node): List[CallInfo] = node match - case LetCall(names, defn, args, body, isTailRec) => - if isTailCall(node) then TailCallInfo(defn.expectDefn) :: Nil - else - if isTailRec then throw IRError("not a tail call") - findTailCalls(body) - case Result(res) => Nil - // case Jump(defn, args) => nodeMap(defn.expectDefn.id) :: Nil // assume that all definition references are resolved - case Jump(defn, args) => Nil // jump points are already optimized and we should not touch them - case Case(scrut, cases) => cases.flatMap((_, body) => findTailCalls(body)) - case LetExpr(name, expr, body) => findTailCalls(body) - case AssignField(_, _, _, _, body) => findTailCalls(body) - + private def findTailCalls(node: Node)(implicit src: Defn): Set[CallInfo] = + getOptimizableCalls(node)(src, None, None, None) match + case Left(callInfo) => Set(callInfo) + case Right(nodes) => nodes.foldLeft(Set())((calls, node) => calls ++ findTailCalls(node)) + // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -212,7 +231,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val defns = nodeMap.values.toSet var ctr = 0 - var stack: List[DefnNode] = Nil + var stack: List[(DefnNode, Set[CallInfo])] = Nil var sccs: List[DefnGraph] = Nil def dfs(src: DefnNode): Unit = { @@ -220,9 +239,11 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { src.lowest = ctr ctr += 1 src.visited = true - stack = src :: stack + + val tailCalls = findTailCalls(src.defn.body)(src.defn) + stack = (src, tailCalls) :: stack - for (u <- findTailCalls(src.defn.body)) do { + for (u <- tailCalls) do { val neighbour = nodeMap(u.getDefn.id) if (neighbour.visited) then if (!neighbour.processed) @@ -235,24 +256,31 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { src.processed = true if (src.num == src.lowest) { - var scc: DefnGraph = Set() + var scc: Set[DefnNode] = Set() + var sccEdges: Set[CallInfo] = Set() - def pop(): DefnNode = { + def pop(): (DefnNode, Set[CallInfo]) = { val ret = stack.head stack = stack.tail ret } - var vertex = pop() + var (vertex, edges) = pop() while (vertex != src) { scc = scc + vertex - vertex = pop() + sccEdges = sccEdges ++ edges + + val next = pop() + vertex = next._1 + edges = next._2 } scc = scc + vertex - - sccs = scc :: sccs + val sccIds = scc.map { d => d.defn.id } + sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} + + sccs = DefnGraph(scc, sccEdges) :: sccs } } @@ -269,7 +297,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Given a strongly connected component `defns`, // returns a set containing the optimized function and the // original functions pointing to an optimized function. - def optimize(defns: Set[Defn], classes: Set[ClassInfo]): Set[Defn] = { + private def optimize(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = { def asLit(x: Int) = Expr.Literal(IntLit(x)) @@ -277,6 +305,8 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get + val defns = component.nodes + // currently, single tail recursive functions are already optimised if (defns.size <= 1) return defns @@ -391,9 +421,9 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { defns.map { d => transformDefn(d) } + newDefn + jpDefn } - def partition(defns: Set[Defn]): List[Set[Defn]] = { + private def partition(defns: Set[Defn]): List[ScComponent] = { val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) - partitionNodes(nodeMap).map(g => g.map(d => d.defn)) + partitionNodes(nodeMap).map(_.removeMetadata) } def apply(p: Program) = run(p) @@ -407,7 +437,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } resolveDefnRef(p.main, newDefs, true) - (Program(p.classes, newDefs, p.main), partitions.map(t => t.map(f => f.name))) + (Program(p.classes, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) } def run(p: Program): Program = { diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index b73c203745..1ec955d777 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -464,15 +464,17 @@ hello() //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:186) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:225) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:261) +//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:38) +//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:46) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:210) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:243) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:289) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:262) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:396) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:403) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:290) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:426) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:433) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -570,72 +572,62 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let* (x$11) = addOne(x$10) in -- #48 //│ x$11 -- #47) //│ -//│ IR Processing Failed: not a tail call +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ -//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:186) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls$$anonfun$1(TailRecOpt.scala:191) -//│ scala.collection.immutable.List.flatMap(List.scala:293) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:191) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:225) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:261) -//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) -//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) -//│ scala.collection.immutable.Set$Set2.foreach(Set.scala:201) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:262) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:396) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:403) -//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) -//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) -//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) -//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) -//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) -//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) -//│ org.scalatest.Transformer.apply(Transformer.scala:22) -//│ org.scalatest.Transformer.apply(Transformer.scala:20) -//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) -//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) -//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) -//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) -//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) -//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) -//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) -//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) -//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) -//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) -//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) -//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) -//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) -//│ mlscript.DiffTests.runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) -//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) -//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) -//│ mlscript.DiffTests.runTests(DiffTests.scala:53) -//│ org.scalatest.Suite.run(Suite.scala:1114) -//│ org.scalatest.Suite.run$(Suite.scala:1096) -//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) -//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) -//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) -//│ mlscript.DiffTests.run(DiffTests.scala:53) -//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) -//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) -//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) -//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) -//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) -//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) -//│ java.base/java.lang.Thread.run(Thread.java:840) +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) +//│ +//│ Interpreted: +//│ Cons(2,Cons(3,Cons(4,Nil()))) From 22dd5185ee718abd28ec320d93d6fb90ab9b388f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 19:27:33 +0800 Subject: [PATCH 20/59] change test --- compiler/shared/test/diff-ir/IRTailRec.mls | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 1ec955d777..f84a4ab3b5 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -9,9 +9,9 @@ class False fun double(x) = x * 2 fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) -g(5, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} +g(6, 0) +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: @@ -57,7 +57,7 @@ g(5, 0) //│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, double, [x$0], @@ -101,7 +101,7 @@ g(5, 0) //│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Promoted: @@ -147,11 +147,11 @@ g(5, 0) //│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Interpreted: -//│ 10 +//│ -12 :interpIR class True @@ -159,9 +159,9 @@ class False fun double(x) = x * 2 fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) -g(5, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|5|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(5, 0,)} +g(6, 0) +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: @@ -207,7 +207,7 @@ g(5, 0) //│ x$9 -- #35 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Strongly Connected Tail Calls: @@ -267,7 +267,7 @@ g(5, 0) //│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Promoted: @@ -326,11 +326,11 @@ g(5, 0) //│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 //│ ) //│ }, -//│ let* (x$15) = g(5,0) in -- #70 +//│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Interpreted: -//│ 10 +//│ -12 class True class False From c1d90d928dff3ad43b0f8726a8c964b5a1d46df0 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 20:18:21 +0800 Subject: [PATCH 21/59] add test, verify mod cons call discovery works --- .../compiler/optimizer/TailRecOpt.scala | 50 +++-- compiler/shared/test/diff-ir/IRTailRec.mls | 206 +++++++++++++++++- 2 files changed, 228 insertions(+), 28 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 7c0586ed84..72715f4e4d 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -14,6 +14,10 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo case ModConsCallInfo(src: Defn, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + def getSrc = this match + case TailCallInfo(src, _, _) => src + case ModConsCallInfo(src, _, _, _) => src + def getDefn = this match case TailCallInfo(_, defn, _) => defn case ModConsCallInfo(_, defn, _, _) => defn @@ -33,6 +37,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], + candReturnName: Option[Name] ): Either[CallInfo, List[Node]] = def returnFailure = letCallNode match case Some(LetCall(_, _, _, _, isTailRec)) if isTailRec => throw IRError("not a tail call") @@ -40,16 +45,18 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => - (calledDefn, letCallNode, letCtorNode) match - case (Some(defn), Some(letCallNode), Some(letCtorName)) => - Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + (calledDefn, letCallNode, letCtorNode, candReturnName) match + case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => + if argsListEqual(List(candReturnName), res) then + Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + else + returnFailure case _ => returnFailure case Jump(jp, args) => // different cases - (calledDefn, letCallNode, letCtorNode) match - case (Some(defn), Some(letCallNode), Some(letCtorName)) => - val ctorValName = letCtorName.node.name - if argsListEqual(List(ctorValName), args) && isIdentityJp(jp.expectDefn) then + (calledDefn, letCallNode, letCtorNode, candReturnName) match + case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => + if argsListEqual(List(candReturnName), args) && isIdentityJp(jp.expectDefn) then Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) else returnFailure @@ -69,7 +76,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered else getOptimizableCalls(body) // OK @@ -87,7 +94,10 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { if inters.isEmpty then // OK, this constructor does not use the mod cons call - getOptimizableCalls(body) + // Now check if the constructor uses the previous ctor. + candReturnName match + case None => getOptimizableCalls(body) // no previous ctor, just continue + case Some(value) => getOptimizableCalls(body)(src, calledDefn, letCallNode, letCtorNode, Some(name)) else // it does use it, further analyse letCtorNode match @@ -106,14 +116,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - getOptimizableCalls(body)(src, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName))) + getOptimizableCalls(body)(src, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName)), Some(name)) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered case Expr.Select(name, cls, field) => letCallNode match @@ -124,7 +134,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered else getOptimizableCalls(body) // OK case Expr.BasicOp(name, args) => @@ -142,7 +152,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered case x: LetCall => val LetCall(names, defn, args, body, isTailRec) = x @@ -151,7 +161,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { else letCallNode match case None => // OK, use this LetCall as the mod cons - getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None) + getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None, None) case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => if isTailRecOld && isTailRec then // 1. If both the old and newly discovered call are marked with tailrec, error @@ -169,7 +179,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec - getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None) + getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None, None) case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => // make sure `value` is not the mod cons call @@ -179,14 +189,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { value match case Expr.Ref(name) => if names.contains(name) && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered case _ => letCtorNode match case None => getOptimizableCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, name, fieldName)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -207,7 +217,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case _ => false private def findTailCalls(node: Node)(implicit src: Defn): Set[CallInfo] = - getOptimizableCalls(node)(src, None, None, None) match + getOptimizableCalls(node)(src, None, None, None, None) match case Left(callInfo) => Set(callInfo) case Right(nodes) => nodes.foldLeft(Set())((calls, node) => calls ++ findTailCalls(node)) @@ -242,7 +252,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val tailCalls = findTailCalls(src.defn.body)(src.defn) stack = (src, tailCalls) :: stack - for (u <- tailCalls) do { val neighbour = nodeMap(u.getDefn.id) if (neighbour.visited) then @@ -279,7 +288,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { scc = scc + vertex val sccIds = scc.map { d => d.defn.id } sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} - + sccs = DefnGraph(scc, sccEdges) :: sccs } } @@ -289,6 +298,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { dfs(v) } + sccs } diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index f84a4ab3b5..3fb49f1da1 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -464,17 +464,17 @@ hello() //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:38) -//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:46) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:210) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:243) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:289) +//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:43) +//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:54) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:220) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:253) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:298) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:290) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:426) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:433) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:299) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:436) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:443) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -631,3 +631,193 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) + +:interpIR +class True +class False +class Zero +class S(x) +fun a(n) = + if n is + S(x) then S(@tailrec b(x)) + Zero then S(Zero) +fun b(n) = + if n is + S(x) then S(S(@tailrec a(x))) + Zero then S(S(Zero)) +a(S(S(S(Zero)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailrec b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailrec a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailrec a(x$7) in -- #x +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #82 +//│ r0 -- #81 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #80 +//│ r0 -- #79 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #77 +//│ if scrut -- #75 +//│ true => +//│ case a_n$0 of -- #x +//│ S => +//│ let x$1 = a_n$0.x in -- #x +//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let x$3 = S(x$2) in -- #x +//│ jump j$0(x$3) -- #x +//│ Zero => +//│ let x$4 = Zero() in -- #x +//│ let x$5 = S(x$4) in -- #x +//│ jump j$0(x$5) -- #x +//│ false => +//│ case b_n$1 of -- #x +//│ S => +//│ let x$7 = b_n$1.x in -- #x +//│ let* (x$8) = @tailrec a(x$7) in -- #x +//│ let x$9 = S(x$8) in -- #x +//│ let x$10 = S(x$9) in -- #x +//│ jump j$1(x$10) -- #x +//│ Zero => +//│ let x$11 = Zero() in -- #x +//│ let x$12 = S(x$11) in -- #x +//│ let x$13 = S(x$12) in -- #x +//│ jump j$1(x$13) -- #x +//│ ) +//│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], +//│ 1, +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #78 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #82 +//│ r0 -- #81 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #80 +//│ r0 -- #79 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #77 +//│ if scrut -- #75 +//│ true => +//│ case a_n$0 of -- #x +//│ S => +//│ let x$1 = a_n$0.x in -- #x +//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let x$3 = S(x$2) in -- #x +//│ jump j$0(x$3) -- #x +//│ Zero => +//│ let x$4 = Zero() in -- #x +//│ let x$5 = S(x$4) in -- #x +//│ jump j$0(x$5) -- #x +//│ false => +//│ case b_n$1 of -- #x +//│ S => +//│ let x$7 = b_n$1.x in -- #x +//│ let* (x$8) = @tailrec a(x$7) in -- #x +//│ let x$9 = S(x$8) in -- #x +//│ let x$10 = S(x$9) in -- #x +//│ jump j$1(x$10) -- #x +//│ Zero => +//│ let x$11 = Zero() in -- #x +//│ let x$12 = S(x$11) in -- #x +//│ let x$13 = S(x$12) in -- #x +//│ jump j$1(x$13) -- #x +//│ ) +//│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], +//│ 1, +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #78 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) From 16134c2d292b087786873869085a165a0b1e0d52 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 21:09:18 +0800 Subject: [PATCH 22/59] add tests, improve formatting --- .../compiler/optimizer/TailRecOpt.scala | 98 +++++++++---------- compiler/shared/test/diff-ir/IRTailRec.mls | 10 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 72715f4e4d..2b8a980804 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -7,26 +7,25 @@ import mlscript.IntLit import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class -class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { +class TailRecOpt(fnUid: FreshInt, tag: FreshInt): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, ctorValName: Name, fieldName: String) enum CallInfo: case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo - case ModConsCallInfo(src: Defn, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo def getSrc = this match case TailCallInfo(src, _, _) => src - case ModConsCallInfo(src, _, _, _) => src + case ModConsCallInfo(src, _, _, _, _) => src def getDefn = this match case TailCallInfo(_, defn, _) => defn - case ModConsCallInfo(_, defn, _, _) => defn + case ModConsCallInfo(_, _, defn, _, _) => defn - private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo]) { + private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo]): def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges) - } - + private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo]) import CallInfo._ @@ -34,6 +33,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { @tailrec private def getOptimizableCalls(node: Node)(implicit src: Defn, + start: Node, calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], @@ -48,7 +48,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { (calledDefn, letCallNode, letCtorNode, candReturnName) match case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => if argsListEqual(List(candReturnName), res) then - Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + Left(ModConsCallInfo(src, start, defn, letCallNode, letCtorName)) else returnFailure case _ => returnFailure @@ -57,7 +57,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { (calledDefn, letCallNode, letCtorNode, candReturnName) match case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => if argsListEqual(List(candReturnName), args) && isIdentityJp(jp.expectDefn) then - Left(ModConsCallInfo(src, defn, letCallNode, letCtorName)) + Left(ModConsCallInfo(src, start, defn, letCallNode, letCtorName)) else returnFailure case _ => returnFailure @@ -76,7 +76,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered else getOptimizableCalls(body) // OK @@ -97,7 +97,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Now check if the constructor uses the previous ctor. candReturnName match case None => getOptimizableCalls(body) // no previous ctor, just continue - case Some(value) => getOptimizableCalls(body)(src, calledDefn, letCallNode, letCtorNode, Some(name)) + case Some(value) => getOptimizableCalls(body)(src, start, calledDefn, letCallNode, letCtorNode, Some(name)) else // it does use it, further analyse letCtorNode match @@ -116,14 +116,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - getOptimizableCalls(body)(src, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName)), Some(name)) + getOptimizableCalls(body)(src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName)), Some(name)) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered case Expr.Select(name, cls, field) => letCallNode match @@ -134,7 +134,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered else getOptimizableCalls(body) // OK case Expr.BasicOp(name, args) => @@ -152,7 +152,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered case x: LetCall => val LetCall(names, defn, args, body, isTailRec) = x @@ -161,7 +161,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { else letCallNode match case None => // OK, use this LetCall as the mod cons - getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None, None) + getOptimizableCalls(body)(src, start, Some(defn.expectDefn), Some(x), None, None) case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => if isTailRecOld && isTailRec then // 1. If both the old and newly discovered call are marked with tailrec, error @@ -179,7 +179,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec - getOptimizableCalls(body)(src, Some(defn.expectDefn), Some(x), None, None) + getOptimizableCalls(body)(src, start, Some(defn.expectDefn), Some(x), None, None) case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => // make sure `value` is not the mod cons call @@ -189,14 +189,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { value match case Expr.Ref(name) => if names.contains(name) && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered case _ => letCtorNode match case None => getOptimizableCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, name, fieldName)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, None, None, None, None) // invalidate everything that's been discovered + else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -217,7 +217,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { case _ => false private def findTailCalls(node: Node)(implicit src: Defn): Set[CallInfo] = - getOptimizableCalls(node)(src, None, None, None, None) match + getOptimizableCalls(node)(src, node, None, None, None, None) match case Left(callInfo) => Set(callInfo) case Right(nodes) => nodes.foldLeft(Set())((calls, node) => calls ++ findTailCalls(node)) @@ -228,23 +228,22 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Wikipedia: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm // Implementation Reference: https://www.baeldung.com/cs/scc-tarjans-algorithm - private class DefnNode(val defn: Defn) { + private class DefnNode(val defn: Defn): override def hashCode(): Int = defn.hashCode var num: Int = Int.MaxValue var lowest: Int = Int.MaxValue var visited: Boolean = false var processed: Boolean = false - } - private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = { + private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = val defns = nodeMap.values.toSet var ctr = 0 var stack: List[(DefnNode, Set[CallInfo])] = Nil var sccs: List[DefnGraph] = Nil - def dfs(src: DefnNode): Unit = { + def dfs(src: DefnNode): Unit = src.num = ctr src.lowest = ctr ctr += 1 @@ -264,15 +263,15 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { src.processed = true - if (src.num == src.lowest) { + if (src.num == src.lowest) then var scc: Set[DefnNode] = Set() var sccEdges: Set[CallInfo] = Set() - def pop(): (DefnNode, Set[CallInfo]) = { + def pop(): (DefnNode, Set[CallInfo]) = val ret = stack.head stack = stack.tail ret - } + var (vertex, edges) = pop() @@ -290,26 +289,31 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} sccs = DefnGraph(scc, sccEdges) :: sccs - } - } + + - for (v <- defns) { + for (v <- defns) if (!v.visited) dfs(v) - } sccs - } + private case class DefnInfo(defn: Defn, stackFrameIdx: Int) - // Given a strongly connected component `defns`, - // returns a set containing the optimized function and the - // original functions pointing to an optimized function. - private def optimize(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = { + def asLit(x: Int) = Expr.Literal(IntLit(x)) + + // Given a strongly connected component `defns` of mutually mod cons functions, + // returns a set containing mutually tail recursive versions of them and + // the original functions pointing to the optimized ones. + private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = ??? - def asLit(x: Int) = Expr.Literal(IntLit(x)) + + // Given a strongly connected component `defns` of mutually + // tail recursive functions, returns a set containing the optimized function and the + // original functions pointing to an optimized function. + private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get @@ -368,7 +372,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { // Tail calls to another function in the component will be replaced with a tail call // to the merged function - def transformDefn(defn: Defn): Defn = { + def transformDefn(defn: Defn): Defn = // TODO: Figure out how to substitute variables with dummy variables. val info = defnInfoMap(defn.id) @@ -384,7 +388,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { val res = Result(namesExpr).attachTag(tag) val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call) - } + // given expressions value, e1, e2, transform it into // let scrut = tailrecBranch == value @@ -429,28 +433,24 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt) { newDefnRef.defn = Left(newDefn) defns.map { d => transformDefn(d) } + newDefn + jpDefn - } + - private def partition(defns: Set[Defn]): List[ScComponent] = { + private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(_.removeMetadata) - } + def apply(p: Program) = run(p) - def run_debug(p: Program): (Program, List[Set[String]]) = { + def run_debug(p: Program): (Program, List[Set[String]]) = // val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) val partitions = partition(p.defs) - val newDefs: Set[Defn] = partitions.flatMap { optimize(_, p.classes) }.toSet + val newDefs: Set[Defn] = partitions.flatMap { optimizeTailRec(_, p.classes) }.toSet // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } resolveDefnRef(p.main, newDefs, true) (Program(p.classes, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) - } - def run(p: Program): Program = { - run_debug(p)._1 - } -} + def run(p: Program): Program = run_debug(p)._1 \ No newline at end of file diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 3fb49f1da1..914bcdc26c 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -467,14 +467,14 @@ hello() //│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:43) //│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:54) //│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:220) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:253) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:298) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:252) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:297) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:299) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:436) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:443) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:297) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:440) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:447) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) From e9d6c54e3d695b8ebf428357ef4ed215a1f70082 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 29 Apr 2024 21:10:21 +0800 Subject: [PATCH 23/59] remove newlines --- .../main/scala/mlscript/compiler/optimizer/TailRecOpt.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 2b8a980804..8468669b4b 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -22,7 +22,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): case TailCallInfo(_, defn, _) => defn case ModConsCallInfo(_, _, defn, _, _) => defn - private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo]): def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges) @@ -290,13 +289,10 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): sccs = DefnGraph(scc, sccEdges) :: sccs - - for (v <- defns) if (!v.visited) dfs(v) - sccs @@ -389,7 +385,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call) - // given expressions value, e1, e2, transform it into // let scrut = tailrecBranch == value // in case scrut of True -> e1 @@ -434,7 +429,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): defns.map { d => transformDefn(d) } + newDefn + jpDefn - private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(_.removeMetadata) From dcd616baacc4842b10eeb6c0a4212a893210023d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 30 Apr 2024 18:05:05 +0800 Subject: [PATCH 24/59] add tostring, improve formatting --- .../compiler/optimizer/TailRecOpt.scala | 31 ++- compiler/shared/test/diff-ir/IRTailRec.mls | 192 +++++++++++++++++- 2 files changed, 206 insertions(+), 17 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 8468669b4b..bf61de6e87 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -8,12 +8,18 @@ import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, tag: FreshInt): - case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, ctorValName: Name, fieldName: String) + case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String) enum CallInfo: case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + override def toString(): String = this match + case TailCallInfo(src, defn, letCallNode) => + f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" + case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode) => + f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" + def getSrc = this match case TailCallInfo(src, _, _) => src case ModConsCallInfo(src, _, _, _, _) => src @@ -115,7 +121,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - getOptimizableCalls(body)(src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, name, fieldName)), Some(name)) + getOptimizableCalls(body)(src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName)), Some(name)) case Some(_) => // another constructor is already using the call. Not OK @@ -192,7 +198,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): case _ => letCtorNode match case None => getOptimizableCalls(body) // OK - case Some(LetCtorNodeInfo(_, ctor, name, fieldName)) => + case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered @@ -250,7 +256,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val tailCalls = findTailCalls(src.defn.body)(src.defn) stack = (src, tailCalls) :: stack - for (u <- tailCalls) do { + for u <- tailCalls do val neighbour = nodeMap(u.getDefn.id) if (neighbour.visited) then if (!neighbour.processed) @@ -258,7 +264,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): else dfs(neighbour) src.lowest = neighbour.lowest.min(src.lowest) - } + src.processed = true @@ -276,7 +282,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): while (vertex != src) { scc = scc + vertex - sccEdges = sccEdges ++ edges + sccEdges = edges ++ sccEdges val next = pop() vertex = next._1 @@ -284,12 +290,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): } scc = scc + vertex + sccEdges = edges ++ sccEdges + val sccIds = scc.map { d => d.defn.id } sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} sccs = DefnGraph(scc, sccEdges) :: sccs - for (v <- defns) + for v <- defns do if (!v.visited) dfs(v) @@ -303,14 +311,19 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // Given a strongly connected component `defns` of mutually mod cons functions, // returns a set containing mutually tail recursive versions of them and // the original functions pointing to the optimized ones. - private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = ??? + private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + // no mod cons, just return the original + if component.edges.collect { case x: ModConsCallInfo => x }.isEmpty then + component.nodes + else + ??? // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a set containing the optimized function and the // original functions pointing to an optimized function. private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = - + println(component.edges) // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 914bcdc26c..81e2039bd9 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -464,17 +464,17 @@ hello() //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:43) -//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:54) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:220) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:252) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:297) +//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:48) +//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:59) +//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:225) +//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:257) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:302) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:297) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:440) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:447) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:302) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:447) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:454) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -821,3 +821,179 @@ a(S(S(S(Zero)))) //│ //│ Interpreted: //│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class True +class False +class A(m, n) +fun a(x) = + if x.n < 0 then A(-1, x.n) + else A(2, b(x.n - 2)) +fun b(n) = + if n < 0 then A(0, n) + else a(A(0, n - 1)) +b(20) +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|m|,| |n|)|↵|#fun| |a|(|x|)| |#=|→|#if| |x|.n| |<| |0| |#then| |A|(|-|1|,| |x|.n|)|↵|#else| |A|(|2|,| |b|(|x|.n| |-| |2|)|)|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |<| |0| |#then| |A|(|0|,| |n|)|↵|#else| |a|(|A|(|0|,| |n| |-| |1|)|)|←|↵|b|(|20|)| +//│ Parsed: {class True {}; class False {}; class A(m, n,) {}; fun a = (x,) => {if (<((x).n,)(0,)) then A(-1, (x).n,) else A(2, b(-((x).n,)(2,),),)}; fun b = (n,) => {if (<(n,)(0,)) then A(0, n,) else a(A(0, -(n,)(1,),),)}; b(20,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let x$1 = x$0.n in -- #35 +//│ let x$2 = <(x$1,0) in -- #34 +//│ if x$2 -- #33 +//│ true => +//│ let x$4 = x$0.n in -- #14 +//│ let x$5 = A(-1,x$4) in -- #13 +//│ jump j$0(x$5) -- #12 +//│ false => +//│ let x$6 = x$0.n in -- #32 +//│ let x$7 = -(x$6,2) in -- #31 +//│ let* (x$8) = b(x$7) in -- #30 +//│ let x$9 = A(2,x$8) in -- #29 +//│ jump j$0(x$9) -- #28 +//│ ) +//│ Def(1, j$0, [x$3], +//│ 1, +//│ x$3 -- #4 +//│ ) +//│ Def(2, b, [n$0], +//│ 1, +//│ let x$10 = <(n$0,0) in -- #65 +//│ if x$10 -- #64 +//│ true => +//│ let x$12 = A(0,n$0) in -- #47 +//│ jump j$1(x$12) -- #46 +//│ false => +//│ let x$13 = -(n$0,1) in -- #63 +//│ let x$14 = A(0,x$13) in -- #62 +//│ let* (x$15) = a(x$14) in -- #61 +//│ jump j$1(x$15) -- #60 +//│ ) +//│ Def(3, j$1, [x$11], +//│ 1, +//│ x$11 -- #39 +//│ ) +//│ }, +//│ let* (x$16) = b(20) in -- #71 +//│ x$16 -- #70) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #80 +//│ r0 -- #79 +//│ ) +//│ Def(1, j$0, [x$3], +//│ 1, +//│ x$3 -- #4 +//│ ) +//│ Def(2, b, [n$0], +//│ 1, +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #78 +//│ r0 -- #77 +//│ ) +//│ Def(3, j$1, [x$11], +//│ 1, +//│ x$11 -- #39 +//│ ) +//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #75 +//│ if scrut -- #73 +//│ true => +//│ let x$1 = a_x$0.n in -- #x +//│ let x$2 = <(x$1,0) in -- #x +//│ if x$2 -- #x +//│ true => +//│ let x$4 = a_x$0.n in -- #x +//│ let x$5 = A(-1,x$4) in -- #x +//│ jump j$0(x$5) -- #x +//│ false => +//│ let x$6 = a_x$0.n in -- #x +//│ let x$7 = -(x$6,2) in -- #x +//│ let* (x$8) = b(x$7) in -- #x +//│ let x$9 = A(2,x$8) in -- #x +//│ jump j$0(x$9) -- #x +//│ false => +//│ let x$10 = <(b_n$0,0) in -- #x +//│ if x$10 -- #x +//│ true => +//│ let x$12 = A(0,b_n$0) in -- #x +//│ jump j$1(x$12) -- #x +//│ false => +//│ let x$13 = -(b_n$0,1) in -- #x +//│ let x$14 = A(0,x$13) in -- #x +//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #72 +//│ ) +//│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], +//│ 1, +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #76 +//│ ) +//│ }, +//│ let* (x$16) = b(20) in -- #71 +//│ x$16 -- #70) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #80 +//│ r0 -- #79 +//│ ) +//│ Def(1, j$0, [x$3], +//│ 1, +//│ x$3 -- #4 +//│ ) +//│ Def(2, b, [n$0], +//│ 1, +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #78 +//│ r0 -- #77 +//│ ) +//│ Def(3, j$1, [x$11], +//│ 1, +//│ x$11 -- #39 +//│ ) +//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch) in -- #75 +//│ if scrut -- #73 +//│ true => +//│ let x$1 = a_x$0.n in -- #x +//│ let x$2 = <(x$1,0) in -- #x +//│ if x$2 -- #x +//│ true => +//│ let x$4 = a_x$0.n in -- #x +//│ let x$5 = A(-1,x$4) in -- #x +//│ jump j$0(x$5) -- #x +//│ false => +//│ let x$6 = a_x$0.n in -- #x +//│ let x$7 = -(x$6,2) in -- #x +//│ let* (x$8) = b(x$7) in -- #x +//│ let x$9 = A(2,x$8) in -- #x +//│ jump j$0(x$9) -- #x +//│ false => +//│ let x$10 = <(b_n$0,0) in -- #x +//│ if x$10 -- #x +//│ true => +//│ let x$12 = A(0,b_n$0) in -- #x +//│ jump j$1(x$12) -- #x +//│ false => +//│ let x$13 = -(b_n$0,1) in -- #x +//│ let x$14 = A(0,x$13) in -- #x +//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #72 +//│ ) +//│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], +//│ 1, +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #76 +//│ ) +//│ }, +//│ let* (x$16) = b(20) in -- #71 +//│ x$16 -- #70) +//│ +//│ Interpreted: +//│ A(2,A(2,A(2,A(2,A(2,A(2,A(2,A(0,-1)))))))) From 8b91f916749f4936f46cdf6f4d872408bcf4a5fd Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 30 Apr 2024 18:05:18 +0800 Subject: [PATCH 25/59] remove println --- .../main/scala/mlscript/compiler/optimizer/TailRecOpt.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index bf61de6e87..6f150a5b67 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -323,7 +323,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // tail recursive functions, returns a set containing the optimized function and the // original functions pointing to an optimized function. private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = - println(component.edges) + // println(component.edges) // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get From 8d6e14ffd5555a6761ee12e49f95b6d2ff352ff2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 30 Apr 2024 18:26:30 +0800 Subject: [PATCH 26/59] actually handle single tail recursive --- .../compiler/optimizer/TailRecOpt.scala | 258 ++++++++------ compiler/shared/test/diff-ir/IRTailRec.mls | 334 ++++++++++++++---- 2 files changed, 405 insertions(+), 187 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 6f150a5b67..d1c816c1d8 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -329,118 +329,158 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val falseClass = classes.find(c => c.ident == "False").get val defns = component.nodes + val edges = component.edges - // currently, single tail recursive functions are already optimised - if (defns.size <= 1) + // dummy case, should not happen + if (defns.size == 0) return defns - // concretely order the functions as soon as possible, since the order of the functions matter - val defnsList = defns.toList - - // assume all defns have the same number of results - // in fact, they should theoretically have the same return type if the program type checked - val resultNum = defnsList.head.resultNum - - // TODO: make sure that name clashes aren't a problem - val trName = Name("tailrecBranch"); - - // To be used to replace variable names inside a definition to avoid variable name clashes - val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap - - val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse - - val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) - .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) - - val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames - - // TODO: This works fine for now, but ideally should find a way to guarantee the new - // name is unique - val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt" - val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp" - - val newDefnRef = DefnRef(Right(newName)) - val jpDefnRef = DefnRef(Right(jpName)) - - def transformStackFrame(args: List[TrivialExpr], info: DefnInfo) = - val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id - val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } - asLit(info.defn.id) :: start ::: args ::: end - - // Build the node which will be contained inside the jump point. - def transformNode(node: Node): Node = node match - case Jump(_, _) => node - case Result(_) => node - case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) - case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) - case LetCall(names, defn, args, body, isTailRec) => - if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then - Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) - else LetCall(names, defn, args, transformNode(body), isTailRec) - case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)) - - // Tail calls to another function in the component will be replaced with a tail call - // to the merged function - def transformDefn(defn: Defn): Defn = - // TODO: Figure out how to substitute variables with dummy variables. - val info = defnInfoMap(defn.id) - - val start = - stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id - val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => Expr.Literal(IntLit(0)) } - val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end - - // We use a let call instead of a jump to avoid newDefn from being turned into a join point, - // which would cause it to be inlined and result in code duplication. - val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList - val namesExpr = names.map(Expr.Ref(_)) - val res = Result(namesExpr).attachTag(tag) - val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) - Defn(defn.id, defn.name, defn.params, defn.resultNum, call) - - // given expressions value, e1, e2, transform it into - // let scrut = tailrecBranch == value - // in case scrut of True -> e1 - // False -> e2 - def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = - val name = Name("scrut") - val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) - LetExpr( - name, - Expr.BasicOp("==", List(asLit(value), Expr.Ref(trName))), - cases - ).attachTag(tag) - - def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match - case None => key - case Some(value) => value - - val first = defnsList.head; - val firstMap = nameMaps(first.id) - val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) - val firstNode = transformNode(firstBodyRenamed) - - val newNode = defnsList.tail - .foldLeft(firstNode)((elz, defn) => - val nmeNap = nameMaps(defn.id) - val renamed = defn.body.mapName(getOrKey(nmeNap)) - val thisNode = transformNode(renamed) - makeCaseBranch(defn.id, thisNode, elz) - ) - .attachTag(tag) - - val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode) - - val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) - val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp) - - // This is the definition that will be called - // val createIntermidDefn = - - jpDefnRef.defn = Left(jpDefn) - newDefnRef.defn = Left(newDefn) - - defns.map { d => transformDefn(d) } + newDefn + jpDefn + // for single tail recursive functions, just move the body into a join point + if (defns.size <= 1) + val defn = defns.head + + // if the function does not even tail call itself, just return + if edges.size == 0 then + return defns + + val jpName = defn.name + "_jp" + val jpDefnRef = DefnRef(Right(jpName)) + + def transformNode(node: Node): Node = node match + case Result(res) => node + case Jump(defn, args) => node + case Case(scrut, cases) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body)))).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetCall(names, defn_, args, body, isTailRec) => + if isTailCall(node) && defn_.expectDefn.id == defn.id then + Jump(jpDefnRef, args) + else + LetCall(names, defn_, args, transformNode(body), isTailRec).attachTag(tag) + case AssignField(assignee, clsInfo, fieldName, value, body) => + AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) + + val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body)) + + val rets = defn.params.map { case Name(x) => Name(x + "_") } + val callJpNode = LetCall( + rets, + DefnRef(Left(jpDef)), + defn.params.map(Expr.Ref(_)), + Result(rets.map(Expr.Ref(_))), + false + ) + + val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode) + Set(newDefn, jpDef) + + else + // concretely order the functions as soon as possible, since the order of the functions matter + val defnsList = defns.toList + + // assume all defns have the same number of results + // in fact, they should theoretically have the same return type if the program type checked + val resultNum = defnsList.head.resultNum + + // TODO: make sure that name clashes aren't a problem + val trName = Name("tailrecBranch"); + + // To be used to replace variable names inside a definition to avoid variable name clashes + val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap + + val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse + + val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) + .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) + + val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames + + // TODO: This works fine for now, but ideally should find a way to guarantee the new + // name is unique + val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt" + val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp" + + val newDefnRef = DefnRef(Right(newName)) + val jpDefnRef = DefnRef(Right(jpName)) + + def transformStackFrame(args: List[TrivialExpr], info: DefnInfo) = + val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } + asLit(info.defn.id) :: start ::: args ::: end + + // Build the node which will be contained inside the jump point. + def transformNode(node: Node): Node = node match + case Jump(_, _) => node + case Result(_) => node + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) + case LetCall(names, defn, args, body, isTailRec) => + if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then + Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) + else LetCall(names, defn, args, transformNode(body), isTailRec).attachTag(tag) + case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)).attachTag(tag) + + // Tail calls to another function in the component will be replaced with a tail call + // to the merged function + def transformDefn(defn: Defn): Defn = + // TODO: Figure out how to substitute variables with dummy variables. + val info = defnInfoMap(defn.id) + + val start = + stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => Expr.Literal(IntLit(0)) } + val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end + + // We use a let call instead of a jump to avoid newDefn from being turned into a join point, + // which would cause it to be inlined and result in code duplication. + val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList + val namesExpr = names.map(Expr.Ref(_)) + val res = Result(namesExpr).attachTag(tag) + val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) + Defn(defn.id, defn.name, defn.params, defn.resultNum, call) + + // given expressions value, e1, e2, transform it into + // let scrut = tailrecBranch == value + // in case scrut of True -> e1 + // False -> e2 + def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = + val name = Name("scrut") + val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) + LetExpr( + name, + Expr.BasicOp("==", List(asLit(value), Expr.Ref(trName))), + cases + ).attachTag(tag) + + def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match + case None => key + case Some(value) => value + + val first = defnsList.head; + val firstMap = nameMaps(first.id) + val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) + val firstNode = transformNode(firstBodyRenamed) + + val newNode = defnsList.tail + .foldLeft(firstNode)((elz, defn) => + val nmeNap = nameMaps(defn.id) + val renamed = defn.body.mapName(getOrKey(nmeNap)) + val thisNode = transformNode(renamed) + makeCaseBranch(defn.id, thisNode, elz) + ) + .attachTag(tag) + + val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode) + + val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) + val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp) + + // This is the definition that will be called + // val createIntermidDefn = + + jpDefnRef.defn = Left(jpDefn) + newDefnRef.defn = Left(newDefn) + + defns.map { d => transformDefn(d) } + newDefn + jpDefn private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 81e2039bd9..d34cff5dca 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -2,6 +2,174 @@ :ParseOnly :UseIR +:noTailRec +:interpIR +class True +class False +fun fact(acc, n) = + if n == 0 then acc + else fact(acc * n, n - 1) +fact(1, 5) +//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Interpreted: +//│ 120 + +:interpIR +class True +class False +fun fact(acc, n) = + if n == 0 then acc + else fact(acc * n, n - 1) +fact(1, 5) +//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #34 +//│ if x$0 -- #33 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #32 +//│ let x$3 = -(n$0,1) in -- #31 +//│ jump fact_jp(x$2,x$3) -- #x +//│ ) +//│ Def(3, fact, [acc$0,n$0], +//│ 1, +//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #x +//│ acc$0_,n$0_ -- #x +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #34 +//│ if x$0 -- #33 +//│ true => +//│ jump j$0(acc$0) -- #5 +//│ false => +//│ let x$2 = *(acc$0,n$0) in -- #32 +//│ let x$3 = -(n$0,1) in -- #31 +//│ jump fact_jp(x$2,x$3) -- #x +//│ ) +//│ Def(3, fact, [acc$0,n$0], +//│ 1, +//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #x +//│ acc$0_,n$0_ -- #x +//│ ) +//│ }, +//│ let* (x$5) = fact(1,5) in -- #30 +//│ x$5 -- #29) +//│ +//│ Interpreted: +//│ 120 + :noTailRec :interpIR class True @@ -220,8 +388,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #82 +//│ r0 -- #81 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -229,8 +397,8 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #78 -//│ r0 -- #77 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #80 +//│ r0 -- #79 //│ ) //│ Def(4, j$1, [x$9], //│ 1, @@ -238,33 +406,33 @@ g(6, 0) //│ ) //│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #75 -//│ if scrut -- #73 +//│ let scrut = ==(1,tailrecBranch) in -- #77 +//│ if scrut -- #75 //│ true => //│ let x$2 = ==(f_n$0,0) in -- #x //│ if x$2 -- #x //│ true => -//│ let* (x$4) = double(f_acc$0) in -- #x +//│ let* (x$4) = double(f_acc$0) in -- #73 //│ jump j$0(x$4) -- #x //│ false => //│ let x$5 = -(f_n$0,1) in -- #x //│ let x$6 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #72 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #74 //│ false => //│ let x$8 = ==(g_m$0,0) in -- #x //│ if x$8 -- #x //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #x +//│ let* (x$10) = double(g_acc$1) in -- #71 //│ let x$11 = -(0,x$10) in -- #x //│ jump j$1(x$11) -- #x //│ false => //│ let x$12 = -(g_m$0,1) in -- #x //│ let x$13 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #71 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #72 //│ ) //│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #78 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -279,8 +447,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #82 +//│ r0 -- #81 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -288,8 +456,8 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #78 -//│ r0 -- #77 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #80 +//│ r0 -- #79 //│ ) //│ Def(4, j$1, [x$9], //│ 1, @@ -297,33 +465,33 @@ g(6, 0) //│ ) //│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #75 -//│ if scrut -- #73 +//│ let scrut = ==(1,tailrecBranch) in -- #77 +//│ if scrut -- #75 //│ true => //│ let x$2 = ==(f_n$0,0) in -- #x //│ if x$2 -- #x //│ true => -//│ let* (x$4) = double(f_acc$0) in -- #x +//│ let* (x$4) = double(f_acc$0) in -- #73 //│ jump j$0(x$4) -- #x //│ false => //│ let x$5 = -(f_n$0,1) in -- #x //│ let x$6 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #72 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #74 //│ false => //│ let x$8 = ==(g_m$0,0) in -- #x //│ if x$8 -- #x //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #x +//│ let* (x$10) = double(g_acc$1) in -- #71 //│ let x$11 = -(0,x$10) in -- #x //│ jump j$1(x$11) -- #x //│ false => //│ let x$12 = -(g_m$0,1) in -- #x //│ let x$13 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #71 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #72 //│ ) //│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #76 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #78 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -473,8 +641,8 @@ hello() //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) //│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:302) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:447) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:454) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:487) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:494) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -575,23 +743,28 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], +//│ Def(1, j$0, [x$0], //│ 1, -//│ case xs$0 of -- #27 +//│ x$0 -- #1 +//│ ) +//│ Def(2, addOne_jp, [xs$0], +//│ 1, +//│ case xs$0 of -- #59 //│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #x -//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ let x$1 = xs$0.t in -- #57 +//│ let x$2 = xs$0.h in -- #56 +//│ let x$3 = +(x$2,1) in -- #55 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #54 +//│ let x$5 = Cons(x$3,x$4) in -- #53 //│ jump j$0(x$5) -- #18 //│ Nil => -//│ let x$6 = Nil() in -- #26 +//│ let x$6 = Nil() in -- #58 //│ jump j$0(x$6) -- #25 //│ ) -//│ Def(1, j$0, [x$0], +//│ Def(3, addOne, [xs$0], //│ 1, -//│ x$0 -- #1 +//│ let* (xs$0_) = addOne_jp(xs$0) in -- #x +//│ xs$0_ -- #x //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -603,23 +776,28 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], +//│ Def(1, j$0, [x$0], //│ 1, -//│ case xs$0 of -- #27 +//│ x$0 -- #1 +//│ ) +//│ Def(2, addOne_jp, [xs$0], +//│ 1, +//│ case xs$0 of -- #59 //│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #x -//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ let x$1 = xs$0.t in -- #57 +//│ let x$2 = xs$0.h in -- #56 +//│ let x$3 = +(x$2,1) in -- #55 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #54 +//│ let x$5 = Cons(x$3,x$4) in -- #53 //│ jump j$0(x$5) -- #18 //│ Nil => -//│ let x$6 = Nil() in -- #26 +//│ let x$6 = Nil() in -- #58 //│ jump j$0(x$6) -- #25 //│ ) -//│ Def(1, j$0, [x$0], +//│ Def(3, addOne, [xs$0], //│ 1, -//│ x$0 -- #1 +//│ let* (xs$0_) = addOne_jp(xs$0) in -- #x +//│ xs$0_ -- #x //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -701,8 +879,8 @@ a(S(S(S(Zero)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #84 +//│ r0 -- #83 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -710,8 +888,8 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #82 +//│ r0 -- #81 //│ ) //│ Def(3, j$1, [x$6], //│ 1, @@ -719,13 +897,13 @@ a(S(S(S(Zero)))) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #77 -//│ if scrut -- #75 +//│ let scrut = ==(0,tailrecBranch) in -- #79 +//│ if scrut -- #77 //│ true => //│ case a_n$0 of -- #x //│ S => //│ let x$1 = a_n$0.x in -- #x -//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let* (x$2) = @tailrec b(x$1) in -- #76 //│ let x$3 = S(x$2) in -- #x //│ jump j$0(x$3) -- #x //│ Zero => @@ -736,7 +914,7 @@ a(S(S(S(Zero)))) //│ case b_n$1 of -- #x //│ S => //│ let x$7 = b_n$1.x in -- #x -//│ let* (x$8) = @tailrec a(x$7) in -- #x +//│ let* (x$8) = @tailrec a(x$7) in -- #75 //│ let x$9 = S(x$8) in -- #x //│ let x$10 = S(x$9) in -- #x //│ jump j$1(x$10) -- #x @@ -748,7 +926,7 @@ a(S(S(S(Zero)))) //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #78 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #80 //│ ) //│ }, //│ let x$14 = Zero() in -- #74 @@ -762,8 +940,8 @@ a(S(S(S(Zero)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #84 +//│ r0 -- #83 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -771,8 +949,8 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #82 +//│ r0 -- #81 //│ ) //│ Def(3, j$1, [x$6], //│ 1, @@ -780,13 +958,13 @@ a(S(S(S(Zero)))) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #77 -//│ if scrut -- #75 +//│ let scrut = ==(0,tailrecBranch) in -- #79 +//│ if scrut -- #77 //│ true => //│ case a_n$0 of -- #x //│ S => //│ let x$1 = a_n$0.x in -- #x -//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let* (x$2) = @tailrec b(x$1) in -- #76 //│ let x$3 = S(x$2) in -- #x //│ jump j$0(x$3) -- #x //│ Zero => @@ -797,7 +975,7 @@ a(S(S(S(Zero)))) //│ case b_n$1 of -- #x //│ S => //│ let x$7 = b_n$1.x in -- #x -//│ let* (x$8) = @tailrec a(x$7) in -- #x +//│ let* (x$8) = @tailrec a(x$7) in -- #75 //│ let x$9 = S(x$8) in -- #x //│ let x$10 = S(x$9) in -- #x //│ jump j$1(x$10) -- #x @@ -809,7 +987,7 @@ a(S(S(S(Zero)))) //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #78 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #80 //│ ) //│ }, //│ let x$14 = Zero() in -- #74 @@ -885,8 +1063,8 @@ b(20) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #81 +//│ r0 -- #80 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -894,8 +1072,8 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #78 -//│ r0 -- #77 +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #79 +//│ r0 -- #78 //│ ) //│ Def(3, j$1, [x$11], //│ 1, @@ -903,8 +1081,8 @@ b(20) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #75 -//│ if scrut -- #73 +//│ let scrut = ==(0,tailrecBranch) in -- #76 +//│ if scrut -- #74 //│ true => //│ let x$1 = a_x$0.n in -- #x //│ let x$2 = <(x$1,0) in -- #x @@ -916,7 +1094,7 @@ b(20) //│ false => //│ let x$6 = a_x$0.n in -- #x //│ let x$7 = -(x$6,2) in -- #x -//│ let* (x$8) = b(x$7) in -- #x +//│ let* (x$8) = b(x$7) in -- #73 //│ let x$9 = A(2,x$8) in -- #x //│ jump j$0(x$9) -- #x //│ false => @@ -932,7 +1110,7 @@ b(20) //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #76 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #77 //│ ) //│ }, //│ let* (x$16) = b(20) in -- #71 @@ -942,8 +1120,8 @@ b(20) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #81 +//│ r0 -- #80 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -951,8 +1129,8 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #78 -//│ r0 -- #77 +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #79 +//│ r0 -- #78 //│ ) //│ Def(3, j$1, [x$11], //│ 1, @@ -960,8 +1138,8 @@ b(20) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #75 -//│ if scrut -- #73 +//│ let scrut = ==(0,tailrecBranch) in -- #76 +//│ if scrut -- #74 //│ true => //│ let x$1 = a_x$0.n in -- #x //│ let x$2 = <(x$1,0) in -- #x @@ -973,7 +1151,7 @@ b(20) //│ false => //│ let x$6 = a_x$0.n in -- #x //│ let x$7 = -(x$6,2) in -- #x -//│ let* (x$8) = b(x$7) in -- #x +//│ let* (x$8) = b(x$7) in -- #73 //│ let x$9 = A(2,x$8) in -- #x //│ jump j$0(x$9) -- #x //│ false => @@ -989,7 +1167,7 @@ b(20) //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #76 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #77 //│ ) //│ }, //│ let* (x$16) = b(20) in -- #71 From 6213d5137bfd19fd6952bcfd35b58ca2cbf0bce9 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 30 Apr 2024 19:19:47 +0800 Subject: [PATCH 27/59] properly attach tags --- .../scala/mlscript/compiler/ir/Builder.scala | 2 +- .../compiler/optimizer/TailRecOpt.scala | 76 +-- compiler/shared/test/diff-ir/IRTailRec.mls | 522 +++++++++--------- 3 files changed, 307 insertions(+), 293 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index fad8cea5fc..7071cd6ef5 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -231,7 +231,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres // TODO: what happens if the tailrec module is overridden? (term, buildResultFromTerm(receiver)(k)) match case (Var("tailrec"), LetCall(names, defn, args, body, isTailRec)) => - LetCall(names, defn, args, body, true) + LetCall(names, defn, args, body, true).attachTag(tag) case _ => tm |> unexpectedTerm diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index d1c816c1d8..95e2e76f4c 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -307,15 +307,38 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): private case class DefnInfo(defn: Defn, stackFrameIdx: Int) def asLit(x: Int) = Expr.Literal(IntLit(x)) + + private def makeSwitch(scrutName: Name, cases: List[(Int, Node)], default: Node)(implicit trueClass: ClassInfo, falseClass: ClassInfo): Node = + // given expressions value, e1, e2, transform it into + // let scrut = tailrecBranch == value + // in case scrut of True -> e1 + // False -> e2 + def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = + val name = Name("scrut") + val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) + LetExpr( + name, + Expr.BasicOp("==", List(asLit(value), Expr.Ref(scrutName))), + cases + ).attachTag(tag) + + cases.foldLeft(default)((elz, item) => + val cmpValue = item._1 + val nodeIfTrue = item._2 + makeCaseBranch(cmpValue, nodeIfTrue, elz) + ) // Given a strongly connected component `defns` of mutually mod cons functions, // returns a set containing mutually tail recursive versions of them and // the original functions pointing to the optimized ones. private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + val modConsCalls = component.edges.collect { case x: ModConsCallInfo => x } + // no mod cons, just return the original - if component.edges.collect { case x: ModConsCallInfo => x }.isEmpty then + if modConsCalls.isEmpty then component.nodes else + ??? @@ -347,13 +370,13 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val jpDefnRef = DefnRef(Right(jpName)) def transformNode(node: Node): Node = node match - case Result(res) => node - case Jump(defn, args) => node + case Result(res) => node.attachTag(tag) + case Jump(defn, args) => node.attachTag(tag) case Case(scrut, cases) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body)))).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) case LetCall(names, defn_, args, body, isTailRec) => if isTailCall(node) && defn_.expectDefn.id == defn.id then - Jump(jpDefnRef, args) + Jump(jpDefnRef, args).attachTag(tag) else LetCall(names, defn_, args, transformNode(body), isTailRec).attachTag(tag) case AssignField(assignee, clsInfo, fieldName, value, body) => @@ -366,14 +389,18 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): rets, DefnRef(Left(jpDef)), defn.params.map(Expr.Ref(_)), - Result(rets.map(Expr.Ref(_))), + Result(rets.map(Expr.Ref(_))).attachTag(tag), false - ) + ).attachTag(tag) val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode) Set(newDefn, jpDef) else + // Note that we do not use the actual edges in ScCompoennt here. + // We assume the only things we can optimize are tail calls, which + // are cheap to identify, and nothing else. + // concretely order the functions as soon as possible, since the order of the functions matter val defnsList = defns.toList @@ -409,10 +436,10 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // Build the node which will be contained inside the jump point. def transformNode(node: Node): Node = node match - case Jump(_, _) => node - case Result(_) => node - case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))) - case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)) + case Jump(_, _) => node.attachTag(tag) + case Result(_) => node.attachTag(tag) + case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) case LetCall(names, defn, args, body, isTailRec) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) @@ -437,19 +464,6 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val res = Result(namesExpr).attachTag(tag) val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call) - - // given expressions value, e1, e2, transform it into - // let scrut = tailrecBranch == value - // in case scrut of True -> e1 - // False -> e2 - def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = - val name = Name("scrut") - val cases = Case(name, List((trueClass, e1), (falseClass, e2))).attachTag(tag) - LetExpr( - name, - Expr.BasicOp("==", List(asLit(value), Expr.Ref(trName))), - cases - ).attachTag(tag) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match case None => key @@ -460,14 +474,14 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) val firstNode = transformNode(firstBodyRenamed) - val newNode = defnsList.tail - .foldLeft(firstNode)((elz, defn) => - val nmeNap = nameMaps(defn.id) - val renamed = defn.body.mapName(getOrKey(nmeNap)) - val thisNode = transformNode(renamed) - makeCaseBranch(defn.id, thisNode, elz) - ) - .attachTag(tag) + val valsAndNodes = defnsList.map(defn => + val nmeMap = nameMaps(defn.id) + val renamed = defn.body.mapName(getOrKey(nmeMap)) + val transformed = transformNode(renamed) + (defn.id, transformed) + ) + + val newNode = makeSwitch(trName, valsAndNodes.tail, valsAndNodes.head._2)(trueClass, falseClass) val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index d34cff5dca..bbda8998cf 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -123,19 +123,19 @@ fact(1, 5) //│ ) //│ Def(2, fact_jp, [acc$0,n$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #34 -//│ if x$0 -- #33 +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ jump j$0(acc$0) -- #31 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #32 -//│ let x$3 = -(n$0,1) in -- #31 -//│ jump fact_jp(x$2,x$3) -- #x +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 //│ ) //│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #x -//│ acc$0_,n$0_ -- #x +//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #38 +//│ acc$0_,n$0_ -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -149,19 +149,19 @@ fact(1, 5) //│ ) //│ Def(2, fact_jp, [acc$0,n$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #34 -//│ if x$0 -- #33 +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ jump j$0(acc$0) -- #31 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #32 -//│ let x$3 = -(n$0,1) in -- #31 -//│ jump fact_jp(x$2,x$3) -- #x +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 //│ ) //│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #x -//│ acc$0_,n$0_ -- #x +//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #38 +//│ acc$0_,n$0_ -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -388,8 +388,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -397,8 +397,8 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, @@ -406,33 +406,33 @@ g(6, 0) //│ ) //│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #77 -//│ if scrut -- #75 +//│ let scrut = ==(1,tailrecBranch) in -- #95 +//│ if scrut -- #94 //│ true => -//│ let x$2 = ==(f_n$0,0) in -- #x -//│ if x$2 -- #x +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 //│ true => -//│ let* (x$4) = double(f_acc$0) in -- #73 -//│ jump j$0(x$4) -- #x +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 //│ false => -//│ let x$5 = -(f_n$0,1) in -- #x -//│ let x$6 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #74 +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 //│ false => -//│ let x$8 = ==(g_m$0,0) in -- #x -//│ if x$8 -- #x +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #71 -//│ let x$11 = -(0,x$10) in -- #x -//│ jump j$1(x$11) -- #x +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 //│ false => -//│ let x$12 = -(g_m$0,1) in -- #x -//│ let x$13 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #72 +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 //│ ) //│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #78 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -447,8 +447,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -456,8 +456,8 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #80 -//│ r0 -- #79 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, @@ -465,33 +465,33 @@ g(6, 0) //│ ) //│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #77 -//│ if scrut -- #75 +//│ let scrut = ==(1,tailrecBranch) in -- #95 +//│ if scrut -- #94 //│ true => -//│ let x$2 = ==(f_n$0,0) in -- #x -//│ if x$2 -- #x +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 //│ true => -//│ let* (x$4) = double(f_acc$0) in -- #73 -//│ jump j$0(x$4) -- #x +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 //│ false => -//│ let x$5 = -(f_n$0,1) in -- #x -//│ let x$6 = +(f_acc$0,1) in -- #x -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #74 +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 //│ false => -//│ let x$8 = ==(g_m$0,0) in -- #x -//│ if x$8 -- #x +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 //│ true => -//│ let* (x$10) = double(g_acc$1) in -- #71 -//│ let x$11 = -(0,x$10) in -- #x -//│ jump j$1(x$11) -- #x +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 //│ false => -//│ let x$12 = -(g_m$0,1) in -- #x -//│ let x$13 = +(g_acc$1,1) in -- #x -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #72 +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 //│ ) //│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #78 +//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -551,16 +551,16 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, //│ let scrut = ==(0,tailrecBranch) in -- #38 -//│ if scrut -- #36 +//│ if scrut -- #37 //│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #35 +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 //│ false => -//│ let scrut = ==(1,tailrecBranch) in -- #34 -//│ if scrut -- #33 +//│ let scrut = ==(1,tailrecBranch) in -- #36 +//│ if scrut -- #35 //│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #32 +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 //│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #31 +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 //│ ) //│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, @@ -589,16 +589,16 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, //│ let scrut = ==(0,tailrecBranch) in -- #38 -//│ if scrut -- #36 +//│ if scrut -- #37 //│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #35 +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 //│ false => -//│ let scrut = ==(1,tailrecBranch) in -- #34 -//│ if scrut -- #33 +//│ let scrut = ==(1,tailrecBranch) in -- #36 +//│ if scrut -- #35 //│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #32 +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 //│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #31 +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 //│ ) //│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, @@ -622,12 +622,12 @@ hello() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, hello, [], //│ 1, -//│ let* (x$0) = @tailrec hello() in -- #x +//│ let* (x$0) = @tailrec hello() in -- #5 //│ 2 -- #3 //│ ) //│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) +//│ let* (x$1) = hello() in -- #9 +//│ x$1 -- #8) //│ //│ IR Processing Failed: not a tail call //│ @@ -641,8 +641,8 @@ hello() //│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) //│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) //│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:302) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:487) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:494) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:501) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:508) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -716,29 +716,29 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, addOne, [xs$0], //│ 1, -//│ case xs$0 of -- #27 +//│ case xs$0 of -- #28 //│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #x +//│ let x$1 = xs$0.t in -- #24 +//│ let x$2 = xs$0.h in -- #23 +//│ let x$3 = +(x$2,1) in -- #22 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #21 //│ let x$5 = Cons(x$3,x$4) in -- #19 //│ jump j$0(x$5) -- #18 //│ Nil => -//│ let x$6 = Nil() in -- #26 -//│ jump j$0(x$6) -- #25 +//│ let x$6 = Nil() in -- #27 +//│ jump j$0(x$6) -- #26 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) //│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ let x$7 = Nil() in -- #53 +//│ let x$8 = Cons(3,x$7) in -- #52 +//│ let x$9 = Cons(2,x$8) in -- #51 +//│ let x$10 = Cons(1,x$9) in -- #50 +//│ let* (x$11) = addOne(x$10) in -- #49 +//│ x$11 -- #48) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) @@ -749,30 +749,30 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ ) //│ Def(2, addOne_jp, [xs$0], //│ 1, -//│ case xs$0 of -- #59 +//│ case xs$0 of -- #62 //│ Cons => -//│ let x$1 = xs$0.t in -- #57 -//│ let x$2 = xs$0.h in -- #56 -//│ let x$3 = +(x$2,1) in -- #55 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #54 -//│ let x$5 = Cons(x$3,x$4) in -- #53 -//│ jump j$0(x$5) -- #18 +//│ let x$1 = xs$0.t in -- #59 +//│ let x$2 = xs$0.h in -- #58 +//│ let x$3 = +(x$2,1) in -- #57 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #56 +//│ let x$5 = Cons(x$3,x$4) in -- #55 +//│ jump j$0(x$5) -- #54 //│ Nil => -//│ let x$6 = Nil() in -- #58 -//│ jump j$0(x$6) -- #25 +//│ let x$6 = Nil() in -- #61 +//│ jump j$0(x$6) -- #60 //│ ) //│ Def(3, addOne, [xs$0], //│ 1, -//│ let* (xs$0_) = addOne_jp(xs$0) in -- #x -//│ xs$0_ -- #x +//│ let* (xs$0_) = addOne_jp(xs$0) in -- #64 +//│ xs$0_ -- #63 //│ ) //│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ let x$7 = Nil() in -- #53 +//│ let x$8 = Cons(3,x$7) in -- #52 +//│ let x$9 = Cons(2,x$8) in -- #51 +//│ let x$10 = Cons(1,x$9) in -- #50 +//│ let* (x$11) = addOne(x$10) in -- #49 +//│ x$11 -- #48) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { @@ -782,30 +782,30 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ ) //│ Def(2, addOne_jp, [xs$0], //│ 1, -//│ case xs$0 of -- #59 +//│ case xs$0 of -- #62 //│ Cons => -//│ let x$1 = xs$0.t in -- #57 -//│ let x$2 = xs$0.h in -- #56 -//│ let x$3 = +(x$2,1) in -- #55 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #54 -//│ let x$5 = Cons(x$3,x$4) in -- #53 -//│ jump j$0(x$5) -- #18 +//│ let x$1 = xs$0.t in -- #59 +//│ let x$2 = xs$0.h in -- #58 +//│ let x$3 = +(x$2,1) in -- #57 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #56 +//│ let x$5 = Cons(x$3,x$4) in -- #55 +//│ jump j$0(x$5) -- #54 //│ Nil => -//│ let x$6 = Nil() in -- #58 -//│ jump j$0(x$6) -- #25 +//│ let x$6 = Nil() in -- #61 +//│ jump j$0(x$6) -- #60 //│ ) //│ Def(3, addOne, [xs$0], //│ 1, -//│ let* (xs$0_) = addOne_jp(xs$0) in -- #x -//│ xs$0_ -- #x +//│ let* (xs$0_) = addOne_jp(xs$0) in -- #64 +//│ xs$0_ -- #63 //│ ) //│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) +//│ let x$7 = Nil() in -- #53 +//│ let x$8 = Cons(3,x$7) in -- #52 +//│ let x$9 = Cons(2,x$8) in -- #51 +//│ let x$10 = Cons(1,x$9) in -- #50 +//│ let* (x$11) = addOne(x$10) in -- #49 +//│ x$11 -- #48) //│ //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) @@ -832,16 +832,16 @@ a(S(S(S(Zero)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ case n$0 of -- #23 +//│ case n$0 of -- #24 //│ S => -//│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailrec b(x$1) in -- #x +//│ let x$1 = n$0.x in -- #16 +//│ let* (x$2) = @tailrec b(x$1) in -- #15 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => -//│ let x$4 = Zero() in -- #22 -//│ let x$5 = S(x$4) in -- #21 -//│ jump j$0(x$5) -- #20 +//│ let x$4 = Zero() in -- #23 +//│ let x$5 = S(x$4) in -- #22 +//│ jump j$0(x$5) -- #21 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -849,38 +849,38 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ case n$1 of -- #55 +//│ case n$1 of -- #57 //│ S => -//│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailrec a(x$7) in -- #x -//│ let x$9 = S(x$8) in -- #41 -//│ let x$10 = S(x$9) in -- #40 -//│ jump j$1(x$10) -- #39 +//│ let x$7 = n$1.x in -- #45 +//│ let* (x$8) = @tailrec a(x$7) in -- #44 +//│ let x$9 = S(x$8) in -- #42 +//│ let x$10 = S(x$9) in -- #41 +//│ jump j$1(x$10) -- #40 //│ Zero => -//│ let x$11 = Zero() in -- #54 -//│ let x$12 = S(x$11) in -- #53 -//│ let x$13 = S(x$12) in -- #52 -//│ jump j$1(x$13) -- #51 +//│ let x$11 = Zero() in -- #56 +//│ let x$12 = S(x$11) in -- #55 +//│ let x$13 = S(x$12) in -- #54 +//│ jump j$1(x$13) -- #53 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #25 +//│ x$6 -- #26 //│ ) //│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ let x$14 = Zero() in -- #76 +//│ let x$15 = S(x$14) in -- #75 +//│ let x$16 = S(x$15) in -- #74 +//│ let x$17 = S(x$16) in -- #73 +//│ let* (x$18) = a(x$17) in -- #72 +//│ x$18 -- #71) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #84 -//│ r0 -- #83 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #111 +//│ r0 -- #110 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -888,60 +888,60 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #109 +//│ r0 -- #108 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #25 +//│ x$6 -- #26 //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #79 -//│ if scrut -- #77 +//│ let scrut = ==(0,tailrecBranch) in -- #106 +//│ if scrut -- #105 //│ true => -//│ case a_n$0 of -- #x +//│ case a_n$0 of -- #104 //│ S => -//│ let x$1 = a_n$0.x in -- #x -//│ let* (x$2) = @tailrec b(x$1) in -- #76 -//│ let x$3 = S(x$2) in -- #x -//│ jump j$0(x$3) -- #x +//│ let x$1 = a_n$0.x in -- #100 +//│ let* (x$2) = @tailrec b(x$1) in -- #99 +//│ let x$3 = S(x$2) in -- #98 +//│ jump j$0(x$3) -- #97 //│ Zero => -//│ let x$4 = Zero() in -- #x -//│ let x$5 = S(x$4) in -- #x -//│ jump j$0(x$5) -- #x +//│ let x$4 = Zero() in -- #103 +//│ let x$5 = S(x$4) in -- #102 +//│ jump j$0(x$5) -- #101 //│ false => -//│ case b_n$1 of -- #x +//│ case b_n$1 of -- #96 //│ S => -//│ let x$7 = b_n$1.x in -- #x -//│ let* (x$8) = @tailrec a(x$7) in -- #75 -//│ let x$9 = S(x$8) in -- #x -//│ let x$10 = S(x$9) in -- #x -//│ jump j$1(x$10) -- #x +//│ let x$7 = b_n$1.x in -- #91 +//│ let* (x$8) = @tailrec a(x$7) in -- #90 +//│ let x$9 = S(x$8) in -- #89 +//│ let x$10 = S(x$9) in -- #88 +//│ jump j$1(x$10) -- #87 //│ Zero => -//│ let x$11 = Zero() in -- #x -//│ let x$12 = S(x$11) in -- #x -//│ let x$13 = S(x$12) in -- #x -//│ jump j$1(x$13) -- #x +//│ let x$11 = Zero() in -- #95 +//│ let x$12 = S(x$11) in -- #94 +//│ let x$13 = S(x$12) in -- #93 +//│ jump j$1(x$13) -- #92 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #80 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #107 //│ ) //│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ let x$14 = Zero() in -- #76 +//│ let x$15 = S(x$14) in -- #75 +//│ let x$16 = S(x$15) in -- #74 +//│ let x$17 = S(x$16) in -- #73 +//│ let* (x$18) = a(x$17) in -- #72 +//│ x$18 -- #71) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #84 -//│ r0 -- #83 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #111 +//│ r0 -- #110 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -949,53 +949,53 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #109 +//│ r0 -- #108 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #25 +//│ x$6 -- #26 //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #79 -//│ if scrut -- #77 +//│ let scrut = ==(0,tailrecBranch) in -- #106 +//│ if scrut -- #105 //│ true => -//│ case a_n$0 of -- #x +//│ case a_n$0 of -- #104 //│ S => -//│ let x$1 = a_n$0.x in -- #x -//│ let* (x$2) = @tailrec b(x$1) in -- #76 -//│ let x$3 = S(x$2) in -- #x -//│ jump j$0(x$3) -- #x +//│ let x$1 = a_n$0.x in -- #100 +//│ let* (x$2) = @tailrec b(x$1) in -- #99 +//│ let x$3 = S(x$2) in -- #98 +//│ jump j$0(x$3) -- #97 //│ Zero => -//│ let x$4 = Zero() in -- #x -//│ let x$5 = S(x$4) in -- #x -//│ jump j$0(x$5) -- #x +//│ let x$4 = Zero() in -- #103 +//│ let x$5 = S(x$4) in -- #102 +//│ jump j$0(x$5) -- #101 //│ false => -//│ case b_n$1 of -- #x +//│ case b_n$1 of -- #96 //│ S => -//│ let x$7 = b_n$1.x in -- #x -//│ let* (x$8) = @tailrec a(x$7) in -- #75 -//│ let x$9 = S(x$8) in -- #x -//│ let x$10 = S(x$9) in -- #x -//│ jump j$1(x$10) -- #x +//│ let x$7 = b_n$1.x in -- #91 +//│ let* (x$8) = @tailrec a(x$7) in -- #90 +//│ let x$9 = S(x$8) in -- #89 +//│ let x$10 = S(x$9) in -- #88 +//│ jump j$1(x$10) -- #87 //│ Zero => -//│ let x$11 = Zero() in -- #x -//│ let x$12 = S(x$11) in -- #x -//│ let x$13 = S(x$12) in -- #x -//│ jump j$1(x$13) -- #x +//│ let x$11 = Zero() in -- #95 +//│ let x$12 = S(x$11) in -- #94 +//│ let x$13 = S(x$12) in -- #93 +//│ jump j$1(x$13) -- #92 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #80 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #107 //│ ) //│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ let x$14 = Zero() in -- #76 +//│ let x$15 = S(x$14) in -- #75 +//│ let x$16 = S(x$15) in -- #74 +//│ let x$17 = S(x$16) in -- #73 +//│ let* (x$18) = a(x$17) in -- #72 +//│ x$18 -- #71) //│ //│ Interpreted: //│ S(S(S(S(S(S(Zero())))))) @@ -1063,8 +1063,8 @@ b(20) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #81 -//│ r0 -- #80 +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #103 +//│ r0 -- #102 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -1072,8 +1072,8 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #79 -//│ r0 -- #78 +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #101 +//│ r0 -- #100 //│ ) //│ Def(3, j$1, [x$11], //│ 1, @@ -1081,36 +1081,36 @@ b(20) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #76 -//│ if scrut -- #74 +//│ let scrut = ==(0,tailrecBranch) in -- #98 +//│ if scrut -- #97 //│ true => -//│ let x$1 = a_x$0.n in -- #x -//│ let x$2 = <(x$1,0) in -- #x -//│ if x$2 -- #x +//│ let x$1 = a_x$0.n in -- #96 +//│ let x$2 = <(x$1,0) in -- #95 +//│ if x$2 -- #94 //│ true => -//│ let x$4 = a_x$0.n in -- #x -//│ let x$5 = A(-1,x$4) in -- #x -//│ jump j$0(x$5) -- #x +//│ let x$4 = a_x$0.n in -- #88 +//│ let x$5 = A(-1,x$4) in -- #87 +//│ jump j$0(x$5) -- #86 //│ false => -//│ let x$6 = a_x$0.n in -- #x -//│ let x$7 = -(x$6,2) in -- #x -//│ let* (x$8) = b(x$7) in -- #73 -//│ let x$9 = A(2,x$8) in -- #x -//│ jump j$0(x$9) -- #x +//│ let x$6 = a_x$0.n in -- #93 +//│ let x$7 = -(x$6,2) in -- #92 +//│ let* (x$8) = b(x$7) in -- #91 +//│ let x$9 = A(2,x$8) in -- #90 +//│ jump j$0(x$9) -- #89 //│ false => -//│ let x$10 = <(b_n$0,0) in -- #x -//│ if x$10 -- #x +//│ let x$10 = <(b_n$0,0) in -- #85 +//│ if x$10 -- #84 //│ true => -//│ let x$12 = A(0,b_n$0) in -- #x -//│ jump j$1(x$12) -- #x +//│ let x$12 = A(0,b_n$0) in -- #80 +//│ jump j$1(x$12) -- #79 //│ false => -//│ let x$13 = -(b_n$0,1) in -- #x -//│ let x$14 = A(0,x$13) in -- #x -//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #72 +//│ let x$13 = -(b_n$0,1) in -- #83 +//│ let x$14 = A(0,x$13) in -- #82 +//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #81 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #77 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #99 //│ ) //│ }, //│ let* (x$16) = b(20) in -- #71 @@ -1120,8 +1120,8 @@ b(20) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #81 -//│ r0 -- #80 +//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #103 +//│ r0 -- #102 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -1129,8 +1129,8 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #79 -//│ r0 -- #78 +//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #101 +//│ r0 -- #100 //│ ) //│ Def(3, j$1, [x$11], //│ 1, @@ -1138,36 +1138,36 @@ b(20) //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #76 -//│ if scrut -- #74 +//│ let scrut = ==(0,tailrecBranch) in -- #98 +//│ if scrut -- #97 //│ true => -//│ let x$1 = a_x$0.n in -- #x -//│ let x$2 = <(x$1,0) in -- #x -//│ if x$2 -- #x +//│ let x$1 = a_x$0.n in -- #96 +//│ let x$2 = <(x$1,0) in -- #95 +//│ if x$2 -- #94 //│ true => -//│ let x$4 = a_x$0.n in -- #x -//│ let x$5 = A(-1,x$4) in -- #x -//│ jump j$0(x$5) -- #x +//│ let x$4 = a_x$0.n in -- #88 +//│ let x$5 = A(-1,x$4) in -- #87 +//│ jump j$0(x$5) -- #86 //│ false => -//│ let x$6 = a_x$0.n in -- #x -//│ let x$7 = -(x$6,2) in -- #x -//│ let* (x$8) = b(x$7) in -- #73 -//│ let x$9 = A(2,x$8) in -- #x -//│ jump j$0(x$9) -- #x +//│ let x$6 = a_x$0.n in -- #93 +//│ let x$7 = -(x$6,2) in -- #92 +//│ let* (x$8) = b(x$7) in -- #91 +//│ let x$9 = A(2,x$8) in -- #90 +//│ jump j$0(x$9) -- #89 //│ false => -//│ let x$10 = <(b_n$0,0) in -- #x -//│ if x$10 -- #x +//│ let x$10 = <(b_n$0,0) in -- #85 +//│ if x$10 -- #84 //│ true => -//│ let x$12 = A(0,b_n$0) in -- #x -//│ jump j$1(x$12) -- #x +//│ let x$12 = A(0,b_n$0) in -- #80 +//│ jump j$1(x$12) -- #79 //│ false => -//│ let x$13 = -(b_n$0,1) in -- #x -//│ let x$14 = A(0,x$13) in -- #x -//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #72 +//│ let x$13 = -(b_n$0,1) in -- #83 +//│ let x$14 = A(0,x$13) in -- #82 +//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #81 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #77 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #99 //│ ) //│ }, //│ let* (x$16) = b(20) in -- #71 From e778810d6aeeb7c0ff18c3a018ea2633897af422 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 1 May 2024 18:29:39 +0800 Subject: [PATCH 28/59] progress, handle join points properly --- .../scala/mlscript/compiler/ir/Builder.scala | 47 +- .../compiler/ir/DefnRefResolver.scala | 2 +- .../main/scala/mlscript/compiler/ir/IR.scala | 10 +- .../scala/mlscript/compiler/ir/Interp.scala | 2 +- .../mlscript/compiler/ir/Validator.scala | 2 +- .../compiler/optimizer/Analysis.scala | 4 +- .../compiler/optimizer/TailRecOpt.scala | 489 ++++++++++++--- compiler/shared/test/diff-ir/IRTailRec.mls | 573 +++++++++++++----- compiler/shared/test/diff-ir/NuScratch.mls | 151 +++++ .../test/scala/mlscript/compiler/TestIR.scala | 5 +- 10 files changed, 1017 insertions(+), 268 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 7071cd6ef5..d64a32035d 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -72,6 +72,28 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres tm private def buildResultFromTerm(using ctx: Ctx)(tm: Term)(k: Node => Node): Node = + def buildLetCall(f: Term, xs: Tup, ann: Option[Term]) = + buildResultFromTerm(f) { node => node match + case Result(Ref(f) :: Nil) if ctx.fnCtx.contains(f.str) => buildResultFromTerm(xs) { + case Result(args) => + val v = fresh.make + + ann match + case Some(Var(nme)) if nme == "tailrec" => + LetCall(List(v), DefnRef(Right(f.str)), args, true, v |> ref |> sresult |> k).attachTag(tag) + case Some(_) => node |> unexpectedNode + case None => LetCall(List(v), DefnRef(Right(f.str)), args, false, v |> ref |> sresult |> k).attachTag(tag) + + case node @ _ => node |> unexpectedNode + } + case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { + case Result(args) => + throw IRError(s"not supported: apply") + case node @ _ => node |> unexpectedNode + } + case node @ _ => node |> unexpectedNode + } + val res = tm match case lit: Lit => Literal(lit) |> sresult |> k case v @ Var(name) => @@ -114,21 +136,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case node @ _ => node |> unexpectedNode } - case App(f, xs @ Tup(_)) => - buildResultFromTerm(f) { - case Result(Ref(f) :: Nil) if ctx.fnCtx.contains(f.str) => buildResultFromTerm(xs) { - case Result(args) => - val v = fresh.make - LetCall(List(v), DefnRef(Right(f.str)), args, v |> ref |> sresult |> k, false).attachTag(tag) - case node @ _ => node |> unexpectedNode - } - case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { - case Result(args) => - throw IRError(s"not supported: apply") - case node @ _ => node |> unexpectedNode - } - case node @ _ => node |> unexpectedNode - } + case App(f, xs @ Tup(_)) => buildLetCall(f, xs, None) + case Ann(ann, App(f, xs @ Tup(_))) => buildLetCall(f, xs, Some(ann)) case Let(false, Var(name), rhs, body) => buildBinding(name, rhs, body)(k) @@ -226,14 +235,6 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres v |> ref |> sresult |> k).attachTag(tag) case node @ _ => node |> unexpectedNode } - - case Ann(term, receiver) => - // TODO: what happens if the tailrec module is overridden? - (term, buildResultFromTerm(receiver)(k)) match - case (Var("tailrec"), LetCall(names, defn, args, body, isTailRec)) => - LetCall(names, defn, args, body, true).attachTag(tag) - case _ => tm |> unexpectedTerm - case tup: Tup => buildResultFromTup(tup)(k) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index 765815ae96..6df55f3235 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -11,7 +11,7 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case Result(res) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(resultNames, defnref, args, body, _) => + case LetCall(resultNames, defnref, args, _, body) => defs.find{_.getName == defnref.getName} match case Some(defn) => defnref.defn = Left(defn) case None => throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 3846ea43c9..34a3ed7695 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -137,7 +137,7 @@ enum Node: case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) - case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], body: Node, isTailRec: Bool) + case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node) case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr, body: Node) var tag = DefnTag(-1) @@ -161,7 +161,7 @@ enum Node: case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case LetCall(names, defn, args, body, isTailRec) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f), isTailRec) + case LetCall(names, defn, args, isTailRec, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f)) case AssignField(assignee, fieldName, clsInfo, value, body) => AssignField(f(assignee), fieldName, clsInfo, value.mapNameOfTrivialExpr(f), body.mapName(f)) @@ -172,9 +172,9 @@ enum Node: case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) - case LetCall(names, defn, args, body, isTailRec) => + case LetCall(names, defn, args, isTailRec, body) => val names_copy = names.map(_.copy) - LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x)), isTailRec) + LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x))) case AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee.trySubst(ctx), clsInfo, fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) @@ -208,7 +208,7 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - case LetCall(xs, defn, args, body, isTailRec) => + case LetCall(xs, defn, args, isTailRec, body) => stack( raw("let*") <:> raw("(") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 3b4e305a13..50a39371e1 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -164,7 +164,7 @@ class Interpreter(verbose: Bool): case INode.Jump(defnref, args) => Jump(DefnRef(Right(defnref.getName)), args |> convertArgs) case INode.Case(scrut, cases) => Case(scrut, cases.map{(cls, node) => (cls, node |> convert)}) case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) - case INode.LetCall(xs, defnref, args, body, _) => + case INode.LetCall(xs, defnref, args, _, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) case INode.AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value |> convert, body |> convert) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index 8682cdebb7..a52fde5ef3 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -11,7 +11,7 @@ private final class DefnRefInSet(defs: Set[Defn]): case Jump(defn, args) => case Case(scrut, cases) => cases map { (_, body) => f(body) } case LetExpr(name, expr, body) => f(body) - case LetCall(res, defnref, args, body, _) => + case LetCall(res, defnref, args, _, body) => defnref.getDefn match { case Some(real_defn) => if (!defs.exists(_ eq real_defn)) throw IRError("ref is not in the set") case _ => diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index aa3c29dbd3..98449d5914 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -45,7 +45,7 @@ class UsefulnessAnalysis(verbose: Bool = false): case Jump(defn, args) => args.foreach(f) case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) - case LetCall(names, defn, args, body, _) => args.foreach(f); names.foreach(addDef); f(body) + case LetCall(names, defn, args, _, body) => args.foreach(f); names.foreach(addDef); f(body) case AssignField(assignee, clsInfo, fieldName, value, body) => f(value); f(body) def run(x: Defn) = @@ -86,7 +86,7 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val fv2 = f(using defined)(expr, fv) val defined2 = defined + name.str f(using defined2)(body, fv2) - case LetCall(resultNames, defnref, args, body, _) => + case LetCall(resultNames, defnref, args, _, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) if (extended_scope && !visited.contains(defnref.getName)) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 95e2e76f4c..1c6655e7bd 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -7,65 +7,134 @@ import mlscript.IntLit import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class -class TailRecOpt(fnUid: FreshInt, tag: FreshInt): +class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String) enum CallInfo: case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo - case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo) extends CallInfo + case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo override def toString(): String = this match case TailCallInfo(src, defn, letCallNode) => f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" - case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode) => + case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" def getSrc = this match case TailCallInfo(src, _, _) => src - case ModConsCallInfo(src, _, _, _, _) => src + case ModConsCallInfo(src, _, _, _, _, _, _) => src def getDefn = this match case TailCallInfo(_, defn, _) => defn - case ModConsCallInfo(_, _, defn, _, _) => defn + case ModConsCallInfo(_, _, defn, _, _, _, _) => defn - private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo]): - def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges) + private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo], val joinPoints: Set[Defn]): + def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges, joinPoints) - private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo]) + private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo], val joinPoints: Set[Defn]) import CallInfo._ + // Hack to make scala think discoverJoinPoints is tail recursive and be + // partially optimized :P + def casesToJps(cases: List[(ClassInfo, Node)], acc: Set[Defn]): Set[Defn] = + cases.foldLeft(acc)((jps, branch) => discoverJoinPoints(branch._2, jps)) + + def discoverJoinPointsCont(defn: Defn, acc: Set[Defn]) = + discoverJoinPoints(defn.body, acc) + defn + + // do a DFS to discover join points @tailrec - private def getOptimizableCalls(node: Node)(implicit + private def discoverJoinPoints(node: Node, acc: Set[Defn]): Set[Defn] = + node match + case Result(res) => Set() + case Jump(defn_, args) => + val defn = defn_.expectDefn + if isIdentityJp(defn) then acc + else if acc.contains(defn) then acc + else discoverJoinPointsCont(defn, acc + defn) + case Case(scrut, cases) => casesToJps(cases, acc) + case LetExpr(name, expr, body) => discoverJoinPoints(body, acc) + case LetCall(names, defn, args, isTailRec, body) => discoverJoinPoints(body, acc) + case AssignField(assignee, clsInfo, fieldName, value, body) => discoverJoinPoints(body, acc) + + private def getRetName(names: Set[Name], retVals: List[TrivialExpr]): Option[Name] = + val names = retVals.collect { case Expr.Ref(nme) => nme } + if names.length != 1 then None + else + val nme = names.head + if names.contains(nme) then Some(nme) + else None + + // would prefer to have this inside discoverOptimizableCalls, but this makes scala think it's not tail recursive + def shadowAndCont(next: Node, nme: Name)(implicit + acc: Map[Int, Set[CallInfo]], src: Defn, start: Node, calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], - candReturnName: Option[Name] - ): Either[CallInfo, List[Node]] = + containingCtors: Set[Name] + ) = discoverOptimizableCalls(next)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors - nme) + + @tailrec + private def discoverOptimizableCalls(node: Node)(implicit + acc: Map[Int, Set[CallInfo]], + src: Defn, + start: Node, + calledDefn: Option[Defn], // The definition that was called in a tailrec mod cons call + letCallNode: Option[LetCall], // The place where that definition was called + letCtorNode: Option[LetCtorNodeInfo], // The place where the result from that call was put into a constructor + containingCtors: Set[Name], // Value names of ctors containing the constructor containing the result from the call + ): Either[Map[Int, Set[CallInfo]], List[Node]] = + def updateMap(acc: Map[Int, Set[CallInfo]], c: Set[CallInfo]) = + acc.get(src.id) match + case None => acc + (src.id -> c) + case Some(value) => acc + (src.id -> (value ++ c)) + + def updateMapSimple(c: CallInfo) = updateMap(acc, Set(c)) + def returnFailure = letCallNode match - case Some(LetCall(_, _, _, _, isTailRec)) if isTailRec => throw IRError("not a tail call") + case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") case _ => Right(Nil) node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => - (calledDefn, letCallNode, letCtorNode, candReturnName) match - case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => - if argsListEqual(List(candReturnName), res) then - Left(ModConsCallInfo(src, start, defn, letCallNode, letCtorName)) - else - returnFailure + (calledDefn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + getRetName(containingCtors, res) match + case None => returnFailure + case Some(value) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) case _ => returnFailure case Jump(jp, args) => + def mergeCalls(acc: Map[Int, Set[CallInfo]], calls: Set[CallInfo]) = + val newCalls = calls.map { + case TailCallInfo(_, defn, letCallNode) => + TailCallInfo(src, defn, letCallNode) + case ModConsCallInfo(_, startNode, defn, letCallNode, letCtorNode, retName, retNode) => + ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, retName, retNode) + } + updateMap(acc, newCalls) + + def updateAndMergeJpCalls = letCallNode match + case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") + case _ => + val jpDefn = jp.expectDefn + acc.get(jpDefn.id) match + case None => // explore the join point + val newAcc = discoverCalls(jpDefn.body)(jpDefn, acc) + mergeCalls(newAcc, newAcc(jpDefn.id)) + case Some(value) => mergeCalls(acc, value) + // different cases - (calledDefn, letCallNode, letCtorNode, candReturnName) match - case (Some(defn), Some(letCallNode), Some(letCtorName), Some(candReturnName)) => - if argsListEqual(List(candReturnName), args) && isIdentityJp(jp.expectDefn) then - Left(ModConsCallInfo(src, start, defn, letCallNode, letCtorName)) - else - returnFailure - case _ => returnFailure + (calledDefn, letCallNode, letCtorNode) match + case (Some(defn), Some(letCallNode), Some(letCtorName)) => + getRetName(containingCtors, args) match + case Some(value) if isIdentityJp(jp.expectDefn) => + Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) + case _ => + Left(updateAndMergeJpCalls) + case _ => Left(updateAndMergeJpCalls) case Case(scrut, cases) => Right(cases.map(_._2)) case x: LetExpr => @@ -74,24 +143,25 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // Check if this let binding references the mod cons call. case Expr.Ref(name) => letCallNode match - case None => getOptimizableCalls(body) // OK - case Some(LetCall(names, _, _, _, isTailRec)) => + case None => + shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered else - getOptimizableCalls(body) // OK + shadowAndCont(body, name) // OK - case Expr.Literal(lit) => getOptimizableCalls(body) // OK + case Expr.Literal(lit) => shadowAndCont(body, name) // OK case y: Expr.CtorApp => val Expr.CtorApp(clsInfo, ctorArgs) = y // if expr is a constructor with a call to some function as a parameter letCallNode match - case None => getOptimizableCalls(body) // OK - case Some(LetCall(letCallNames, _, _, _, isTailRec)) => // there was a previous call + case None => shadowAndCont(body, name) // OK + case Some(LetCall(letCallNames, _, _, isTailRec, _)) => // there was a previous call // 1. Check if the ctor application contains this call val argNames = ctorArgs.collect { case Expr.Ref(name) => name }.toSet val namesSet = letCallNames.toSet @@ -99,10 +169,15 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): if inters.isEmpty then // OK, this constructor does not use the mod cons call - // Now check if the constructor uses the previous ctor. - candReturnName match - case None => getOptimizableCalls(body) // no previous ctor, just continue - case Some(value) => getOptimizableCalls(body)(src, start, calledDefn, letCallNode, letCtorNode, Some(name)) + // Now check if the constructor uses any previous ctor containing the call. + // If it does, then add this name to the list of constructors containing the call + val inters = containingCtors.intersect(argNames) + + if inters.isEmpty then + shadowAndCont(body, name) // does not use, OK to ignore this one + else + // add this name to the list of constructors containing the call + discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors + name) else // it does use it, further analyse letCtorNode match @@ -121,53 +196,57 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - getOptimizableCalls(body)(src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName)), Some(name)) + discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName)), Set(name)) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered case Expr.Select(name, cls, field) => letCallNode match - case None => getOptimizableCalls(body) // OK - case Some(LetCall(names, _, _, _, isTailRec)) => + case None => shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered else - getOptimizableCalls(body) // OK - case Expr.BasicOp(name, args) => + shadowAndCont(body, name) // OK + case Expr.BasicOp(_, args) => letCallNode match - case None => getOptimizableCalls(body) // OK - case Some(LetCall(names, _, _, _, isTailRec)) => + case None => shadowAndCont(body, name) // OK + case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. val argNames = args.collect { case Expr.Ref(name) => name }.toSet val namesSet = names.toSet val inters = argNames.intersect(namesSet) if inters.isEmpty then - getOptimizableCalls(body) // OK + shadowAndCont(body, name) // OK else // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue if isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered case x: LetCall => - val LetCall(names, defn, args, body, isTailRec) = x + val LetCall(names, defn, args, isTailRec, body) = x if isTailCall(x) then - Left(TailCallInfo(src, defn.expectDefn, x)) + Left(updateMapSimple(TailCallInfo(src, defn.expectDefn, x))) else letCallNode match case None => // OK, use this LetCall as the mod cons - getOptimizableCalls(body)(src, start, Some(defn.expectDefn), Some(x), None, None) - case Some(LetCall(namesOld, defnOld, argsOld, bodyOld, isTailRecOld)) => + // For now, only optimize functions which return one value + if defn.expectDefn.resultNum == 1 then + discoverOptimizableCalls(body)(acc, src, start, Some(defn.expectDefn), Some(x), None, Set()) + else + discoverOptimizableCalls(body) + case Some(LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld)) => if isTailRecOld && isTailRec then // 1. If both the old and newly discovered call are marked with tailrec, error throw IRError("multiple calls in the same branch marked with tailrec") @@ -178,30 +257,34 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val namesSet = namesOld.toSet val inters = argNames.intersect(namesSet) - if inters.isEmpty then getOptimizableCalls(body) // OK + if inters.isEmpty then discoverOptimizableCalls(body) // OK else throw IRError("not a mod cons call") else // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec - getOptimizableCalls(body)(src, start, Some(defn.expectDefn), Some(x), None, None) + if defn.expectDefn.resultNum == 1 then + discoverOptimizableCalls(body)(acc, src, start, Some(defn.expectDefn), Some(x), None, Set()) + else + // shadow all the variables in this letcall + discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => // make sure `value` is not the mod cons call letCallNode match - case None => getOptimizableCalls(body) // OK - case Some(LetCall(names, defn, args, body, isTailRec)) => + case None => discoverOptimizableCalls(body) // OK + case Some(LetCall(names, defn, args, isTailRec, body)) => value match case Expr.Ref(name) => if names.contains(name) && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered case _ => letCtorNode match - case None => getOptimizableCalls(body) // OK + case None => discoverOptimizableCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") - else getOptimizableCalls(body)(src, start, None, None, None, None) // invalidate everything that's been discovered + else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -211,20 +294,29 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): else false - private def isIdentityJp(d: Defn): Bool = true + private def isIdentityJp(d: Defn): Bool = d.body match + case Result(res) => argsListEqual(d.params, res) + case Jump(defn, args) => argsListEqual(d.params, args) && isIdentityJp(defn.expectDefn) + case _ => false private def isTailCall(node: Node): Boolean = node match - case LetCall(names, defn, args, body, _) => + case LetCall(names, defn, args, _, body) => body match case Result(res) => argsListEqual(names, res) case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) case _ => false case _ => false - private def findTailCalls(node: Node)(implicit src: Defn): Set[CallInfo] = - getOptimizableCalls(node)(src, node, None, None, None, None) match - case Left(callInfo) => Set(callInfo) - case Right(nodes) => nodes.foldLeft(Set())((calls, node) => calls ++ findTailCalls(node)) + private def discoverCallsCont(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = + discoverOptimizableCalls(node)(acc, src, node, None, None, None, Set()) match + case Left(acc) => acc + case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverCalls(node)(src, acc)) + + private def discoverCalls(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = + val ret = discoverCallsCont(node) + ret.get(src.id) match + case None => ret + (src.id -> Set()) + case Some(value) => ret // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -243,8 +335,12 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = val defns = nodeMap.values.toSet + val inital = Map[Int, Set[CallInfo]]() + val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn.body)(defn.defn, acc)).withDefaultValue(Set()) + print(edges) var ctr = 0 + // nodes, edges var stack: List[(DefnNode, Set[CallInfo])] = Nil var sccs: List[DefnGraph] = Nil @@ -254,7 +350,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): ctr += 1 src.visited = true - val tailCalls = findTailCalls(src.defn.body)(src.defn) + val tailCalls = edges(src.defn.id) stack = (src, tailCalls) :: stack for u <- tailCalls do val neighbour = nodeMap(u.getDefn.id) @@ -295,7 +391,9 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val sccIds = scc.map { d => d.defn.id } sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} - sccs = DefnGraph(scc, sccEdges) :: sccs + val joinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => discoverJoinPoints(defn.defn.body, jps)) + println(joinPoints.map(_.name)) + sccs = DefnGraph(scc, sccEdges, joinPoints) :: sccs for v <- defns do if (!v.visited) @@ -328,18 +426,243 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): makeCaseBranch(cmpValue, nodeIfTrue, elz) ) + // TAIL RECURSION MOD CONS + // Uses the ideas in section 2.2 of the paper `Tail Recursion Modulo Context` + // by Leijen and Lorenzen: https://dl.acm.org/doi/abs/10.1145/3571233 + // of whom attribute the method to Risch, Friedman, Wise, Minamide. + + final val ID_CONTEXT_NAME = "_IdContext" + final val CONTEXT_NAME = "_Context" + + // `ctx` class for tailrec mod cons. + // The paper uses two values `res: T` and `hole: ptr` to represent the context. + // We represent the context as three values instead of two to avoid needing pointers: + // + // acc: The accumulated value. This is the same as `res` in the paper. If the functions f1, ..., fn + // in the compoennt return type T1, ..., Tn, then acc has type T1 | ... | Tn. + // + // The following together represent `hole` in the paper: + // ptr: Represents the object containing the "hole" to be written to. + // field: Integer representing which class and field the "hole" belongs to. Which class and field this + // represents is different for each strongly connected component. + final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) + final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) + // Given a strongly connected component `defns` of mutually mod cons functions, // returns a set containing mutually tail recursive versions of them and // the original functions pointing to the optimized ones. - private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Set[ClassInfo]) = val modConsCalls = component.edges.collect { case x: ModConsCallInfo => x } - + val defns = component.nodes + val defnsIdSet = defns.map(_.id).toSet + // no mod cons, just return the original if modConsCalls.isEmpty then - component.nodes + (component.nodes, classes) else + val trueClass = classes.find(c => c.ident == "True").get + val falseClass = classes.find(c => c.ident == "False").get + + // CONOTEXT APPLICATION + + val mergedNames = defns.foldLeft("")(_ + "_" + _.name) + + val ctxAppName = mergedNames + "_ctx_app" + val ctxCompName = mergedNames + "_ctx_comp" + + // map integers to classes and fields which will be assigned to + val classIdMap = classes.map(c => c.id -> c).toMap + val possibleAssigns = modConsCalls.map(call => (call.letCtorNode.cls.id, call.letCtorNode.fieldName)).toSet + val possibleAssignsIdxes = possibleAssigns.toList.zipWithIndex + + val assignToIdx = possibleAssignsIdxes.map((item, idx) => item -> idx).toMap + + // fun app(ctx, x: T): T + val appCtxName = Name("ctx") + val appValName = Name("x") + + val assignmentCases = possibleAssignsIdxes.map((item, idx) => + val clsId = item._1 + val fieldName = item._2 + val cls = classIdMap(clsId) + + // let ptr = ctx.ptr in + // ptr. = x in + // let acc = ctx.acc + // acc + val node = LetExpr( + Name("ptr"), + Expr.Select(appCtxName, CTX_CLASS, "ptr"), + AssignField( + Name("ptr"), + cls, + fieldName, + Expr.Ref(appValName), + LetExpr( + Name("acc"), + Expr.Select(appCtxName, CTX_CLASS, "acc"), // this could be a join point but it's not that bad + Result( + List(Expr.Ref(Name("acc"))) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + + (idx, node) + ) + + + val ctxBranch = LetExpr( + Name("field"), Expr.Select(appCtxName, CTX_CLASS, "field"), + makeSwitch(Name("field"), assignmentCases.tail, assignmentCases.head._2)(trueClass, falseClass) + ) + + val idBranch = Result(List(Expr.Ref(appValName))).attachTag(tag) + + val appNode = Case(appCtxName, + List( + (ID_CTX_CLASS, idBranch), + (CTX_CLASS, ctxBranch) + ) + ).attachTag(tag) + + val appDefn = Defn(fnUid.make, ctxAppName, List(appCtxName, appValName), 1, appNode) + + // CONTEXT COMPOSITION + val cmpCtx1Name = Name("ctx1") + val cmpCtx2Name = Name("ctx2") + + // Note that ctx2 may never be an identity context. If we ever want to compose ctx1 and ctx2 + // where ctx2 is the identity, just use ctx1 directly. + // Ctx(app(ctx1, ctx2), ctx2.ptr, ctx2.field) -> + // let ctx2acc = ctx2.acc in + // let ctx2ptr = ctx2.ptr in + // let ctx2field = ctx2.field in + // let newAcc = app(ctx1, ctx2acc) in + // let ret = Ctx(newAcc, ctx2ptr, ctx2field) in + // ret + val cmpNode = LetExpr( + Name("ctx2acc"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "acc"), + LetExpr( + Name("ctx2ptr"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "ptr"), + LetExpr( + Name("ctx2field"), + Expr.Select(cmpCtx2Name, CTX_CLASS, "field"), + LetCall( + List(Name("newAcc")), + DefnRef(Left(appDefn)), List(Expr.Ref(cmpCtx1Name), Expr.Ref(Name("ctx2acc"))), + false, + LetExpr( + Name("ret"), + Expr.CtorApp(CTX_CLASS, List("newAcc", "ctx2ptr", "ctx2field").map(n => Expr.Ref(Name(n)))), + Result( + List(Expr.Ref(Name("ret"))) + ).attachTag(tag) + ).attachTag(tag), + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + + val cmpDefn = Defn(fnUid.make, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode) + + // We use tags to identify nodes + // a bit hacky but it's the most elegant way + // First, build a map of all branches that contain a mod cons call + val modConsBranches = modConsCalls.toList.map(call => (call.startNode.tag.inner -> call)).toMap + + val MOD_CONS_PTR_NAME = Name("_mod_cons_ptr") + + val modConsMap = defns.map(d => d.id -> DefnRef(Right(d.name + "_modcons"))).toMap + + def transformModConsBranch(node: Node, call: ModConsCallInfo): (Node, Set[Defn]) = + + def makeCall = + val field = assignToIdx((call.letCtorNode.cls.id, call.letCtorNode.fieldName)) + + // let composed = comp(ctx, ctx2) in + // f(composed, *args) + LetExpr( + Name("ctx2"), + Expr.CtorApp(CTX_CLASS, List(Expr.Ref(call.retName), Expr.Ref(call.letCtorNode.ctorValName), asLit(field))), + LetCall( + List(Name("composed")), + DefnRef(Left(cmpDefn)), + List("ctx", "ctx2").map(n => Expr.Ref(Name(n))), + false, + LetCall( + List(Name("res")), + modConsMap(call.defn.id), + Expr.Ref(Name("composed")) :: call.letCallNode.args, // TODO: change + false, + Result( + List(Expr.Ref(Name("res"))) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + + node match + case Result(res) => (node, Set()) + case Jump(defn, args) => ??? + case Case(scrut, cases) => ??? + case LetExpr(name, expr, body) => ??? + case LetCall(names, defn, args, isTailRec, body) => ??? + case AssignField(assignee, clsInfo, fieldName, value, body) => ??? + + // Here, we assume we are inside the modcons function and hence have an extra + // `ctx` parameter at the start. + def transformNode(node: Node): (Node, Set[Defn]) = + modConsBranches.get(node.tag.inner) match + case Some(call) => transformModConsBranch(node, call) + case None => node match + case Result(_) => (node.attachTag(tag), Set()) + case Jump(_, _) => (node.attachTag(tag), Set()) + case Case(scrut, cases) => + val casesAndDefns = cases.map { (cls, body) => (cls, transformNode(body)) } + + val transformedCases = casesAndDefns.map { (cls, item) => (cls, item._1) } + val newDefns: Set[Defn] = casesAndDefns.map((_, item) => item._2).foldLeft(Set())((a, b) => a ++ b) + + (Case(scrut, transformedCases).attachTag(tag), newDefns) + case LetExpr(name, expr, body) => + val (transformed, newDefns) = transformNode(body) + (LetExpr(name, expr, transformed).attachTag(tag), newDefns) + case LetCall(names, defn, args, isTailRec, body) => + // This case is not handled by the paper. The way to transform this is: + // let res = foo(*args) in res + // --> let res = foo_modcons(ctx, *args) in res + if isTailCall(node) && defnsIdSet.contains(defn.expectDefn.id) then + // Transform it into a tail recursive call where we pass on the current context + ( + LetCall( + List(Name("res")), + modConsMap(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, + isTailRec, + Result(List(Expr.Ref(Name("res")))) + ), + Set() + ) + else + val (transformed, newDefns) = transformNode(body) + (LetCall(names, defn, args, isTailRec, transformed).attachTag(tag), newDefns) + case AssignField(assignee, clsInfo, fieldName, value, body) => + val (transformed, newDefns) = transformNode(body) + (AssignField(assignee, clsInfo, fieldName, value, transformed).attachTag(tag), newDefns) + + // will create two definitions: one has the same signature as the original function, + // while the other one will have extra parameters to support mod cons + + // We will need to rewrite join points, thus new definitions may be created. + def transformDefn(d: Defn): Set[Defn] = + val (transformed, newDefns) = transformNode(d.body) + newDefns + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) + ??? + // Given a strongly connected component `defns` of mutually @@ -350,8 +673,10 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get - - val defns = component.nodes + + // join points need to be rewritten. For now, just combine them into the rest of the function. They will be inlined anyways + val defns = component.nodes ++ component.joinPoints + val defnsNoJp = component.nodes val edges = component.edges // dummy case, should not happen @@ -374,23 +699,23 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): case Jump(defn, args) => node.attachTag(tag) case Case(scrut, cases) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body)))).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) - case LetCall(names, defn_, args, body, isTailRec) => + case LetCall(names, defn_, args, isTailRec, body) => if isTailCall(node) && defn_.expectDefn.id == defn.id then Jump(jpDefnRef, args).attachTag(tag) else - LetCall(names, defn_, args, transformNode(body), isTailRec).attachTag(tag) + LetCall(names, defn_, args, isTailRec, transformNode(body)).attachTag(tag) case AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body)) - val rets = defn.params.map { case Name(x) => Name(x + "_") } + val rets = (0 until defn.resultNum).map(n => Name("r" + n.toString)).toList val callJpNode = LetCall( rets, DefnRef(Left(jpDef)), defn.params.map(Expr.Ref(_)), + false, Result(rets.map(Expr.Ref(_))).attachTag(tag), - false ).attachTag(tag) val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode) @@ -436,14 +761,18 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): // Build the node which will be contained inside the jump point. def transformNode(node: Node): Node = node match - case Jump(_, _) => node.attachTag(tag) + case Jump(defn, args) => + if defnInfoMap.contains(defn.expectDefn.id) then + Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) + else + node.attachTag(tag) case Result(_) => node.attachTag(tag) case Case(scrut, cases) => Case(scrut, cases.map(n => (n._1, transformNode(n._2)))).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) - case LetCall(names, defn, args, body, isTailRec) => + case LetCall(names, defn, args, isTailRec, body) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) - else LetCall(names, defn, args, transformNode(body), isTailRec).attachTag(tag) + else LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)).attachTag(tag) // Tail calls to another function in the component will be replaced with a tail call @@ -462,7 +791,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList val namesExpr = names.map(Expr.Ref(_)) val res = Result(namesExpr).attachTag(tag) - val call = LetCall(names, newDefnRef, args, res, false).attachTag(tag) + val call = LetCall(names, newDefnRef, args, false, res).attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match @@ -494,7 +823,7 @@ class TailRecOpt(fnUid: FreshInt, tag: FreshInt): jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) - defns.map { d => transformDefn(d) } + newDefn + jpDefn + defnsNoJp.map { d => transformDefn(d) } + newDefn + jpDefn private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index bbda8998cf..c685fdf055 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -134,8 +134,8 @@ fact(1, 5) //│ ) //│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #38 -//│ acc$0_,n$0_ -- #37 +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -160,8 +160,8 @@ fact(1, 5) //│ ) //│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ let* (acc$0_,n$0_) = fact_jp(acc$0,n$0) in -- #38 -//│ acc$0_,n$0_ -- #37 +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -170,6 +170,128 @@ fact(1, 5) //│ Interpreted: //│ 120 +:interpIR +class True +class False +fun fact(acc, n) = + val x = if n > 0 then n - 1 + else 0 + if x <= 0 then + acc + else + @tailrec fact(n * acc, x) +fact(1, 5) +//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailrec fact(*(n,)(acc,), x,)}}; fact(1, 5,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(j$1), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ Def(3, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #45 +//│ if x$0 -- #44 +//│ true => +//│ let x$6 = -(n$0,1) in -- #42 +//│ jump j$0(x$6,acc$0,n$0) -- #41 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #43 +//│ ) +//│ Def(4, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #47 +//│ r0 -- #46 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ Def(3, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #45 +//│ if x$0 -- #44 +//│ true => +//│ let x$6 = -(n$0,1) in -- #42 +//│ jump j$0(x$6,acc$0,n$0) -- #41 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #43 +//│ ) +//│ Def(4, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #47 +//│ r0 -- #46 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Interpreted: +//│ 120 + :noTailRec :interpIR class True @@ -608,6 +730,7 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ 2 -- #30) // TODO: should error, but it seems IR errors are not considered errors by the unit test framework +// :e class True class False fun hello() = @@ -622,28 +745,28 @@ hello() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, hello, [], //│ 1, -//│ let* (x$0) = @tailrec hello() in -- #5 +//│ let* (x$0) = @tailrec hello() in -- #4 //│ 2 -- #3 //│ ) //│ }, -//│ let* (x$1) = hello() in -- #9 -//│ x$1 -- #8) +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) //│ //│ IR Processing Failed: not a tail call //│ //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:48) -//│ mlscript.compiler.optimizer.TailRecOpt.getOptimizableCalls(TailRecOpt.scala:59) -//│ mlscript.compiler.optimizer.TailRecOpt.findTailCalls(TailRecOpt.scala:225) -//│ mlscript.compiler.optimizer.TailRecOpt.dfs$1(TailRecOpt.scala:257) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes$$anonfun$1(TailRecOpt.scala:302) -//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) -//│ scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) -//│ scala.collection.immutable.Set$Set1.foreach(Set.scala:168) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:302) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:501) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:508) -//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:28) +//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:98) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverOptimizableCalls(TailRecOpt.scala:108) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:308) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:313) +//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:338) +//│ scala.collection.IterableOnceOps.foldLeft(IterableOnce.scala:646) +//│ scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) +//│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:338) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:829) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:836) +//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) //│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) @@ -716,29 +839,29 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, addOne, [xs$0], //│ 1, -//│ case xs$0 of -- #28 +//│ case xs$0 of -- #27 //│ Cons => -//│ let x$1 = xs$0.t in -- #24 -//│ let x$2 = xs$0.h in -- #23 -//│ let x$3 = +(x$2,1) in -- #22 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #21 +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #20 //│ let x$5 = Cons(x$3,x$4) in -- #19 //│ jump j$0(x$5) -- #18 //│ Nil => -//│ let x$6 = Nil() in -- #27 -//│ jump j$0(x$6) -- #26 +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) //│ }, -//│ let x$7 = Nil() in -- #53 -//│ let x$8 = Cons(3,x$7) in -- #52 -//│ let x$9 = Cons(2,x$8) in -- #51 -//│ let x$10 = Cons(1,x$9) in -- #50 -//│ let* (x$11) = addOne(x$10) in -- #49 -//│ x$11 -- #48) +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) @@ -749,30 +872,30 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ ) //│ Def(2, addOne_jp, [xs$0], //│ 1, -//│ case xs$0 of -- #62 +//│ case xs$0 of -- #61 //│ Cons => -//│ let x$1 = xs$0.t in -- #59 -//│ let x$2 = xs$0.h in -- #58 -//│ let x$3 = +(x$2,1) in -- #57 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #56 -//│ let x$5 = Cons(x$3,x$4) in -- #55 -//│ jump j$0(x$5) -- #54 +//│ let x$1 = xs$0.t in -- #58 +//│ let x$2 = xs$0.h in -- #57 +//│ let x$3 = +(x$2,1) in -- #56 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #55 +//│ let x$5 = Cons(x$3,x$4) in -- #54 +//│ jump j$0(x$5) -- #53 //│ Nil => -//│ let x$6 = Nil() in -- #61 -//│ jump j$0(x$6) -- #60 +//│ let x$6 = Nil() in -- #60 +//│ jump j$0(x$6) -- #59 //│ ) //│ Def(3, addOne, [xs$0], //│ 1, -//│ let* (xs$0_) = addOne_jp(xs$0) in -- #64 -//│ xs$0_ -- #63 +//│ let* (r0) = addOne_jp(xs$0) in -- #63 +//│ r0 -- #62 //│ ) //│ }, -//│ let x$7 = Nil() in -- #53 -//│ let x$8 = Cons(3,x$7) in -- #52 -//│ let x$9 = Cons(2,x$8) in -- #51 -//│ let x$10 = Cons(1,x$9) in -- #50 -//│ let* (x$11) = addOne(x$10) in -- #49 -//│ x$11 -- #48) +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { @@ -782,30 +905,30 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ ) //│ Def(2, addOne_jp, [xs$0], //│ 1, -//│ case xs$0 of -- #62 +//│ case xs$0 of -- #61 //│ Cons => -//│ let x$1 = xs$0.t in -- #59 -//│ let x$2 = xs$0.h in -- #58 -//│ let x$3 = +(x$2,1) in -- #57 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #56 -//│ let x$5 = Cons(x$3,x$4) in -- #55 -//│ jump j$0(x$5) -- #54 +//│ let x$1 = xs$0.t in -- #58 +//│ let x$2 = xs$0.h in -- #57 +//│ let x$3 = +(x$2,1) in -- #56 +//│ let* (x$4) = @tailrec addOne(x$1) in -- #55 +//│ let x$5 = Cons(x$3,x$4) in -- #54 +//│ jump j$0(x$5) -- #53 //│ Nil => -//│ let x$6 = Nil() in -- #61 -//│ jump j$0(x$6) -- #60 +//│ let x$6 = Nil() in -- #60 +//│ jump j$0(x$6) -- #59 //│ ) //│ Def(3, addOne, [xs$0], //│ 1, -//│ let* (xs$0_) = addOne_jp(xs$0) in -- #64 -//│ xs$0_ -- #63 +//│ let* (r0) = addOne_jp(xs$0) in -- #63 +//│ r0 -- #62 //│ ) //│ }, -//│ let x$7 = Nil() in -- #53 -//│ let x$8 = Cons(3,x$7) in -- #52 -//│ let x$9 = Cons(2,x$8) in -- #51 -//│ let x$10 = Cons(1,x$9) in -- #50 -//│ let* (x$11) = addOne(x$10) in -- #49 -//│ x$11 -- #48) +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) @@ -832,16 +955,16 @@ a(S(S(S(Zero)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ case n$0 of -- #24 +//│ case n$0 of -- #23 //│ S => -//│ let x$1 = n$0.x in -- #16 -//│ let* (x$2) = @tailrec b(x$1) in -- #15 +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailrec b(x$1) in -- #14 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => -//│ let x$4 = Zero() in -- #23 -//│ let x$5 = S(x$4) in -- #22 -//│ jump j$0(x$5) -- #21 +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -849,38 +972,38 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ case n$1 of -- #57 +//│ case n$1 of -- #55 //│ S => -//│ let x$7 = n$1.x in -- #45 -//│ let* (x$8) = @tailrec a(x$7) in -- #44 -//│ let x$9 = S(x$8) in -- #42 -//│ let x$10 = S(x$9) in -- #41 -//│ jump j$1(x$10) -- #40 +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 //│ Zero => -//│ let x$11 = Zero() in -- #56 -//│ let x$12 = S(x$11) in -- #55 -//│ let x$13 = S(x$12) in -- #54 -//│ jump j$1(x$13) -- #53 +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #26 +//│ x$6 -- #25 //│ ) //│ }, -//│ let x$14 = Zero() in -- #76 -//│ let x$15 = S(x$14) in -- #75 -//│ let x$16 = S(x$15) in -- #74 -//│ let x$17 = S(x$16) in -- #73 -//│ let* (x$18) = a(x$17) in -- #72 -//│ x$18 -- #71) +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #111 -//│ r0 -- #110 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #109 +//│ r0 -- #108 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -888,60 +1011,60 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #109 -//│ r0 -- #108 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #107 +//│ r0 -- #106 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #26 +//│ x$6 -- #25 //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #106 -//│ if scrut -- #105 +//│ let scrut = ==(0,tailrecBranch) in -- #104 +//│ if scrut -- #103 //│ true => -//│ case a_n$0 of -- #104 +//│ case a_n$0 of -- #102 //│ S => -//│ let x$1 = a_n$0.x in -- #100 -//│ let* (x$2) = @tailrec b(x$1) in -- #99 -//│ let x$3 = S(x$2) in -- #98 -//│ jump j$0(x$3) -- #97 +//│ let x$1 = a_n$0.x in -- #98 +//│ let* (x$2) = @tailrec b(x$1) in -- #97 +//│ let x$3 = S(x$2) in -- #96 +//│ jump j$0(x$3) -- #95 //│ Zero => -//│ let x$4 = Zero() in -- #103 -//│ let x$5 = S(x$4) in -- #102 -//│ jump j$0(x$5) -- #101 +//│ let x$4 = Zero() in -- #101 +//│ let x$5 = S(x$4) in -- #100 +//│ jump j$0(x$5) -- #99 //│ false => -//│ case b_n$1 of -- #96 +//│ case b_n$1 of -- #94 //│ S => -//│ let x$7 = b_n$1.x in -- #91 -//│ let* (x$8) = @tailrec a(x$7) in -- #90 -//│ let x$9 = S(x$8) in -- #89 -//│ let x$10 = S(x$9) in -- #88 -//│ jump j$1(x$10) -- #87 +//│ let x$7 = b_n$1.x in -- #89 +//│ let* (x$8) = @tailrec a(x$7) in -- #88 +//│ let x$9 = S(x$8) in -- #87 +//│ let x$10 = S(x$9) in -- #86 +//│ jump j$1(x$10) -- #85 //│ Zero => -//│ let x$11 = Zero() in -- #95 -//│ let x$12 = S(x$11) in -- #94 -//│ let x$13 = S(x$12) in -- #93 -//│ jump j$1(x$13) -- #92 +//│ let x$11 = Zero() in -- #93 +//│ let x$12 = S(x$11) in -- #92 +//│ let x$13 = S(x$12) in -- #91 +//│ jump j$1(x$13) -- #90 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #107 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #105 //│ ) //│ }, -//│ let x$14 = Zero() in -- #76 -//│ let x$15 = S(x$14) in -- #75 -//│ let x$16 = S(x$15) in -- #74 -//│ let x$17 = S(x$16) in -- #73 -//│ let* (x$18) = a(x$17) in -- #72 -//│ x$18 -- #71) +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #111 -//│ r0 -- #110 +//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #109 +//│ r0 -- #108 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -949,57 +1072,201 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #109 -//│ r0 -- #108 +//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #107 +//│ r0 -- #106 //│ ) //│ Def(3, j$1, [x$6], //│ 1, -//│ x$6 -- #26 +//│ x$6 -- #25 //│ ) //│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #106 -//│ if scrut -- #105 +//│ let scrut = ==(0,tailrecBranch) in -- #104 +//│ if scrut -- #103 //│ true => -//│ case a_n$0 of -- #104 +//│ case a_n$0 of -- #102 //│ S => -//│ let x$1 = a_n$0.x in -- #100 -//│ let* (x$2) = @tailrec b(x$1) in -- #99 -//│ let x$3 = S(x$2) in -- #98 -//│ jump j$0(x$3) -- #97 +//│ let x$1 = a_n$0.x in -- #98 +//│ let* (x$2) = @tailrec b(x$1) in -- #97 +//│ let x$3 = S(x$2) in -- #96 +//│ jump j$0(x$3) -- #95 //│ Zero => -//│ let x$4 = Zero() in -- #103 -//│ let x$5 = S(x$4) in -- #102 -//│ jump j$0(x$5) -- #101 +//│ let x$4 = Zero() in -- #101 +//│ let x$5 = S(x$4) in -- #100 +//│ jump j$0(x$5) -- #99 //│ false => -//│ case b_n$1 of -- #96 +//│ case b_n$1 of -- #94 //│ S => -//│ let x$7 = b_n$1.x in -- #91 -//│ let* (x$8) = @tailrec a(x$7) in -- #90 -//│ let x$9 = S(x$8) in -- #89 -//│ let x$10 = S(x$9) in -- #88 -//│ jump j$1(x$10) -- #87 +//│ let x$7 = b_n$1.x in -- #89 +//│ let* (x$8) = @tailrec a(x$7) in -- #88 +//│ let x$9 = S(x$8) in -- #87 +//│ let x$10 = S(x$9) in -- #86 +//│ jump j$1(x$10) -- #85 //│ Zero => -//│ let x$11 = Zero() in -- #95 -//│ let x$12 = S(x$11) in -- #94 -//│ let x$13 = S(x$12) in -- #93 -//│ jump j$1(x$13) -- #92 +//│ let x$11 = Zero() in -- #93 +//│ let x$12 = S(x$11) in -- #92 +//│ let x$13 = S(x$12) in -- #91 +//│ jump j$1(x$13) -- #90 //│ ) //│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #107 +//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #105 //│ ) //│ }, -//│ let x$14 = Zero() in -- #76 -//│ let x$15 = S(x$14) in -- #75 -//│ let x$16 = S(x$15) in -- #74 -//│ let x$17 = S(x$16) in -- #73 -//│ let* (x$18) = a(x$17) in -- #72 -//│ x$18 -- #71) +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) //│ //│ Interpreted: //│ S(S(S(S(S(S(Zero())))))) +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun addOne(xs) = + if xs is + Cons(h, t) then + val next = @tailrec addOne(t) + val ret = Cons(h + 1, nnext) + val rett = ret + rett + Nil then + Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |nnext|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), nnext,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ +//│ IR: +//│ +//│ IR Processing Failed: unknown name nnext in Ctx(HashMap(xs -> xs$0, t -> x$1, < -> <, >= -> >=, == -> ==, next -> x$3, * -> *, <= -> <=, - -> -, xs$0 -> xs$0, addOne -> addOne, + -> +, != -> !=, h -> x$2, / -> /, > -> >),Map(True -> ClassInfo(0, True, []), False -> ClassInfo(1, False, []), Cons -> ClassInfo(2, Cons, [h,t]), Nil -> ClassInfo(3, Nil, [])),Map(h -> (Cons,ClassInfo(2, Cons, [h,t])), t -> (Cons,ClassInfo(2, Cons, [h,t]))),Set(addOne),HashSet(<, >=, -, ==, +, !=, *, <=, /, >),ListBuffer(Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ))) +//│ +//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:108) +//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) +//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1(Builder.scala:57) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.$anonfun$8$$anonfun$1(Builder.scala:123) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:98) +//│ mlscript.compiler.ir.Builder.$anonfun$8(Builder.scala:125) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:127) +//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:239) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:137) +//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:226) +//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildLetCall$1$$anonfun$1$$anonfun$1(Builder.scala:83) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1$$anonfun$1(Builder.scala:55) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:60) +//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1(Builder.scala:57) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:239) +//│ mlscript.compiler.ir.Builder.buildLetCall$1$$anonfun$1(Builder.scala:88) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildLetCall$1(Builder.scala:94) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:140) +//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:226) +//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.$anonfun$22(Builder.scala:235) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:237) +//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:143) +//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.$anonfun$22(Builder.scala:235) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:237) +//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:143) +//│ mlscript.compiler.ir.Builder.$anonfun$21(Builder.scala:203) +//│ scala.collection.immutable.List.map(List.scala:246) +//│ mlscript.compiler.ir.Builder.$anonfun$17(Builder.scala:211) +//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:214) +//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:218) +//│ mlscript.compiler.ir.Builder.buildDefFromNuFunDef(Builder.scala:258) +//│ mlscript.compiler.ir.Builder.$anonfun$36(Builder.scala:320) +//│ scala.collection.immutable.List.map(List.scala:246) +//│ mlscript.compiler.ir.Builder.buildGraph(Builder.scala:320) +//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:24) +//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) +//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) +//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) +//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) +//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) +//│ org.scalatest.Transformer.apply(Transformer.scala:22) +//│ org.scalatest.Transformer.apply(Transformer.scala:20) +//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) +//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) +//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) +//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) +//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) +//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) +//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) +//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) +//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) +//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) +//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) +//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) +//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) +//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) +//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) +//│ mlscript.DiffTests.runTest(DiffTests.scala:53) +//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) +//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) +//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) +//│ mlscript.DiffTests.runTests(DiffTests.scala:53) +//│ org.scalatest.Suite.run(Suite.scala:1114) +//│ org.scalatest.Suite.run$(Suite.scala:1096) +//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) +//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) +//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) +//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) +//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) +//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) +//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) +//│ mlscript.DiffTests.run(DiffTests.scala:53) +//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) +//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) +//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) +//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) +//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) +//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) +//│ java.base/java.lang.Thread.run(Thread.java:840) + :interpIR class True class False diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls index 907012746a..02d97b8857 100644 --- a/compiler/shared/test/diff-ir/NuScratch.mls +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -1,3 +1,154 @@ :NewParser :ParseOnly :UseIR + +:interpIR +class True +class False +fun fact(acc, n) = + val x = if n > 0 then n - 1 else 0 + if x <= 0 then + acc + else + @tailrec fact(n * acc, x) +fact(1, 5) +//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1| |#else| |0|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailrec fact(*(n,)(acc,), x,)}}; fact(1, 5,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(j$1), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) +//│ +//│ Interpreted: +//│ 120 diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 58c6b81401..c06bd63a8e 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -17,14 +17,15 @@ class IRDiffTestCompiler extends DiffTests { try output("\n\nIR:") val fnUid = FreshInt() + val classUid = FreshInt() val tag = FreshInt() - val gb = Builder(Fresh(), fnUid, FreshInt(), tag) + val gb = Builder(Fresh(), fnUid, classUid, tag) val graph_ = gb.buildGraph(unit) output(graph_.toString()) val graph = if (!mode.noTailRecOpt) { - val tailRecOpt = new TailRecOpt(fnUid, tag) + val tailRecOpt = new TailRecOpt(fnUid, classUid, tag) val (g, comps) = tailRecOpt.run_debug(graph_) output("\nStrongly Connected Tail Calls:") output(comps.toString) From 934c3247e4ea927844db1b5ee5014826c4c2d12b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 1 May 2024 18:30:18 +0800 Subject: [PATCH 29/59] fix test --- compiler/shared/test/diff-ir/IRTailRec.mls | 295 +++++++++++---------- 1 file changed, 149 insertions(+), 146 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index c685fdf055..a713946be8 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -219,6 +219,11 @@ fact(1, 5) //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(j$1), Set(fact)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 +//│ ) //│ Def(1, j$1, [x$3], //│ 1, //│ x$3 -- #7 @@ -234,20 +239,30 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) -//│ Def(3, fact_jp, [acc$0,n$0], +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, -//│ let x$0 = >(n$0,0) in -- #45 -//│ if x$0 -- #44 +//│ let scrut = ==(2,tailrecBranch) in -- #57 +//│ if scrut -- #56 //│ true => -//│ let x$6 = -(n$0,1) in -- #42 -//│ jump j$0(x$6,acc$0,n$0) -- #41 +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 //│ false => -//│ jump j$0(0,acc$0,n$0) -- #43 +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 //│ ) -//│ Def(4, fact, [acc$0,n$0], +//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #47 -//│ r0 -- #46 +//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 //│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 @@ -255,6 +270,11 @@ fact(1, 5) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 +//│ ) //│ Def(1, j$1, [x$3], //│ 1, //│ x$3 -- #7 @@ -270,20 +290,30 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) -//│ Def(3, fact_jp, [acc$0,n$0], +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, -//│ let x$0 = >(n$0,0) in -- #45 -//│ if x$0 -- #44 +//│ let scrut = ==(2,tailrecBranch) in -- #57 +//│ if scrut -- #56 //│ true => -//│ let x$6 = -(n$0,1) in -- #42 -//│ jump j$0(x$6,acc$0,n$0) -- #41 +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 //│ false => -//│ jump j$0(0,acc$0,n$0) -- #43 +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 //│ ) -//│ Def(4, fact, [acc$0,n$0], +//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #47 -//│ r0 -- #46 +//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 //│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 @@ -757,15 +787,15 @@ hello() //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) //│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:98) //│ mlscript.compiler.optimizer.TailRecOpt.discoverOptimizableCalls(TailRecOpt.scala:108) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:308) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:313) -//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:338) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:311) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:316) +//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:339) //│ scala.collection.IterableOnceOps.foldLeft(IterableOnce.scala:646) //│ scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) //│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:338) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:829) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:836) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:339) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:830) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:837) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -1132,140 +1162,113 @@ fun addOne(xs) = if xs is Cons(h, t) then val next = @tailrec addOne(t) - val ret = Cons(h + 1, nnext) + val ret = Cons(h + 1, next) val rett = ret rett Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |nnext|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), nnext,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #30 +//│ Cons => +//│ let x$1 = xs$0.t in -- #26 +//│ let x$2 = xs$0.h in -- #25 +//│ let* (x$3) = @tailrec addOne(x$1) in -- #24 +//│ let x$4 = +(x$2,1) in -- #23 +//│ let x$5 = Cons(x$4,x$3) in -- #22 +//│ jump j$0(x$5) -- #21 +//│ Nil => +//│ let x$6 = Nil() in -- #29 +//│ jump j$0(x$6) -- #28 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) //│ -//│ IR Processing Failed: unknown name nnext in Ctx(HashMap(xs -> xs$0, t -> x$1, < -> <, >= -> >=, == -> ==, next -> x$3, * -> *, <= -> <=, - -> -, xs$0 -> xs$0, addOne -> addOne, + -> +, != -> !=, h -> x$2, / -> /, > -> >),Map(True -> ClassInfo(0, True, []), False -> ClassInfo(1, False, []), Cons -> ClassInfo(2, Cons, [h,t]), Nil -> ClassInfo(3, Nil, [])),Map(h -> (Cons,ClassInfo(2, Cons, [h,t])), t -> (Cons,ClassInfo(2, Cons, [h,t]))),Set(addOne),HashSet(<, >=, -, ==, +, !=, *, <=, /, >),ListBuffer(Def(1, j$0, [x$0], +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 -//│ ))) +//│ ) +//│ Def(2, addOne_jp, [xs$0], +//│ 1, +//│ case xs$0 of -- #64 +//│ Cons => +//│ let x$1 = xs$0.t in -- #61 +//│ let x$2 = xs$0.h in -- #60 +//│ let* (x$3) = @tailrec addOne(x$1) in -- #59 +//│ let x$4 = +(x$2,1) in -- #58 +//│ let x$5 = Cons(x$4,x$3) in -- #57 +//│ jump j$0(x$5) -- #56 +//│ Nil => +//│ let x$6 = Nil() in -- #63 +//│ jump j$0(x$6) -- #62 +//│ ) +//│ Def(3, addOne, [xs$0], +//│ 1, +//│ let* (r0) = addOne_jp(xs$0) in -- #66 +//│ r0 -- #65 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) //│ -//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:108) -//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) -//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1(Builder.scala:57) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.$anonfun$8$$anonfun$1(Builder.scala:123) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:98) -//│ mlscript.compiler.ir.Builder.$anonfun$8(Builder.scala:125) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:127) -//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:239) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:137) -//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:226) -//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildLetCall$1$$anonfun$1$$anonfun$1(Builder.scala:83) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1$$anonfun$1(Builder.scala:55) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:60) -//│ mlscript.compiler.ir.Builder.buildResultFromTup$$anonfun$1(Builder.scala:57) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildResultFromTup(Builder.scala:59) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:239) -//│ mlscript.compiler.ir.Builder.buildLetCall$1$$anonfun$1(Builder.scala:88) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildLetCall$1(Builder.scala:94) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:140) -//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:226) -//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.$anonfun$22(Builder.scala:235) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:237) -//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:143) -//│ mlscript.compiler.ir.Builder.buildBinding$$anonfun$1(Builder.scala:40) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.$anonfun$22(Builder.scala:235) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:237) -//│ mlscript.compiler.ir.Builder.buildBinding(Builder.scala:48) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:143) -//│ mlscript.compiler.ir.Builder.$anonfun$21(Builder.scala:203) -//│ scala.collection.immutable.List.map(List.scala:246) -//│ mlscript.compiler.ir.Builder.$anonfun$17(Builder.scala:211) -//│ mlscript.utils.package$GenHelper$.$bar$greater$extension(package.scala:119) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:107) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:214) -//│ mlscript.compiler.ir.Builder.buildResultFromTerm(Builder.scala:218) -//│ mlscript.compiler.ir.Builder.buildDefFromNuFunDef(Builder.scala:258) -//│ mlscript.compiler.ir.Builder.$anonfun$36(Builder.scala:320) -//│ scala.collection.immutable.List.map(List.scala:246) -//│ mlscript.compiler.ir.Builder.buildGraph(Builder.scala:320) -//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:24) -//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) -//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) -//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) -//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) -//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) -//│ org.scalatest.Transformer.apply(Transformer.scala:22) -//│ org.scalatest.Transformer.apply(Transformer.scala:20) -//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) -//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) -//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) -//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) -//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) -//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) -//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) -//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) -//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) -//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) -//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) -//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) -//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) -//│ mlscript.DiffTests.runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) -//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) -//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) -//│ mlscript.DiffTests.runTests(DiffTests.scala:53) -//│ org.scalatest.Suite.run(Suite.scala:1114) -//│ org.scalatest.Suite.run$(Suite.scala:1096) -//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) -//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) -//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) -//│ mlscript.DiffTests.run(DiffTests.scala:53) -//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) -//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) -//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) -//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) -//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) -//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) -//│ java.base/java.lang.Thread.run(Thread.java:840) +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, addOne_jp, [xs$0], +//│ 1, +//│ case xs$0 of -- #64 +//│ Cons => +//│ let x$1 = xs$0.t in -- #61 +//│ let x$2 = xs$0.h in -- #60 +//│ let* (x$3) = @tailrec addOne(x$1) in -- #59 +//│ let x$4 = +(x$2,1) in -- #58 +//│ let x$5 = Cons(x$4,x$3) in -- #57 +//│ jump j$0(x$5) -- #56 +//│ Nil => +//│ let x$6 = Nil() in -- #63 +//│ jump j$0(x$6) -- #62 +//│ ) +//│ Def(3, addOne, [xs$0], +//│ 1, +//│ let* (r0) = addOne_jp(xs$0) in -- #66 +//│ r0 -- #65 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) +//│ +//│ Interpreted: +//│ Cons(2,Cons(3,Cons(4,Nil()))) :interpIR class True From b27cf3a797fe39646e67fdd204b00be1d053c663 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 1 May 2024 21:40:50 +0800 Subject: [PATCH 30/59] Done --- .../scala/mlscript/compiler/ir/Interp.scala | 5 +- .../compiler/optimizer/TailRecOpt.scala | 237 +-- compiler/shared/test/diff-ir/IRTailRec.mls | 1319 +++++++++++------ compiler/shared/test/diff-ir/NuScratch.mls | 151 -- 4 files changed, 1029 insertions(+), 683 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 50a39371e1..dd0d05fd23 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -222,6 +222,7 @@ class Interpreter(verbose: Bool): private def evalArgs(using ctx: Ctx, clsctx: ClassCtx)(exprs: Ls[Expr]): Either[Ls[Expr], Ls[Expr]] = var changed = false + val xs = exprs.map { arg => eval(arg) match case Left(expr) => changed = true; expr @@ -242,7 +243,7 @@ class Interpreter(verbose: Bool): case CtorApp(name, args) => evalArgs(args) match case Left(xs) => Left(CtorApp(name, xs)) - case _ => Right(expr) + case Right(xs) => Right(CtorApp(name, xs)) // TODO: This makes recursion modulo cons work, but should be investigated further. case Select(name, cls, field) => ctx.get(name.str).map { case CtorApp(cls2, xs) if cls == cls2 => @@ -306,7 +307,7 @@ class Interpreter(verbose: Bool): case Some(x: CtorApp) => val CtorApp(cls, args) = x val idx = cls.fields.indexOf(fieldName) - val newArgs = args.take(idx - 1) ::: value :: args.drop(idx + 1) + val newArgs = args.updated(idx, value) x.args = newArgs case Some(_) => IRInterpreterError("tried to assign a field of a non-ctor") case None => IRInterpreterError("could not find value " + assignee) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 1c6655e7bd..3cc1c63fb3 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -8,24 +8,24 @@ import mlscript.utils.shorthands.Bool // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): - case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String) + case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) enum CallInfo: - case TailCallInfo(src: Defn, defn: Defn, letCallNode: LetCall) extends CallInfo + case TailCallInfo(src: Defn, defn: Defn) extends CallInfo case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo override def toString(): String = this match - case TailCallInfo(src, defn, letCallNode) => + case TailCallInfo(src, defn) => f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" def getSrc = this match - case TailCallInfo(src, _, _) => src + case TailCallInfo(src, _) => src case ModConsCallInfo(src, _, _, _, _, _, _) => src def getDefn = this match - case TailCallInfo(_, defn, _) => defn + case TailCallInfo(_, defn) => defn case ModConsCallInfo(_, _, defn, _, _, _, _) => defn private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo], val joinPoints: Set[Defn]): @@ -109,8 +109,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case Jump(jp, args) => def mergeCalls(acc: Map[Int, Set[CallInfo]], calls: Set[CallInfo]) = val newCalls = calls.map { - case TailCallInfo(_, defn, letCallNode) => - TailCallInfo(src, defn, letCallNode) + case TailCallInfo(_, defn) => + TailCallInfo(src, defn) case ModConsCallInfo(_, startNode, defn, letCallNode, letCtorNode, retName, retNode) => ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, retName, retNode) } @@ -196,7 +196,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName)), Set(name)) + discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName, ctorArgIndex)), Set(name)) case Some(_) => // another constructor is already using the call. Not OK @@ -237,7 +237,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val LetCall(names, defn, args, isTailRec, body) = x if isTailCall(x) then - Left(updateMapSimple(TailCallInfo(src, defn.expectDefn, x))) + Left(updateMapSimple(TailCallInfo(src, defn.expectDefn))) else letCallNode match case None => // OK, use this LetCall as the mod cons @@ -260,10 +260,11 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if inters.isEmpty then discoverOptimizableCalls(body) // OK else throw IRError("not a mod cons call") else - // old call is not tailrec, so we can override it however we want - // we take a lucky guess and mark this as the mod cons call, but the - // user really should mark which calls should be tailrec - if defn.expectDefn.resultNum == 1 then + // only include mod cons calls that have one return value + if defn.expectDefn.resultNum == 1 then + // old call is not tailrec, so we can override it however we want + // we take a lucky guess and mark this as the mod cons call, but the + // user really should mark which calls should be tailrec discoverOptimizableCalls(body)(acc, src, start, Some(defn.expectDefn), Some(x), None, Set()) else // shadow all the variables in this letcall @@ -281,7 +282,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case _ => letCtorNode match case None => discoverOptimizableCalls(body) // OK - case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName)) => + case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => // If this assignment overwrites the mod cons value, forget it if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered @@ -448,17 +449,20 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) - // Given a strongly connected component `defns` of mutually mod cons functions, - // returns a set containing mutually tail recursive versions of them and - // the original functions pointing to the optimized ones. - private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Set[ClassInfo]) = + // Given a strongly connected component `defns` of mutually + // tail recursive functions, returns a strongly connected component contaning the + // optimized functions and their associated join points, and also + // new function definitions not in this component, such as the + // original functions pointing to an optimized function and the context + // composition and application functions. + private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): (ScComponent, Set[Defn]) = val modConsCalls = component.edges.collect { case x: ModConsCallInfo => x } val defns = component.nodes val defnsIdSet = defns.map(_.id).toSet // no mod cons, just return the original if modConsCalls.isEmpty then - (component.nodes, classes) + (component, Set()) else val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get @@ -497,7 +501,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Name("ptr"), cls, fieldName, - Expr.Ref(appValName), + Expr.Ref(appValName), LetExpr( Name("acc"), Expr.Select(appCtxName, CTX_CLASS, "acc"), // this could be a join point but it's not that bad @@ -515,7 +519,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val ctxBranch = LetExpr( Name("field"), Expr.Select(appCtxName, CTX_CLASS, "field"), makeSwitch(Name("field"), assignmentCases.tail, assignmentCases.head._2)(trueClass, falseClass) - ) + ).attachTag(tag) val idBranch = Result(List(Expr.Ref(appValName))).attachTag(tag) @@ -573,17 +577,58 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // a bit hacky but it's the most elegant way // First, build a map of all branches that contain a mod cons call val modConsBranches = modConsCalls.toList.map(call => (call.startNode.tag.inner -> call)).toMap - - val MOD_CONS_PTR_NAME = Name("_mod_cons_ptr") - val modConsMap = defns.map(d => d.id -> DefnRef(Right(d.name + "_modcons"))).toMap + val modConsRefs = defns.map(d => d.id -> DefnRef(Right(d.name + "_modcons"))).toMap + val jpRefs = component.joinPoints.map(jp => jp.id -> DefnRef(Right(jp.name + "_modcons"))).toMap + + def makeRet(ret: TrivialExpr): Node = + LetCall( + List(Name("res")), + DefnRef(Left(appDefn)), + List(Expr.Ref(Name("ctx")), ret), + false, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + ).attachTag(tag) - def transformModConsBranch(node: Node, call: ModConsCallInfo): (Node, Set[Defn]) = - + // Here, we assume we are inside the modcons version of the function and hence have an extra + // `ctx` parameter at the start. + def transformNode(node: Node): Node = + modConsBranches.get(node.tag.inner) match + case Some(call) => transformModConsBranch(node)(call) + case None => node match + case Result(res) => + makeRet(res.head) + case Jump(defn, args) => + if isIdentityJp(defn.expectDefn) then makeRet(args.head) + else jpRefs.get(defn.expectDefn.id) match + case None => throw IRError("could not find jump point with id" + defn.expectDefn.id) + case Some(value) => Jump(value, Expr.Ref(Name("ctx")) :: args) + + case Case(scrut, cases) => Case(scrut, cases.map { (cls, body) => (cls, transformNode(body)) }).attachTag(tag) + case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) + case LetCall(names, defn, args, isTailRec, body) => + // Handle the case when we see a tail call. + // This case is not handled by the paper. The way to transform this is: + // let res = foo(*args) in res + // --> let res = foo_modcons(ctx, *args) in res + if isTailCall(node) && defnsIdSet.contains(defn.expectDefn.id) then + // Transform it into a tail recursive call where we pass on the current context + LetCall( + List(Name("res")), + modConsRefs(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, + isTailRec, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + ).attachTag(tag) + else + LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) + case AssignField(assignee, clsInfo, fieldName, value, body) => + AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) + + def transformModConsBranch(node: Node)(implicit call: ModConsCallInfo): Node = def makeCall = val field = assignToIdx((call.letCtorNode.cls.id, call.letCtorNode.fieldName)) - // let composed = comp(ctx, ctx2) in + // let composed = comp(ctx, Ctx(retVal, ptr, field)) in // f(composed, *args) LetExpr( Name("ctx2"), @@ -595,8 +640,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): false, LetCall( List(Name("res")), - modConsMap(call.defn.id), - Expr.Ref(Name("composed")) :: call.letCallNode.args, // TODO: change + modConsRefs(call.defn.id), + Expr.Ref(Name("composed")) :: call.letCallNode.args, false, Result( List(Expr.Ref(Name("res"))) @@ -606,64 +651,79 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): ).attachTag(tag) node match - case Result(res) => (node, Set()) - case Jump(defn, args) => ??? - case Case(scrut, cases) => ??? - case LetExpr(name, expr, body) => ??? - case LetCall(names, defn, args, isTailRec, body) => ??? - case AssignField(assignee, clsInfo, fieldName, value, body) => ??? - - // Here, we assume we are inside the modcons function and hence have an extra - // `ctx` parameter at the start. - def transformNode(node: Node): (Node, Set[Defn]) = - modConsBranches.get(node.tag.inner) match - case Some(call) => transformModConsBranch(node, call) - case None => node match - case Result(_) => (node.attachTag(tag), Set()) - case Jump(_, _) => (node.attachTag(tag), Set()) - case Case(scrut, cases) => - val casesAndDefns = cases.map { (cls, body) => (cls, transformNode(body)) } - - val transformedCases = casesAndDefns.map { (cls, item) => (cls, item._1) } - val newDefns: Set[Defn] = casesAndDefns.map((_, item) => item._2).foldLeft(Set())((a, b) => a ++ b) - - (Case(scrut, transformedCases).attachTag(tag), newDefns) - case LetExpr(name, expr, body) => - val (transformed, newDefns) = transformNode(body) - (LetExpr(name, expr, transformed).attachTag(tag), newDefns) - case LetCall(names, defn, args, isTailRec, body) => - // This case is not handled by the paper. The way to transform this is: - // let res = foo(*args) in res - // --> let res = foo_modcons(ctx, *args) in res - if isTailCall(node) && defnsIdSet.contains(defn.expectDefn.id) then - // Transform it into a tail recursive call where we pass on the current context - ( - LetCall( - List(Name("res")), - modConsMap(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, - isTailRec, - Result(List(Expr.Ref(Name("res")))) - ), - Set() - ) - else - val (transformed, newDefns) = transformNode(body) - (LetCall(names, defn, args, isTailRec, transformed).attachTag(tag), newDefns) - case AssignField(assignee, clsInfo, fieldName, value, body) => - val (transformed, newDefns) = transformNode(body) - (AssignField(assignee, clsInfo, fieldName, value, transformed).attachTag(tag), newDefns) + case Result(res) if node.tag.inner == call.retNode.tag.inner => + makeCall + case Jump(defn, args) if node.tag.inner == call.retNode.tag.inner => + makeCall + case LetExpr(name, expr, body) => + if node.tag.inner == call.letCtorNode.node.tag.inner then + // rewrite the ctor, but set the field containing the call as to 0 + val idx = call.letCtorNode.idx + val argsList = call.letCtorNode.ctor.args.updated(idx, asLit(0)) + LetExpr(name, Expr.CtorApp(call.letCtorNode.cls, argsList), transformModConsBranch(body)).attachTag(tag) + else + LetExpr(name, expr, transformModConsBranch(body)).attachTag(tag) + case LetCall(names, defn, args, isTailRec, body) => + if node.tag.inner == call.letCallNode.tag.inner then + // discard it + transformModConsBranch(body) + else + LetCall(names, defn, args, isTailRec, transformModConsBranch(body)).attachTag(tag) + case AssignField(assignee, clsInfo, fieldName, value, body) => + AssignField(assignee, clsInfo, fieldName, value, transformModConsBranch(body)).attachTag(tag) + case _ => throw IRError("unreachable case when transforming mod cons call") // will create two definitions: one has the same signature as the original function, // while the other one will have extra parameters to support mod cons + // replaceOriginal should be false if and only if the definition is a join point + def rewriteDefn(d: Defn): Defn = + val transformed = transformNode(d.body) + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) + + // returns (new defn, mod cons defn) + // where new defn has the same signature and ids as the original, but immediately calls the mod cons defn + // and mod cons defn is the rewritten definition + def replaceDefn(d: Defn): (Defn, Defn) = + val modConsDefn = rewriteDefn(d) + val modConsCall = + LetExpr( + Name("idCtx"), + Expr.CtorApp(ID_CTX_CLASS, Nil), + LetCall( + List(Name("res")), + DefnRef(Left(modConsDefn)), + Expr.Ref(Name("idCtx")) :: d.params.map(Expr.Ref(_)), + false, + Result(List(Expr.Ref(Name("res")))).attachTag(tag) + ).attachTag(tag) + ).attachTag(tag) + val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall) + (newDefn, modConsDefn) - // We will need to rewrite join points, thus new definitions may be created. - def transformDefn(d: Defn): Set[Defn] = - val (transformed, newDefns) = transformNode(d.body) - newDefns + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) + //newDefns + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) - ??? - + val jpsTransformed = component.joinPoints.map(d => d.id -> rewriteDefn(d)).toMap + val defnsTransformed = component.nodes.map(d => d.id -> replaceDefn(d)).toMap + + // update defn refs + for (id, ref) <- jpRefs do + ref.defn = Left(jpsTransformed(id)) + + for (id, ref) <- modConsRefs do + ref.defn = Left(defnsTransformed(id)._2) // set it to the mod cons defn, not the one with the original signature + val jps = jpsTransformed.values.toSet + val modConsDefs = defnsTransformed.values.map((a, b) => b).toSet + val normalDefs = defnsTransformed.values.map((a, b) => a).toSet + appDefn + cmpDefn + + // the edges are not used later, but still, rewrite them for correctness + val newEdges = component.edges.map { c => + val src = c.getSrc + val defn = c.getDefn + TailCallInfo(defnsTransformed(src.id)._2, defnsTransformed(defn.id)._2) + } + + (ScComponent(modConsDefs, newEdges, jps), normalDefs) // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a set containing the optimized function and the @@ -733,8 +793,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // in fact, they should theoretically have the same return type if the program type checked val resultNum = defnsList.head.resultNum - // TODO: make sure that name clashes aren't a problem - val trName = Name("tailrecBranch"); + val trName = Name("tailrecBranch$"); // To be used to replace variable names inside a definition to avoid variable name clashes val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap @@ -828,19 +887,25 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(_.removeMetadata) - + + private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + val (modConsComp, other) = optimizeModCons(component, classes) + // val trOpt = optimizeTailRec(modConsComp, classes) + // other ++ trOpt + modConsComp.nodes ++ modConsComp.joinPoints ++ other def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = // val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) val partitions = partition(p.defs) - val newDefs: Set[Defn] = partitions.flatMap { optimizeTailRec(_, p.classes) }.toSet + + val newDefs: Set[Defn] = partitions.flatMap { optimizeParition(_, p.classes) }.toSet // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } resolveDefnRef(p.main, newDefs, true) - (Program(p.classes, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) + (Program(p.classes + ID_CTX_CLASS + CTX_CLASS, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) def run(p: Program): Program = run_debug(p)._1 \ No newline at end of file diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index a713946be8..58c8370e99 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -116,52 +116,44 @@ fact(1, 5) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, fact_jp, [acc$0,n$0], +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #36 -//│ if x$0 -- #35 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(acc$0) -- #31 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #34 -//│ let x$3 = -(n$0,1) in -- #33 -//│ jump fact_jp(x$2,x$3) -- #32 +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) -//│ Def(3, fact, [acc$0,n$0], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 -//│ r0 -- #37 +//│ x$1 -- #3 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 //│ x$5 -- #29) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, fact_jp, [acc$0,n$0], +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #36 -//│ if x$0 -- #35 +//│ let x$0 = ==(n$0,0) in -- #22 +//│ if x$0 -- #21 //│ true => -//│ jump j$0(acc$0) -- #31 +//│ jump j$0(acc$0) -- #5 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #34 -//│ let x$3 = -(n$0,1) in -- #33 -//│ jump fact_jp(x$2,x$3) -- #32 +//│ let x$2 = *(acc$0,n$0) in -- #20 +//│ let x$3 = -(n$0,1) in -- #19 +//│ let* (x$4) = fact(x$2,x$3) in -- #18 +//│ jump j$0(x$4) -- #17 //│ ) -//│ Def(3, fact, [acc$0,n$0], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 -//│ r0 -- #37 +//│ x$1 -- #3 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -218,11 +210,16 @@ fact(1, 5) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 -//│ r0 -- #59 +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 //│ ) //│ Def(1, j$1, [x$3], //│ 1, @@ -239,41 +236,21 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 -//│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 -//│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 //│ x$7 -- #39) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 -//│ r0 -- #59 +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 //│ ) //│ Def(1, j$1, [x$3], //│ 1, @@ -290,31 +267,6 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 -//│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 -//│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 //│ x$7 -- #39) @@ -532,7 +484,7 @@ g(6, 0) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -540,8 +492,16 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 -//│ r0 -- #99 +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 +//│ true => +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 +//│ false => +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -549,49 +509,28 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 -//│ r0 -- #97 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #95 -//│ if scrut -- #94 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$2 = ==(f_n$0,0) in -- #93 -//│ if x$2 -- #92 -//│ true => -//│ let* (x$4) = double(f_acc$0) in -- #88 -//│ jump j$0(x$4) -- #87 -//│ false => -//│ let x$5 = -(f_n$0,1) in -- #91 -//│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = ==(g_m$0,0) in -- #86 -//│ if x$8 -- #85 -//│ true => -//│ let* (x$10) = double(g_acc$1) in -- #81 -//│ let x$11 = -(0,x$10) in -- #80 -//│ jump j$1(x$11) -- #79 -//│ false => -//│ let x$12 = -(g_m$0,1) in -- #84 -//│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(4, j$1, [x$9], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ x$9 -- #35 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -599,8 +538,16 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 -//│ r0 -- #99 +//│ let x$2 = ==(n$0,0) in -- #31 +//│ if x$2 -- #30 +//│ true => +//│ let* (x$4) = double(acc$0) in -- #14 +//│ jump j$0(x$4) -- #13 +//│ false => +//│ let x$5 = -(n$0,1) in -- #29 +//│ let x$6 = +(acc$0,1) in -- #28 +//│ let* (x$7) = g(x$5,x$6) in -- #27 +//│ jump j$0(x$7) -- #26 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -608,42 +555,21 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 -//│ r0 -- #97 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ Def(5, _g_f_opt_jp, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ let scrut = ==(1,tailrecBranch) in -- #95 -//│ if scrut -- #94 +//│ let x$8 = ==(m$0,0) in -- #62 +//│ if x$8 -- #61 //│ true => -//│ let x$2 = ==(f_n$0,0) in -- #93 -//│ if x$2 -- #92 -//│ true => -//│ let* (x$4) = double(f_acc$0) in -- #88 -//│ jump j$0(x$4) -- #87 -//│ false => -//│ let x$5 = -(f_n$0,1) in -- #91 -//│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ let* (x$10) = double(acc$1) in -- #45 +//│ let x$11 = -(0,x$10) in -- #44 +//│ jump j$1(x$11) -- #43 //│ false => -//│ let x$8 = ==(g_m$0,0) in -- #86 -//│ if x$8 -- #85 -//│ true => -//│ let* (x$10) = double(g_acc$1) in -- #81 -//│ let x$11 = -(0,x$10) in -- #80 -//│ jump j$1(x$11) -- #79 -//│ false => -//│ let x$12 = -(g_m$0,1) in -- #84 -//│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ let x$12 = -(m$0,1) in -- #60 +//│ let x$13 = +(acc$1,1) in -- #59 +//│ let* (x$14) = f(x$12,x$13) in -- #58 +//│ jump j$1(x$14) -- #57 //│ ) -//│ Def(6, _g_f_opt, [tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(4, j$1, [x$9], //│ 1, -//│ jump _g_f_opt_jp(tailrecBranch,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ x$9 -- #35 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -684,77 +610,41 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 -//│ r0 -- #44 +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 -//│ r0 -- #42 +//│ let* (x$1) = h(0,0,0,0) in -- #19 +//│ x$1 -- #18 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 -//│ r0 -- #40 -//│ ) -//│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #38 -//│ if scrut -- #37 -//│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 -//│ false => -//│ let scrut = ==(1,tailrecBranch) in -- #36 -//│ if scrut -- #35 -//│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 -//│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp(tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ let* (x$2) = f(0,0,0) in -- #29 +//│ x$2 -- #28 //│ ) //│ }, //│ 2 -- #30) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 -//│ r0 -- #44 +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 -//│ r0 -- #42 +//│ let* (x$1) = h(0,0,0,0) in -- #19 +//│ x$1 -- #18 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 -//│ r0 -- #40 -//│ ) -//│ Def(3, _h_g_f_opt_jp, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #38 -//│ if scrut -- #37 -//│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 -//│ false => -//│ let scrut = ==(1,tailrecBranch) in -- #36 -//│ if scrut -- #35 -//│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 -//│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ Def(4, _h_g_f_opt, [tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp(tailrecBranch,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ let* (x$2) = f(0,0,0) in -- #29 +//│ x$2 -- #28 //│ ) //│ }, //│ 2 -- #30) @@ -787,15 +677,15 @@ hello() //│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) //│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:98) //│ mlscript.compiler.optimizer.TailRecOpt.discoverOptimizableCalls(TailRecOpt.scala:108) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:311) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:316) -//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:339) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:312) +//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:317) +//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:340) //│ scala.collection.IterableOnceOps.foldLeft(IterableOnce.scala:646) //│ scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) //│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:339) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:830) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:837) +//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:340) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:889) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:901) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -895,29 +785,54 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 +//│ res -- #78 +//│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, addOne_jp, [xs$0], +//│ Def(2, _addOne_ctx_app, [ctx,x], //│ 1, -//│ case xs$0 of -- #61 -//│ Cons => -//│ let x$1 = xs$0.t in -- #58 -//│ let x$2 = xs$0.h in -- #57 -//│ let x$3 = +(x$2,1) in -- #56 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #55 -//│ let x$5 = Cons(x$3,x$4) in -- #54 -//│ jump j$0(x$5) -- #53 -//│ Nil => -//│ let x$6 = Nil() in -- #60 -//│ jump j$0(x$6) -- #59 +//│ case ctx of -- #59 +//│ _IdContext => +//│ x -- #58 +//│ _Context => +//│ let field = ctx.field in -- #57 +//│ let ptr = ctx.ptr in -- #56 +//│ assign ptr.t := x in -- #55 +//│ let acc = ctx.acc in -- #54 +//│ acc -- #53 +//│ ) +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #65 +//│ let ctx2ptr = ctx2.ptr in -- #64 +//│ let ctx2field = ctx2.field in -- #63 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 +//│ ret -- #60 //│ ) -//│ Def(3, addOne, [xs$0], +//│ Def(4, addOne_modcons, [ctx,xs$0], //│ 1, -//│ let* (r0) = addOne_jp(xs$0) in -- #63 -//│ r0 -- #62 +//│ case xs$0 of -- #77 +//│ Cons => +//│ let x$1 = xs$0.t in -- #73 +//│ let x$2 = xs$0.h in -- #72 +//│ let x$3 = +(x$2,1) in -- #71 +//│ let x$5 = Cons(x$3,0) in -- #70 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #69 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #68 +//│ let* (res) = addOne_modcons(composed,x$1) in -- #67 +//│ res -- #66 +//│ Nil => +//│ let x$6 = Nil() in -- #76 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #75 +//│ res -- #74 //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -928,29 +843,54 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #47) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 +//│ res -- #78 +//│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, addOne_jp, [xs$0], +//│ Def(2, _addOne_ctx_app, [ctx,x], //│ 1, -//│ case xs$0 of -- #61 -//│ Cons => -//│ let x$1 = xs$0.t in -- #58 -//│ let x$2 = xs$0.h in -- #57 -//│ let x$3 = +(x$2,1) in -- #56 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #55 -//│ let x$5 = Cons(x$3,x$4) in -- #54 -//│ jump j$0(x$5) -- #53 -//│ Nil => -//│ let x$6 = Nil() in -- #60 -//│ jump j$0(x$6) -- #59 +//│ case ctx of -- #59 +//│ _IdContext => +//│ x -- #58 +//│ _Context => +//│ let field = ctx.field in -- #57 +//│ let ptr = ctx.ptr in -- #56 +//│ assign ptr.t := x in -- #55 +//│ let acc = ctx.acc in -- #54 +//│ acc -- #53 //│ ) -//│ Def(3, addOne, [xs$0], +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let* (r0) = addOne_jp(xs$0) in -- #63 -//│ r0 -- #62 +//│ let ctx2acc = ctx2.acc in -- #65 +//│ let ctx2ptr = ctx2.ptr in -- #64 +//│ let ctx2field = ctx2.field in -- #63 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 +//│ ret -- #60 +//│ ) +//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #77 +//│ Cons => +//│ let x$1 = xs$0.t in -- #73 +//│ let x$2 = xs$0.h in -- #72 +//│ let x$3 = +(x$2,1) in -- #71 +//│ let x$5 = Cons(x$3,0) in -- #70 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #69 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #68 +//│ let* (res) = addOne_modcons(composed,x$1) in -- #67 +//│ res -- #66 +//│ Nil => +//│ let x$6 = Nil() in -- #76 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #75 +//│ res -- #74 //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -963,6 +903,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) +:noTailRec :interpIR class True class False @@ -1026,14 +967,19 @@ a(S(S(S(Zero)))) //│ let x$17 = S(x$16) in -- #71 //│ let* (x$18) = a(x$17) in -- #70 //│ x$18 -- #69) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(b, a)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #109 -//│ r0 -- #108 +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -1041,46 +987,23 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #107 -//│ r0 -- #106 +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 //│ ) //│ Def(3, j$1, [x$6], //│ 1, //│ x$6 -- #25 //│ ) -//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #104 -//│ if scrut -- #103 -//│ true => -//│ case a_n$0 of -- #102 -//│ S => -//│ let x$1 = a_n$0.x in -- #98 -//│ let* (x$2) = @tailrec b(x$1) in -- #97 -//│ let x$3 = S(x$2) in -- #96 -//│ jump j$0(x$3) -- #95 -//│ Zero => -//│ let x$4 = Zero() in -- #101 -//│ let x$5 = S(x$4) in -- #100 -//│ jump j$0(x$5) -- #99 -//│ false => -//│ case b_n$1 of -- #94 -//│ S => -//│ let x$7 = b_n$1.x in -- #89 -//│ let* (x$8) = @tailrec a(x$7) in -- #88 -//│ let x$9 = S(x$8) in -- #87 -//│ let x$10 = S(x$9) in -- #86 -//│ jump j$1(x$10) -- #85 -//│ Zero => -//│ let x$11 = Zero() in -- #93 -//│ let x$12 = S(x$11) in -- #92 -//│ let x$13 = S(x$12) in -- #91 -//│ jump j$1(x$13) -- #90 -//│ ) -//│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], -//│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #105 -//│ ) //│ }, //│ let x$14 = Zero() in -- #74 //│ let x$15 = S(x$14) in -- #73 @@ -1093,8 +1016,16 @@ a(S(S(S(Zero)))) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,n$0) in -- #109 -//│ r0 -- #108 +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -1102,46 +1033,23 @@ a(S(S(S(Zero)))) //│ ) //│ Def(2, b, [n$1], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$1,0) in -- #107 -//│ r0 -- #106 +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 //│ ) //│ Def(3, j$1, [x$6], //│ 1, //│ x$6 -- #25 //│ ) -//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$1,a_n$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #104 -//│ if scrut -- #103 -//│ true => -//│ case a_n$0 of -- #102 -//│ S => -//│ let x$1 = a_n$0.x in -- #98 -//│ let* (x$2) = @tailrec b(x$1) in -- #97 -//│ let x$3 = S(x$2) in -- #96 -//│ jump j$0(x$3) -- #95 -//│ Zero => -//│ let x$4 = Zero() in -- #101 -//│ let x$5 = S(x$4) in -- #100 -//│ jump j$0(x$5) -- #99 -//│ false => -//│ case b_n$1 of -- #94 -//│ S => -//│ let x$7 = b_n$1.x in -- #89 -//│ let* (x$8) = @tailrec a(x$7) in -- #88 -//│ let x$9 = S(x$8) in -- #87 -//│ let x$10 = S(x$9) in -- #86 -//│ jump j$1(x$10) -- #85 -//│ Zero => -//│ let x$11 = Zero() in -- #93 -//│ let x$12 = S(x$11) in -- #92 -//│ let x$13 = S(x$12) in -- #91 -//│ jump j$1(x$13) -- #90 -//│ ) -//│ Def(5, _b_a_opt, [tailrecBranch,b_n$1,a_n$0], -//│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$1,a_n$0) -- #105 -//│ ) //│ }, //│ let x$14 = Zero() in -- #74 //│ let x$15 = S(x$14) in -- #73 @@ -1156,26 +1064,264 @@ a(S(S(S(Zero)))) :interpIR class True class False -class Cons(h, t) -class Nil -fun addOne(xs) = - if xs is - Cons(h, t) then - val next = @tailrec addOne(t) - val ret = Cons(h + 1, next) - val rett = ret - rett - Nil then - Nil -addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, +class Zero +class S(x) +fun a(n) = + if n is + S(x) then S(@tailrec b(x)) + Zero then S(Zero) +fun b(n) = + if n is + S(x) then S(S(@tailrec a(x))) + Zero then S(S(Zero)) +a(S(S(S(Zero)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailrec b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailrec a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ case n$0 of -- #23 +//│ S => +//│ let x$1 = n$0.x in -- #15 +//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let x$3 = S(x$2) in -- #13 +//│ jump j$0(x$3) -- #12 +//│ Zero => +//│ let x$4 = Zero() in -- #22 +//│ let x$5 = S(x$4) in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ case n$1 of -- #55 +//│ S => +//│ let x$7 = n$1.x in -- #43 +//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let x$9 = S(x$8) in -- #41 +//│ let x$10 = S(x$9) in -- #40 +//│ jump j$1(x$10) -- #39 +//│ Zero => +//│ let x$11 = Zero() in -- #54 +//│ let x$12 = S(x$11) in -- #53 +//│ let x$13 = S(x$12) in -- #52 +//│ jump j$1(x$13) -- #51 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #117 +//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 +//│ res -- #115 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let idCtx = _IdContext() in -- #103 +//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 +//│ res -- #101 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #81 +//│ _IdContext => +//│ x -- #80 +//│ _Context => +//│ let field = ctx.field in -- #79 +//│ let ptr = ctx.ptr in -- #78 +//│ assign ptr.x := x in -- #77 +//│ let acc = ctx.acc in -- #76 +//│ acc -- #75 +//│ ) +//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #87 +//│ let ctx2ptr = ctx2.ptr in -- #86 +//│ let ctx2field = ctx2.field in -- #85 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 +//│ ret -- #82 +//│ ) +//│ Def(6, b_modcons, [ctx,n$1], +//│ 1, +//│ case n$1 of -- #100 +//│ S => +//│ let x$7 = n$1.x in -- #94 +//│ let x$9 = S(0) in -- #93 +//│ let x$10 = S(x$9) in -- #92 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #91 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #90 +//│ let* (res) = a_modcons(composed,x$7) in -- #89 +//│ res -- #88 +//│ Zero => +//│ let x$11 = Zero() in -- #99 +//│ let x$12 = S(x$11) in -- #98 +//│ let x$13 = S(x$12) in -- #97 +//│ let* (res) = _b_a_ctx_app(ctx,x$13) in -- #96 +//│ res -- #95 +//│ ) +//│ Def(7, a_modcons, [ctx,n$0], +//│ 1, +//│ case n$0 of -- #114 +//│ S => +//│ let x$1 = n$0.x in -- #109 +//│ let x$3 = S(0) in -- #108 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #107 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #106 +//│ let* (res) = b_modcons(composed,x$1) in -- #105 +//│ res -- #104 +//│ Zero => +//│ let x$4 = Zero() in -- #113 +//│ let x$5 = S(x$4) in -- #112 +//│ let* (res) = _b_a_ctx_app(ctx,x$5) in -- #111 +//│ res -- #110 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #117 +//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 +//│ res -- #115 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let idCtx = _IdContext() in -- #103 +//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 +//│ res -- #101 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #81 +//│ _IdContext => +//│ x -- #80 +//│ _Context => +//│ let field = ctx.field in -- #79 +//│ let ptr = ctx.ptr in -- #78 +//│ assign ptr.x := x in -- #77 +//│ let acc = ctx.acc in -- #76 +//│ acc -- #75 +//│ ) +//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #87 +//│ let ctx2ptr = ctx2.ptr in -- #86 +//│ let ctx2field = ctx2.field in -- #85 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 +//│ ret -- #82 +//│ ) +//│ Def(6, b_modcons, [ctx,n$1], +//│ 1, +//│ case n$1 of -- #100 +//│ S => +//│ let x$7 = n$1.x in -- #94 +//│ let x$9 = S(0) in -- #93 +//│ let x$10 = S(x$9) in -- #92 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #91 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #90 +//│ let* (res) = a_modcons(composed,x$7) in -- #89 +//│ res -- #88 +//│ Zero => +//│ let x$11 = Zero() in -- #99 +//│ let x$12 = S(x$11) in -- #98 +//│ let x$13 = S(x$12) in -- #97 +//│ let* (res) = _b_a_ctx_app(ctx,x$13) in -- #96 +//│ res -- #95 +//│ ) +//│ Def(7, a_modcons, [ctx,n$0], +//│ 1, +//│ case n$0 of -- #114 +//│ S => +//│ let x$1 = n$0.x in -- #109 +//│ let x$3 = S(0) in -- #108 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #107 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #106 +//│ let* (res) = b_modcons(composed,x$1) in -- #105 +//│ res -- #104 +//│ Zero => +//│ let x$4 = Zero() in -- #113 +//│ let x$5 = S(x$4) in -- #112 +//│ let* (res) = _b_a_ctx_app(ctx,x$5) in -- #111 +//│ res -- #110 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun addOne(xs) = + if xs is + Cons(h, t) then + val next = @tailrec addOne(t) + val ret = Cons(h + 1, next) + val rett = ret + rett + Nil then + Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, //│ case xs$0 of -- #30 //│ Cons => //│ let x$1 = xs$0.t in -- #26 @@ -1202,29 +1348,54 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #83 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 +//│ res -- #81 +//│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, addOne_jp, [xs$0], +//│ Def(2, _addOne_ctx_app, [ctx,x], //│ 1, -//│ case xs$0 of -- #64 -//│ Cons => -//│ let x$1 = xs$0.t in -- #61 -//│ let x$2 = xs$0.h in -- #60 -//│ let* (x$3) = @tailrec addOne(x$1) in -- #59 -//│ let x$4 = +(x$2,1) in -- #58 -//│ let x$5 = Cons(x$4,x$3) in -- #57 -//│ jump j$0(x$5) -- #56 -//│ Nil => -//│ let x$6 = Nil() in -- #63 -//│ jump j$0(x$6) -- #62 +//│ case ctx of -- #62 +//│ _IdContext => +//│ x -- #61 +//│ _Context => +//│ let field = ctx.field in -- #60 +//│ let ptr = ctx.ptr in -- #59 +//│ assign ptr.t := x in -- #58 +//│ let acc = ctx.acc in -- #57 +//│ acc -- #56 //│ ) -//│ Def(3, addOne, [xs$0], +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let* (r0) = addOne_jp(xs$0) in -- #66 -//│ r0 -- #65 +//│ let ctx2acc = ctx2.acc in -- #68 +//│ let ctx2ptr = ctx2.ptr in -- #67 +//│ let ctx2field = ctx2.field in -- #66 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 +//│ ret -- #63 +//│ ) +//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #80 +//│ Cons => +//│ let x$1 = xs$0.t in -- #76 +//│ let x$2 = xs$0.h in -- #75 +//│ let x$4 = +(x$2,1) in -- #74 +//│ let x$5 = Cons(x$4,0) in -- #73 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #72 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #71 +//│ let* (res) = addOne_modcons(composed,x$1) in -- #70 +//│ res -- #69 +//│ Nil => +//│ let x$6 = Nil() in -- #79 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #78 +//│ res -- #77 //│ ) //│ }, //│ let x$7 = Nil() in -- #55 @@ -1235,29 +1406,54 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #50) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #83 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 +//│ res -- #81 +//│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, addOne_jp, [xs$0], +//│ Def(2, _addOne_ctx_app, [ctx,x], //│ 1, -//│ case xs$0 of -- #64 -//│ Cons => -//│ let x$1 = xs$0.t in -- #61 -//│ let x$2 = xs$0.h in -- #60 -//│ let* (x$3) = @tailrec addOne(x$1) in -- #59 -//│ let x$4 = +(x$2,1) in -- #58 -//│ let x$5 = Cons(x$4,x$3) in -- #57 -//│ jump j$0(x$5) -- #56 -//│ Nil => -//│ let x$6 = Nil() in -- #63 -//│ jump j$0(x$6) -- #62 +//│ case ctx of -- #62 +//│ _IdContext => +//│ x -- #61 +//│ _Context => +//│ let field = ctx.field in -- #60 +//│ let ptr = ctx.ptr in -- #59 +//│ assign ptr.t := x in -- #58 +//│ let acc = ctx.acc in -- #57 +//│ acc -- #56 //│ ) -//│ Def(3, addOne, [xs$0], +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let* (r0) = addOne_jp(xs$0) in -- #66 -//│ r0 -- #65 +//│ let ctx2acc = ctx2.acc in -- #68 +//│ let ctx2ptr = ctx2.ptr in -- #67 +//│ let ctx2field = ctx2.field in -- #66 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 +//│ ret -- #63 +//│ ) +//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #80 +//│ Cons => +//│ let x$1 = xs$0.t in -- #76 +//│ let x$2 = xs$0.h in -- #75 +//│ let x$4 = +(x$2,1) in -- #74 +//│ let x$5 = Cons(x$4,0) in -- #73 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #72 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #71 +//│ let* (res) = addOne_modcons(composed,x$1) in -- #70 +//│ res -- #69 +//│ Nil => +//│ let x$6 = Nil() in -- #79 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #78 +//│ res -- #77 //│ ) //│ }, //│ let x$7 = Nil() in -- #55 @@ -1270,6 +1466,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) +:noTailRec :interpIR class True class False @@ -1327,14 +1524,22 @@ b(20) //│ }, //│ let* (x$16) = b(20) in -- #71 //│ x$16 -- #70) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(b, a)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #103 -//│ r0 -- #102 +//│ let x$1 = x$0.n in -- #35 +//│ let x$2 = <(x$1,0) in -- #34 +//│ if x$2 -- #33 +//│ true => +//│ let x$4 = x$0.n in -- #14 +//│ let x$5 = A(-1,x$4) in -- #13 +//│ jump j$0(x$5) -- #12 +//│ false => +//│ let x$6 = x$0.n in -- #32 +//│ let x$7 = -(x$6,2) in -- #31 +//│ let* (x$8) = b(x$7) in -- #30 +//│ let x$9 = A(2,x$8) in -- #29 +//│ jump j$0(x$9) -- #28 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -1342,45 +1547,20 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #101 -//│ r0 -- #100 -//│ ) -//│ Def(3, j$1, [x$11], -//│ 1, -//│ x$11 -- #39 -//│ ) -//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #98 -//│ if scrut -- #97 +//│ let x$10 = <(n$0,0) in -- #65 +//│ if x$10 -- #64 //│ true => -//│ let x$1 = a_x$0.n in -- #96 -//│ let x$2 = <(x$1,0) in -- #95 -//│ if x$2 -- #94 -//│ true => -//│ let x$4 = a_x$0.n in -- #88 -//│ let x$5 = A(-1,x$4) in -- #87 -//│ jump j$0(x$5) -- #86 -//│ false => -//│ let x$6 = a_x$0.n in -- #93 -//│ let x$7 = -(x$6,2) in -- #92 -//│ let* (x$8) = b(x$7) in -- #91 -//│ let x$9 = A(2,x$8) in -- #90 -//│ jump j$0(x$9) -- #89 +//│ let x$12 = A(0,n$0) in -- #47 +//│ jump j$1(x$12) -- #46 //│ false => -//│ let x$10 = <(b_n$0,0) in -- #85 -//│ if x$10 -- #84 -//│ true => -//│ let x$12 = A(0,b_n$0) in -- #80 -//│ jump j$1(x$12) -- #79 -//│ false => -//│ let x$13 = -(b_n$0,1) in -- #83 -//│ let x$14 = A(0,x$13) in -- #82 -//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #81 +//│ let x$13 = -(n$0,1) in -- #63 +//│ let x$14 = A(0,x$13) in -- #62 +//│ let* (x$15) = a(x$14) in -- #61 +//│ jump j$1(x$15) -- #60 //│ ) -//│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], +//│ Def(3, j$1, [x$11], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #99 +//│ x$11 -- #39 //│ ) //│ }, //│ let* (x$16) = b(20) in -- #71 @@ -1390,8 +1570,19 @@ b(20) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { //│ Def(0, a, [x$0], //│ 1, -//│ let* (r0) = _b_a_opt(0,0,x$0) in -- #103 -//│ r0 -- #102 +//│ let x$1 = x$0.n in -- #35 +//│ let x$2 = <(x$1,0) in -- #34 +//│ if x$2 -- #33 +//│ true => +//│ let x$4 = x$0.n in -- #14 +//│ let x$5 = A(-1,x$4) in -- #13 +//│ jump j$0(x$5) -- #12 +//│ false => +//│ let x$6 = x$0.n in -- #32 +//│ let x$7 = -(x$6,2) in -- #31 +//│ let* (x$8) = b(x$7) in -- #30 +//│ let x$9 = A(2,x$8) in -- #29 +//│ jump j$0(x$9) -- #28 //│ ) //│ Def(1, j$0, [x$3], //│ 1, @@ -1399,49 +1590,289 @@ b(20) //│ ) //│ Def(2, b, [n$0], //│ 1, -//│ let* (r0) = _b_a_opt(2,n$0,0) in -- #101 -//│ r0 -- #100 +//│ let x$10 = <(n$0,0) in -- #65 +//│ if x$10 -- #64 +//│ true => +//│ let x$12 = A(0,n$0) in -- #47 +//│ jump j$1(x$12) -- #46 +//│ false => +//│ let x$13 = -(n$0,1) in -- #63 +//│ let x$14 = A(0,x$13) in -- #62 +//│ let* (x$15) = a(x$14) in -- #61 +//│ jump j$1(x$15) -- #60 //│ ) //│ Def(3, j$1, [x$11], //│ 1, //│ x$11 -- #39 //│ ) -//│ Def(4, _b_a_opt_jp, [tailrecBranch,b_n$0,a_x$0], +//│ }, +//│ let* (x$16) = b(20) in -- #71 +//│ x$16 -- #70) +//│ +//│ Interpreted: +//│ A(2,A(2,A(2,A(2,A(2,A(2,A(2,A(0,-1)))))))) + +:interpIR +class True +class False +class Nil +class Cons(m, n) +fun a(x) = + if x is + Cons(m, n) then + if m < 0 then + Cons(-1, Nil) + else + Cons(m * 4, b(m - 2)) + Nil then Nil +fun b(n) = + if n <= 0 then + Cons(0, Nil) + else + a(Cons(n, Nil)) +b(16) +//│ |#class| |True|↵|#class| |False|↵|#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| +//│ Parsed: {class True {}; class False {}; class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { +//│ Def(0, a, [x$0], //│ 1, -//│ let scrut = ==(0,tailrecBranch) in -- #98 -//│ if scrut -- #97 -//│ true => -//│ let x$1 = a_x$0.n in -- #96 -//│ let x$2 = <(x$1,0) in -- #95 -//│ if x$2 -- #94 +//│ case x$0 of -- #46 +//│ Cons => +//│ let x$2 = x$0.n in -- #42 +//│ let x$3 = x$0.m in -- #41 +//│ let x$4 = <(x$3,0) in -- #40 +//│ if x$4 -- #39 //│ true => -//│ let x$4 = a_x$0.n in -- #88 -//│ let x$5 = A(-1,x$4) in -- #87 -//│ jump j$0(x$5) -- #86 +//│ let x$6 = Nil() in -- #19 +//│ let x$7 = Cons(-1,x$6) in -- #18 +//│ jump j$1(x$7) -- #17 //│ false => -//│ let x$6 = a_x$0.n in -- #93 -//│ let x$7 = -(x$6,2) in -- #92 -//│ let* (x$8) = b(x$7) in -- #91 -//│ let x$9 = A(2,x$8) in -- #90 -//│ jump j$0(x$9) -- #89 +//│ let x$8 = *(x$3,4) in -- #38 +//│ let x$9 = -(x$3,2) in -- #37 +//│ let* (x$10) = b(x$9) in -- #36 +//│ let x$11 = Cons(x$8,x$10) in -- #35 +//│ jump j$1(x$11) -- #34 +//│ Nil => +//│ let x$12 = Nil() in -- #45 +//│ jump j$0(x$12) -- #44 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let x$13 = <=(n$0,0) in -- #75 +//│ if x$13 -- #74 +//│ true => +//│ let x$15 = Nil() in -- #59 +//│ let x$16 = Cons(0,x$15) in -- #58 +//│ jump j$2(x$16) -- #57 //│ false => -//│ let x$10 = <(b_n$0,0) in -- #85 -//│ if x$10 -- #84 +//│ let x$17 = Nil() in -- #73 +//│ let x$18 = Cons(n$0,x$17) in -- #72 +//│ let* (x$19) = a(x$18) in -- #71 +//│ jump j$2(x$19) -- #70 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #129 +//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 +//│ res -- #127 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #107 +//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 +//│ res -- #105 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ Def(5, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = ctx.field in -- #86 +//│ let ptr = ctx.ptr in -- #85 +//│ assign ptr.n := x in -- #84 +//│ let acc = ctx.acc in -- #83 +//│ acc -- #82 +//│ ) +//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #94 +//│ let ctx2ptr = ctx2.ptr in -- #93 +//│ let ctx2field = ctx2.field in -- #92 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ ) +//│ Def(7, b_modcons, [ctx,n$0], +//│ 1, +//│ let x$13 = <=(n$0,0) in -- #104 +//│ if x$13 -- #103 +//│ true => +//│ let x$15 = Nil() in -- #98 +//│ let x$16 = Cons(0,x$15) in -- #97 +//│ let* (res) = _b_a_ctx_app(ctx,x$16) in -- #96 +//│ res -- #95 +//│ false => +//│ let x$17 = Nil() in -- #102 +//│ let x$18 = Cons(n$0,x$17) in -- #101 +//│ let* (res) = a_modcons(ctx,x$18) in -- #100 +//│ res -- #99 +//│ ) +//│ Def(8, a_modcons, [ctx,x$0], +//│ 1, +//│ case x$0 of -- #126 +//│ Cons => +//│ let x$2 = x$0.n in -- #122 +//│ let x$3 = x$0.m in -- #121 +//│ let x$4 = <(x$3,0) in -- #120 +//│ if x$4 -- #119 //│ true => -//│ let x$12 = A(0,b_n$0) in -- #80 -//│ jump j$1(x$12) -- #79 +//│ let x$6 = Nil() in -- #111 +//│ let x$7 = Cons(-1,x$6) in -- #110 +//│ let* (res) = _b_a_ctx_app(ctx,x$7) in -- #109 +//│ res -- #108 //│ false => -//│ let x$13 = -(b_n$0,1) in -- #83 -//│ let x$14 = A(0,x$13) in -- #82 -//│ jump _b_a_opt_jp(0,b_n$0,x$14) -- #81 +//│ let x$8 = *(x$3,4) in -- #118 +//│ let x$9 = -(x$3,2) in -- #117 +//│ let x$11 = Cons(x$8,0) in -- #116 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #115 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #114 +//│ let* (res) = b_modcons(composed,x$9) in -- #113 +//│ res -- #112 +//│ Nil => +//│ let x$12 = Nil() in -- #125 +//│ let* (res) = _b_a_ctx_app(ctx,x$12) in -- #124 +//│ res -- #123 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #129 +//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 +//│ res -- #127 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #107 +//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 +//│ res -- #105 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ Def(5, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = ctx.field in -- #86 +//│ let ptr = ctx.ptr in -- #85 +//│ assign ptr.n := x in -- #84 +//│ let acc = ctx.acc in -- #83 +//│ acc -- #82 +//│ ) +//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #94 +//│ let ctx2ptr = ctx2.ptr in -- #93 +//│ let ctx2field = ctx2.field in -- #92 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ ) +//│ Def(7, b_modcons, [ctx,n$0], +//│ 1, +//│ let x$13 = <=(n$0,0) in -- #104 +//│ if x$13 -- #103 +//│ true => +//│ let x$15 = Nil() in -- #98 +//│ let x$16 = Cons(0,x$15) in -- #97 +//│ let* (res) = _b_a_ctx_app(ctx,x$16) in -- #96 +//│ res -- #95 +//│ false => +//│ let x$17 = Nil() in -- #102 +//│ let x$18 = Cons(n$0,x$17) in -- #101 +//│ let* (res) = a_modcons(ctx,x$18) in -- #100 +//│ res -- #99 //│ ) -//│ Def(5, _b_a_opt, [tailrecBranch,b_n$0,a_x$0], +//│ Def(8, a_modcons, [ctx,x$0], //│ 1, -//│ jump _b_a_opt_jp(tailrecBranch,b_n$0,a_x$0) -- #99 +//│ case x$0 of -- #126 +//│ Cons => +//│ let x$2 = x$0.n in -- #122 +//│ let x$3 = x$0.m in -- #121 +//│ let x$4 = <(x$3,0) in -- #120 +//│ if x$4 -- #119 +//│ true => +//│ let x$6 = Nil() in -- #111 +//│ let x$7 = Cons(-1,x$6) in -- #110 +//│ let* (res) = _b_a_ctx_app(ctx,x$7) in -- #109 +//│ res -- #108 +//│ false => +//│ let x$8 = *(x$3,4) in -- #118 +//│ let x$9 = -(x$3,2) in -- #117 +//│ let x$11 = Cons(x$8,0) in -- #116 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #115 +//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #114 +//│ let* (res) = b_modcons(composed,x$9) in -- #113 +//│ res -- #112 +//│ Nil => +//│ let x$12 = Nil() in -- #125 +//│ let* (res) = _b_a_ctx_app(ctx,x$12) in -- #124 +//│ res -- #123 //│ ) //│ }, -//│ let* (x$16) = b(20) in -- #71 -//│ x$16 -- #70) +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) //│ //│ Interpreted: -//│ A(2,A(2,A(2,A(2,A(2,A(2,A(2,A(0,-1)))))))) +//│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) diff --git a/compiler/shared/test/diff-ir/NuScratch.mls b/compiler/shared/test/diff-ir/NuScratch.mls index 02d97b8857..907012746a 100644 --- a/compiler/shared/test/diff-ir/NuScratch.mls +++ b/compiler/shared/test/diff-ir/NuScratch.mls @@ -1,154 +1,3 @@ :NewParser :ParseOnly :UseIR - -:interpIR -class True -class False -fun fact(acc, n) = - val x = if n > 0 then n - 1 else 0 - if x <= 0 then - acc - else - @tailrec fact(n * acc, x) -fact(1, 5) -//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1| |#else| |0|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailrec fact(*(n,)(acc,), x,)}}; fact(1, 5,)} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = >(n$0,0) in -- #32 -//│ if x$0 -- #31 -//│ true => -//│ let x$6 = -(n$0,1) in -- #28 -//│ jump j$0(x$6,acc$0,n$0) -- #27 -//│ false => -//│ jump j$0(0,acc$0,n$0) -- #30 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 -//│ r0 -- #59 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 -//│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 -//│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 -//│ r0 -- #59 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 -//│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 -//│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) -//│ -//│ Interpreted: -//│ 120 From 31bd2f50638a6b2873f15cdee006dfb8ee5a53c8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 1 May 2024 22:04:07 +0800 Subject: [PATCH 31/59] re-enable normal tailrec optimization, clean up code --- .../compiler/optimizer/TailRecOpt.scala | 15 +- compiler/shared/test/diff-ir/IRTailRec.mls | 1065 ++++++++++------- 2 files changed, 659 insertions(+), 421 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 3cc1c63fb3..d3857c9f68 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -338,7 +338,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val defns = nodeMap.values.toSet val inital = Map[Int, Set[CallInfo]]() val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn.body)(defn.defn, acc)).withDefaultValue(Set()) - print(edges) var ctr = 0 // nodes, edges @@ -393,7 +392,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} val joinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => discoverJoinPoints(defn.defn.body, jps)) - println(joinPoints.map(_.name)) sccs = DefnGraph(scc, sccEdges, joinPoints) :: sccs for v <- defns do @@ -446,6 +444,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // ptr: Represents the object containing the "hole" to be written to. // field: Integer representing which class and field the "hole" belongs to. Which class and field this // represents is different for each strongly connected component. + // + // The idea to use `ptr` and `field` to represent a pointer is by @LPTK. final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) @@ -700,8 +700,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall) (newDefn, modConsDefn) - //newDefns + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) - val jpsTransformed = component.joinPoints.map(d => d.id -> rewriteDefn(d)).toMap val defnsTransformed = component.nodes.map(d => d.id -> replaceDefn(d)).toMap @@ -876,9 +874,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp) - // This is the definition that will be called - // val createIntermidDefn = - jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) @@ -890,14 +885,12 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = val (modConsComp, other) = optimizeModCons(component, classes) - // val trOpt = optimizeTailRec(modConsComp, classes) - // other ++ trOpt - modConsComp.nodes ++ modConsComp.joinPoints ++ other + val trOpt = optimizeTailRec(modConsComp, classes) + other ++ trOpt def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = - // val rewritten = p.defs.map(d => Defn(d.id, d.name, d.params, d.resultNum, rewriteTailCalls(d.body))) val partitions = partition(p.defs) val newDefs: Set[Defn] = partitions.flatMap { optimizeParition(_, p.classes) }.toSet diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 58c8370e99..0307b7f027 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -117,21 +117,25 @@ fact(1, 5) //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, fact, [acc$0,n$0], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ jump j$0(acc$0) -- #31 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ x$1 -- #3 +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -139,21 +143,25 @@ fact(1, 5) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, fact, [acc$0,n$0], +//│ Def(1, j$0, [x$1], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ jump j$0(acc$0) -- #31 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ x$1 -- #3 +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 @@ -213,13 +221,8 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let x$0 = >(n$0,0) in -- #32 -//│ if x$0 -- #31 -//│ true => -//│ let x$6 = -(n$0,1) in -- #28 -//│ jump j$0(x$6,acc$0,n$0) -- #27 -//│ false => -//│ jump j$0(0,acc$0,n$0) -- #30 +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], //│ 1, @@ -236,6 +239,31 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch$) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 //│ x$7 -- #39) @@ -244,13 +272,8 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let x$0 = >(n$0,0) in -- #32 -//│ if x$0 -- #31 -//│ true => -//│ let x$6 = -(n$0,1) in -- #28 -//│ jump j$0(x$6,acc$0,n$0) -- #27 -//│ false => -//│ jump j$0(0,acc$0,n$0) -- #30 +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], //│ 1, @@ -267,6 +290,31 @@ fact(1, 5) //│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch$) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 //│ x$7 -- #39) @@ -492,16 +540,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -509,22 +549,43 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, //│ x$9 -- #35 //│ ) +//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 +//│ true => +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 +//│ false => +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ false => +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 +//│ true => +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 +//│ false => +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ ) +//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) @@ -538,16 +599,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -555,22 +608,43 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, //│ x$9 -- #35 //│ ) +//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 +//│ true => +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 +//│ false => +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ false => +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 +//│ true => +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 +//│ false => +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ ) +//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) @@ -613,18 +687,36 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (x$0) = g(0,0) in -- #7 -//│ x$0 -- #6 +//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (x$1) = h(0,0,0,0) in -- #19 -//│ x$1 -- #18 +//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (x$2) = f(0,0,0) in -- #29 -//│ x$2 -- #28 +//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #38 +//│ if scrut -- #37 +//│ true => +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ false => +//│ let scrut = ==(1,tailrecBranch$) in -- #36 +//│ if scrut -- #35 +//│ true => +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ false => +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 +//│ ) +//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 //│ ) //│ }, //│ 2 -- #30) @@ -633,18 +725,36 @@ fun h(p, q, r, s) = f(0, 0, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (x$0) = g(0,0) in -- #7 -//│ x$0 -- #6 +//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (x$1) = h(0,0,0,0) in -- #19 -//│ x$1 -- #18 +//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (x$2) = f(0,0,0) in -- #29 -//│ x$2 -- #28 +//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #38 +//│ if scrut -- #37 +//│ true => +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ false => +//│ let scrut = ==(1,tailrecBranch$) in -- #36 +//│ if scrut -- #35 +//│ true => +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ false => +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 +//│ ) +//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 //│ ) //│ }, //│ 2 -- #30) @@ -684,8 +794,8 @@ hello() //│ scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) //│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) //│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:340) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:889) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:901) +//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:884) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:896) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -817,22 +927,26 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 //│ ret -- #60 //│ ) -//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], //│ 1, -//│ case xs$0 of -- #77 +//│ case xs$0 of -- #91 //│ Cons => -//│ let x$1 = xs$0.t in -- #73 -//│ let x$2 = xs$0.h in -- #72 -//│ let x$3 = +(x$2,1) in -- #71 -//│ let x$5 = Cons(x$3,0) in -- #70 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #69 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #68 -//│ let* (res) = addOne_modcons(composed,x$1) in -- #67 -//│ res -- #66 +//│ let x$1 = xs$0.t in -- #87 +//│ let x$2 = xs$0.h in -- #86 +//│ let x$3 = +(x$2,1) in -- #85 +//│ let x$5 = Cons(x$3,0) in -- #84 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 +//│ jump addOne_modcons_jp(composed,x$1) -- #81 //│ Nil => -//│ let x$6 = Nil() in -- #76 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #75 -//│ res -- #74 +//│ let x$6 = Nil() in -- #90 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 +//│ res -- #88 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 +//│ r0 -- #92 //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -875,22 +989,26 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 //│ ret -- #60 //│ ) -//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], //│ 1, -//│ case xs$0 of -- #77 +//│ case xs$0 of -- #91 //│ Cons => -//│ let x$1 = xs$0.t in -- #73 -//│ let x$2 = xs$0.h in -- #72 -//│ let x$3 = +(x$2,1) in -- #71 -//│ let x$5 = Cons(x$3,0) in -- #70 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #69 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #68 -//│ let* (res) = addOne_modcons(composed,x$1) in -- #67 -//│ res -- #66 +//│ let x$1 = xs$0.t in -- #87 +//│ let x$2 = xs$0.h in -- #86 +//│ let x$3 = +(x$2,1) in -- #85 +//│ let x$5 = Cons(x$3,0) in -- #84 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 +//│ jump addOne_modcons_jp(composed,x$1) -- #81 //│ Nil => -//│ let x$6 = Nil() in -- #76 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #75 -//│ res -- #74 +//│ let x$6 = Nil() in -- #90 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 +//│ res -- #88 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 +//│ r0 -- #92 //│ ) //│ }, //│ let x$7 = Nil() in -- #52 @@ -1171,37 +1289,50 @@ a(S(S(S(Zero)))) //│ ) //│ Def(6, b_modcons, [ctx,n$1], //│ 1, -//│ case n$1 of -- #100 -//│ S => -//│ let x$7 = n$1.x in -- #94 -//│ let x$9 = S(0) in -- #93 -//│ let x$10 = S(x$9) in -- #92 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #91 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #90 -//│ let* (res) = a_modcons(composed,x$7) in -- #89 -//│ res -- #88 -//│ Zero => -//│ let x$11 = Zero() in -- #99 -//│ let x$12 = S(x$11) in -- #98 -//│ let x$13 = S(x$12) in -- #97 -//│ let* (res) = _b_a_ctx_app(ctx,x$13) in -- #96 -//│ res -- #95 +//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 +//│ r0 -- #155 //│ ) //│ Def(7, a_modcons, [ctx,n$0], //│ 1, -//│ case n$0 of -- #114 -//│ S => -//│ let x$1 = n$0.x in -- #109 -//│ let x$3 = S(0) in -- #108 -//│ let ctx2 = _Context(x$3,x$3,0) in -- #107 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #106 -//│ let* (res) = b_modcons(composed,x$1) in -- #105 -//│ res -- #104 -//│ Zero => -//│ let x$4 = Zero() in -- #113 -//│ let x$5 = S(x$4) in -- #112 -//│ let* (res) = _b_a_ctx_app(ctx,x$5) in -- #111 -//│ res -- #110 +//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 +//│ r0 -- #157 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #153 +//│ if scrut -- #152 +//│ true => +//│ case a_modcons_n$0 of -- #151 +//│ S => +//│ let x$1 = a_modcons_n$0.x in -- #146 +//│ let x$3 = S(0) in -- #145 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 +//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 +//│ Zero => +//│ let x$4 = Zero() in -- #150 +//│ let x$5 = S(x$4) in -- #149 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 +//│ res -- #147 +//│ false => +//│ case b_modcons_n$1 of -- #141 +//│ S => +//│ let x$7 = b_modcons_n$1.x in -- #135 +//│ let x$9 = S(0) in -- #134 +//│ let x$10 = S(x$9) in -- #133 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 +//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 +//│ Zero => +//│ let x$11 = Zero() in -- #140 +//│ let x$12 = S(x$11) in -- #139 +//│ let x$13 = S(x$12) in -- #138 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 +//│ res -- #136 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 //│ ) //│ }, //│ let x$14 = Zero() in -- #74 @@ -1256,37 +1387,50 @@ a(S(S(S(Zero)))) //│ ) //│ Def(6, b_modcons, [ctx,n$1], //│ 1, -//│ case n$1 of -- #100 -//│ S => -//│ let x$7 = n$1.x in -- #94 -//│ let x$9 = S(0) in -- #93 -//│ let x$10 = S(x$9) in -- #92 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #91 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #90 -//│ let* (res) = a_modcons(composed,x$7) in -- #89 -//│ res -- #88 -//│ Zero => -//│ let x$11 = Zero() in -- #99 -//│ let x$12 = S(x$11) in -- #98 -//│ let x$13 = S(x$12) in -- #97 -//│ let* (res) = _b_a_ctx_app(ctx,x$13) in -- #96 -//│ res -- #95 +//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 +//│ r0 -- #155 //│ ) //│ Def(7, a_modcons, [ctx,n$0], //│ 1, -//│ case n$0 of -- #114 -//│ S => -//│ let x$1 = n$0.x in -- #109 -//│ let x$3 = S(0) in -- #108 -//│ let ctx2 = _Context(x$3,x$3,0) in -- #107 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #106 -//│ let* (res) = b_modcons(composed,x$1) in -- #105 -//│ res -- #104 -//│ Zero => -//│ let x$4 = Zero() in -- #113 -//│ let x$5 = S(x$4) in -- #112 -//│ let* (res) = _b_a_ctx_app(ctx,x$5) in -- #111 -//│ res -- #110 +//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 +//│ r0 -- #157 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #153 +//│ if scrut -- #152 +//│ true => +//│ case a_modcons_n$0 of -- #151 +//│ S => +//│ let x$1 = a_modcons_n$0.x in -- #146 +//│ let x$3 = S(0) in -- #145 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 +//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 +//│ Zero => +//│ let x$4 = Zero() in -- #150 +//│ let x$5 = S(x$4) in -- #149 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 +//│ res -- #147 +//│ false => +//│ case b_modcons_n$1 of -- #141 +//│ S => +//│ let x$7 = b_modcons_n$1.x in -- #135 +//│ let x$9 = S(0) in -- #134 +//│ let x$10 = S(x$9) in -- #133 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 +//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 +//│ Zero => +//│ let x$11 = Zero() in -- #140 +//│ let x$12 = S(x$11) in -- #139 +//│ let x$13 = S(x$12) in -- #138 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 +//│ res -- #136 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 //│ ) //│ }, //│ let x$14 = Zero() in -- #74 @@ -1380,22 +1524,26 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 //│ ret -- #63 //│ ) -//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], //│ 1, -//│ case xs$0 of -- #80 +//│ case xs$0 of -- #94 //│ Cons => -//│ let x$1 = xs$0.t in -- #76 -//│ let x$2 = xs$0.h in -- #75 -//│ let x$4 = +(x$2,1) in -- #74 -//│ let x$5 = Cons(x$4,0) in -- #73 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #72 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #71 -//│ let* (res) = addOne_modcons(composed,x$1) in -- #70 -//│ res -- #69 +//│ let x$1 = xs$0.t in -- #90 +//│ let x$2 = xs$0.h in -- #89 +//│ let x$4 = +(x$2,1) in -- #88 +//│ let x$5 = Cons(x$4,0) in -- #87 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 +//│ jump addOne_modcons_jp(composed,x$1) -- #84 //│ Nil => -//│ let x$6 = Nil() in -- #79 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #78 -//│ res -- #77 +//│ let x$6 = Nil() in -- #93 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 +//│ res -- #91 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 +//│ r0 -- #95 //│ ) //│ }, //│ let x$7 = Nil() in -- #55 @@ -1438,22 +1586,26 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 //│ ret -- #63 //│ ) -//│ Def(4, addOne_modcons, [ctx,xs$0], +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], //│ 1, -//│ case xs$0 of -- #80 +//│ case xs$0 of -- #94 //│ Cons => -//│ let x$1 = xs$0.t in -- #76 -//│ let x$2 = xs$0.h in -- #75 -//│ let x$4 = +(x$2,1) in -- #74 -//│ let x$5 = Cons(x$4,0) in -- #73 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #72 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #71 -//│ let* (res) = addOne_modcons(composed,x$1) in -- #70 -//│ res -- #69 +//│ let x$1 = xs$0.t in -- #90 +//│ let x$2 = xs$0.h in -- #89 +//│ let x$4 = +(x$2,1) in -- #88 +//│ let x$5 = Cons(x$4,0) in -- #87 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 +//│ jump addOne_modcons_jp(composed,x$1) -- #84 //│ Nil => -//│ let x$6 = Nil() in -- #79 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #78 -//│ res -- #77 +//│ let x$6 = Nil() in -- #93 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 +//│ res -- #91 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 +//│ r0 -- #95 //│ ) //│ }, //│ let x$7 = Nil() in -- #55 @@ -1466,152 +1618,6 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Interpreted: //│ Cons(2,Cons(3,Cons(4,Nil()))) -:noTailRec -:interpIR -class True -class False -class A(m, n) -fun a(x) = - if x.n < 0 then A(-1, x.n) - else A(2, b(x.n - 2)) -fun b(n) = - if n < 0 then A(0, n) - else a(A(0, n - 1)) -b(20) -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|m|,| |n|)|↵|#fun| |a|(|x|)| |#=|→|#if| |x|.n| |<| |0| |#then| |A|(|-|1|,| |x|.n|)|↵|#else| |A|(|2|,| |b|(|x|.n| |-| |2|)|)|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |<| |0| |#then| |A|(|0|,| |n|)|↵|#else| |a|(|A|(|0|,| |n| |-| |1|)|)|←|↵|b|(|20|)| -//│ Parsed: {class True {}; class False {}; class A(m, n,) {}; fun a = (x,) => {if (<((x).n,)(0,)) then A(-1, (x).n,) else A(2, b(-((x).n,)(2,),),)}; fun b = (n,) => {if (<(n,)(0,)) then A(0, n,) else a(A(0, -(n,)(1,),),)}; b(20,)} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let x$1 = x$0.n in -- #35 -//│ let x$2 = <(x$1,0) in -- #34 -//│ if x$2 -- #33 -//│ true => -//│ let x$4 = x$0.n in -- #14 -//│ let x$5 = A(-1,x$4) in -- #13 -//│ jump j$0(x$5) -- #12 -//│ false => -//│ let x$6 = x$0.n in -- #32 -//│ let x$7 = -(x$6,2) in -- #31 -//│ let* (x$8) = b(x$7) in -- #30 -//│ let x$9 = A(2,x$8) in -- #29 -//│ jump j$0(x$9) -- #28 -//│ ) -//│ Def(1, j$0, [x$3], -//│ 1, -//│ x$3 -- #4 -//│ ) -//│ Def(2, b, [n$0], -//│ 1, -//│ let x$10 = <(n$0,0) in -- #65 -//│ if x$10 -- #64 -//│ true => -//│ let x$12 = A(0,n$0) in -- #47 -//│ jump j$1(x$12) -- #46 -//│ false => -//│ let x$13 = -(n$0,1) in -- #63 -//│ let x$14 = A(0,x$13) in -- #62 -//│ let* (x$15) = a(x$14) in -- #61 -//│ jump j$1(x$15) -- #60 -//│ ) -//│ Def(3, j$1, [x$11], -//│ 1, -//│ x$11 -- #39 -//│ ) -//│ }, -//│ let* (x$16) = b(20) in -- #71 -//│ x$16 -- #70) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let x$1 = x$0.n in -- #35 -//│ let x$2 = <(x$1,0) in -- #34 -//│ if x$2 -- #33 -//│ true => -//│ let x$4 = x$0.n in -- #14 -//│ let x$5 = A(-1,x$4) in -- #13 -//│ jump j$0(x$5) -- #12 -//│ false => -//│ let x$6 = x$0.n in -- #32 -//│ let x$7 = -(x$6,2) in -- #31 -//│ let* (x$8) = b(x$7) in -- #30 -//│ let x$9 = A(2,x$8) in -- #29 -//│ jump j$0(x$9) -- #28 -//│ ) -//│ Def(1, j$0, [x$3], -//│ 1, -//│ x$3 -- #4 -//│ ) -//│ Def(2, b, [n$0], -//│ 1, -//│ let x$10 = <(n$0,0) in -- #65 -//│ if x$10 -- #64 -//│ true => -//│ let x$12 = A(0,n$0) in -- #47 -//│ jump j$1(x$12) -- #46 -//│ false => -//│ let x$13 = -(n$0,1) in -- #63 -//│ let x$14 = A(0,x$13) in -- #62 -//│ let* (x$15) = a(x$14) in -- #61 -//│ jump j$1(x$15) -- #60 -//│ ) -//│ Def(3, j$1, [x$11], -//│ 1, -//│ x$11 -- #39 -//│ ) -//│ }, -//│ let* (x$16) = b(20) in -- #71 -//│ x$16 -- #70) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [m,n])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let x$1 = x$0.n in -- #35 -//│ let x$2 = <(x$1,0) in -- #34 -//│ if x$2 -- #33 -//│ true => -//│ let x$4 = x$0.n in -- #14 -//│ let x$5 = A(-1,x$4) in -- #13 -//│ jump j$0(x$5) -- #12 -//│ false => -//│ let x$6 = x$0.n in -- #32 -//│ let x$7 = -(x$6,2) in -- #31 -//│ let* (x$8) = b(x$7) in -- #30 -//│ let x$9 = A(2,x$8) in -- #29 -//│ jump j$0(x$9) -- #28 -//│ ) -//│ Def(1, j$0, [x$3], -//│ 1, -//│ x$3 -- #4 -//│ ) -//│ Def(2, b, [n$0], -//│ 1, -//│ let x$10 = <(n$0,0) in -- #65 -//│ if x$10 -- #64 -//│ true => -//│ let x$12 = A(0,n$0) in -- #47 -//│ jump j$1(x$12) -- #46 -//│ false => -//│ let x$13 = -(n$0,1) in -- #63 -//│ let x$14 = A(0,x$13) in -- #62 -//│ let* (x$15) = a(x$14) in -- #61 -//│ jump j$1(x$15) -- #60 -//│ ) -//│ Def(3, j$1, [x$11], -//│ 1, -//│ x$11 -- #39 -//│ ) -//│ }, -//│ let* (x$16) = b(20) in -- #71 -//│ x$16 -- #70) -//│ -//│ Interpreted: -//│ A(2,A(2,A(2,A(2,A(2,A(2,A(2,A(0,-1)))))))) - :interpIR class True class False @@ -1739,44 +1745,57 @@ b(16) //│ ) //│ Def(7, b_modcons, [ctx,n$0], //│ 1, -//│ let x$13 = <=(n$0,0) in -- #104 -//│ if x$13 -- #103 -//│ true => -//│ let x$15 = Nil() in -- #98 -//│ let x$16 = Cons(0,x$15) in -- #97 -//│ let* (res) = _b_a_ctx_app(ctx,x$16) in -- #96 -//│ res -- #95 -//│ false => -//│ let x$17 = Nil() in -- #102 -//│ let x$18 = Cons(n$0,x$17) in -- #101 -//│ let* (res) = a_modcons(ctx,x$18) in -- #100 -//│ res -- #99 +//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 +//│ r0 -- #169 //│ ) //│ Def(8, a_modcons, [ctx,x$0], //│ 1, -//│ case x$0 of -- #126 -//│ Cons => -//│ let x$2 = x$0.n in -- #122 -//│ let x$3 = x$0.m in -- #121 -//│ let x$4 = <(x$3,0) in -- #120 -//│ if x$4 -- #119 +//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 +//│ r0 -- #171 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ let scrut = ==(8,tailrecBranch$) in -- #167 +//│ if scrut -- #166 +//│ true => +//│ case a_modcons_x$0 of -- #165 +//│ Cons => +//│ let x$2 = a_modcons_x$0.n in -- #161 +//│ let x$3 = a_modcons_x$0.m in -- #160 +//│ let x$4 = <(x$3,0) in -- #159 +//│ if x$4 -- #158 +//│ true => +//│ let x$6 = Nil() in -- #151 +//│ let x$7 = Cons(-1,x$6) in -- #150 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 +//│ res -- #148 +//│ false => +//│ let x$8 = *(x$3,4) in -- #157 +//│ let x$9 = -(x$3,2) in -- #156 +//│ let x$11 = Cons(x$8,0) in -- #155 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 +//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 +//│ Nil => +//│ let x$12 = Nil() in -- #164 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 +//│ res -- #162 +//│ false => +//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 +//│ if x$13 -- #146 //│ true => -//│ let x$6 = Nil() in -- #111 -//│ let x$7 = Cons(-1,x$6) in -- #110 -//│ let* (res) = _b_a_ctx_app(ctx,x$7) in -- #109 -//│ res -- #108 +//│ let x$15 = Nil() in -- #142 +//│ let x$16 = Cons(0,x$15) in -- #141 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 +//│ res -- #139 //│ false => -//│ let x$8 = *(x$3,4) in -- #118 -//│ let x$9 = -(x$3,2) in -- #117 -//│ let x$11 = Cons(x$8,0) in -- #116 -//│ let ctx2 = _Context(x$11,x$11,0) in -- #115 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #114 -//│ let* (res) = b_modcons(composed,x$9) in -- #113 -//│ res -- #112 -//│ Nil => -//│ let x$12 = Nil() in -- #125 -//│ let* (res) = _b_a_ctx_app(ctx,x$12) in -- #124 -//│ res -- #123 +//│ let x$17 = Nil() in -- #145 +//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 +//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 +//│ ) +//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 //│ ) //│ }, //│ let* (x$20) = b(16) in -- #81 @@ -1831,44 +1850,57 @@ b(16) //│ ) //│ Def(7, b_modcons, [ctx,n$0], //│ 1, -//│ let x$13 = <=(n$0,0) in -- #104 -//│ if x$13 -- #103 -//│ true => -//│ let x$15 = Nil() in -- #98 -//│ let x$16 = Cons(0,x$15) in -- #97 -//│ let* (res) = _b_a_ctx_app(ctx,x$16) in -- #96 -//│ res -- #95 -//│ false => -//│ let x$17 = Nil() in -- #102 -//│ let x$18 = Cons(n$0,x$17) in -- #101 -//│ let* (res) = a_modcons(ctx,x$18) in -- #100 -//│ res -- #99 +//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 +//│ r0 -- #169 //│ ) //│ Def(8, a_modcons, [ctx,x$0], //│ 1, -//│ case x$0 of -- #126 -//│ Cons => -//│ let x$2 = x$0.n in -- #122 -//│ let x$3 = x$0.m in -- #121 -//│ let x$4 = <(x$3,0) in -- #120 -//│ if x$4 -- #119 +//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 +//│ r0 -- #171 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ let scrut = ==(8,tailrecBranch$) in -- #167 +//│ if scrut -- #166 +//│ true => +//│ case a_modcons_x$0 of -- #165 +//│ Cons => +//│ let x$2 = a_modcons_x$0.n in -- #161 +//│ let x$3 = a_modcons_x$0.m in -- #160 +//│ let x$4 = <(x$3,0) in -- #159 +//│ if x$4 -- #158 +//│ true => +//│ let x$6 = Nil() in -- #151 +//│ let x$7 = Cons(-1,x$6) in -- #150 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 +//│ res -- #148 +//│ false => +//│ let x$8 = *(x$3,4) in -- #157 +//│ let x$9 = -(x$3,2) in -- #156 +//│ let x$11 = Cons(x$8,0) in -- #155 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 +//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 +//│ Nil => +//│ let x$12 = Nil() in -- #164 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 +//│ res -- #162 +//│ false => +//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 +//│ if x$13 -- #146 //│ true => -//│ let x$6 = Nil() in -- #111 -//│ let x$7 = Cons(-1,x$6) in -- #110 -//│ let* (res) = _b_a_ctx_app(ctx,x$7) in -- #109 -//│ res -- #108 +//│ let x$15 = Nil() in -- #142 +//│ let x$16 = Cons(0,x$15) in -- #141 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 +//│ res -- #139 //│ false => -//│ let x$8 = *(x$3,4) in -- #118 -//│ let x$9 = -(x$3,2) in -- #117 -//│ let x$11 = Cons(x$8,0) in -- #116 -//│ let ctx2 = _Context(x$11,x$11,0) in -- #115 -//│ let* (composed) = _b_a_ctx_comp(ctx,ctx2) in -- #114 -//│ let* (res) = b_modcons(composed,x$9) in -- #113 -//│ res -- #112 -//│ Nil => -//│ let x$12 = Nil() in -- #125 -//│ let* (res) = _b_a_ctx_app(ctx,x$12) in -- #124 -//│ res -- #123 +//│ let x$17 = Nil() in -- #145 +//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 +//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 +//│ ) +//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 //│ ) //│ }, //│ let* (x$20) = b(16) in -- #81 @@ -1876,3 +1908,216 @@ b(16) //│ //│ Interpreted: //│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then foo(t) + else Cons(0, Cons(h, foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |Cons|(|0|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else Cons(0, Cons(h, foo(t,),),)}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ case xs$0 of -- #44 +//│ Cons => +//│ let x$1 = xs$0.t in -- #40 +//│ let x$2 = xs$0.h in -- #39 +//│ let x$3 = >(x$2,5) in -- #38 +//│ if x$3 -- #37 +//│ true => +//│ let* (x$5) = foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 +//│ false => +//│ let* (x$6) = foo(x$1) in -- #36 +//│ let x$7 = Cons(x$2,x$6) in -- #35 +//│ let x$8 = Cons(0,x$7) in -- #34 +//│ jump j$1(x$8) -- #33 +//│ Nil => +//│ let x$9 = Nil() in -- #43 +//│ jump j$0(x$9) -- #42 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ }, +//│ let x$10 = Nil() in -- #93 +//│ let x$11 = Cons(9,x$10) in -- #92 +//│ let x$12 = Cons(3,x$11) in -- #91 +//│ let x$13 = Cons(2,x$12) in -- #90 +//│ let x$14 = Cons(4,x$13) in -- #89 +//│ let x$15 = Cons(7,x$14) in -- #88 +//│ let x$16 = Cons(6,x$15) in -- #87 +//│ let x$17 = Cons(1,x$16) in -- #86 +//│ let* (x$18) = foo(x$17) in -- #85 +//│ x$18 -- #84) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #125 +//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #124 +//│ res -- #123 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, _foo_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #100 +//│ _IdContext => +//│ x -- #99 +//│ _Context => +//│ let field = ctx.field in -- #98 +//│ let ptr = ctx.ptr in -- #97 +//│ assign ptr.t := x in -- #96 +//│ let acc = ctx.acc in -- #95 +//│ acc -- #94 +//│ ) +//│ Def(4, _foo_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #106 +//│ let ctx2ptr = ctx2.ptr in -- #105 +//│ let ctx2field = ctx2.field in -- #104 +//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #103 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #102 +//│ ret -- #101 +//│ ) +//│ Def(6, foo_modcons_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #139 +//│ Cons => +//│ let x$1 = xs$0.t in -- #135 +//│ let x$2 = xs$0.h in -- #134 +//│ let x$3 = >(x$2,5) in -- #133 +//│ if x$3 -- #132 +//│ true => +//│ jump foo_modcons_jp(ctx,x$1) -- #126 +//│ false => +//│ let x$7 = Cons(x$2,0) in -- #131 +//│ let x$8 = Cons(0,x$7) in -- #130 +//│ let ctx2 = _Context(x$8,x$7,0) in -- #129 +//│ let* (composed) = _foo_ctx_comp(ctx,ctx2) in -- #128 +//│ jump foo_modcons_jp(composed,x$1) -- #127 +//│ Nil => +//│ let x$9 = Nil() in -- #138 +//│ let* (res) = _foo_ctx_app(ctx,x$9) in -- #137 +//│ res -- #136 +//│ ) +//│ Def(7, foo_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = foo_modcons_jp(ctx,xs$0) in -- #141 +//│ r0 -- #140 +//│ ) +//│ }, +//│ let x$10 = Nil() in -- #93 +//│ let x$11 = Cons(9,x$10) in -- #92 +//│ let x$12 = Cons(3,x$11) in -- #91 +//│ let x$13 = Cons(2,x$12) in -- #90 +//│ let x$14 = Cons(4,x$13) in -- #89 +//│ let x$15 = Cons(7,x$14) in -- #88 +//│ let x$16 = Cons(6,x$15) in -- #87 +//│ let x$17 = Cons(1,x$16) in -- #86 +//│ let* (x$18) = foo(x$17) in -- #85 +//│ x$18 -- #84) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #125 +//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #124 +//│ res -- #123 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, _foo_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #100 +//│ _IdContext => +//│ x -- #99 +//│ _Context => +//│ let field = ctx.field in -- #98 +//│ let ptr = ctx.ptr in -- #97 +//│ assign ptr.t := x in -- #96 +//│ let acc = ctx.acc in -- #95 +//│ acc -- #94 +//│ ) +//│ Def(4, _foo_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #106 +//│ let ctx2ptr = ctx2.ptr in -- #105 +//│ let ctx2field = ctx2.field in -- #104 +//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #103 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #102 +//│ ret -- #101 +//│ ) +//│ Def(6, foo_modcons_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #139 +//│ Cons => +//│ let x$1 = xs$0.t in -- #135 +//│ let x$2 = xs$0.h in -- #134 +//│ let x$3 = >(x$2,5) in -- #133 +//│ if x$3 -- #132 +//│ true => +//│ jump foo_modcons_jp(ctx,x$1) -- #126 +//│ false => +//│ let x$7 = Cons(x$2,0) in -- #131 +//│ let x$8 = Cons(0,x$7) in -- #130 +//│ let ctx2 = _Context(x$8,x$7,0) in -- #129 +//│ let* (composed) = _foo_ctx_comp(ctx,ctx2) in -- #128 +//│ jump foo_modcons_jp(composed,x$1) -- #127 +//│ Nil => +//│ let x$9 = Nil() in -- #138 +//│ let* (res) = _foo_ctx_app(ctx,x$9) in -- #137 +//│ res -- #136 +//│ ) +//│ Def(7, foo_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = foo_modcons_jp(ctx,xs$0) in -- #141 +//│ r0 -- #140 +//│ ) +//│ }, +//│ let x$10 = Nil() in -- #93 +//│ let x$11 = Cons(9,x$10) in -- #92 +//│ let x$12 = Cons(3,x$11) in -- #91 +//│ let x$13 = Cons(2,x$12) in -- #90 +//│ let x$14 = Cons(4,x$13) in -- #89 +//│ let x$15 = Cons(7,x$14) in -- #88 +//│ let x$16 = Cons(6,x$15) in -- #87 +//│ let x$17 = Cons(1,x$16) in -- #86 +//│ let* (x$18) = foo(x$17) in -- #85 +//│ x$18 -- #84) +//│ +//│ Interpreted: +//│ Cons(0,Cons(1,Cons(0,Cons(4,Cons(0,Cons(2,Cons(0,Cons(3,Nil())))))))) From 715ef73b11baeeafa3e1da99c3108b212a06f939 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 1 May 2024 22:24:57 +0800 Subject: [PATCH 32/59] Update tests --- compiler/shared/test/diff-ir/IRTailRec.mls | 481 +++++++++++++++------ 1 file changed, 354 insertions(+), 127 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 0307b7f027..6da446cc74 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -795,7 +795,7 @@ hello() //│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) //│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:340) //│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:884) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:896) +//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:894) //│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) //│ mlscript.DiffTests.rec$1(DiffTests.scala:470) //│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) @@ -1909,6 +1909,7 @@ b(16) //│ Interpreted: //│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) +:noTailRec :interpIR class True class False @@ -1918,35 +1919,39 @@ fun foo(xs) = if xs is Cons(h, t) then if h > 5 then foo(t) - else Cons(0, Cons(h, foo(t))) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, foo(t))) Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |Cons|(|0|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else Cons(0, Cons(h, foo(t,),),)}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], //│ 1, -//│ case xs$0 of -- #44 +//│ case xs$0 of -- #54 //│ Cons => -//│ let x$1 = xs$0.t in -- #40 -//│ let x$2 = xs$0.h in -- #39 -//│ let x$3 = >(x$2,5) in -- #38 -//│ if x$3 -- #37 +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 //│ true => //│ let* (x$5) = foo(x$1) in -- #17 //│ jump j$1(x$5) -- #16 //│ false => -//│ let* (x$6) = foo(x$1) in -- #36 -//│ let x$7 = Cons(x$2,x$6) in -- #35 -//│ let x$8 = Cons(0,x$7) in -- #34 -//│ jump j$1(x$8) -- #33 +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 //│ Nil => -//│ let x$9 = Nil() in -- #43 -//│ jump j$0(x$9) -- #42 +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -1956,26 +1961,46 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) +//│ Def(3, j$2, [x$7,x$1,x$2], +//│ 1, +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) //│ }, -//│ let x$10 = Nil() in -- #93 -//│ let x$11 = Cons(9,x$10) in -- #92 -//│ let x$12 = Cons(3,x$11) in -- #91 -//│ let x$13 = Cons(2,x$12) in -- #90 -//│ let x$14 = Cons(4,x$13) in -- #89 -//│ let x$15 = Cons(7,x$14) in -- #88 -//│ let x$16 = Cons(6,x$15) in -- #87 -//│ let x$17 = Cons(1,x$16) in -- #86 -//│ let* (x$18) = foo(x$17) in -- #85 -//│ x$18 -- #84) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], //│ 1, -//│ let idCtx = _IdContext() in -- #125 -//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #124 -//│ res -- #123 +//│ case xs$0 of -- #54 +//│ Cons => +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 +//│ true => +//│ let* (x$5) = foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 +//│ false => +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 +//│ Nil => +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -1985,72 +2010,253 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(3, _foo_ctx_app, [ctx,x], +//│ Def(3, j$2, [x$7,x$1,x$2], //│ 1, -//│ case ctx of -- #100 -//│ _IdContext => -//│ x -- #99 -//│ _Context => -//│ let field = ctx.field in -- #98 -//│ let ptr = ctx.ptr in -- #97 -//│ assign ptr.t := x in -- #96 -//│ let acc = ctx.acc in -- #95 -//│ acc -- #94 +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ case xs$0 of -- #54 +//│ Cons => +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 +//│ true => +//│ let* (x$5) = foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 +//│ false => +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 +//│ Nil => +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 //│ ) -//│ Def(4, _foo_ctx_comp, [ctx1,ctx2], +//│ Def(3, j$2, [x$7,x$1,x$2], //│ 1, -//│ let ctx2acc = ctx2.acc in -- #106 -//│ let ctx2ptr = ctx2.ptr in -- #105 -//│ let ctx2field = ctx2.field in -- #104 -//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #103 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #102 -//│ ret -- #101 +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 //│ ) -//│ Def(6, foo_modcons_jp, [ctx,xs$0], +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Interpreted: +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then foo(t) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, foo, [xs$0], //│ 1, -//│ case xs$0 of -- #139 +//│ case xs$0 of -- #54 //│ Cons => -//│ let x$1 = xs$0.t in -- #135 -//│ let x$2 = xs$0.h in -- #134 -//│ let x$3 = >(x$2,5) in -- #133 -//│ if x$3 -- #132 +//│ let x$1 = xs$0.t in -- #50 +//│ let x$2 = xs$0.h in -- #49 +//│ let x$3 = >(x$2,5) in -- #48 +//│ if x$3 -- #47 //│ true => -//│ jump foo_modcons_jp(ctx,x$1) -- #126 +//│ let* (x$5) = foo(x$1) in -- #17 +//│ jump j$1(x$5) -- #16 //│ false => -//│ let x$7 = Cons(x$2,0) in -- #131 -//│ let x$8 = Cons(0,x$7) in -- #130 -//│ let ctx2 = _Context(x$8,x$7,0) in -- #129 -//│ let* (composed) = _foo_ctx_comp(ctx,ctx2) in -- #128 -//│ jump foo_modcons_jp(composed,x$1) -- #127 +//│ let x$6 = <(x$2,3) in -- #46 +//│ if x$6 -- #45 +//│ true => +//│ jump j$2(-1,x$1,x$2) -- #42 +//│ false => +//│ jump j$2(100,x$1,x$2) -- #44 //│ Nil => -//│ let x$9 = Nil() in -- #138 -//│ let* (res) = _foo_ctx_app(ctx,x$9) in -- #137 -//│ res -- #136 +//│ let x$11 = Nil() in -- #53 +//│ jump j$0(x$11) -- #52 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, j$2, [x$7,x$1,x$2], +//│ 1, +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$2), Set(j$1), Set(j$0), Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #137 +//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 +//│ res -- #135 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, j$1, [x$4], +//│ 1, +//│ jump j$0(x$4) -- #10 +//│ ) +//│ Def(3, j$2, [x$7,x$1,x$2], +//│ 1, +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ Def(4, _foo_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #110 +//│ _IdContext => +//│ x -- #109 +//│ _Context => +//│ let field = ctx.field in -- #108 +//│ let ptr = ctx.ptr in -- #107 +//│ assign ptr.t := x in -- #106 +//│ let acc = ctx.acc in -- #105 +//│ acc -- #104 +//│ ) +//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #116 +//│ let ctx2ptr = ctx2.ptr in -- #115 +//│ let ctx2field = ctx2.field in -- #114 +//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 +//│ ret -- #111 //│ ) //│ Def(7, foo_modcons, [ctx,xs$0], //│ 1, -//│ let* (r0) = foo_modcons_jp(ctx,xs$0) in -- #141 -//│ r0 -- #140 +//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ r0 -- #172 +//│ ) +//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #170 +//│ if scrut -- #169 +//│ true => +//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 +//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 +//│ false => +//│ case foo_modcons_xs$0 of -- #163 +//│ Cons => +//│ let x$1 = foo_modcons_xs$0.t in -- #159 +//│ let x$2 = foo_modcons_xs$0.h in -- #158 +//│ let x$3 = >(x$2,5) in -- #157 +//│ if x$3 -- #156 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 +//│ false => +//│ let x$6 = <(x$2,3) in -- #155 +//│ if x$6 -- #154 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 +//│ false => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 +//│ Nil => +//│ let x$11 = Nil() in -- #162 +//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 +//│ res -- #160 +//│ ) +//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ 1, +//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 //│ ) //│ }, -//│ let x$10 = Nil() in -- #93 -//│ let x$11 = Cons(9,x$10) in -- #92 -//│ let x$12 = Cons(3,x$11) in -- #91 -//│ let x$13 = Cons(2,x$12) in -- #90 -//│ let x$14 = Cons(4,x$13) in -- #89 -//│ let x$15 = Cons(7,x$14) in -- #88 -//│ let x$16 = Cons(6,x$15) in -- #87 -//│ let x$17 = Cons(1,x$16) in -- #86 -//│ let* (x$18) = foo(x$17) in -- #85 -//│ x$18 -- #84) +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, -//│ let idCtx = _IdContext() in -- #125 -//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #124 -//│ res -- #123 +//│ let idCtx = _IdContext() in -- #137 +//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 +//│ res -- #135 //│ ) //│ Def(1, j$0, [x$0], //│ 1, @@ -2060,64 +2266,85 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(3, _foo_ctx_app, [ctx,x], +//│ Def(3, j$2, [x$7,x$1,x$2], //│ 1, -//│ case ctx of -- #100 +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 +//│ ) +//│ Def(4, _foo_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #110 //│ _IdContext => -//│ x -- #99 +//│ x -- #109 //│ _Context => -//│ let field = ctx.field in -- #98 -//│ let ptr = ctx.ptr in -- #97 -//│ assign ptr.t := x in -- #96 -//│ let acc = ctx.acc in -- #95 -//│ acc -- #94 +//│ let field = ctx.field in -- #108 +//│ let ptr = ctx.ptr in -- #107 +//│ assign ptr.t := x in -- #106 +//│ let acc = ctx.acc in -- #105 +//│ acc -- #104 //│ ) -//│ Def(4, _foo_ctx_comp, [ctx1,ctx2], +//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let ctx2acc = ctx2.acc in -- #106 -//│ let ctx2ptr = ctx2.ptr in -- #105 -//│ let ctx2field = ctx2.field in -- #104 -//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #103 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #102 -//│ ret -- #101 +//│ let ctx2acc = ctx2.acc in -- #116 +//│ let ctx2ptr = ctx2.ptr in -- #115 +//│ let ctx2field = ctx2.field in -- #114 +//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 +//│ ret -- #111 //│ ) -//│ Def(6, foo_modcons_jp, [ctx,xs$0], +//│ Def(7, foo_modcons, [ctx,xs$0], //│ 1, -//│ case xs$0 of -- #139 -//│ Cons => -//│ let x$1 = xs$0.t in -- #135 -//│ let x$2 = xs$0.h in -- #134 -//│ let x$3 = >(x$2,5) in -- #133 -//│ if x$3 -- #132 -//│ true => -//│ jump foo_modcons_jp(ctx,x$1) -- #126 -//│ false => -//│ let x$7 = Cons(x$2,0) in -- #131 -//│ let x$8 = Cons(0,x$7) in -- #130 -//│ let ctx2 = _Context(x$8,x$7,0) in -- #129 -//│ let* (composed) = _foo_ctx_comp(ctx,ctx2) in -- #128 -//│ jump foo_modcons_jp(composed,x$1) -- #127 -//│ Nil => -//│ let x$9 = Nil() in -- #138 -//│ let* (res) = _foo_ctx_app(ctx,x$9) in -- #137 -//│ res -- #136 +//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ r0 -- #172 //│ ) -//│ Def(7, foo_modcons, [ctx,xs$0], +//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #170 +//│ if scrut -- #169 +//│ true => +//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 +//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 +//│ false => +//│ case foo_modcons_xs$0 of -- #163 +//│ Cons => +//│ let x$1 = foo_modcons_xs$0.t in -- #159 +//│ let x$2 = foo_modcons_xs$0.h in -- #158 +//│ let x$3 = >(x$2,5) in -- #157 +//│ if x$3 -- #156 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 +//│ false => +//│ let x$6 = <(x$2,3) in -- #155 +//│ if x$6 -- #154 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 +//│ false => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 +//│ Nil => +//│ let x$11 = Nil() in -- #162 +//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 +//│ res -- #160 +//│ ) +//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], //│ 1, -//│ let* (r0) = foo_modcons_jp(ctx,xs$0) in -- #141 -//│ r0 -- #140 +//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 //│ ) //│ }, -//│ let x$10 = Nil() in -- #93 -//│ let x$11 = Cons(9,x$10) in -- #92 -//│ let x$12 = Cons(3,x$11) in -- #91 -//│ let x$13 = Cons(2,x$12) in -- #90 -//│ let x$14 = Cons(4,x$13) in -- #89 -//│ let x$15 = Cons(7,x$14) in -- #88 -//│ let x$16 = Cons(6,x$15) in -- #87 -//│ let x$17 = Cons(1,x$16) in -- #86 -//│ let* (x$18) = foo(x$17) in -- #85 -//│ x$18 -- #84) +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) //│ //│ Interpreted: -//│ Cons(0,Cons(1,Cons(0,Cons(4,Cons(0,Cons(2,Cons(0,Cons(3,Nil())))))))) +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) From d8d05be182f0e74e4d697e356392ebffd645b5fb Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 12 May 2024 17:32:27 +0800 Subject: [PATCH 33/59] fix some tests --- compiler/shared/test/diff-ir/IRTailRec.mls | 71 +------------------ .../test/scala/mlscript/compiler/TestIR.scala | 6 +- shared/src/test/diff/nu/Annotations.mls | 34 +++++++++ 3 files changed, 40 insertions(+), 71 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 6da446cc74..af8ba52122 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -766,8 +766,8 @@ class False fun hello() = @tailrec hello() 2 -hello() -//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailrec| |hello|(||)|↵|2|←|↵|hello|(||)| +hello() +//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailrec| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {class True {}; class False {}; fun hello = () => {@tailrec hello(); 2}; hello()} //│ //│ @@ -783,73 +783,6 @@ hello() //│ x$1 -- #7) //│ //│ IR Processing Failed: not a tail call -//│ -//│ mlscript.compiler.ir.IRError$.apply(IR.scala:14) -//│ mlscript.compiler.optimizer.TailRecOpt.returnFailure$1(TailRecOpt.scala:98) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverOptimizableCalls(TailRecOpt.scala:108) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCallsCont(TailRecOpt.scala:312) -//│ mlscript.compiler.optimizer.TailRecOpt.discoverCalls(TailRecOpt.scala:317) -//│ mlscript.compiler.optimizer.TailRecOpt.$anonfun$3(TailRecOpt.scala:340) -//│ scala.collection.IterableOnceOps.foldLeft(IterableOnce.scala:646) -//│ scala.collection.IterableOnceOps.foldLeft$(IterableOnce.scala:642) -//│ scala.collection.AbstractIterable.foldLeft(Iterable.scala:926) -//│ mlscript.compiler.optimizer.TailRecOpt.partitionNodes(TailRecOpt.scala:340) -//│ mlscript.compiler.optimizer.TailRecOpt.partition(TailRecOpt.scala:884) -//│ mlscript.compiler.optimizer.TailRecOpt.run_debug(TailRecOpt.scala:894) -//│ mlscript.compiler.IRDiffTestCompiler.postProcess(TestIR.scala:29) -//│ mlscript.DiffTests.rec$1(DiffTests.scala:470) -//│ mlscript.DiffTests.$anonfun$new$3(DiffTests.scala:1076) -//│ org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85) -//│ org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83) -//│ org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104) -//│ org.scalatest.Transformer.apply(Transformer.scala:22) -//│ org.scalatest.Transformer.apply(Transformer.scala:20) -//│ org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:226) -//│ org.scalatest.TestSuite.withFixture(TestSuite.scala:196) -//│ org.scalatest.TestSuite.withFixture$(TestSuite.scala:195) -//│ mlscript.DiffTests.org$scalatest$concurrent$TimeLimitedTests$$super$withFixture(DiffTests.scala:53) -//│ org.scalatest.concurrent.TimeLimitedTests.$anonfun$withFixture$3(TimeLimitedTests.scala:154) -//│ org.scalatest.enablers.Timed$$anon$1.timeoutAfter(Timed.scala:127) -//│ org.scalatest.concurrent.TimeLimits$.failAfterImpl(TimeLimits.scala:282) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture(TimeLimitedTests.scala:153) -//│ org.scalatest.concurrent.TimeLimitedTests.withFixture$(TimeLimitedTests.scala:150) -//│ mlscript.DiffTests.withFixture(DiffTests.scala:53) -//│ org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:224) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$runTest$1(AnyFunSuiteLike.scala:236) -//│ org.scalatest.SuperEngine.runTestImpl(Engine.scala:306) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:236) -//│ org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:218) -//│ mlscript.DiffTests.org$scalatest$OneInstancePerTest$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTest(OneInstancePerTest.scala:131) -//│ org.scalatest.OneInstancePerTest.runTest$(OneInstancePerTest.scala:123) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTest(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTest(ParallelTestExecution.scala:164) -//│ org.scalatest.ParallelTestExecution.runTest$(ParallelTestExecution.scala:148) -//│ mlscript.DiffTests.runTest(DiffTests.scala:53) -//│ org.scalatest.OneInstancePerTest.runTests(OneInstancePerTest.scala:181) -//│ org.scalatest.OneInstancePerTest.runTests$(OneInstancePerTest.scala:172) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$runTests(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.runTests(ParallelTestExecution.scala:97) -//│ org.scalatest.ParallelTestExecution.runTests$(ParallelTestExecution.scala:79) -//│ mlscript.DiffTests.runTests(DiffTests.scala:53) -//│ org.scalatest.Suite.run(Suite.scala:1114) -//│ org.scalatest.Suite.run$(Suite.scala:1096) -//│ org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563) -//│ org.scalatest.funsuite.AnyFunSuiteLike.$anonfun$run$1(AnyFunSuiteLike.scala:273) -//│ org.scalatest.SuperEngine.runImpl(Engine.scala:535) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:273) -//│ org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:272) -//│ mlscript.DiffTests.org$scalatest$ParallelTestExecution$$super$run(DiffTests.scala:53) -//│ org.scalatest.ParallelTestExecution.run(ParallelTestExecution.scala:261) -//│ org.scalatest.ParallelTestExecution.run$(ParallelTestExecution.scala:258) -//│ mlscript.DiffTests.run(DiffTests.scala:53) -//│ org.scalatest.tools.DistributedTestRunnerSuite.run(DistributedTestRunnerSuite.scala:22) -//│ org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47) -//│ java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) -//│ java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) -//│ java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) -//│ java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) -//│ java.base/java.lang.Thread.run(Thread.java:840) :interpIR class True diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index c06bd63a8e..bb77e9bed7 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -46,10 +46,12 @@ class IRDiffTestCompiler extends DiffTests { catch case err: Exception => output(s"\nIR Processing Failed: ${err.getMessage()}") - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + if (mode.irVerbose) then + output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) case err: StackOverflowError => output(s"\nIR Processing Failed: ${err.getMessage()}") - output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) + if (mode.irVerbose) then + output("\n" ++ err.getStackTrace().map(_.toString()).mkString("\n")) (outputBuilder.toString().linesIterator.toList, None) diff --git a/shared/src/test/diff/nu/Annotations.mls b/shared/src/test/diff/nu/Annotations.mls index be99f9cf45..e85bb285d3 100644 --- a/shared/src/test/diff/nu/Annotations.mls +++ b/shared/src/test/diff/nu/Annotations.mls @@ -40,6 +40,40 @@ Foo //│ res //│ = 5 +:e +let x = 1 +@x 2 +//│ ╔══[ERROR] Type mismatch in annotated integer literal: +//│ ║ l.45: @x 2 +//│ ║ ^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Annotation` +//│ ║ l.44: let x = 1 +//│ ║ ^ +//│ ╟── but it flows into reference with expected type `Annotation` +//│ ║ l.45: @x 2 +//│ ╙── ^ +//│ let x: 1 +//│ 2 +//│ x +//│ = 1 +//│ res +//│ = 2 + +:e +let x = 1 +@x +fun foo(x) = 1 +//│ ╔══[ERROR] Type mismatch in definition: +//│ ║ l.65: fun foo(x) = 1 +//│ ║ ^^^^^^^^^^ +//│ ╟── integer literal of type `1` is not an instance of type `Annotation` +//│ ║ l.63: let x = 1 +//│ ╙── ^ +//│ let x: 1 +//│ fun foo: anything -> 1 +//│ x +//│ = 1 + fun foo(n) = if n > 0.5 then log(join of "hi ", String(n)) From 5887014d81d422e9c6cec81ceae3d9127340e651 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 12 May 2024 20:08:38 +0800 Subject: [PATCH 34/59] Propagate tailrec, fix join point infinite recursion bug --- .../scala/mlscript/compiler/ir/Builder.scala | 11 ++- .../main/scala/mlscript/compiler/ir/IR.scala | 3 +- .../compiler/optimizer/TailRecOpt.scala | 89 ++++++++----------- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index d64a32035d..7537c8135a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -156,7 +156,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres jp.str, params = res :: fvs.map(x => Name(x)), resultNum = 1, - jpbody + jpbody, + false ) ctx.jpAcc.addOne(jpdef) val tru2 = buildResultFromTerm(tru) { @@ -189,6 +190,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, + false ) ctx.jpAcc.addOne(jpdef) val cases: Ls[(ClassInfo, Node)] = lines map { @@ -243,7 +245,9 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres res private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match - case NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) => + case nfd: NuFunDef => + val NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) = nfd + val strs = fields map { case N -> Fld(FldFlags.empty, Var(x)) => x case _ => throw IRError("unsupported field") @@ -255,7 +259,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres name, params = names, resultNum = 1, - buildResultFromTerm(body) { x => x } + buildResultFromTerm(body) { x => x }, + nfd.annotations.find { case Var("annotation") => true; case _ => false }.isDefined ) case _ => throw IRError("unsupported NuFunDef") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 34a3ed7695..a106d46287 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -67,7 +67,8 @@ case class Defn( val name: Str, val params: Ls[Name], val resultNum: Int, - val body: Node + val body: Node, + val isTailRec: Bool ): override def hashCode: Int = id def getName: String = name diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index d3857c9f68..1a9d1f3880 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -94,7 +94,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): def updateMapSimple(c: CallInfo) = updateMap(acc, Set(c)) - def returnFailure = letCallNode match + def returnNone = letCallNode match case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") case _ => Right(Nil) @@ -103,38 +103,18 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): (calledDefn, letCallNode, letCtorNode) match case (Some(defn), Some(letCallNode), Some(letCtorName)) => getRetName(containingCtors, res) match - case None => returnFailure + case None => returnNone case Some(value) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) - case _ => returnFailure + case _ => returnNone case Jump(jp, args) => - def mergeCalls(acc: Map[Int, Set[CallInfo]], calls: Set[CallInfo]) = - val newCalls = calls.map { - case TailCallInfo(_, defn) => - TailCallInfo(src, defn) - case ModConsCallInfo(_, startNode, defn, letCallNode, letCtorNode, retName, retNode) => - ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, retName, retNode) - } - updateMap(acc, newCalls) - - def updateAndMergeJpCalls = letCallNode match - case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") - case _ => - val jpDefn = jp.expectDefn - acc.get(jpDefn.id) match - case None => // explore the join point - val newAcc = discoverCalls(jpDefn.body)(jpDefn, acc) - mergeCalls(newAcc, newAcc(jpDefn.id)) - case Some(value) => mergeCalls(acc, value) - // different cases (calledDefn, letCallNode, letCtorNode) match case (Some(defn), Some(letCallNode), Some(letCtorName)) => getRetName(containingCtors, args) match case Some(value) if isIdentityJp(jp.expectDefn) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) - case _ => - Left(updateAndMergeJpCalls) - case _ => Left(updateAndMergeJpCalls) + case _ => returnNone + case _ => returnNone case Case(scrut, cases) => Right(cases.map(_._2)) case x: LetExpr => @@ -311,13 +291,17 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def discoverCallsCont(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = discoverOptimizableCalls(node)(acc, src, node, None, None, None, Set()) match case Left(acc) => acc - case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverCalls(node)(src, acc)) + case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverCallsNode(node)(src, acc)) - private def discoverCalls(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = + private def discoverCallsNode(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = val ret = discoverCallsCont(node) ret.get(src.id) match case None => ret + (src.id -> Set()) case Some(value) => ret + + private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = + val combined = jps + defn + combined.foldLeft(acc)((acc, defn_) => discoverCallsNode(defn_.body)(defn, acc)) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -337,7 +321,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = val defns = nodeMap.values.toSet val inital = Map[Int, Set[CallInfo]]() - val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn.body)(defn.defn, acc)).withDefaultValue(Set()) + val joinPoints = defns.map(d => (d.defn.id -> discoverJoinPoints(d.defn.body, Set()))).toMap + val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn, joinPoints(defn.defn.id))(acc)).withDefaultValue(Set()) var ctr = 0 // nodes, edges @@ -391,8 +376,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val sccIds = scc.map { d => d.defn.id } sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} - val joinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => discoverJoinPoints(defn.defn.body, jps)) - sccs = DefnGraph(scc, sccEdges, joinPoints) :: sccs + val sccJoinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => joinPoints(defn.defn.id)) + sccs = DefnGraph(scc, sccEdges, sccJoinPoints) :: sccs for v <- defns do if (!v.visited) @@ -530,7 +515,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): ) ).attachTag(tag) - val appDefn = Defn(fnUid.make, ctxAppName, List(appCtxName, appValName), 1, appNode) + val appDefn = Defn(fnUid.make, ctxAppName, List(appCtxName, appValName), 1, appNode, false) // CONTEXT COMPOSITION val cmpCtx1Name = Name("ctx1") @@ -571,7 +556,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): ).attachTag(tag) ).attachTag(tag) - val cmpDefn = Defn(fnUid.make, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode) + val cmpDefn = Defn(fnUid.make, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode, false) // We use tags to identify nodes // a bit hacky but it's the most elegant way @@ -673,12 +658,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): AssignField(assignee, clsInfo, fieldName, value, transformModConsBranch(body)).attachTag(tag) case _ => throw IRError("unreachable case when transforming mod cons call") - // will create two definitions: one has the same signature as the original function, - // while the other one will have extra parameters to support mod cons - // replaceOriginal should be false if and only if the definition is a join point def rewriteDefn(d: Defn): Defn = val transformed = transformNode(d.body) - Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed) + Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed, d.isTailRec) // returns (new defn, mod cons defn) // where new defn has the same signature and ids as the original, but immediately calls the mod cons defn @@ -697,7 +679,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Result(List(Expr.Ref(Name("res")))).attachTag(tag) ).attachTag(tag) ).attachTag(tag) - val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall) + val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall, false) (newDefn, modConsDefn) val jpsTransformed = component.joinPoints.map(d => d.id -> rewriteDefn(d)).toMap @@ -726,7 +708,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a set containing the optimized function and the // original functions pointing to an optimized function. - private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + // Explicitly returns the merged function in case tailrec needs to be checked. + private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = // println(component.edges) // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get @@ -739,7 +722,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // dummy case, should not happen if (defns.size == 0) - return defns + throw IRError("strongly connected component was empty") // for single tail recursive functions, just move the body into a join point if (defns.size <= 1) @@ -747,7 +730,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // if the function does not even tail call itself, just return if edges.size == 0 then - return defns + return (defns, defns.head) val jpName = defn.name + "_jp" val jpDefnRef = DefnRef(Right(jpName)) @@ -765,7 +748,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) - val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body)) + val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body), false) val rets = (0 until defn.resultNum).map(n => Name("r" + n.toString)).toList val callJpNode = LetCall( @@ -776,8 +759,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Result(rets.map(Expr.Ref(_))).attachTag(tag), ).attachTag(tag) - val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode) - Set(newDefn, jpDef) + val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode, true) + (Set(newDefn, jpDef), newDefn) else // Note that we do not use the actual edges in ScCompoennt here. @@ -849,7 +832,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val namesExpr = names.map(Expr.Ref(_)) val res = Result(namesExpr).attachTag(tag) val call = LetCall(names, newDefnRef, args, false, res).attachTag(tag) - Defn(defn.id, defn.name, defn.params, defn.resultNum, call) + Defn(defn.id, defn.name, defn.params, defn.resultNum, call, false) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match case None => key @@ -869,31 +852,35 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val newNode = makeSwitch(trName, valsAndNodes.tail, valsAndNodes.head._2)(trueClass, falseClass) - val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode) + val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode, false) val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) - val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp) + val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp, defnsNoJp.find { _.isTailRec }.isDefined ) jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) - defnsNoJp.map { d => transformDefn(d) } + newDefn + jpDefn + (defnsNoJp.map { d => transformDefn(d) } + newDefn + jpDefn, newDefn) private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(_.removeMetadata) - private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = + private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = val (modConsComp, other) = optimizeModCons(component, classes) - val trOpt = optimizeTailRec(modConsComp, classes) - other ++ trOpt + val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) + (other ++ trOpt, mergedDefn) def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = val partitions = partition(p.defs) - val newDefs: Set[Defn] = partitions.flatMap { optimizeParition(_, p.classes) }.toSet + val optimized = partitions.map { optimizeParition(_, p.classes) } + val newDefs: Set[Defn] = optimized.flatMap { _._1 }.toSet + val mergedDefs: List[Defn] = optimized.map { _._2 } + + // Build the call graph of the program // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } From 78da9103c72839b7f1363ea76fd68f94044f512d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 12 May 2024 22:54:57 +0800 Subject: [PATCH 35/59] refactor and check @tailrec for function definitions --- .../scala/mlscript/compiler/ir/Builder.scala | 2 +- .../compiler/optimizer/TailRecOpt.scala | 189 ++++--- compiler/shared/test/diff-ir/IRTailRec.mls | 469 +++++++++++++++++- 3 files changed, 573 insertions(+), 87 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 7537c8135a..a0b6ffe499 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -260,7 +260,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres params = names, resultNum = 1, buildResultFromTerm(body) { x => x }, - nfd.annotations.find { case Var("annotation") => true; case _ => false }.isDefined + nfd.annotations.find { case Var("tailrec") => true; case _ => false }.isDefined ) case _ => throw IRError("unsupported NuFunDef") diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 1a9d1f3880..83b4e91863 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -11,20 +11,25 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) enum CallInfo: + case NormalCallInfo(src: Defn, defn: Defn) extends CallInfo case TailCallInfo(src: Defn, defn: Defn) extends CallInfo case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo override def toString(): String = this match + case NormalCallInfo(src, defn) => + f"Call { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case TailCallInfo(src, defn) => f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.ident}, field: ${letCtorNode.fieldName} }" def getSrc = this match + case NormalCallInfo(src, _) => src case TailCallInfo(src, _) => src case ModConsCallInfo(src, _, _, _, _, _, _) => src def getDefn = this match + case NormalCallInfo(_, defn) => defn case TailCallInfo(_, defn) => defn case ModConsCallInfo(_, _, defn, _, _, _, _) => defn @@ -35,6 +40,12 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): import CallInfo._ + def filterOptCalls(calls: Iterable[CallInfo]) = + calls.collect { case c: TailCallInfo => c; case c: ModConsCallInfo => c } + + def filterNormalCalls(calls: Iterable[CallInfo]) = + calls.collect { case c: NormalCallInfo => c } + // Hack to make scala think discoverJoinPoints is tail recursive and be // partially optimized :P def casesToJps(cases: List[(ClassInfo, Node)], acc: Set[Defn]): Set[Defn] = @@ -66,37 +77,57 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if names.contains(nme) then Some(nme) else None - // would prefer to have this inside discoverOptimizableCalls, but this makes scala think it's not tail recursive + // would prefer to have this inside discoverOptCalls, but scala does not support partially tail recursive functions directly def shadowAndCont(next: Node, nme: Name)(implicit - acc: Map[Int, Set[CallInfo]], + acc: Set[CallInfo], + src: Defn, + scc: Set[Defn], + start: Node, + calledDefn: Option[Defn], + letCallNode: Option[LetCall], + letCtorNode: Option[LetCtorNodeInfo], + containingCtors: Set[Name] + ) = searchOptCalls(next)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors - nme) + + // same here... + def invalidateAndCont(body: Node)(implicit + acc: Set[CallInfo], src: Defn, + scc: Set[Defn], start: Node, calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], containingCtors: Set[Name] - ) = discoverOptimizableCalls(next)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors - nme) + ) = + letCallNode match + case None => searchOptCalls(body)(acc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered + case Some(LetCall(_, defn, _, isTailRec, _)) => + if isTailRec then + throw IRError("not a tail call") + else + val newAcc = acc + NormalCallInfo(src, defn.expectDefn) + searchOptCalls(body)(newAcc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered @tailrec - private def discoverOptimizableCalls(node: Node)(implicit - acc: Map[Int, Set[CallInfo]], + private def searchOptCalls(node: Node)(implicit + acc: Set[CallInfo], src: Defn, + scc: Set[Defn], start: Node, calledDefn: Option[Defn], // The definition that was called in a tailrec mod cons call letCallNode: Option[LetCall], // The place where that definition was called letCtorNode: Option[LetCtorNodeInfo], // The place where the result from that call was put into a constructor containingCtors: Set[Name], // Value names of ctors containing the constructor containing the result from the call - ): Either[Map[Int, Set[CallInfo]], List[Node]] = - def updateMap(acc: Map[Int, Set[CallInfo]], c: Set[CallInfo]) = - acc.get(src.id) match - case None => acc + (src.id -> c) - case Some(value) => acc + (src.id -> (value ++ c)) - - def updateMapSimple(c: CallInfo) = updateMap(acc, Set(c)) + ): Either[Set[CallInfo], List[Node]] = + + def updateMapSimple(c: CallInfo) = acc + c def returnNone = letCallNode match case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") - case _ => Right(Nil) + case _ => calledDefn match + case None => Left(acc) + case Some(dest) => Left(updateMapSimple(NormalCallInfo(src, dest))) // treat the discovered call as a normal call node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => @@ -130,8 +161,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue - if isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + invalidateAndCont(body) else shadowAndCont(body, name) // OK @@ -157,7 +187,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): shadowAndCont(body, name) // does not use, OK to ignore this one else // add this name to the list of constructors containing the call - discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors + name) + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors + name) else // it does use it, further analyse letCtorNode match @@ -176,14 +206,13 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val fieldName = clsInfo.fields(ctorArgIndex) // populate required values - discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName, ctorArgIndex)), Set(name)) + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo, name, fieldName, ctorArgIndex)), Set(name)) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue - if isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + invalidateAndCont(body) case Expr.Select(name, cls, field) => letCallNode match @@ -193,8 +222,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue - if isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + invalidateAndCont(body) else shadowAndCont(body, name) // OK case Expr.BasicOp(_, args) => @@ -211,21 +239,25 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): else // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue - if isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + invalidateAndCont(body) case x: LetCall => val LetCall(names, defn, args, isTailRec, body) = x - if isTailCall(x) then + val callInScc = scc.contains(defn.expectDefn) + + // Only deal with + if callInScc && isTailCall(x) then Left(updateMapSimple(TailCallInfo(src, defn.expectDefn))) else letCallNode match - case None => // OK, use this LetCall as the mod cons + case None => // OK, we may use this LetCall as the mod cons // For now, only optimize functions which return one value - if defn.expectDefn.resultNum == 1 then - discoverOptimizableCalls(body)(acc, src, start, Some(defn.expectDefn), Some(x), None, Set()) + if callInScc && defn.expectDefn.resultNum == 1 then + searchOptCalls(body)(acc, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else - discoverOptimizableCalls(body) + // Treat this as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) + searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) case Some(LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld)) => if isTailRecOld && isTailRec then // 1. If both the old and newly discovered call are marked with tailrec, error @@ -237,35 +269,43 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val namesSet = namesOld.toSet val inters = argNames.intersect(namesSet) - if inters.isEmpty then discoverOptimizableCalls(body) // OK - else throw IRError("not a mod cons call") + if inters.isEmpty then + // Treat this as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) + searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK + else throw IRError("not a tail call") else // only include mod cons calls that have one return value - if defn.expectDefn.resultNum == 1 then + if callInScc && defn.expectDefn.resultNum == 1 then // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec - discoverOptimizableCalls(body)(acc, src, start, Some(defn.expectDefn), Some(x), None, Set()) + + // Treat the old call as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)) + searchOptCalls(body)(newMap, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else // shadow all the variables in this letcall - discoverOptimizableCalls(body)(acc, src, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) + + // Treat this as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) + searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => // make sure `value` is not the mod cons call letCallNode match - case None => discoverOptimizableCalls(body) // OK + case None => searchOptCalls(body) // OK case Some(LetCall(names, defn, args, isTailRec, body)) => value match case Expr.Ref(name) => - if names.contains(name) && isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + invalidateAndCont(body) case _ => letCtorNode match - case None => discoverOptimizableCalls(body) // OK + case None => searchOptCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => // If this assignment overwrites the mod cons value, forget it - if fieldName == assignmentFieldName && isTailRec then throw IRError("not a mod cons call") - else discoverOptimizableCalls(body)(acc, src, start, None, None, None, Set()) // invalidate everything that's been discovered + if containingCtors.contains(assignee) then invalidateAndCont(body) + else searchOptCalls(body) // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -288,20 +328,32 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case _ => false case _ => false - private def discoverCallsCont(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = - discoverOptimizableCalls(node)(acc, src, node, None, None, None, Set()) match + private def discoverOptCallsNode(node: Node)(implicit src: Defn, scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = + searchOptCalls(node)(acc, src, scc, node, None, None, None, Set()) match case Left(acc) => acc - case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverCallsNode(node)(src, acc)) + case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverOptCallsNode(node)(src, scc, acc)) + + private def discoverOptCalls(defn: Defn, jps: Set[Defn])(implicit scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = + val combined = jps + defn + combined.foldLeft(acc)((acc, defn_) => discoverOptCallsNode(defn_.body)(defn, scc, acc)) - private def discoverCallsNode(node: Node)(implicit src: Defn, acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = - val ret = discoverCallsCont(node) - ret.get(src.id) match - case None => ret + (src.id -> Set()) - case Some(value) => ret + private def searchCalls(node: Node)(implicit src: Defn, acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = + node match + case Result(res) => acc + case Jump(defn, args) => acc + case Case(scrut, cases) => cases.foldLeft(acc)((acc, item) => searchCalls(item._2)(src, acc)) + case LetExpr(name, expr, body) => searchCalls(body) + case LetCall(names, defn, args, isTailRec, body) => + val newSet = acc.get(src.id) match + case None => Set(defn.expectDefn) + case Some(defns) => defns + defn.expectDefn + searchCalls(body)(src, acc + (src.id -> newSet)) + case AssignField(assignee, clsInfo, fieldName, value, body) => searchCalls(body) + - private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[CallInfo]]): Map[Int, Set[CallInfo]] = + private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = val combined = jps + defn - combined.foldLeft(acc)((acc, defn_) => discoverCallsNode(defn_.body)(defn, acc)) + combined.foldLeft(acc)((acc, defn_) => searchCalls(defn_.body)(defn, acc)) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component @@ -320,13 +372,13 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = val defns = nodeMap.values.toSet - val inital = Map[Int, Set[CallInfo]]() + val inital = Map[Int, Set[Defn]]() val joinPoints = defns.map(d => (d.defn.id -> discoverJoinPoints(d.defn.body, Set()))).toMap val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn, joinPoints(defn.defn.id))(acc)).withDefaultValue(Set()) var ctr = 0 // nodes, edges - var stack: List[(DefnNode, Set[CallInfo])] = Nil + var stack: List[DefnNode] = Nil var sccs: List[DefnGraph] = Nil def dfs(src: DefnNode): Unit = @@ -336,9 +388,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): src.visited = true val tailCalls = edges(src.defn.id) - stack = (src, tailCalls) :: stack + stack = src :: stack for u <- tailCalls do - val neighbour = nodeMap(u.getDefn.id) + val neighbour = nodeMap(u.id) if (neighbour.visited) then if (!neighbour.processed) src.lowest = neighbour.num.min(src.lowest) @@ -351,33 +403,37 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if (src.num == src.lowest) then var scc: Set[DefnNode] = Set() - var sccEdges: Set[CallInfo] = Set() - def pop(): (DefnNode, Set[CallInfo]) = + def pop(): DefnNode = val ret = stack.head stack = stack.tail ret - var (vertex, edges) = pop() + var vertex = pop() while (vertex != src) { scc = scc + vertex - sccEdges = edges ++ sccEdges val next = pop() - vertex = next._1 - edges = next._2 + vertex = next } scc = scc + vertex - sccEdges = edges ++ sccEdges val sccIds = scc.map { d => d.defn.id } - sccEdges = sccEdges.filter { c => sccIds.contains(c.getDefn.id)} val sccJoinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => joinPoints(defn.defn.id)) - sccs = DefnGraph(scc, sccEdges, sccJoinPoints) :: sccs + + val sccDefns = scc.map(d => d.defn) + + val categorizedEdges = scc + .foldLeft(Set[CallInfo]())( + (calls, defn) => discoverOptCalls(defn.defn, joinPoints(defn.defn.id))(sccDefns, calls) + ) + .filter(c => sccDefns.contains(c.getDefn)) + + sccs = DefnGraph(scc, categorizedEdges, sccJoinPoints) :: sccs for v <- defns do if (!v.visited) @@ -729,7 +785,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val defn = defns.head // if the function does not even tail call itself, just return - if edges.size == 0 then + if filterOptCalls(edges).size == 0 then return (defns, defns.head) val jpName = defn.name + "_jp" @@ -867,6 +923,11 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): partitionNodes(nodeMap).map(_.removeMetadata) private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = + val isTailRec = component.nodes.find { _.isTailRec }.isDefined + // println(component.edges) + if isTailRec && filterNormalCalls(component.edges).size != 0 then + throw IRError("at least one function is not tail recursive") // TODO: better error message + val (modConsComp, other) = optimizeModCons(component, classes) val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) (other ++ trOpt, mergedDefn) @@ -880,8 +941,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val newDefs: Set[Defn] = optimized.flatMap { _._1 }.toSet val mergedDefs: List[Defn] = optimized.map { _._2 } - // Build the call graph of the program - // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } resolveDefnRef(p.main, newDefs, true) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index af8ba52122..3143f172f8 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -82,13 +82,14 @@ fact(1, 5) //│ 120 :interpIR +@tailrec class True class False fun fact(acc, n) = if n == 0 then acc else fact(acc * n, n - 1) fact(1, 5) -//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ //│ @@ -171,6 +172,7 @@ fact(1, 5) //│ 120 :interpIR +@tailrec class True class False fun fact(acc, n) = @@ -181,7 +183,7 @@ fun fact(acc, n) = else @tailrec fact(n * acc, x) fact(1, 5) -//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| //│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailrec fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ //│ @@ -477,10 +479,10 @@ g(6, 0) class True class False fun double(x) = x * 2 -fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) -fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) +@tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +@tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(6, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ @@ -654,11 +656,11 @@ g(6, 0) class True class False -fun f(a, b, c) = g(0, 0) -fun g(d, e) = h(0, 0, 0, 0) -fun h(p, q, r, s) = f(0, 0, 0) +@tailrec fun f(a, b, c) = g(0, 0) +@tailrec fun g(d, e) = h(0, 0, 0, 0) +@tailrec fun h(p, q, r, s) = f(0, 0, 0) 2 -//│ |#class| |True|↵|#class| |False|↵|#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | +//│ |#class| |True|↵|#class| |False|↵|@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | //│ Parsed: {class True {}; class False {}; fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} //│ //│ @@ -789,12 +791,12 @@ class True class False class Cons(h, t) class Nil -fun addOne(xs) = +@tailrec fun addOne(xs) = if xs is Cons(h, t) then Cons(h + 1, @tailrec addOne(t)) Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailrec| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailrec| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailrec addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ @@ -1117,16 +1119,16 @@ class True class False class Zero class S(x) -fun a(n) = +@tailrec fun a(n) = if n is S(x) then S(@tailrec b(x)) Zero then S(Zero) -fun b(n) = +@tailrec fun b(n) = if n is S(x) then S(S(@tailrec a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailrec b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailrec a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ @@ -1381,7 +1383,7 @@ class True class False class Cons(h, t) class Nil -fun addOne(xs) = +@tailrec fun addOne(xs) = if xs is Cons(h, t) then val next = @tailrec addOne(t) @@ -1391,7 +1393,7 @@ fun addOne(xs) = Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ @@ -1556,7 +1558,7 @@ class True class False class Nil class Cons(m, n) -fun a(x) = +@tailrec fun a(x) = if x is Cons(m, n) then if m < 0 then @@ -1564,13 +1566,13 @@ fun a(x) = else Cons(m * 4, b(m - 2)) Nil then Nil -fun b(n) = +@tailrec fun b(n) = if n <= 0 then Cons(0, Nil) else a(Cons(n, Nil)) b(16) -//│ |#class| |True|↵|#class| |False|↵|#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| +//│ |#class| |True|↵|#class| |False|↵|#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| //│ Parsed: {class True {}; class False {}; class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} //│ //│ @@ -2021,7 +2023,7 @@ class True class False class Cons(h, t) class Nil -fun foo(xs) = +@tailrec fun foo(xs) = if xs is Cons(h, t) then if h > 5 then foo(t) @@ -2031,7 +2033,7 @@ fun foo(xs) = Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ @@ -2281,3 +2283,428 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ //│ Interpreted: //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +class True +class False +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(a(), c()) +fun c() = A(b(), 1) +a() +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(a(), c(),); fun c = () => A(b(), 1,); a()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = a() in -- #22 +//│ let* (x$3) = c() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let* (x$5) = b() in -- #32 +//│ let x$6 = A(x$5,1) in -- #31 +//│ x$6 -- #30 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) +//│ +//│ IR Processing Failed: at least one function is not tail recursive + +class True +class False +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(a(), c()) +fun c() = A(0, 1) +a() +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(a(), c(),); fun c = () => A(0, 1,); a()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = a() in -- #22 +//│ let* (x$3) = c() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(b, a), Set(c)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = a_modcons(idCtx) in -- #62 +//│ res -- #61 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #55 +//│ let* (res) = b_modcons(idCtx) in -- #54 +//│ res -- #53 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #40 +//│ _IdContext => +//│ x -- #39 +//│ _Context => +//│ let field = ctx.field in -- #38 +//│ let ptr = ctx.ptr in -- #37 +//│ assign ptr.a := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #46 +//│ let ctx2ptr = ctx2.ptr in -- #45 +//│ let ctx2field = ctx2.field in -- #44 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 +//│ ret -- #41 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 +//│ r0 -- #81 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 +//│ r0 -- #83 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #79 +//│ if scrut -- #78 +//│ true => +//│ let x$1 = A(0,1) in -- #77 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 +//│ false => +//│ let* (x$3) = c() in -- #73 +//│ let x$4 = A(0,x$3) in -- #72 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = a_modcons(idCtx) in -- #62 +//│ res -- #61 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #55 +//│ let* (res) = b_modcons(idCtx) in -- #54 +//│ res -- #53 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #40 +//│ _IdContext => +//│ x -- #39 +//│ _Context => +//│ let field = ctx.field in -- #38 +//│ let ptr = ctx.ptr in -- #37 +//│ assign ptr.a := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #46 +//│ let ctx2ptr = ctx2.ptr in -- #45 +//│ let ctx2field = ctx2.field in -- #44 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 +//│ ret -- #41 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 +//│ r0 -- #81 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 +//│ r0 -- #83 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #79 +//│ if scrut -- #78 +//│ true => +//│ let x$1 = A(0,1) in -- #77 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 +//│ false => +//│ let* (x$3) = c() in -- #73 +//│ let x$4 = A(0,x$3) in -- #72 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) + +class True +class False +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(c(), a()) +fun c() = A(0, 1) +a() +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), a(),); fun c = () => A(0, 1,); a()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = c() in -- #22 +//│ let* (x$3) = a() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(b, a), Set(c)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #69 +//│ let* (res) = a_modcons(idCtx) in -- #68 +//│ res -- #67 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #61 +//│ let* (res) = b_modcons(idCtx) in -- #60 +//│ res -- #59 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #46 +//│ _IdContext => +//│ x -- #45 +//│ _Context => +//│ let field = ctx.field in -- #44 +//│ let scrut = ==(1,field) in -- #43 +//│ if scrut -- #42 +//│ true => +//│ let ptr = ctx.ptr in -- #41 +//│ assign ptr.a := x in -- #40 +//│ let acc = ctx.acc in -- #39 +//│ acc -- #38 +//│ false => +//│ let ptr = ctx.ptr in -- #37 +//│ assign ptr.b := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #52 +//│ let ctx2ptr = ctx2.ptr in -- #51 +//│ let ctx2field = ctx2.field in -- #50 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 +//│ ret -- #47 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 +//│ r0 -- #87 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 +//│ r0 -- #89 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #85 +//│ if scrut -- #84 +//│ true => +//│ let x$1 = A(0,1) in -- #83 +//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 +//│ false => +//│ let* (x$2) = c() in -- #79 +//│ let x$4 = A(x$2,0) in -- #78 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #69 +//│ let* (res) = a_modcons(idCtx) in -- #68 +//│ res -- #67 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #61 +//│ let* (res) = b_modcons(idCtx) in -- #60 +//│ res -- #59 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #46 +//│ _IdContext => +//│ x -- #45 +//│ _Context => +//│ let field = ctx.field in -- #44 +//│ let scrut = ==(1,field) in -- #43 +//│ if scrut -- #42 +//│ true => +//│ let ptr = ctx.ptr in -- #41 +//│ assign ptr.a := x in -- #40 +//│ let acc = ctx.acc in -- #39 +//│ acc -- #38 +//│ false => +//│ let ptr = ctx.ptr in -- #37 +//│ assign ptr.b := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #52 +//│ let ctx2ptr = ctx2.ptr in -- #51 +//│ let ctx2field = ctx2.field in -- #50 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 +//│ ret -- #47 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 +//│ r0 -- #87 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 +//│ r0 -- #89 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #85 +//│ if scrut -- #84 +//│ true => +//│ let x$1 = A(0,1) in -- #83 +//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 +//│ false => +//│ let* (x$2) = c() in -- #79 +//│ let x$4 = A(x$2,0) in -- #78 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) From 42b264f14f40cb8f90771f70e762c6e8be3d9d70 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 12 May 2024 22:55:10 +0800 Subject: [PATCH 36/59] update --- .../main/scala/mlscript/compiler/optimizer/TailRecOpt.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 83b4e91863..417a6fc820 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -766,7 +766,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // original functions pointing to an optimized function. // Explicitly returns the merged function in case tailrec needs to be checked. private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = - // println(component.edges) // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get @@ -924,7 +923,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = val isTailRec = component.nodes.find { _.isTailRec }.isDefined - // println(component.edges) if isTailRec && filterNormalCalls(component.edges).size != 0 then throw IRError("at least one function is not tail recursive") // TODO: better error message From 2199e373df7c97a4bef10a1637c666fb5cd65053 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 16:20:43 +0800 Subject: [PATCH 37/59] make AssiggnField an Expr instead of a Node --- .../compiler/ir/DefnRefResolver.scala | 1 - .../main/scala/mlscript/compiler/ir/IR.scala | 26 +++----- .../scala/mlscript/compiler/ir/Interp.scala | 46 +++++++------- .../mlscript/compiler/ir/Validator.scala | 1 - .../compiler/optimizer/Analysis.scala | 12 ++-- .../compiler/optimizer/TailRecOpt.scala | 61 ++++++++----------- compiler/shared/test/diff-ir/IRTailRec.mls | 32 +++++----- 7 files changed, 81 insertions(+), 98 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala index 6df55f3235..96ee1d635d 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/DefnRefResolver.scala @@ -23,7 +23,6 @@ private final class DefnRefResolver(defs: Set[Defn], allowInlineJp: Bool): case None => if (!allowInlineJp) throw IRError(f"unknown function ${defnref.getName} in ${defs.map{_.getName}.mkString(",")}") - case AssignField(assignee, clsInfo, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(node: Defn) = f(node.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index a106d46287..3f8fa04e4c 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -97,6 +97,7 @@ enum Expr: case CtorApp(name: ClassInfo, args: Ls[TrivialExpr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr) override def toString: String = show @@ -115,6 +116,13 @@ enum Expr: raw(s.toString) <#> raw(".") <#> raw(fld) case BasicOp(name: Str, args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") + case AssignField(assignee, clsInfo, fieldName, value) => + stack( + raw("assign") + <:> raw(assignee.toString + "." + fieldName) + <:> raw(":=") + <:> value.toDocument + ) def mapName(f: Name => Name): Expr = this match case Ref(name) => Ref(f(name)) @@ -122,6 +130,7 @@ enum Expr: case CtorApp(cls, args) => CtorApp(cls, args.map(_.mapNameOfTrivialExpr(f))) case Select(x, cls, field) => Select(f(x), cls, field) case BasicOp(name, args) => BasicOp(name, args.map(_.mapNameOfTrivialExpr(f))) + case AssignField(assignee, clsInfo, fieldName, value) => AssignField(f(assignee), clsInfo, fieldName, value.mapNameOfTrivialExpr(f)) def locMarker: LocMarker = this match case Ref(name) => LocMarker.MRef(name.str) @@ -129,7 +138,7 @@ enum Expr: case CtorApp(name, args) => LocMarker.MCtorApp(name, args.map(_.toExpr.locMarker)) case Select(name, cls, field) => LocMarker.MSelect(name.str, cls, field) case BasicOp(name, args) => LocMarker.MBasicOp(name, args.map(_.toExpr.locMarker)) - + case AssignField(assignee, clsInfo, fieldName, value) => LocMarker.MAssignField(assignee.str, fieldName, value.toExpr.locMarker) enum Node: // Terminal forms: @@ -139,7 +148,6 @@ enum Node: // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node) - case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: TrivialExpr, body: Node) var tag = DefnTag(-1) @@ -163,8 +171,6 @@ enum Node: case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) case LetCall(names, defn, args, isTailRec, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f)) - case AssignField(assignee, fieldName, clsInfo, value, body) => - AssignField(f(assignee), fieldName, clsInfo, value.mapNameOfTrivialExpr(f), body.mapName(f)) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) @@ -176,8 +182,6 @@ enum Node: case LetCall(names, defn, args, isTailRec, body) => val names_copy = names.map(_.copy) LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x))) - case AssignField(assignee, clsInfo, fieldName, value, body) => - AssignField(assignee.trySubst(ctx), clsInfo, fieldName, value.mapNameOfTrivialExpr(_.trySubst(ctx)), body.copy(ctx)) private def toDocument: Document = this match case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") @@ -223,15 +227,6 @@ enum Node: <:> raw("in") <:> raw(s"-- $tag"), body.toDocument) - case AssignField(assignee, clsInfo, fieldName, value, body) => - stack( - raw("assign") - <:> raw(assignee.toString + "." + fieldName) - <:> raw(":=") - <:> value.toDocument - <:> raw("in") - <:> raw(s"-- $tag"), - body.toDocument) def locMarker: LocMarker = val marker = this match case Result(res) => LocMarker.MResult(res.map(_.toExpr.locMarker)) @@ -239,7 +234,6 @@ enum Node: case Case(scrut, cases) => LocMarker.MCase(scrut.str, cases.map(_._1)) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.getName, args.map(_.toExpr.locMarker)) - case AssignField(assignee, clsInfo, field, value, _) => LocMarker.MAssignField(assignee.toString, field, value.toExpr.locMarker) marker.tag = this.tag marker diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index dd0d05fd23..016ab09938 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -42,6 +42,7 @@ class Interpreter(verbose: Bool): case CtorApp(name: ClassInfo, var args: Ls[Expr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[Expr]) + case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: Expr) def show: Str = document.print @@ -60,6 +61,15 @@ class Interpreter(verbose: Bool): raw(s) <#> raw(".") <#> raw(fld) case BasicOp(name: Str, args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") + case AssignField(Name(assignee), clsInfo, fieldName, value) => + stack( + raw("assign") + <:> raw(assignee) + <#> raw(".") + <#> raw(fieldName) + <:> raw("=") + <:> value.document, + ) private enum Node: case Result(res: Ls[Expr]) @@ -68,7 +78,6 @@ class Interpreter(verbose: Bool): case LetExpr(name: Name, expr: Expr, body: Node) case LetJoin(joinName: Name, params: Ls[Name], rhs: Node, body: Node) case LetCall(resultNames: Ls[Name], defn: DefnRef, args: Ls[Expr], body: Node) - case AssignField(assignee: Name, clsInfo: ClassInfo, fieldName: Str, value: Expr, body: Node) def show: Str = document.print @@ -127,16 +136,6 @@ class Interpreter(verbose: Bool): <#> raw(")"), raw("in") <:> body.document |> indent ) - case AssignField(Name(assignee), clsInfo, fieldName, value, body) => - stack( - raw("assign") - <:> raw(assignee) - <#> raw(".") - <#> raw(fieldName) - <:> raw("=") - <:> value.document, - raw("in") <:> body.document |> indent - ) private class DefnRef(var defn: Either[Defn, Str]): def name = defn match @@ -158,6 +157,7 @@ class Interpreter(verbose: Bool): case IExpr.CtorApp(name, args) => CtorApp(name, args |> convertArgs) case IExpr.Select(name, cls, field) => Select(name, cls, field) case IExpr.BasicOp(name, args) => BasicOp(name, args |> convertArgs) + case IExpr.AssignField(assignee, clsInfo, fieldName, value) => AssignField(assignee, clsInfo, fieldName, value |> convert) private def convert(node: INode): Node = node match case INode.Result(xs) => Result(xs |> convertArgs) @@ -166,7 +166,6 @@ class Interpreter(verbose: Bool): case INode.LetExpr(name, expr, body) => LetExpr(name, expr |> convert, body |> convert) case INode.LetCall(xs, defnref, args, _, body) => LetCall(xs, DefnRef(Right(defnref.getName)), args |> convertArgs, body |> convert) - case INode.AssignField(assignee, clsInfo, fieldName, value, body) => AssignField(assignee, clsInfo, fieldName, value |> convert, body |> convert) private def convert(defn: IDefn): Defn = Defn(defn.name, defn.params, defn.body |> convert) @@ -259,6 +258,17 @@ class Interpreter(verbose: Bool): eval(using ctx, clsctx)(name, xs.head, xs.tail.head) case _ => throw IRInterpreterError("unexpected basic operation") x.toLeft(expr) + case AssignField(assignee, clsInfo, fieldName, expr) => + val value = evalMayNotProgress(expr) + ctx.get(assignee.str) match + case Some(x: CtorApp) => + val CtorApp(cls, args) = x + val idx = cls.fields.indexOf(fieldName) + val newArgs = args.updated(idx, value) + x.args = newArgs + Left(x) + case Some(_) => throw IRInterpreterError("tried to assign a field of a non-ctor") + case None => throw IRInterpreterError("could not find value " + assignee) private def expectDefn(r: DefnRef) = r.defn match case Left(value) => value @@ -301,18 +311,6 @@ class Interpreter(verbose: Bool): } val ctx2 = ctx ++ xs.map{_.str}.zip(res) eval(using ctx2, clsctx)(body) - case AssignField(Name(assignee), clsInfo, fieldName, expr, body) => - val value = evalMayNotProgress(expr) - val x = ctx.get(assignee) match - case Some(x: CtorApp) => - val CtorApp(cls, args) = x - val idx = cls.fields.indexOf(fieldName) - val newArgs = args.updated(idx, value) - x.args = newArgs - case Some(_) => IRInterpreterError("tried to assign a field of a non-ctor") - case None => IRInterpreterError("could not find value " + assignee) - - eval(body) case _ => throw IRInterpreterError("unexpected node") private def interpret(prog: Program): Node = diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala index a52fde5ef3..ce7f09c2c9 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala @@ -17,7 +17,6 @@ private final class DefnRefInSet(defs: Set[Defn]): case _ => } f(body) - case AssignField(assignee, clsInfo, fieldName, value, body) => f(body) def run(node: Node) = f(node) def run(defn: Defn) = f(defn.body) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala index 98449d5914..f0b02d2777 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala @@ -39,6 +39,9 @@ class UsefulnessAnalysis(verbose: Bool = false): case CtorApp(name, args) => args.foreach(f) case Select(name, cls, field) => addUse(name) case BasicOp(name, args) => args.foreach(f) + case AssignField(assignee, _, _, value) => + addUse(assignee) + f(value) private def f(x: Node): Unit = x match case Result(res) => res.foreach(f) @@ -46,7 +49,6 @@ class UsefulnessAnalysis(verbose: Bool = false): case Case(scrut, cases) => addUse(scrut); cases.foreach { case (cls, body) => f(body) } case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, _, body) => args.foreach(f); names.foreach(addDef); f(body) - case AssignField(assignee, clsInfo, fieldName, value, body) => f(value); f(body) def run(x: Defn) = x.params.foreach(addDef) @@ -67,6 +69,10 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): case CtorApp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Select(name, cls, field) => if (defined.contains(name.str)) fv else fv + name.str case BasicOp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) + case AssignField(assignee, _, _, value) => f(using defined)( + value.toExpr, + if defined.contains(assignee.str) then fv + assignee.str else fv + ) private def f(using defined: Set[Str])(node: Node, fv: Set[Str]): Set[Str] = node match case Result(res) => res.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Jump(defnref, args) => @@ -95,9 +101,5 @@ class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) f(using defined2)(body, fv2) - case AssignField(assignee, clsInfo, fieldName, value, body) => - val fv2 = if (defined.contains(assignee.str)) fv else fv + assignee.str - val fv3 = f(using defined)(value.toExpr, fv2) - f(using defined)(body, fv3) def run(node: Node) = f(using Set.empty)(node, Set.empty) def run_with(node: Node, defined: Set[Str]) = f(using defined)(node, Set.empty) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 417a6fc820..b46f897b7c 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -67,7 +67,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case Case(scrut, cases) => casesToJps(cases, acc) case LetExpr(name, expr, body) => discoverJoinPoints(body, acc) case LetCall(names, defn, args, isTailRec, body) => discoverJoinPoints(body, acc) - case AssignField(assignee, clsInfo, fieldName, value, body) => discoverJoinPoints(body, acc) private def getRetName(names: Set[Name], retVals: List[TrivialExpr]): Option[Name] = val names = retVals.collect { case Expr.Ref(nme) => nme } @@ -240,6 +239,21 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue invalidateAndCont(body) + case Expr.AssignField(assignee, clsInfo, assignmentFieldName, value) => + // make sure `value` is not the mod cons call + letCallNode match + case None => searchOptCalls(body) // OK + case Some(LetCall(names, defn, args, isTailRec, _)) => + value match + case Expr.Ref(name) => + invalidateAndCont(body) + case _ => + letCtorNode match + case None => searchOptCalls(body) // OK + case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => + // If this assignment overwrites the mod cons value, forget it + if containingCtors.contains(assignee) then invalidateAndCont(body) + else searchOptCalls(body) case x: LetCall => val LetCall(names, defn, args, isTailRec, body) = x @@ -290,22 +304,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // Treat this as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) - - case AssignField(assignee, clsInfo, assignmentFieldName, value, body) => - // make sure `value` is not the mod cons call - letCallNode match - case None => searchOptCalls(body) // OK - case Some(LetCall(names, defn, args, isTailRec, body)) => - value match - case Expr.Ref(name) => - invalidateAndCont(body) - case _ => - letCtorNode match - case None => searchOptCalls(body) // OK - case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => - // If this assignment overwrites the mod cons value, forget it - if containingCtors.contains(assignee) then invalidateAndCont(body) - else searchOptCalls(body) // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = @@ -348,7 +346,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case None => Set(defn.expectDefn) case Some(defns) => defns + defn.expectDefn searchCalls(body)(src, acc + (src.id -> newSet)) - case AssignField(assignee, clsInfo, fieldName, value, body) => searchCalls(body) private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = @@ -538,11 +535,14 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val node = LetExpr( Name("ptr"), Expr.Select(appCtxName, CTX_CLASS, "ptr"), - AssignField( - Name("ptr"), - cls, - fieldName, - Expr.Ref(appValName), + LetExpr( + Name("_"), + Expr.AssignField( + Name("ptr"), + cls, + fieldName, + Expr.Ref(appValName) + ), LetExpr( Name("acc"), Expr.Select(appCtxName, CTX_CLASS, "acc"), // this could be a join point but it's not that bad @@ -662,8 +662,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): ).attachTag(tag) else LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) - case AssignField(assignee, clsInfo, fieldName, value, body) => - AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) def transformModConsBranch(node: Node)(implicit call: ModConsCallInfo): Node = def makeCall = @@ -710,8 +708,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): transformModConsBranch(body) else LetCall(names, defn, args, isTailRec, transformModConsBranch(body)).attachTag(tag) - case AssignField(assignee, clsInfo, fieldName, value, body) => - AssignField(assignee, clsInfo, fieldName, value, transformModConsBranch(body)).attachTag(tag) case _ => throw IRError("unreachable case when transforming mod cons call") def rewriteDefn(d: Defn): Defn = @@ -800,8 +796,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Jump(jpDefnRef, args).attachTag(tag) else LetCall(names, defn_, args, isTailRec, transformNode(body)).attachTag(tag) - case AssignField(assignee, clsInfo, fieldName, value, body) => - AssignField(assignee, clsInfo, fieldName, value, transformNode(body)).attachTag(tag) val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body), false) @@ -868,7 +862,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) - case AssignField(assignee, clsInfo, field, value, body) => AssignField(assignee, clsInfo, field, value, transformNode(body)).attachTag(tag) // Tail calls to another function in the component will be replaced with a tail call // to the merged function @@ -921,23 +914,21 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(nodeMap).map(_.removeMetadata) - private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = + private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = val isTailRec = component.nodes.find { _.isTailRec }.isDefined if isTailRec && filterNormalCalls(component.edges).size != 0 then throw IRError("at least one function is not tail recursive") // TODO: better error message val (modConsComp, other) = optimizeModCons(component, classes) val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) - (other ++ trOpt, mergedDefn) + other ++ trOpt def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = val partitions = partition(p.defs) - val optimized = partitions.map { optimizeParition(_, p.classes) } - val newDefs: Set[Defn] = optimized.flatMap { _._1 }.toSet - val mergedDefs: List[Defn] = optimized.map { _._2 } + val newDefs = partitions.flatMap { optimizeParition(_, p.classes) }.toSet // update the definition refs newDefs.foreach { defn => resolveDefnRef(defn.body, newDefs, true) } diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 3143f172f8..50597c0456 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -849,7 +849,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ _Context => //│ let field = ctx.field in -- #57 //│ let ptr = ctx.ptr in -- #56 -//│ assign ptr.t := x in -- #55 +//│ let _ = assign ptr.t := x in -- #55 //│ let acc = ctx.acc in -- #54 //│ acc -- #53 //│ ) @@ -911,7 +911,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ _Context => //│ let field = ctx.field in -- #57 //│ let ptr = ctx.ptr in -- #56 -//│ assign ptr.t := x in -- #55 +//│ let _ = assign ptr.t := x in -- #55 //│ let acc = ctx.acc in -- #54 //│ acc -- #53 //│ ) @@ -1209,7 +1209,7 @@ a(S(S(S(Zero)))) //│ _Context => //│ let field = ctx.field in -- #79 //│ let ptr = ctx.ptr in -- #78 -//│ assign ptr.x := x in -- #77 +//│ let _ = assign ptr.x := x in -- #77 //│ let acc = ctx.acc in -- #76 //│ acc -- #75 //│ ) @@ -1307,7 +1307,7 @@ a(S(S(S(Zero)))) //│ _Context => //│ let field = ctx.field in -- #79 //│ let ptr = ctx.ptr in -- #78 -//│ assign ptr.x := x in -- #77 +//│ let _ = assign ptr.x := x in -- #77 //│ let acc = ctx.acc in -- #76 //│ acc -- #75 //│ ) @@ -1446,7 +1446,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ _Context => //│ let field = ctx.field in -- #60 //│ let ptr = ctx.ptr in -- #59 -//│ assign ptr.t := x in -- #58 +//│ let _ = assign ptr.t := x in -- #58 //│ let acc = ctx.acc in -- #57 //│ acc -- #56 //│ ) @@ -1508,7 +1508,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ _Context => //│ let field = ctx.field in -- #60 //│ let ptr = ctx.ptr in -- #59 -//│ assign ptr.t := x in -- #58 +//│ let _ = assign ptr.t := x in -- #58 //│ let acc = ctx.acc in -- #57 //│ acc -- #56 //│ ) @@ -1665,7 +1665,7 @@ b(16) //│ _Context => //│ let field = ctx.field in -- #86 //│ let ptr = ctx.ptr in -- #85 -//│ assign ptr.n := x in -- #84 +//│ let _ = assign ptr.n := x in -- #84 //│ let acc = ctx.acc in -- #83 //│ acc -- #82 //│ ) @@ -1770,7 +1770,7 @@ b(16) //│ _Context => //│ let field = ctx.field in -- #86 //│ let ptr = ctx.ptr in -- #85 -//│ assign ptr.n := x in -- #84 +//│ let _ = assign ptr.n := x in -- #84 //│ let acc = ctx.acc in -- #83 //│ acc -- #82 //│ ) @@ -2120,7 +2120,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ _Context => //│ let field = ctx.field in -- #108 //│ let ptr = ctx.ptr in -- #107 -//│ assign ptr.t := x in -- #106 +//│ let _ = assign ptr.t := x in -- #106 //│ let acc = ctx.acc in -- #105 //│ acc -- #104 //│ ) @@ -2216,7 +2216,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ _Context => //│ let field = ctx.field in -- #108 //│ let ptr = ctx.ptr in -- #107 -//│ assign ptr.t := x in -- #106 +//│ let _ = assign ptr.t := x in -- #106 //│ let acc = ctx.acc in -- #105 //│ acc -- #104 //│ ) @@ -2387,7 +2387,7 @@ a() //│ _Context => //│ let field = ctx.field in -- #38 //│ let ptr = ctx.ptr in -- #37 -//│ assign ptr.a := x in -- #36 +//│ let _ = assign ptr.a := x in -- #36 //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) @@ -2461,7 +2461,7 @@ a() //│ _Context => //│ let field = ctx.field in -- #38 //│ let ptr = ctx.ptr in -- #37 -//│ assign ptr.a := x in -- #36 +//│ let _ = assign ptr.a := x in -- #36 //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) @@ -2575,12 +2575,12 @@ a() //│ if scrut -- #42 //│ true => //│ let ptr = ctx.ptr in -- #41 -//│ assign ptr.a := x in -- #40 +//│ let _ = assign ptr.a := x in -- #40 //│ let acc = ctx.acc in -- #39 //│ acc -- #38 //│ false => //│ let ptr = ctx.ptr in -- #37 -//│ assign ptr.b := x in -- #36 +//│ let _ = assign ptr.b := x in -- #36 //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) @@ -2657,12 +2657,12 @@ a() //│ if scrut -- #42 //│ true => //│ let ptr = ctx.ptr in -- #41 -//│ assign ptr.a := x in -- #40 +//│ let _ = assign ptr.a := x in -- #40 //│ let acc = ctx.acc in -- #39 //│ acc -- #38 //│ false => //│ let ptr = ctx.ptr in -- #37 -//│ assign ptr.b := x in -- #36 +//│ let _ = assign ptr.b := x in -- #36 //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) From eacf538edc412410dd6cfa9daed13ca8470d0caa Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 16:23:47 +0800 Subject: [PATCH 38/59] Fix unsafe partial destruction --- .../scala/mlscript/compiler/ir/Builder.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index a0b6ffe499..986d2abf62 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -245,23 +245,23 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres res private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match - case nfd: NuFunDef => - val NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) = nfd - - val strs = fields map { - case N -> Fld(FldFlags.empty, Var(x)) => x - case _ => throw IRError("unsupported field") - } - val names = strs map (fresh.make(_)) - given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) - Defn( - fnUid.make, - name, - params = names, - resultNum = 1, - buildResultFromTerm(body) { x => x }, - nfd.annotations.find { case Var("tailrec") => true; case _ => false }.isDefined - ) + case nfd: NuFunDef => nfd match + case NuFunDef(_, Var(name), None, Nil, L(Lam(Tup(fields), body))) => + val strs = fields map { + case N -> Fld(FldFlags.empty, Var(x)) => x + case _ => throw IRError("unsupported field") + } + val names = strs map (fresh.make(_)) + given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) + Defn( + fnUid.make, + name, + params = names, + resultNum = 1, + buildResultFromTerm(body) { x => x }, + nfd.annotations.find { case Var("tailrec") => true; case _ => false }.isDefined + ) + case _ => throw IRError("unsupported NuFunDef") case _ => throw IRError("unsupported NuFunDef") private def buildClassInfo(ntd: Statement): ClassInfo = ntd match From 214f9d3e1d34d793e79687a04d59f17bcc05ea6a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 16:31:34 +0800 Subject: [PATCH 39/59] rename @tailrec to @tailcall for call-level annotations --- .../scala/mlscript/compiler/ir/Builder.scala | 2 +- .../main/scala/mlscript/compiler/ir/IR.scala | 2 +- compiler/shared/test/diff-ir/IRTailRec.mls | 68 +++++++++---------- shared/src/main/scala/mlscript/Typer.scala | 1 + 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 986d2abf62..15290aa98c 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -79,7 +79,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres val v = fresh.make ann match - case Some(Var(nme)) if nme == "tailrec" => + case Some(Var(nme)) if nme == "tailcall" => LetCall(List(v), DefnRef(Right(f.str)), args, true, v |> ref |> sresult |> k).attachTag(tag) case Some(_) => node |> unexpectedNode case None => LetCall(List(v), DefnRef(Right(f.str)), args, false, v |> ref |> sresult |> k).attachTag(tag) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 3f8fa04e4c..fb15901d8f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -220,7 +220,7 @@ enum Node: <#> raw(xs.map(_.toString).mkString(",")) <#> raw(")") <:> raw("=") - <:> raw((if isTailRec then "@tailrec " else "") + defn.getName) + <:> raw((if isTailRec then "@tailcall " else "") + defn.getName) <#> raw("(") <#> raw(args.map{ x => x.toString }.mkString(",")) <#> raw(")") diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 50597c0456..a7046adb0a 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -181,10 +181,10 @@ fun fact(acc, n) = if x <= 0 then acc else - @tailrec fact(n * acc, x) + @tailcall fact(n * acc, x) fact(1, 5) -//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailrec| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailrec fact(*(n,)(acc,), x,)}}; fact(1, 5,)} +//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ //│ //│ IR: @@ -211,7 +211,7 @@ fact(1, 5) //│ jump j$1(acc$0) -- #9 //│ false => //│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) //│ }, @@ -238,7 +238,7 @@ fact(1, 5) //│ jump j$1(acc$0) -- #9 //│ false => //│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) //│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], @@ -289,7 +289,7 @@ fact(1, 5) //│ jump j$1(acc$0) -- #9 //│ false => //│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailrec fact(x$4,x$1) in -- #20 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 //│ jump j$1(x$5) -- #19 //│ ) //│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], @@ -766,18 +766,18 @@ class False class True class False fun hello() = - @tailrec hello() + @tailcall hello() 2 hello() -//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailrec| |hello|(||)|↵|2|←|↵|hello|(||)| | -//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailrec hello(); 2}; hello()} +//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); 2}; hello()} //│ //│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, hello, [], //│ 1, -//│ let* (x$0) = @tailrec hello() in -- #4 +//│ let* (x$0) = @tailcall hello() in -- #4 //│ 2 -- #3 //│ ) //│ }, @@ -793,11 +793,11 @@ class Cons(h, t) class Nil @tailrec fun addOne(xs) = if xs is - Cons(h, t) then Cons(h + 1, @tailrec addOne(t)) + Cons(h, t) then Cons(h + 1, @tailcall addOne(t)) Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailrec| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailrec addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: @@ -809,7 +809,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let x$1 = xs$0.t in -- #23 //│ let x$2 = xs$0.h in -- #22 //│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailrec addOne(x$1) in -- #20 +//│ let* (x$4) = @tailcall addOne(x$1) in -- #20 //│ let x$5 = Cons(x$3,x$4) in -- #19 //│ jump j$0(x$5) -- #18 //│ Nil => @@ -964,15 +964,15 @@ class Zero class S(x) fun a(n) = if n is - S(x) then S(@tailrec b(x)) + S(x) then S(@tailcall b(x)) Zero then S(Zero) fun b(n) = if n is - S(x) then S(S(@tailrec a(x))) + S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailrec b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailrec a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: @@ -982,7 +982,7 @@ a(S(S(S(Zero)))) //│ case n$0 of -- #23 //│ S => //│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => @@ -999,7 +999,7 @@ a(S(S(S(Zero)))) //│ case n$1 of -- #55 //│ S => //│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 //│ let x$9 = S(x$8) in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$1(x$10) -- #39 @@ -1026,7 +1026,7 @@ a(S(S(S(Zero)))) //│ case n$0 of -- #23 //│ S => //│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => @@ -1043,7 +1043,7 @@ a(S(S(S(Zero)))) //│ case n$1 of -- #55 //│ S => //│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 //│ let x$9 = S(x$8) in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$1(x$10) -- #39 @@ -1072,7 +1072,7 @@ a(S(S(S(Zero)))) //│ case n$0 of -- #23 //│ S => //│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => @@ -1089,7 +1089,7 @@ a(S(S(S(Zero)))) //│ case n$1 of -- #55 //│ S => //│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 //│ let x$9 = S(x$8) in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$1(x$10) -- #39 @@ -1121,15 +1121,15 @@ class Zero class S(x) @tailrec fun a(n) = if n is - S(x) then S(@tailrec b(x)) + S(x) then S(@tailcall b(x)) Zero then S(Zero) @tailrec fun b(n) = if n is - S(x) then S(S(@tailrec a(x))) + S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailrec| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailrec| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailrec b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailrec a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: @@ -1139,7 +1139,7 @@ a(S(S(S(Zero)))) //│ case n$0 of -- #23 //│ S => //│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailrec b(x$1) in -- #14 +//│ let* (x$2) = @tailcall b(x$1) in -- #14 //│ let x$3 = S(x$2) in -- #13 //│ jump j$0(x$3) -- #12 //│ Zero => @@ -1156,7 +1156,7 @@ a(S(S(S(Zero)))) //│ case n$1 of -- #55 //│ S => //│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailrec a(x$7) in -- #42 +//│ let* (x$8) = @tailcall a(x$7) in -- #42 //│ let x$9 = S(x$8) in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$1(x$10) -- #39 @@ -1386,15 +1386,15 @@ class Nil @tailrec fun addOne(xs) = if xs is Cons(h, t) then - val next = @tailrec addOne(t) + val next = @tailcall addOne(t) val ret = Cons(h + 1, next) val rett = ret rett Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailrec| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailrec addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: @@ -1405,7 +1405,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Cons => //│ let x$1 = xs$0.t in -- #26 //│ let x$2 = xs$0.h in -- #25 -//│ let* (x$3) = @tailrec addOne(x$1) in -- #24 +//│ let* (x$3) = @tailcall addOne(x$1) in -- #24 //│ let x$4 = +(x$2,1) in -- #23 //│ let x$5 = Cons(x$4,x$3) in -- #22 //│ jump j$0(x$5) -- #21 diff --git a/shared/src/main/scala/mlscript/Typer.scala b/shared/src/main/scala/mlscript/Typer.scala index 86b5e03482..0c7d4e52ee 100644 --- a/shared/src/main/scala/mlscript/Typer.scala +++ b/shared/src/main/scala/mlscript/Typer.scala @@ -303,6 +303,7 @@ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val ne NuTypeDef(Cls, TN("Code"), (S(VarianceInfo.co) -> TN("T")) :: (S(VarianceInfo.co) -> TN("C")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), + NuTypeDef(Mod, TN("tailcall"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), ) val builtinTypes: Ls[TypeDef] = TypeDef(Cls, TN("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations From 51af35fbd6e872616a8f4af2db2e37b043501733 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 21:29:24 +0800 Subject: [PATCH 40/59] Propagate positions of calls and @tailrec annotations --- .../scala/mlscript/compiler/ir/Builder.scala | 17 ++++++++----- .../main/scala/mlscript/compiler/ir/IR.scala | 16 +++++++++---- .../compiler/optimizer/TailRecOpt.scala | 24 +++++++++---------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 15290aa98c..d7ba106188 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -74,15 +74,15 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres private def buildResultFromTerm(using ctx: Ctx)(tm: Term)(k: Node => Node): Node = def buildLetCall(f: Term, xs: Tup, ann: Option[Term]) = buildResultFromTerm(f) { node => node match - case Result(Ref(f) :: Nil) if ctx.fnCtx.contains(f.str) => buildResultFromTerm(xs) { + case Result(Ref(g) :: Nil) if ctx.fnCtx.contains(g.str) => buildResultFromTerm(xs) { case Result(args) => val v = fresh.make ann match case Some(Var(nme)) if nme == "tailcall" => - LetCall(List(v), DefnRef(Right(f.str)), args, true, v |> ref |> sresult |> k).attachTag(tag) + LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case Some(_) => node |> unexpectedNode - case None => LetCall(List(v), DefnRef(Right(f.str)), args, false, v |> ref |> sresult |> k).attachTag(tag) + case None => LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case node @ _ => node |> unexpectedNode } @@ -157,7 +157,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, - false + false, + None ) ctx.jpAcc.addOne(jpdef) val tru2 = buildResultFromTerm(tru) { @@ -190,7 +191,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, - false + false, + None ) ctx.jpAcc.addOne(jpdef) val cases: Ls[(ClassInfo, Node)] = lines map { @@ -253,13 +255,16 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres } val names = strs map (fresh.make(_)) given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) + val trAnn = nfd.annotations.find { case Var("tailrec") => true; case _ => false } + Defn( fnUid.make, name, params = names, resultNum = 1, buildResultFromTerm(body) { x => x }, - nfd.annotations.find { case Var("tailrec") => true; case _ => false }.isDefined + trAnn.isDefined, + trAnn.flatMap(_.toLoc) ) case _ => throw IRError("unsupported NuFunDef") case _ => throw IRError("unsupported NuFunDef") diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index fb15901d8f..e3b6eca99a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -6,6 +6,8 @@ import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import mlscript.compiler.optimizer._ +import mlscript.Loc + import collection.mutable.{Map as MutMap, Set as MutSet, HashMap, ListBuffer} import annotation.unused import util.Sorting @@ -68,7 +70,8 @@ case class Defn( val params: Ls[Name], val resultNum: Int, val body: Node, - val isTailRec: Bool + val isTailRec: Bool, + val declareLoc: Opt[Loc] = None ): override def hashCode: Int = id def getName: String = name @@ -147,7 +150,7 @@ enum Node: case Case(scrut: Name, cases: Ls[(ClassInfo, Node)]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) - case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node) + case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node)(val loc: Opt[Loc] = None) var tag = DefnTag(-1) @@ -170,7 +173,9 @@ enum Node: case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) - case LetCall(names, defn, args, isTailRec, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f)) + case x: LetCall => + val LetCall(names, defn, args, isTailRec, body) = x + LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), isTailRec, body.mapName(f))(x.loc) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) @@ -179,9 +184,10 @@ enum Node: case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) - case LetCall(names, defn, args, isTailRec, body) => + case x: LetCall => + val LetCall(names, defn, args, isTailRec, body) = x val names_copy = names.map(_.copy) - LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x))) + LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), isTailRec, body.copy(ctx ++ names_copy.map(x => x.str -> x)))(x.loc) private def toDocument: Document = this match case Result(res) => raw(res |> show_args) <:> raw(s"-- $tag") diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index b46f897b7c..a0c17c7f2f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -607,7 +607,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): List(Expr.Ref(Name("ret"))) ).attachTag(tag) ).attachTag(tag), - ).attachTag(tag) + )().attachTag(tag) ).attachTag(tag) ).attachTag(tag) ).attachTag(tag) @@ -629,7 +629,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): List(Expr.Ref(Name("ctx")), ret), false, Result(List(Expr.Ref(Name("res")))).attachTag(tag) - ).attachTag(tag) + )().attachTag(tag) // Here, we assume we are inside the modcons version of the function and hence have an extra // `ctx` parameter at the start. @@ -659,9 +659,9 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): modConsRefs(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, isTailRec, Result(List(Expr.Ref(Name("res")))).attachTag(tag) - ).attachTag(tag) + )().attachTag(tag) else - LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) + LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) def transformModConsBranch(node: Node)(implicit call: ModConsCallInfo): Node = def makeCall = @@ -685,8 +685,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Result( List(Expr.Ref(Name("res"))) ).attachTag(tag) - ).attachTag(tag) - ).attachTag(tag) + )().attachTag(tag) + )().attachTag(tag) ).attachTag(tag) node match @@ -707,7 +707,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // discard it transformModConsBranch(body) else - LetCall(names, defn, args, isTailRec, transformModConsBranch(body)).attachTag(tag) + LetCall(names, defn, args, isTailRec, transformModConsBranch(body))().attachTag(tag) case _ => throw IRError("unreachable case when transforming mod cons call") def rewriteDefn(d: Defn): Defn = @@ -729,7 +729,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): Expr.Ref(Name("idCtx")) :: d.params.map(Expr.Ref(_)), false, Result(List(Expr.Ref(Name("res")))).attachTag(tag) - ).attachTag(tag) + )().attachTag(tag) ).attachTag(tag) val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall, false) (newDefn, modConsDefn) @@ -795,7 +795,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): if isTailCall(node) && defn_.expectDefn.id == defn.id then Jump(jpDefnRef, args).attachTag(tag) else - LetCall(names, defn_, args, isTailRec, transformNode(body)).attachTag(tag) + LetCall(names, defn_, args, isTailRec, transformNode(body))().attachTag(tag) val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body), false) @@ -806,7 +806,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): defn.params.map(Expr.Ref(_)), false, Result(rets.map(Expr.Ref(_))).attachTag(tag), - ).attachTag(tag) + )().attachTag(tag) val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode, true) (Set(newDefn, jpDef), newDefn) @@ -861,7 +861,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): case LetCall(names, defn, args, isTailRec, body) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) - else LetCall(names, defn, args, isTailRec, transformNode(body)).attachTag(tag) + else LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) // Tail calls to another function in the component will be replaced with a tail call // to the merged function @@ -879,7 +879,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList val namesExpr = names.map(Expr.Ref(_)) val res = Result(namesExpr).attachTag(tag) - val call = LetCall(names, newDefnRef, args, false, res).attachTag(tag) + val call = LetCall(names, newDefnRef, args, false, res)().attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call, false) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match From 8ce1b35ad9553cd1ad4a1e7354c3c59bf1928eeb Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 22:55:00 +0800 Subject: [PATCH 41/59] Add error reporting to IR diff tests, report tailrec IR errors, fix bug --- .../main/scala/mlscript/compiler/ir/IR.scala | 2 +- .../compiler/optimizer/TailRecOpt.scala | 112 +++-- compiler/shared/test/diff-ir/IRTailRec.mls | 407 +++++++++++++++++- .../test/scala/mlscript/compiler/Test.scala | 3 +- .../test/scala/mlscript/compiler/TestIR.scala | 5 +- .../src/test/scala/mlscript/DiffTests.scala | 17 +- 6 files changed, 504 insertions(+), 42 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index e3b6eca99a..30816156ca 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -71,7 +71,7 @@ case class Defn( val resultNum: Int, val body: Node, val isTailRec: Bool, - val declareLoc: Opt[Loc] = None + val loc: Opt[Loc] = None ): override def hashCode: Int = id def getName: String = name diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index a0c17c7f2f..722b27318b 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -5,13 +5,18 @@ import mlscript.compiler.ir.Node._ import scala.annotation.tailrec import mlscript.IntLit import mlscript.utils.shorthands.Bool +import mlscript.Diagnostic +import mlscript.ErrorReport +import mlscript.Message +import mlscript.Message.MessageContext +import mlscript.Loc // fnUid should be the same FreshInt that was used to build the graph being passed into this class -class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): +class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) enum CallInfo: - case NormalCallInfo(src: Defn, defn: Defn) extends CallInfo + case NormalCallInfo(src: Defn, defn: Defn)(val loc: Option[Loc]) extends CallInfo case TailCallInfo(src: Defn, defn: Defn) extends CallInfo case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo @@ -101,12 +106,13 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): ) = letCallNode match case None => searchOptCalls(body)(acc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered - case Some(LetCall(_, defn, _, isTailRec, _)) => + case Some(x: LetCall) => + val LetCall(_, defn, _, isTailRec, _) = x if isTailRec then - throw IRError("not a tail call") - else - val newAcc = acc + NormalCallInfo(src, defn.expectDefn) - searchOptCalls(body)(newAcc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + + val newAcc = acc + NormalCallInfo(src, defn.expectDefn)(x.loc) + searchOptCalls(body)(newAcc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered @tailrec private def searchOptCalls(node: Node)(implicit @@ -122,11 +128,18 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): def updateMapSimple(c: CallInfo) = acc + c + def returnNoneCont = calledDefn match + case None => Left(acc) + case Some(dest) => + Left(updateMapSimple(NormalCallInfo(src, dest)(letCallNode.flatMap(_.loc)))) // treat the discovered call as a normal call + def returnNone = letCallNode match - case Some(LetCall(_, _, _, isTailRec, _)) if isTailRec => throw IRError("not a tail call") - case _ => calledDefn match - case None => Left(acc) - case Some(dest) => Left(updateMapSimple(NormalCallInfo(src, dest))) // treat the discovered call as a normal call + case Some(x: LetCall) => + val LetCall(_, _, _, isTailRec, _) = x + if isTailRec then + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + returnNoneCont + case _ => returnNoneCont node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => @@ -259,9 +272,32 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): val callInScc = scc.contains(defn.expectDefn) - // Only deal with + // Only deal with calls in the scc if callInScc && isTailCall(x) then - Left(updateMapSimple(TailCallInfo(src, defn.expectDefn))) + // If there is an old call marked as @tailcall, it cannot be a tail call, error + + val updatedMap = letCallNode match + case Some(y) => + // If both these calls are marked @tailrec, error + if y.isTailRec && x.isTailRec then + raise(ErrorReport( + List( + msg"multiple calls in the same branch marked with @tailcall" -> None, + msg"first call" -> y.loc, + msg"second call" -> x.loc, + ), + true, + Diagnostic.Compilation + ) + ) + if y.isTailRec then + raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) + + updateMapSimple(NormalCallInfo(src, y.defn.expectDefn)(y.loc)) + + case None => acc + + Left(updatedMap + TailCallInfo(src, defn.expectDefn)) else letCallNode match case None => // OK, we may use this LetCall as the mod cons @@ -270,24 +306,34 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): searchOptCalls(body)(acc, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else // Treat this as a normal call - val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) - case Some(LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld)) => - if isTailRecOld && isTailRec then + case Some(y: LetCall) => + val LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld) = y + if isTailRecOld then // 1. If both the old and newly discovered call are marked with tailrec, error - throw IRError("multiple calls in the same branch marked with tailrec") - else if isTailRecOld then + if isTailRec then + raise(ErrorReport( + List( + msg"multiple calls in the same branch marked with @tailcall" -> None, + msg"first call" -> y.loc, + msg"second call" -> x.loc, + ), + true, + Diagnostic.Compilation + ) + ) // 2. old call is marked as tailrec so we must continue using it as the mod cons call. // make sure the newly discovered call does not use the current call as a parameter val argNames = args.collect { case Expr.Ref(name) => name }.toSet val namesSet = namesOld.toSet val inters = argNames.intersect(namesSet) - if inters.isEmpty then - // Treat this as a normal call - val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) - searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK - else throw IRError("not a tail call") + if !inters.isEmpty then + raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) + // Treat new call as a normal call + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) + searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK else // only include mod cons calls that have one return value if callInScc && defn.expectDefn.resultNum == 1 then @@ -296,13 +342,13 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): // user really should mark which calls should be tailrec // Treat the old call as a normal call - val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)) + val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)(y.loc)) searchOptCalls(body)(newMap, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else // shadow all the variables in this letcall // Treat this as a normal call - val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)) + val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) // checks whether a list of names is equal to a list of trivial expressions referencing those names @@ -915,9 +961,19 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): partitionNodes(nodeMap).map(_.removeMetadata) private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = - val isTailRec = component.nodes.find { _.isTailRec }.isDefined - if isTailRec && filterNormalCalls(component.edges).size != 0 then - throw IRError("at least one function is not tail recursive") // TODO: better error message + val trFn = component.nodes.find { _.isTailRec }.headOption + val normalCall = filterNormalCalls(component.edges).headOption + + (trFn, normalCall) match + case (Some(fn), Some(call)) => + raise(ErrorReport( + List( + msg"function `${fn.name}` is not tail-recursive, but is marked as @tailrec" -> fn.loc, + msg"it could self-recursive through this call, which may not be a tail-call" -> call.loc + ), + true, Diagnostic.Compilation) + ) + case _ => val (modConsComp, other) = optimizeModCons(component, classes) val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index a7046adb0a..c387d086d0 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -761,8 +761,66 @@ class False //│ }, //│ 2 -- #30) -// TODO: should error, but it seems IR errors are not considered errors by the unit test framework -// :e +:ce +class True +class False +fun hello() = + @tailcall hello() + @tailcall hello() + 2 +hello() +//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) +//│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall +//│ ╟── first call +//│ ║ l.768: @tailcall hello() +//│ ║ ^^^^^ +//│ ╟── second call +//│ ║ l.769: @tailcall hello() +//│ ╙── ^^^^^ +//│ ╔══[COMPILATION ERROR] not a tail call +//│ ║ l.768: @tailcall hello() +//│ ╙── ^^^^^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) + +:ce class True class False fun hello() = @@ -783,8 +841,32 @@ hello() //│ }, //│ let* (x$1) = hello() in -- #8 //│ x$1 -- #7) +//│ ╔══[COMPILATION ERROR] not a tail call +//│ ║ l.827: @tailcall hello() +//│ ╙── ^^^^^ //│ -//│ IR Processing Failed: not a tail call +//│ Strongly Connected Tail Calls: +//│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) :interpIR class True @@ -2284,16 +2366,143 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ Interpreted: //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) +:ce +class True +class False +fun b() = + a() + a() +@tailrec +fun a() = + if 0 < 1 then a() + else b() +a() +//│ |#class| |True|↵|#class| |False|↵|#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (x$0) = a() in -- #7 +//│ let* (x$1) = a() in -- #6 +//│ x$1 -- #5 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let x$2 = <(0,1) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ let* (x$4) = a() in -- #16 +//│ jump j$0(x$4) -- #15 +//│ false => +//│ let* (x$5) = b() in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2375: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recursive through this call, which may not be a tail-call +//│ ║ l.2373: a() +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(a, b)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp(0) -- #33 +//│ ) +//│ Def(4, _a_b_opt, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp(0) -- #33 +//│ ) +//│ Def(4, _a_b_opt, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) + class True class False class A(a, b) @tailrec fun a() = A(b(), 1) -fun b() = A(a(), c()) +fun b() = A(@tailcall a(), c()) fun c() = A(b(), 1) a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(a(), c(),); fun c = () => A(b(), 1,); a()} +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(b(), 1,); a()} //│ //│ //│ IR: @@ -2306,7 +2515,7 @@ a() //│ ) //│ Def(1, b, [], //│ 1, -//│ let* (x$2) = a() in -- #22 +//│ let* (x$2) = @tailcall a() in -- #22 //│ let* (x$3) = c() in -- #21 //│ let x$4 = A(x$2,x$3) in -- #20 //│ x$4 -- #19 @@ -2320,8 +2529,190 @@ a() //│ }, //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2499: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recursive through this call, which may not be a tail-call +//│ ║ l.2501: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ +//│ TEST CASE FAILURE: There was an unexpected compilation error //│ -//│ IR Processing Failed: at least one function is not tail recursive +//│ Strongly Connected Tail Calls: +//│ List(Set(c, b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #74 +//│ let* (res) = a_modcons(idCtx) in -- #73 +//│ res -- #72 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #66 +//│ let* (res) = b_modcons(idCtx) in -- #65 +//│ res -- #64 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #57 +//│ let* (res) = c_modcons(idCtx) in -- #56 +//│ res -- #55 +//│ ) +//│ Def(3, _c_b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #43 +//│ _IdContext => +//│ x -- #42 +//│ _Context => +//│ let field = ctx.field in -- #41 +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 +//│ ) +//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #49 +//│ let ctx2ptr = ctx2.ptr in -- #48 +//│ let ctx2field = ctx2.field in -- #47 +//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 +//│ ret -- #44 +//│ ) +//│ Def(5, c_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 +//│ r0 -- #97 +//│ ) +//│ Def(6, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 +//│ r0 -- #99 +//│ ) +//│ Def(7, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 +//│ r0 -- #101 +//│ ) +//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$1 = A(0,1) in -- #91 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 +//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ false => +//│ let scrut = ==(6,tailrecBranch$) in -- #93 +//│ if scrut -- #92 +//│ true => +//│ let* (x$3) = c() in -- #87 +//│ let x$4 = A(0,x$3) in -- #86 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 +//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ false => +//│ let x$6 = A(0,1) in -- #82 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 +//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ ) +//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #74 +//│ let* (res) = a_modcons(idCtx) in -- #73 +//│ res -- #72 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #66 +//│ let* (res) = b_modcons(idCtx) in -- #65 +//│ res -- #64 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #57 +//│ let* (res) = c_modcons(idCtx) in -- #56 +//│ res -- #55 +//│ ) +//│ Def(3, _c_b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #43 +//│ _IdContext => +//│ x -- #42 +//│ _Context => +//│ let field = ctx.field in -- #41 +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 +//│ ) +//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #49 +//│ let ctx2ptr = ctx2.ptr in -- #48 +//│ let ctx2field = ctx2.field in -- #47 +//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 +//│ ret -- #44 +//│ ) +//│ Def(5, c_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 +//│ r0 -- #97 +//│ ) +//│ Def(6, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 +//│ r0 -- #99 +//│ ) +//│ Def(7, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 +//│ r0 -- #101 +//│ ) +//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$1 = A(0,1) in -- #91 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 +//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ false => +//│ let scrut = ==(6,tailrecBranch$) in -- #93 +//│ if scrut -- #92 +//│ true => +//│ let* (x$3) = c() in -- #87 +//│ let x$4 = A(0,x$3) in -- #86 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 +//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ false => +//│ let x$6 = A(0,1) in -- #82 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 +//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ ) +//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) class True class False diff --git a/compiler/shared/test/scala/mlscript/compiler/Test.scala b/compiler/shared/test/scala/mlscript/compiler/Test.scala index 2738c2f011..77b53a9acf 100644 --- a/compiler/shared/test/scala/mlscript/compiler/Test.scala +++ b/compiler/shared/test/scala/mlscript/compiler/Test.scala @@ -7,10 +7,11 @@ import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.TreeDebug import mlscript.compiler.mono.Monomorph import mlscript.compiler.mono.MonomorphError +import mlscript.Diagnostic class DiffTestCompiler extends DiffTests { import DiffTestCompiler.* - override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (List[Str], Option[TypingUnit]) = + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() if (mode.lift) output(PrettyPrinter.showTypingUnit(unit)) diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index bb77e9bed7..2df7ae574a 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -7,11 +7,12 @@ import scala.collection.mutable.StringBuilder import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.ir.{Interpreter, Fresh, FreshInt, Builder} import mlscript.compiler.optimizer.TailRecOpt +import mlscript.Diagnostic class IRDiffTestCompiler extends DiffTests { import IRDiffTestCompiler.* - override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (List[Str], Option[TypingUnit]) = + override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() if (mode.useIR || mode.irVerbose) try @@ -25,7 +26,7 @@ class IRDiffTestCompiler extends DiffTests { output(graph_.toString()) val graph = if (!mode.noTailRecOpt) { - val tailRecOpt = new TailRecOpt(fnUid, classUid, tag) + val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) val (g, comps) = tailRecOpt.run_debug(graph_) output("\nStrongly Connected Tail Calls:") output(comps.toString) diff --git a/shared/src/test/scala/mlscript/DiffTests.scala b/shared/src/test/scala/mlscript/DiffTests.scala index 5b87b0567a..2c4730ad7d 100644 --- a/shared/src/test/scala/mlscript/DiffTests.scala +++ b/shared/src/test/scala/mlscript/DiffTests.scala @@ -58,7 +58,7 @@ class DiffTests /** Hook for dependent projects, like the monomorphizer. */ - def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) + def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) @SuppressWarnings(Array("org.wartremover.warts.RedundantIsInstanceOf")) @@ -149,6 +149,7 @@ class DiffTests case class Mode( expectTypeErrors: Bool = false, + expectCompileErrors: Bool = false, expectWarnings: Bool = false, expectParseErrors: Bool = false, fixme: Bool = false, @@ -190,6 +191,7 @@ class DiffTests var parseOnly = basePath.headOption.contains("parser") var allowTypeErrors = false + var allowCompileErrors = false var allowParseErrors = false var showRelativeLineNums = false var noJavaScript = false @@ -227,6 +229,7 @@ class DiffTests out.println(line) val newMode = line.tail.takeWhile(!_.isWhitespace) match { case "e" => mode.copy(expectTypeErrors = true) + case "ce" => mode.copy(expectCompileErrors = true) case "w" => mode.copy(expectWarnings = true) case "pe" => mode.copy(expectParseErrors = true) case "p" => mode.copy(showParse = true) @@ -246,6 +249,7 @@ class DiffTests case "precise-rec-typing" => mode.copy(preciselyTypeRecursion = true) case "ParseOnly" => parseOnly = true; mode case "AllowTypeErrors" => allowTypeErrors = true; mode + case "AllowCompileErrors" => allowCompileErrors = true; mode case "AllowParseErrors" => allowParseErrors = true; mode case "AllowRuntimeErrors" => allowRuntimeErrors = true; mode case "ShowRelativeLineNums" => showRelativeLineNums = true; mode @@ -356,6 +360,7 @@ class DiffTests var totalTypeErrors = 0 var totalParseErrors = 0 + var totalCompileErrors = 0 var totalWarnings = 0 var totalRuntimeErrors = 0 var totalCodeGenErrors = 0 @@ -373,6 +378,9 @@ class DiffTests case Diagnostic.Parsing => totalParseErrors += 1 s"╔══[PARSE ERROR] " + case Diagnostic.Compilation => + totalCompileErrors += 1 + s"╔══[COMPILATION ERROR] " case _ => // TODO customize too totalTypeErrors += 1 s"╔══[ERROR] " @@ -429,6 +437,9 @@ class DiffTests if (!allowParseErrors && !mode.expectParseErrors && diag.isInstanceOf[ErrorReport] && (diag.source =:= Diagnostic.Lexing || diag.source =:= Diagnostic.Parsing)) { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } + if (!allowCompileErrors + && !mode.expectCompileErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Compilation) + { output("TEST CASE FAILURE: There was an unexpected compilation error"); failures += globalLineNum } if (!allowTypeErrors && !allowParseErrors && !mode.expectWarnings && diag.isInstanceOf[WarningReport]) { output("TEST CASE FAILURE: There was an unexpected warning"); failures += globalLineNum } @@ -468,7 +479,7 @@ class DiffTests val newMode = if (useIR) { mode.copy(useIR = true) } else mode val newNewMode = if (noTailRec) { newMode.copy(noTailRecOpt = true) } else newMode - val (postLines, nuRes) = postProcess(newNewMode, basePath, testName, res, output) + val (postLines, nuRes) = postProcess(newNewMode, basePath, testName, res, output, raise) postLines.foreach(output) if (parseOnly) @@ -1048,6 +1059,8 @@ class DiffTests { output("TEST CASE FAILURE: There was an unexpected lack of parse error"); failures += blockLineNum } if (mode.expectTypeErrors && totalTypeErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of type error"); failures += blockLineNum } + if (mode.expectCompileErrors && totalCompileErrors =:= 0) + { output("TEST CASE FAILURE: There was an unexpected lack of compilation error"); failures += blockLineNum } if (mode.expectWarnings && totalWarnings =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of warning"); failures += blockLineNum } if (mode.expectCodeGenErrors && totalCodeGenErrors =:= 0) From ee1596965bc228986a43b5750c30604cd57848e1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 23:00:29 +0800 Subject: [PATCH 42/59] fix tests --- compiler/shared/test/diff-ir/IRTailRec.mls | 1518 ++--------------- .../diff/Defunctionalize/RecursiveFunc.mls | 2 +- .../test/diff/Defunctionalize/SimpleFunc.mls | 2 +- .../test/scala/mlscript/compiler/TestIR.scala | 9 +- 4 files changed, 122 insertions(+), 1409 deletions(-) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index c387d086d0..4d1506aca3 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -94,53 +94,9 @@ fact(1, 5) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 -//│ true => -//│ jump j$0(acc$0) -- #5 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, fact_jp, [acc$0,n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #36 -//│ if x$0 -- #35 -//│ true => -//│ jump j$0(acc$0) -- #31 -//│ false => -//│ let x$2 = *(acc$0,n$0) in -- #34 -//│ let x$3 = -(n$0,1) in -- #33 -//│ jump fact_jp(x$2,x$3) -- #32 -//│ ) -//│ Def(3, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 -//│ r0 -- #37 -//│ ) -//│ }, -//│ let* (x$5) = fact(1,5) in -- #30 -//│ x$5 -- #29) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -188,87 +144,9 @@ fact(1, 5) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let x$0 = >(n$0,0) in -- #32 -//│ if x$0 -- #31 -//│ true => -//│ let x$6 = -(n$0,1) in -- #28 -//│ jump j$0(x$6,acc$0,n$0) -- #27 -//│ false => -//│ jump j$0(0,acc$0,n$0) -- #30 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, fact, [acc$0,n$0], -//│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 -//│ r0 -- #59 -//│ ) -//│ Def(1, j$1, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ let scrut = ==(2,tailrecBranch$) in -- #57 -//│ if scrut -- #56 -//│ true => -//│ let x$2 = <=(j$0_x$1,0) in -- #55 -//│ if x$2 -- #54 -//│ true => -//│ jump j$1(j$0_acc$0) -- #51 -//│ false => -//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 -//│ false => -//│ let x$0 = >(fact_n$0,0) in -- #50 -//│ if x$0 -- #49 -//│ true => -//│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 -//│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 -//│ ) -//│ }, -//│ let* (x$7) = fact(1,5) in -- #40 -//│ x$7 -- #39) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -487,110 +365,9 @@ g(6, 0) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, double, [x$0], -//│ 1, -//│ let x$1 = *(x$0,2) in -- #3 -//│ x$1 -- #2 -//│ ) -//│ Def(1, f, [n$0,acc$0], -//│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 -//│ r0 -- #99 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #7 -//│ ) -//│ Def(3, g, [m$0,acc$1], -//│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 -//│ r0 -- #97 -//│ ) -//│ Def(4, j$1, [x$9], -//│ 1, -//│ x$9 -- #35 -//│ ) -//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ let scrut = ==(1,tailrecBranch$) in -- #95 -//│ if scrut -- #94 -//│ true => -//│ let x$2 = ==(f_n$0,0) in -- #93 -//│ if x$2 -- #92 -//│ true => -//│ let* (x$4) = double(f_acc$0) in -- #88 -//│ jump j$0(x$4) -- #87 -//│ false => -//│ let x$5 = -(f_n$0,1) in -- #91 -//│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 -//│ false => -//│ let x$8 = ==(g_m$0,0) in -- #86 -//│ if x$8 -- #85 -//│ true => -//│ let* (x$10) = double(g_acc$1) in -- #81 -//│ let x$11 = -(0,x$10) in -- #80 -//│ jump j$1(x$11) -- #79 -//│ false => -//│ let x$12 = -(g_m$0,1) in -- #84 -//│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 -//│ ) -//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 -//│ ) -//│ }, -//│ let* (x$15) = g(6,0) in -- #70 -//│ x$15 -- #69) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -665,27 +442,11 @@ class False //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, f, [a$0,b$0,c$0], -//│ 1, -//│ let* (x$0) = g(0,0) in -- #7 -//│ x$0 -- #6 -//│ ) -//│ Def(1, g, [d$0,e$0], -//│ 1, -//│ let* (x$1) = h(0,0,0,0) in -- #19 -//│ x$1 -- #18 -//│ ) -//│ Def(2, h, [p$0,q$0,r$0,s$0], -//│ 1, -//│ let* (x$2) = f(0,0,0) in -- #29 -//│ x$2 -- #28 -//│ ) -//│ }, -//│ 2 -- #30) //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, @@ -722,91 +483,33 @@ class False //│ ) //│ }, //│ 2 -- #30) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, f, [a$0,b$0,c$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 -//│ r0 -- #44 -//│ ) -//│ Def(1, g, [d$0,e$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 -//│ r0 -- #42 -//│ ) -//│ Def(2, h, [p$0,q$0,r$0,s$0], -//│ 1, -//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 -//│ r0 -- #40 -//│ ) -//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #38 -//│ if scrut -- #37 -//│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 -//│ false => -//│ let scrut = ==(1,tailrecBranch$) in -- #36 -//│ if scrut -- #35 -//│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 -//│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 -//│ ) -//│ }, -//│ 2 -- #30) - -:ce -class True -class False -fun hello() = - @tailcall hello() - @tailcall hello() - 2 -hello() -//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | -//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #8 -//│ let* (x$1) = @tailcall hello() in -- #7 -//│ 2 -- #6 -//│ ) -//│ }, -//│ let* (x$2) = hello() in -- #12 -//│ x$2 -- #11) -//│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall -//│ ╟── first call -//│ ║ l.768: @tailcall hello() -//│ ║ ^^^^^ -//│ ╟── second call -//│ ║ l.769: @tailcall hello() -//│ ╙── ^^^^^ -//│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.768: @tailcall hello() -//│ ╙── ^^^^^ -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #8 -//│ let* (x$1) = @tailcall hello() in -- #7 -//│ 2 -- #6 -//│ ) -//│ }, -//│ let* (x$2) = hello() in -- #12 -//│ x$2 -- #11) + +:ce +class True +class False +fun hello() = + @tailcall hello() + @tailcall hello() + 2 +hello() +//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall +//│ ╟── first call +//│ ║ l.657: @tailcall hello() +//│ ║ ^^^^^ +//│ ╟── second call +//│ ║ l.658: @tailcall hello() +//│ ╙── ^^^^^ +//│ ╔══[COMPILATION ERROR] not a tail call +//│ ║ l.657: @tailcall hello() +//│ ╙── ^^^^^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(hello)) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -832,30 +535,12 @@ hello() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #4 -//│ 2 -- #3 -//│ ) -//│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.827: @tailcall hello() +//│ ║ l.706: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, hello, [], -//│ 1, -//│ let* (x$0) = @tailcall hello() in -- #4 -//│ 2 -- #3 -//│ ) -//│ }, -//│ let* (x$1) = hello() in -- #8 -//│ x$1 -- #7) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -883,95 +568,9 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #27 -//│ Cons => -//│ let x$1 = xs$0.t in -- #23 -//│ let x$2 = xs$0.h in -- #22 -//│ let x$3 = +(x$2,1) in -- #21 -//│ let* (x$4) = @tailcall addOne(x$1) in -- #20 -//│ let x$5 = Cons(x$3,x$4) in -- #19 -//│ jump j$0(x$5) -- #18 -//│ Nil => -//│ let x$6 = Nil() in -- #26 -//│ jump j$0(x$6) -- #25 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 -//│ res -- #78 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #59 -//│ _IdContext => -//│ x -- #58 -//│ _Context => -//│ let field = ctx.field in -- #57 -//│ let ptr = ctx.ptr in -- #56 -//│ let _ = assign ptr.t := x in -- #55 -//│ let acc = ctx.acc in -- #54 -//│ acc -- #53 -//│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #65 -//│ let ctx2ptr = ctx2.ptr in -- #64 -//│ let ctx2field = ctx2.field in -- #63 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 -//│ ret -- #60 -//│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #91 -//│ Cons => -//│ let x$1 = xs$0.t in -- #87 -//│ let x$2 = xs$0.h in -- #86 -//│ let x$3 = +(x$2,1) in -- #85 -//│ let x$5 = Cons(x$3,0) in -- #84 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 -//│ jump addOne_modcons_jp(composed,x$1) -- #81 -//│ Nil => -//│ let x$6 = Nil() in -- #90 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 -//│ res -- #88 -//│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 -//│ r0 -- #92 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #52 -//│ let x$8 = Cons(3,x$7) in -- #51 -//│ let x$9 = Cons(2,x$8) in -- #50 -//│ let x$10 = Cons(1,x$9) in -- #49 -//│ let* (x$11) = addOne(x$10) in -- #48 -//│ x$11 -- #47) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { @@ -1215,53 +814,11 @@ a(S(S(S(Zero)))) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ case n$0 of -- #23 -//│ S => -//│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailcall b(x$1) in -- #14 -//│ let x$3 = S(x$2) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ Zero => -//│ let x$4 = Zero() in -- #22 -//│ let x$5 = S(x$4) in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ case n$1 of -- #55 -//│ S => -//│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailcall a(x$7) in -- #42 -//│ let x$9 = S(x$8) in -- #41 -//│ let x$10 = S(x$9) in -- #40 -//│ jump j$1(x$10) -- #39 -//│ Zero => -//│ let x$11 = Zero() in -- #54 -//│ let x$12 = S(x$11) in -- #53 -//│ let x$13 = S(x$12) in -- #52 -//│ jump j$1(x$13) -- #51 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, @@ -1359,220 +916,36 @@ a(S(S(S(Zero)))) //│ let* (x$18) = a(x$17) in -- #70 //│ x$18 -- #69) //│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +@tailrec fun addOne(xs) = + if xs is + Cons(h, t) then + val next = @tailcall addOne(t) + val ret = Cons(h + 1, next) + val rett = ret + rett + Nil then + Nil +addOne(Cons(1, Cons(2, Cons(3, Nil)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ +//│ +//│ IR: +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(addOne)) +//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #117 -//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 -//│ res -- #115 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ let idCtx = _IdContext() in -- #103 -//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 -//│ res -- #101 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ Def(4, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #81 -//│ _IdContext => -//│ x -- #80 -//│ _Context => -//│ let field = ctx.field in -- #79 -//│ let ptr = ctx.ptr in -- #78 -//│ let _ = assign ptr.x := x in -- #77 -//│ let acc = ctx.acc in -- #76 -//│ acc -- #75 -//│ ) -//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #87 -//│ let ctx2ptr = ctx2.ptr in -- #86 -//│ let ctx2field = ctx2.field in -- #85 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 -//│ ret -- #82 -//│ ) -//│ Def(6, b_modcons, [ctx,n$1], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 -//│ r0 -- #155 -//│ ) -//│ Def(7, a_modcons, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 -//│ r0 -- #157 -//│ ) -//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], -//│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #153 -//│ if scrut -- #152 -//│ true => -//│ case a_modcons_n$0 of -- #151 -//│ S => -//│ let x$1 = a_modcons_n$0.x in -- #146 -//│ let x$3 = S(0) in -- #145 -//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 -//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 -//│ Zero => -//│ let x$4 = Zero() in -- #150 -//│ let x$5 = S(x$4) in -- #149 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 -//│ res -- #147 -//│ false => -//│ case b_modcons_n$1 of -- #141 -//│ S => -//│ let x$7 = b_modcons_n$1.x in -- #135 -//│ let x$9 = S(0) in -- #134 -//│ let x$10 = S(x$9) in -- #133 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 -//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 -//│ Zero => -//│ let x$11 = Zero() in -- #140 -//│ let x$12 = S(x$11) in -- #139 -//│ let x$13 = S(x$12) in -- #138 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 -//│ res -- #136 -//│ ) -//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) -//│ -//│ Interpreted: -//│ S(S(S(S(S(S(Zero())))))) - -:interpIR -class True -class False -class Cons(h, t) -class Nil -@tailrec fun addOne(xs) = - if xs is - Cons(h, t) then - val next = @tailcall addOne(t) - val ret = Cons(h + 1, next) - val rett = ret - rett - Nil then - Nil -addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ case xs$0 of -- #30 -//│ Cons => -//│ let x$1 = xs$0.t in -- #26 -//│ let x$2 = xs$0.h in -- #25 -//│ let* (x$3) = @tailcall addOne(x$1) in -- #24 -//│ let x$4 = +(x$2,1) in -- #23 -//│ let x$5 = Cons(x$4,x$3) in -- #22 -//│ jump j$0(x$5) -- #21 -//│ Nil => -//│ let x$6 = Nil() in -- #29 -//│ jump j$0(x$6) -- #28 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #55 -//│ let x$8 = Cons(3,x$7) in -- #54 -//│ let x$9 = Cons(2,x$8) in -- #53 -//│ let x$10 = Cons(1,x$9) in -- #52 -//│ let* (x$11) = addOne(x$10) in -- #51 -//│ x$11 -- #50) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #83 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 -//│ res -- #81 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #62 -//│ _IdContext => -//│ x -- #61 -//│ _Context => -//│ let field = ctx.field in -- #60 -//│ let ptr = ctx.ptr in -- #59 -//│ let _ = assign ptr.t := x in -- #58 -//│ let acc = ctx.acc in -- #57 -//│ acc -- #56 -//│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #68 -//│ let ctx2ptr = ctx2.ptr in -- #67 -//│ let ctx2field = ctx2.field in -- #66 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 -//│ ret -- #63 -//│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], -//│ 1, -//│ case xs$0 of -- #94 -//│ Cons => -//│ let x$1 = xs$0.t in -- #90 -//│ let x$2 = xs$0.h in -- #89 -//│ let x$4 = +(x$2,1) in -- #88 -//│ let x$5 = Cons(x$4,0) in -- #87 -//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 -//│ jump addOne_modcons_jp(composed,x$1) -- #84 -//│ Nil => -//│ let x$6 = Nil() in -- #93 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 -//│ res -- #91 -//│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], -//│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 -//│ r0 -- #95 -//│ ) -//│ }, -//│ let x$7 = Nil() in -- #55 -//│ let x$8 = Cons(3,x$7) in -- #54 -//│ let x$9 = Cons(2,x$8) in -- #53 -//│ let x$10 = Cons(1,x$9) in -- #52 -//│ let* (x$11) = addOne(x$10) in -- #51 -//│ x$11 -- #50) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, addOne, [xs$0], +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 //│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 @@ -1659,166 +1032,11 @@ b(16) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ case x$0 of -- #46 -//│ Cons => -//│ let x$2 = x$0.n in -- #42 -//│ let x$3 = x$0.m in -- #41 -//│ let x$4 = <(x$3,0) in -- #40 -//│ if x$4 -- #39 -//│ true => -//│ let x$6 = Nil() in -- #19 -//│ let x$7 = Cons(-1,x$6) in -- #18 -//│ jump j$1(x$7) -- #17 -//│ false => -//│ let x$8 = *(x$3,4) in -- #38 -//│ let x$9 = -(x$3,2) in -- #37 -//│ let* (x$10) = b(x$9) in -- #36 -//│ let x$11 = Cons(x$8,x$10) in -- #35 -//│ jump j$1(x$11) -- #34 -//│ Nil => -//│ let x$12 = Nil() in -- #45 -//│ jump j$0(x$12) -- #44 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #10 -//│ ) -//│ Def(3, b, [n$0], -//│ 1, -//│ let x$13 = <=(n$0,0) in -- #75 -//│ if x$13 -- #74 -//│ true => -//│ let x$15 = Nil() in -- #59 -//│ let x$16 = Cons(0,x$15) in -- #58 -//│ jump j$2(x$16) -- #57 -//│ false => -//│ let x$17 = Nil() in -- #73 -//│ let x$18 = Cons(n$0,x$17) in -- #72 -//│ let* (x$19) = a(x$18) in -- #71 -//│ jump j$2(x$19) -- #70 -//│ ) -//│ Def(4, j$2, [x$14], -//│ 1, -//│ x$14 -- #50 -//│ ) -//│ }, -//│ let* (x$20) = b(16) in -- #81 -//│ x$20 -- #80) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [x$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #129 -//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 -//│ res -- #127 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #10 -//│ ) -//│ Def(3, b, [n$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #107 -//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 -//│ res -- #105 -//│ ) -//│ Def(4, j$2, [x$14], -//│ 1, -//│ x$14 -- #50 -//│ ) -//│ Def(5, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #88 -//│ _IdContext => -//│ x -- #87 -//│ _Context => -//│ let field = ctx.field in -- #86 -//│ let ptr = ctx.ptr in -- #85 -//│ let _ = assign ptr.n := x in -- #84 -//│ let acc = ctx.acc in -- #83 -//│ acc -- #82 -//│ ) -//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #94 -//│ let ctx2ptr = ctx2.ptr in -- #93 -//│ let ctx2field = ctx2.field in -- #92 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 -//│ ret -- #89 -//│ ) -//│ Def(7, b_modcons, [ctx,n$0], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 -//│ r0 -- #169 -//│ ) -//│ Def(8, a_modcons, [ctx,x$0], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 -//│ r0 -- #171 -//│ ) -//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], -//│ 1, -//│ let scrut = ==(8,tailrecBranch$) in -- #167 -//│ if scrut -- #166 -//│ true => -//│ case a_modcons_x$0 of -- #165 -//│ Cons => -//│ let x$2 = a_modcons_x$0.n in -- #161 -//│ let x$3 = a_modcons_x$0.m in -- #160 -//│ let x$4 = <(x$3,0) in -- #159 -//│ if x$4 -- #158 -//│ true => -//│ let x$6 = Nil() in -- #151 -//│ let x$7 = Cons(-1,x$6) in -- #150 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 -//│ res -- #148 -//│ false => -//│ let x$8 = *(x$3,4) in -- #157 -//│ let x$9 = -(x$3,2) in -- #156 -//│ let x$11 = Cons(x$8,0) in -- #155 -//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 -//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 -//│ Nil => -//│ let x$12 = Nil() in -- #164 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 -//│ res -- #162 -//│ false => -//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 -//│ if x$13 -- #146 -//│ true => -//│ let x$15 = Nil() in -- #142 -//│ let x$16 = Cons(0,x$15) in -- #141 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 -//│ res -- #139 -//│ false => -//│ let x$17 = Nil() in -- #145 -//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 -//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 -//│ ) -//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 -//│ ) -//│ }, -//│ let* (x$20) = b(16) in -- #81 -//│ x$20 -- #80) -//│ -//│ Promoted: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, @@ -2120,152 +1338,9 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ case xs$0 of -- #54 -//│ Cons => -//│ let x$1 = xs$0.t in -- #50 -//│ let x$2 = xs$0.h in -- #49 -//│ let x$3 = >(x$2,5) in -- #48 -//│ if x$3 -- #47 -//│ true => -//│ let* (x$5) = foo(x$1) in -- #17 -//│ jump j$1(x$5) -- #16 -//│ false => -//│ let x$6 = <(x$2,3) in -- #46 -//│ if x$6 -- #45 -//│ true => -//│ jump j$2(-1,x$1,x$2) -- #42 -//│ false => -//│ jump j$2(100,x$1,x$2) -- #44 -//│ Nil => -//│ let x$11 = Nil() in -- #53 -//│ jump j$0(x$11) -- #52 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { -//│ Def(0, foo, [xs$0], -//│ 1, -//│ let idCtx = _IdContext() in -- #137 -//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 -//│ res -- #135 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, j$1, [x$4], -//│ 1, -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) -//│ Def(4, _foo_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #110 -//│ _IdContext => -//│ x -- #109 -//│ _Context => -//│ let field = ctx.field in -- #108 -//│ let ptr = ctx.ptr in -- #107 -//│ let _ = assign ptr.t := x in -- #106 -//│ let acc = ctx.acc in -- #105 -//│ acc -- #104 -//│ ) -//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #116 -//│ let ctx2ptr = ctx2.ptr in -- #115 -//│ let ctx2field = ctx2.field in -- #114 -//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 -//│ ret -- #111 -//│ ) -//│ Def(7, foo_modcons, [ctx,xs$0], -//│ 1, -//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 -//│ r0 -- #172 -//│ ) -//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #170 -//│ if scrut -- #169 -//│ true => -//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 -//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 -//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 -//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 -//│ false => -//│ case foo_modcons_xs$0 of -- #163 -//│ Cons => -//│ let x$1 = foo_modcons_xs$0.t in -- #159 -//│ let x$2 = foo_modcons_xs$0.h in -- #158 -//│ let x$3 = >(x$2,5) in -- #157 -//│ if x$3 -- #156 -//│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 -//│ false => -//│ let x$6 = <(x$2,3) in -- #155 -//│ if x$6 -- #154 -//│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 -//│ false => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 -//│ Nil => -//│ let x$11 = Nil() in -- #162 -//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 -//│ res -- #160 -//│ ) -//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], -//│ 1, -//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 -//│ ) -//│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { @@ -2382,249 +1457,78 @@ a() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (x$0) = a() in -- #7 -//│ let* (x$1) = a() in -- #6 -//│ x$1 -- #5 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let x$2 = <(0,1) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ let* (x$4) = a() in -- #16 -//│ jump j$0(x$4) -- #15 -//│ false => -//│ let* (x$5) = b() in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2375: @tailrec +//│ ║ l.2048: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.2373: a() +//│ ║ l.2046: a() //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(a, b)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (r0) = _a_b_opt(0) in -- #44 -//│ r0 -- #43 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let* (r0) = _a_b_opt(1) in -- #42 -//│ r0 -- #41 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ Def(3, _a_b_opt_jp, [tailrecBranch$], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #39 -//│ if scrut -- #38 -//│ true => -//│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp(1) -- #36 -//│ false => -//│ let x$2 = <(0,1) in -- #35 -//│ if x$2 -- #34 -//│ true => -//│ jump _a_b_opt_jp(1) -- #32 -//│ false => -//│ jump _a_b_opt_jp(0) -- #33 -//│ ) -//│ Def(4, _a_b_opt, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, b, [], -//│ 1, -//│ let* (r0) = _a_b_opt(0) in -- #44 -//│ r0 -- #43 -//│ ) -//│ Def(1, a, [], -//│ 1, -//│ let* (r0) = _a_b_opt(1) in -- #42 -//│ r0 -- #41 -//│ ) -//│ Def(2, j$0, [x$3], -//│ 1, -//│ x$3 -- #11 -//│ ) -//│ Def(3, _a_b_opt_jp, [tailrecBranch$], -//│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #39 -//│ if scrut -- #38 -//│ true => -//│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp(1) -- #36 -//│ false => -//│ let x$2 = <(0,1) in -- #35 -//│ if x$2 -- #34 -//│ true => -//│ jump _a_b_opt_jp(1) -- #32 -//│ false => -//│ jump _a_b_opt_jp(0) -- #33 -//│ ) -//│ Def(4, _a_b_opt, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #27 -//│ x$6 -- #26) - -class True -class False -class A(a, b) -@tailrec -fun a() = A(b(), 1) -fun b() = A(@tailcall a(), c()) -fun c() = A(b(), 1) -a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(b(), 1,); a()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = @tailcall a() in -- #22 -//│ let* (x$3) = c() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let* (x$5) = b() in -- #32 -//│ let x$6 = A(x$5,1) in -- #31 -//│ x$6 -- #30 -//│ ) -//│ }, -//│ let* (x$7) = a() in -- #36 -//│ x$7 -- #35) -//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2499: @tailrec -//│ ║ ^^^^^^^ -//│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.2501: fun b() = A(@tailcall a(), c()) -//│ ╙── ^ -//│ TEST CASE FAILURE: There was an unexpected compilation error -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(c, b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #74 -//│ let* (res) = a_modcons(idCtx) in -- #73 -//│ res -- #72 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #66 -//│ let* (res) = b_modcons(idCtx) in -- #65 -//│ res -- #64 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #57 -//│ let* (res) = c_modcons(idCtx) in -- #56 -//│ res -- #55 -//│ ) -//│ Def(3, _c_b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #43 -//│ _IdContext => -//│ x -- #42 -//│ _Context => -//│ let field = ctx.field in -- #41 -//│ let ptr = ctx.ptr in -- #40 -//│ let _ = assign ptr.a := x in -- #39 -//│ let acc = ctx.acc in -- #38 -//│ acc -- #37 -//│ ) -//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #49 -//│ let ctx2ptr = ctx2.ptr in -- #48 -//│ let ctx2field = ctx2.field in -- #47 -//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 -//│ ret -- #44 -//│ ) -//│ Def(5, c_modcons, [ctx], +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 -//│ r0 -- #97 +//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ r0 -- #43 //│ ) -//│ Def(6, b_modcons, [ctx], +//│ Def(1, a, [], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 -//│ r0 -- #99 +//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ r0 -- #41 //│ ) -//│ Def(7, a_modcons, [ctx], +//│ Def(2, j$0, [x$3], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 -//│ r0 -- #101 +//│ x$3 -- #11 //│ ) -//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ Def(3, _a_b_opt_jp, [tailrecBranch$], //│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #95 -//│ if scrut -- #94 +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 //│ true => -//│ let x$1 = A(0,1) in -- #91 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 -//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp(1) -- #36 //│ false => -//│ let scrut = ==(6,tailrecBranch$) in -- #93 -//│ if scrut -- #92 +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 //│ true => -//│ let* (x$3) = c() in -- #87 -//│ let x$4 = A(0,x$3) in -- #86 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 -//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ jump _a_b_opt_jp(1) -- #32 //│ false => -//│ let x$6 = A(0,1) in -- #82 -//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 -//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ jump _a_b_opt_jp(0) -- #33 //│ ) -//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ Def(4, _a_b_opt, [tailrecBranch$], //│ 1, -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 //│ ) //│ }, -//│ let* (x$7) = a() in -- #36 -//│ x$7 -- #35) +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) + +:ce +class True +class False +class A(a, b) +@tailrec +fun a() = A(b(), 1) +fun b() = A(@tailcall a(), c()) +fun c() = A(b(), 1) +a() +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(b(), 1,); a()} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2148: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recursive through this call, which may not be a tail-call +//│ ║ l.2150: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(c, b, a)) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { @@ -2727,103 +1631,9 @@ a() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = a() in -- #22 -//│ let* (x$3) = c() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = a_modcons(idCtx) in -- #62 -//│ res -- #61 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #55 -//│ let* (res) = b_modcons(idCtx) in -- #54 -//│ res -- #53 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #40 -//│ _IdContext => -//│ x -- #39 -//│ _Context => -//│ let field = ctx.field in -- #38 -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.a := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #46 -//│ let ctx2ptr = ctx2.ptr in -- #45 -//│ let ctx2field = ctx2.field in -- #44 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 -//│ ret -- #41 -//│ ) -//│ Def(5, b_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 -//│ r0 -- #81 -//│ ) -//│ Def(6, a_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 -//│ r0 -- #83 -//│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #79 -//│ if scrut -- #78 -//│ true => -//│ let x$1 = A(0,1) in -- #77 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 -//│ false => -//│ let* (x$3) = c() in -- #73 -//│ let x$4 = A(0,x$3) in -- #72 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 -//│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { @@ -2912,111 +1722,9 @@ a() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = c() in -- #22 -//│ let* (x$3) = a() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #69 -//│ let* (res) = a_modcons(idCtx) in -- #68 -//│ res -- #67 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #61 -//│ let* (res) = b_modcons(idCtx) in -- #60 -//│ res -- #59 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #46 -//│ _IdContext => -//│ x -- #45 -//│ _Context => -//│ let field = ctx.field in -- #44 -//│ let scrut = ==(1,field) in -- #43 -//│ if scrut -- #42 -//│ true => -//│ let ptr = ctx.ptr in -- #41 -//│ let _ = assign ptr.a := x in -- #40 -//│ let acc = ctx.acc in -- #39 -//│ acc -- #38 -//│ false => -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.b := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #52 -//│ let ctx2ptr = ctx2.ptr in -- #51 -//│ let ctx2field = ctx2.field in -- #50 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 -//│ ret -- #47 -//│ ) -//│ Def(5, b_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 -//│ r0 -- #87 -//│ ) -//│ Def(6, a_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 -//│ r0 -- #89 -//│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #85 -//│ if scrut -- #84 -//│ true => -//│ let x$1 = A(0,1) in -- #83 -//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 -//│ false => -//│ let* (x$2) = c() in -- #79 -//│ let x$4 = A(x$2,0) in -- #78 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 -//│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { diff --git a/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls b/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls index efa36b2e69..a39292fb77 100644 --- a/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls +++ b/compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls @@ -40,6 +40,6 @@ count(new List(new List(new Nil(undefined, false), true), true)) //│ Code(List(count$1((new List$1)((new List$1)((new Nil$2)(undefined, false,), true,), true,),))) //│ } //│ Mono: -//│ ╔══[ERROR] Post-process failed to produce AST. +//│ ╔══[COMPILATION ERROR] Post-process failed to produce AST. //│ ╙── //│ diff --git a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls index e3ec697b00..c97eddf260 100644 --- a/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls +++ b/compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls @@ -53,6 +53,6 @@ let b = foo(23, false) //│ let b$2 = foo$3(23, false,) //│ } //│ Mono: -//│ ╔══[ERROR] Post-process failed to produce AST. +//│ ╔══[COMPILATION ERROR] Post-process failed to produce AST. //│ ╙── //│ diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 2df7ae574a..1bdc7ab79f 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -23,7 +23,9 @@ class IRDiffTestCompiler extends DiffTests { val gb = Builder(Fresh(), fnUid, classUid, tag) val graph_ = gb.buildGraph(unit) - output(graph_.toString()) + + if mode.noTailRecOpt then + output(graph_.toString()) val graph = if (!mode.noTailRecOpt) { val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) @@ -34,7 +36,10 @@ class IRDiffTestCompiler extends DiffTests { } else { graph_ } - output(graph.toString()) + + if mode.noTailRecOpt then + output(graph.toString()) + output("\nPromoted:") output(graph.toString()) var interp_result: Opt[Str] = None From 5af362109b6f58046f1551a3a578880da5886e85 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 23:02:18 +0800 Subject: [PATCH 43/59] fix tests --- compiler/shared/test/diff-ir/IR.mls | 550 +---- compiler/shared/test/diff-ir/IRComplex.mls | 478 ---- compiler/shared/test/diff-ir/IRRec.mls | 1988 ++--------------- compiler/shared/test/diff-ir/IRTailRec.mls | 1594 ++++++++++--- .../test/scala/mlscript/compiler/TestIR.scala | 4 +- 5 files changed, 1454 insertions(+), 3160 deletions(-) diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 4061b25e99..9181242f9b 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -15,44 +15,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, mktup2, [x$0,y$0], -//│ 1, -//│ let* (x$1) = mktup(x$0,y$0) in -- #7 -//│ x$1 -- #6 -//│ ) -//│ Def(1, mktup, [x$2,y$1], -//│ 1, -//│ let x$3 = Pair(x$2,y$1) in -- #14 -//│ x$3 -- #13 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (x$4) = mktup2(1,2) in -- #22 -//│ x$4 -- #21 -//│ ) -//│ }, -//│ let* (x$5) = foo() in -- #26 -//│ x$5 -- #25) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, mktup2, [x$0,y$0], -//│ 1, -//│ let* (x$1) = mktup(x$0,y$0) in -- #7 -//│ x$1 -- #6 -//│ ) -//│ Def(1, mktup, [x$2,y$1], -//│ 1, -//│ let x$3 = Pair(x$2,y$1) in -- #14 -//│ x$3 -- #13 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let* (x$4) = mktup2(1,2) in -- #22 -//│ x$4 -- #21 -//│ ) -//│ }, -//│ let* (x$5) = foo() in -- #26 -//│ x$5 -- #25) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -91,52 +53,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [pair$0], -//│ 1, -//│ case pair$0 of -- #16 -//│ Pair => -//│ let x$1 = pair$0.y in -- #15 -//│ let x$2 = pair$0.x in -- #14 -//│ let x$3 = Pair(x$2,x$1) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = Pair(1,2) in -- #28 -//│ let* (x$5) = foo(x$4) in -- #27 -//│ x$5 -- #26 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #32 -//│ x$6 -- #31) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [pair$0], -//│ 1, -//│ case pair$0 of -- #16 -//│ Pair => -//│ let x$1 = pair$0.y in -- #15 -//│ let x$2 = pair$0.x in -- #14 -//│ let x$3 = Pair(x$2,x$1) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = Pair(1,2) in -- #28 -//│ let* (x$5) = foo(x$4) in -- #27 -//│ x$5 -- #26 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #32 -//│ x$6 -- #31) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -187,72 +103,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, silly, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #29 -//│ case pair$0 of -- #28 -//│ Pair => -//│ let x$3 = pair$0.y in -- #27 -//│ let x$4 = pair$0.x in -- #26 -//│ case pair$0 of -- #25 -//│ Pair => -//│ let x$6 = pair$0.y in -- #24 -//│ let x$7 = pair$0.x in -- #23 -//│ let x$8 = +(x$7,1) in -- #22 -//│ jump j$1(x$8) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ let x$2 = +(x$1,1) in -- #6 -//│ x$2 -- #5 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #13 -//│ ) -//│ Def(3, foo, [], -//│ 1, -//│ let x$9 = Pair(0,1) in -- #43 -//│ let* (x$10) = silly(x$9) in -- #42 -//│ x$10 -- #41 -//│ ) -//│ }, -//│ let* (x$11) = foo() in -- #47 -//│ x$11 -- #46) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, silly, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #29 -//│ case pair$0 of -- #28 -//│ Pair => -//│ let x$3 = pair$0.y in -- #27 -//│ let x$4 = pair$0.x in -- #26 -//│ case pair$0 of -- #25 -//│ Pair => -//│ let x$6 = pair$0.y in -- #24 -//│ let x$7 = pair$0.x in -- #23 -//│ let x$8 = +(x$7,1) in -- #22 -//│ jump j$1(x$8) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ let x$2 = +(x$1,1) in -- #6 -//│ x$2 -- #5 -//│ ) -//│ Def(2, j$1, [x$5], -//│ 1, -//│ jump j$0(x$5) -- #13 -//│ ) -//│ Def(3, foo, [], -//│ 1, -//│ let x$9 = Pair(0,1) in -- #43 -//│ let* (x$10) = silly(x$9) in -- #42 -//│ x$10 -- #41 -//│ ) -//│ }, -//│ let* (x$11) = foo() in -- #47 -//│ x$11 -- #46) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -311,54 +161,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 2 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$3,x$0) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #29 -//│ let* (x$6) = inc_fst(x$5) in -- #28 -//│ x$6 -- #27 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #33 -//│ x$7 -- #32) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 2 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$3,x$0) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #29 -//│ let* (x$6) = inc_fst(x$5) in -- #28 -//│ x$6 -- #27 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #33 -//│ x$7 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -404,54 +206,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$2,1) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #28 -//│ let* (x$6) = inc_fst(x$5) in -- #27 -//│ x$6 -- #26 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #32 -//│ x$7 -- #31) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, inc_fst, [pair$0], -//│ 1, -//│ let x$0 = 0 in -- #15 -//│ case pair$0 of -- #14 -//│ Pair => -//│ let x$2 = pair$0.y in -- #13 -//│ let x$3 = pair$0.x in -- #12 -//│ let x$4 = +(x$2,1) in -- #11 -//│ jump j$0(x$4) -- #10 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #2 -//│ ) -//│ Def(2, foo, [], -//│ 1, -//│ let x$5 = Pair(0,1) in -- #28 -//│ let* (x$6) = inc_fst(x$5) in -- #27 -//│ x$6 -- #26 -//│ ) -//│ }, -//│ let* (x$7) = foo() in -- #32 -//│ x$7 -- #31) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -500,82 +254,8 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ case a$0 of -- #36 -//│ Left => -//│ let x$4 = a$0.x in -- #26 -//│ let x$5 = +(x$4,1) in -- #25 -//│ let x$6 = Left(x$5) in -- #24 -//│ jump j$0(x$6) -- #23 -//│ Right => -//│ let x$7 = a$0.y in -- #35 -//│ let x$8 = Right(b$0) in -- #34 -//│ jump j$0(x$8) -- #33 -//│ ) -//│ Def(1, j$1, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ case x$0 of -- #14 -//│ Left => -//│ let x$2 = x$0.x in -- #8 -//│ jump j$1(x$2) -- #7 -//│ Right => -//│ let x$3 = x$0.y in -- #13 -//│ jump j$1(x$3) -- #12 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$9 = Right(2) in -- #48 -//│ let* (x$10) = foo(x$9,2) in -- #47 -//│ x$10 -- #46 -//│ ) -//│ }, -//│ let* (x$11) = bar() in -- #52 -//│ x$11 -- #51) -//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ case a$0 of -- #36 -//│ Left => -//│ let x$4 = a$0.x in -- #26 -//│ let x$5 = +(x$4,1) in -- #25 -//│ let x$6 = Left(x$5) in -- #24 -//│ jump j$0(x$6) -- #23 -//│ Right => -//│ let x$7 = a$0.y in -- #35 -//│ let x$8 = Right(b$0) in -- #34 -//│ jump j$0(x$8) -- #33 -//│ ) -//│ Def(1, j$1, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ case x$0 of -- #14 -//│ Left => -//│ let x$2 = x$0.x in -- #8 -//│ jump j$1(x$2) -- #7 -//│ Right => -//│ let x$3 = x$0.y in -- #13 -//│ jump j$1(x$3) -- #12 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$9 = Right(2) in -- #48 -//│ let* (x$10) = foo(x$9,2) in -- #47 -//│ x$10 -- #46 -//│ ) -//│ }, -//│ let* (x$11) = bar() in -- #52 -//│ x$11 -- #51) -//│ -//│ Promoted: +//│ +//│ Promoted: //│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { //│ Def(0, foo, [a$0,b$0], //│ 1, @@ -630,40 +310,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ let x$0 = a$0.x in -- #7 -//│ let x$1 = a$0.y in -- #6 -//│ let x$2 = +(x$0,x$1) in -- #5 -//│ x$2 -- #4 -//│ ) -//│ Def(1, bar, [], -//│ 1, -//│ let x$3 = Pair(1,0) in -- #19 -//│ let* (x$4) = foo(x$3) in -- #18 -//│ x$4 -- #17 -//│ ) -//│ }, -//│ let* (x$5) = bar() in -- #23 -//│ x$5 -- #22) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ let x$0 = a$0.x in -- #7 -//│ let x$1 = a$0.y in -- #6 -//│ let x$2 = +(x$0,x$1) in -- #5 -//│ x$2 -- #4 -//│ ) -//│ Def(1, bar, [], -//│ 1, -//│ let x$3 = Pair(1,0) in -- #19 -//│ let* (x$4) = foo(x$3) in -- #18 -//│ x$4 -- #17 -//│ ) -//│ }, -//│ let* (x$5) = bar() in -- #23 -//│ x$5 -- #22) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { @@ -702,56 +348,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ case a$0 of -- #15 -//│ C1 => -//│ let x$1 = a$0.y in -- #9 -//│ let x$2 = a$0.x in -- #8 -//│ jump j$0(x$2) -- #7 -//│ C2 => -//│ let x$3 = a$0.z in -- #14 -//│ jump j$0(x$3) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = C1(0,1) in -- #27 -//│ let* (x$5) = foo(x$4) in -- #26 -//│ x$5 -- #25 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #31 -//│ x$6 -- #30) -//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { -//│ Def(0, foo, [a$0], -//│ 1, -//│ case a$0 of -- #15 -//│ C1 => -//│ let x$1 = a$0.y in -- #9 -//│ let x$2 = a$0.x in -- #8 -//│ jump j$0(x$2) -- #7 -//│ C2 => -//│ let x$3 = a$0.z in -- #14 -//│ jump j$0(x$3) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, bar, [], -//│ 1, -//│ let x$4 = C1(0,1) in -- #27 -//│ let* (x$5) = foo(x$4) in -- #26 -//│ x$5 -- #25 -//│ ) -//│ }, -//│ let* (x$6) = bar() in -- #31 -//│ x$6 -- #30) //│ //│ Promoted: //│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { @@ -803,70 +399,6 @@ baz() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ let x$0 = a$0.x in -- #21 -//│ let x$1 = a$0.y in -- #20 -//│ let x$2 = b$0.x in -- #19 -//│ let x$3 = b$0.y in -- #18 -//│ let x$4 = +(x$0,x$1) in -- #17 -//│ let x$5 = +(x$4,x$2) in -- #16 -//│ let x$6 = +(x$5,x$3) in -- #15 -//│ x$6 -- #14 -//│ ) -//│ Def(1, bar, [c$0], -//│ 1, -//│ let x$7 = Pair(0,1) in -- #69 -//│ let* (x$8) = foo(x$7,c$0) in -- #68 -//│ let x$9 = Pair(2,3) in -- #67 -//│ let* (x$10) = foo(c$0,x$9) in -- #66 -//│ let x$11 = Pair(0,1) in -- #65 -//│ let x$12 = Pair(2,3) in -- #64 -//│ let* (x$13) = foo(x$11,x$12) in -- #63 -//│ x$13 -- #62 -//│ ) -//│ Def(2, baz, [], -//│ 1, -//│ let x$14 = Pair(4,5) in -- #81 -//│ let* (x$15) = bar(x$14) in -- #80 -//│ x$15 -- #79 -//│ ) -//│ }, -//│ let* (x$16) = baz() in -- #85 -//│ x$16 -- #84) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [a$0,b$0], -//│ 1, -//│ let x$0 = a$0.x in -- #21 -//│ let x$1 = a$0.y in -- #20 -//│ let x$2 = b$0.x in -- #19 -//│ let x$3 = b$0.y in -- #18 -//│ let x$4 = +(x$0,x$1) in -- #17 -//│ let x$5 = +(x$4,x$2) in -- #16 -//│ let x$6 = +(x$5,x$3) in -- #15 -//│ x$6 -- #14 -//│ ) -//│ Def(1, bar, [c$0], -//│ 1, -//│ let x$7 = Pair(0,1) in -- #69 -//│ let* (x$8) = foo(x$7,c$0) in -- #68 -//│ let x$9 = Pair(2,3) in -- #67 -//│ let* (x$10) = foo(c$0,x$9) in -- #66 -//│ let x$11 = Pair(0,1) in -- #65 -//│ let x$12 = Pair(2,3) in -- #64 -//│ let* (x$13) = foo(x$11,x$12) in -- #63 -//│ x$13 -- #62 -//│ ) -//│ Def(2, baz, [], -//│ 1, -//│ let x$14 = Pair(4,5) in -- #81 -//│ let* (x$15) = bar(x$14) in -- #80 -//│ x$15 -- #79 -//│ ) -//│ }, -//│ let* (x$16) = baz() in -- #85 -//│ x$16 -- #84) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -917,26 +449,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = Pair(0,1) in -- #10 -//│ let x$1 = x$0.x in -- #9 -//│ x$1 -- #8 -//│ ) -//│ }, -//│ let* (x$2) = foo() in -- #14 -//│ x$2 -- #13) -//│ Program({ClassInfo(0, Pair, [x,y])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = Pair(0,1) in -- #10 -//│ let x$1 = x$0.x in -- #9 -//│ x$1 -- #8 -//│ ) -//│ }, -//│ let* (x$2) = foo() in -- #14 -//│ x$2 -- #13) //│ //│ Promoted: //│ Program({ClassInfo(0, Pair, [x,y])}, { @@ -970,64 +482,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = O() in -- #10 -//│ let x$1 = S(x$0) in -- #9 -//│ let* (x$2) = bar(x$1) in -- #8 -//│ x$2 -- #7 -//│ ) -//│ Def(1, bar, [x$3], -//│ 1, -//│ let* (x$4) = baz(x$3) in -- #16 -//│ x$4 -- #15 -//│ ) -//│ Def(2, baz, [x$5], -//│ 1, -//│ case x$5 of -- #26 -//│ S => -//│ let x$7 = x$5.s in -- #23 -//│ jump j$0(x$7) -- #22 -//│ O => -//│ jump j$0(x$5) -- #25 -//│ ) -//│ Def(3, j$0, [x$6], -//│ 1, -//│ x$6 -- #18 -//│ ) -//│ }, -//│ let* (x$8) = foo() in -- #30 -//│ x$8 -- #29) -//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { -//│ Def(0, foo, [], -//│ 1, -//│ let x$0 = O() in -- #10 -//│ let x$1 = S(x$0) in -- #9 -//│ let* (x$2) = bar(x$1) in -- #8 -//│ x$2 -- #7 -//│ ) -//│ Def(1, bar, [x$3], -//│ 1, -//│ let* (x$4) = baz(x$3) in -- #16 -//│ x$4 -- #15 -//│ ) -//│ Def(2, baz, [x$5], -//│ 1, -//│ case x$5 of -- #26 -//│ S => -//│ let x$7 = x$5.s in -- #23 -//│ jump j$0(x$7) -- #22 -//│ O => -//│ jump j$0(x$5) -- #25 -//│ ) -//│ Def(3, j$0, [x$6], -//│ 1, -//│ x$6 -- #18 -//│ ) -//│ }, -//│ let* (x$8) = foo() in -- #30 -//│ x$8 -- #29) //│ //│ Promoted: //│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index 1de0cf2d73..8a3e4182e4 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -24,100 +24,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ case t$0 of -- #63 -//│ A => -//│ let x$9 = t$0.z in -- #51 -//│ let x$10 = t$0.y in -- #50 -//│ let x$11 = t$0.x in -- #49 -//│ let x$12 = *(x$10,x$9) in -- #48 -//│ let x$13 = +(x$11,x$12) in -- #47 -//│ jump j$0(x$13) -- #46 -//│ B => -//│ let x$14 = t$0.n in -- #62 -//│ let x$15 = t$0.m in -- #61 -//│ let x$16 = -(x$15,x$14) in -- #60 -//│ jump j$0(x$16) -- #59 -//│ ) -//│ Def(1, j$1, [x$2,x$0], -//│ 1, -//│ let x$3 = +(x$0,x$2) in -- #13 -//│ x$3 -- #12 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ let x$1 = B(1,2) in -- #34 -//│ case x$1 of -- #33 -//│ A => -//│ let x$4 = x$1.z in -- #24 -//│ let x$5 = x$1.y in -- #23 -//│ let x$6 = x$1.x in -- #22 -//│ jump j$1(3,x$0) -- #21 -//│ B => -//│ let x$7 = x$1.n in -- #32 -//│ let x$8 = x$1.m in -- #31 -//│ jump j$1(4,x$0) -- #30 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$17 = A(6,7,8) in -- #89 -//│ let* (x$18) = complex_foo(x$17) in -- #88 -//│ let x$19 = B(9,10) in -- #87 -//│ let* (x$20) = complex_foo(x$19) in -- #86 -//│ x$20 -- #85 -//│ ) -//│ }, -//│ let* (x$21) = bar() in -- #93 -//│ x$21 -- #92) -//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ case t$0 of -- #63 -//│ A => -//│ let x$9 = t$0.z in -- #51 -//│ let x$10 = t$0.y in -- #50 -//│ let x$11 = t$0.x in -- #49 -//│ let x$12 = *(x$10,x$9) in -- #48 -//│ let x$13 = +(x$11,x$12) in -- #47 -//│ jump j$0(x$13) -- #46 -//│ B => -//│ let x$14 = t$0.n in -- #62 -//│ let x$15 = t$0.m in -- #61 -//│ let x$16 = -(x$15,x$14) in -- #60 -//│ jump j$0(x$16) -- #59 -//│ ) -//│ Def(1, j$1, [x$2,x$0], -//│ 1, -//│ let x$3 = +(x$0,x$2) in -- #13 -//│ x$3 -- #12 -//│ ) -//│ Def(2, j$0, [x$0], -//│ 1, -//│ let x$1 = B(1,2) in -- #34 -//│ case x$1 of -- #33 -//│ A => -//│ let x$4 = x$1.z in -- #24 -//│ let x$5 = x$1.y in -- #23 -//│ let x$6 = x$1.x in -- #22 -//│ jump j$1(3,x$0) -- #21 -//│ B => -//│ let x$7 = x$1.n in -- #32 -//│ let x$8 = x$1.m in -- #31 -//│ jump j$1(4,x$0) -- #30 -//│ ) -//│ Def(3, bar, [], -//│ 1, -//│ let x$17 = A(6,7,8) in -- #89 -//│ let* (x$18) = complex_foo(x$17) in -- #88 -//│ let x$19 = B(9,10) in -- #87 -//│ let* (x$20) = complex_foo(x$19) in -- #86 -//│ x$20 -- #85 -//│ ) -//│ }, -//│ let* (x$21) = bar() in -- #93 -//│ x$21 -- #92) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { @@ -209,196 +115,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #140 -//│ let x$1 = *(1,2) in -- #139 -//│ case t$0 of -- #138 -//│ A => -//│ let x$27 = t$0.x in -- #116 -//│ let x$28 = t$0.w in -- #115 -//│ jump j$0(x$27,x$0,x$1) -- #114 -//│ B => -//│ let x$29 = t$0.y in -- #128 -//│ let x$30 = +(x$29,x$1) in -- #127 -//│ let x$31 = B(x$30) in -- #126 -//│ jump j$0(x$31,x$0,x$1) -- #125 -//│ C => -//│ let x$32 = t$0.z in -- #137 -//│ let x$33 = C(0) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$34 = B(10) in -- #162 -//│ let x$35 = A(9,x$34) in -- #161 -//│ let x$36 = A(10,x$35) in -- #160 -//│ let* (x$37) = complex_foo(x$36) in -- #159 -//│ x$37 -- #158 -//│ ) -//│ }, -//│ let* (x$38) = bar() in -- #166 -//│ x$38 -- #165) -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #140 -//│ let x$1 = *(1,2) in -- #139 -//│ case t$0 of -- #138 -//│ A => -//│ let x$27 = t$0.x in -- #116 -//│ let x$28 = t$0.w in -- #115 -//│ jump j$0(x$27,x$0,x$1) -- #114 -//│ B => -//│ let x$29 = t$0.y in -- #128 -//│ let x$30 = +(x$29,x$1) in -- #127 -//│ let x$31 = B(x$30) in -- #126 -//│ jump j$0(x$31,x$0,x$1) -- #125 -//│ C => -//│ let x$32 = t$0.z in -- #137 -//│ let x$33 = C(0) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$34 = B(10) in -- #162 -//│ let x$35 = A(9,x$34) in -- #161 -//│ let x$36 = A(10,x$35) in -- #160 -//│ let* (x$37) = complex_foo(x$36) in -- #159 -//│ x$37 -- #158 -//│ ) -//│ }, -//│ let* (x$38) = bar() in -- #166 -//│ x$38 -- #165) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { @@ -538,200 +254,6 @@ bar() //│ //│ //│ IR: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #150 -//│ let x$1 = *(1,2) in -- #149 -//│ case t$0 of -- #148 -//│ A => -//│ let x$27 = t$0.x in -- #126 -//│ let x$28 = t$0.w in -- #125 -//│ let x$29 = C(0) in -- #124 -//│ let x$30 = A(x$28,x$29) in -- #123 -//│ jump j$0(x$30,x$0,x$1) -- #122 -//│ B => -//│ let x$31 = t$0.y in -- #138 -//│ let x$32 = +(x$31,x$1) in -- #137 -//│ let x$33 = B(x$32) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ C => -//│ let x$34 = t$0.z in -- #147 -//│ let x$35 = C(0) in -- #146 -//│ jump j$0(x$35,x$0,x$1) -- #145 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$36 = B(10) in -- #172 -//│ let x$37 = A(9,x$36) in -- #171 -//│ let x$38 = A(10,x$37) in -- #170 -//│ let* (x$39) = complex_foo(x$38) in -- #169 -//│ x$39 -- #168 -//│ ) -//│ }, -//│ let* (x$40) = bar() in -- #176 -//│ x$40 -- #175) -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { -//│ Def(0, complex_foo, [t$0], -//│ 1, -//│ let x$0 = +(1,2) in -- #150 -//│ let x$1 = *(1,2) in -- #149 -//│ case t$0 of -- #148 -//│ A => -//│ let x$27 = t$0.x in -- #126 -//│ let x$28 = t$0.w in -- #125 -//│ let x$29 = C(0) in -- #124 -//│ let x$30 = A(x$28,x$29) in -- #123 -//│ jump j$0(x$30,x$0,x$1) -- #122 -//│ B => -//│ let x$31 = t$0.y in -- #138 -//│ let x$32 = +(x$31,x$1) in -- #137 -//│ let x$33 = B(x$32) in -- #136 -//│ jump j$0(x$33,x$0,x$1) -- #135 -//│ C => -//│ let x$34 = t$0.z in -- #147 -//│ let x$35 = C(0) in -- #146 -//│ jump j$0(x$35,x$0,x$1) -- #145 -//│ ) -//│ Def(1, j$2, [x$6], -//│ 1, -//│ x$6 -- #21 -//│ ) -//│ Def(2, j$3, [x$11], -//│ 1, -//│ jump j$2(x$11) -- #39 -//│ ) -//│ Def(3, j$1, [x$5,x$3,x$4], -//│ 1, -//│ case x$3 of -- #60 -//│ A => -//│ let x$7 = x$3.x in -- #29 -//│ let x$8 = x$3.w in -- #28 -//│ jump j$2(x$8) -- #27 -//│ B => -//│ let x$9 = x$3.y in -- #34 -//│ jump j$2(4) -- #33 -//│ C => -//│ let x$10 = x$3.z in -- #59 -//│ case x$4 of -- #58 -//│ A => -//│ let x$12 = x$4.x in -- #47 -//│ let x$13 = x$4.w in -- #46 -//│ jump j$3(x$13) -- #45 -//│ B => -//│ let x$14 = x$4.y in -- #52 -//│ jump j$3(7) -- #51 -//│ C => -//│ let x$15 = x$4.z in -- #57 -//│ jump j$3(8) -- #56 -//│ ) -//│ Def(4, j$4, [x$20,x$3,x$4], -//│ 1, -//│ jump j$1(x$20,x$3,x$4) -- #72 -//│ ) -//│ Def(5, j$0, [x$2,x$0,x$1], -//│ 1, -//│ let x$3 = A(5,x$2) in -- #108 -//│ let x$4 = B(6) in -- #107 -//│ case x$2 of -- #106 -//│ A => -//│ let x$16 = x$2.x in -- #95 -//│ let x$17 = x$2.w in -- #94 -//│ let x$18 = +(x$17,x$0) in -- #93 -//│ let x$19 = +(x$18,x$1) in -- #92 -//│ case x$16 of -- #91 -//│ A => -//│ let x$21 = x$16.x in -- #80 -//│ let x$22 = x$16.w in -- #79 -//│ jump j$4(x$22,x$3,x$4) -- #78 -//│ B => -//│ let x$23 = x$16.y in -- #85 -//│ jump j$4(x$19,x$3,x$4) -- #84 -//│ C => -//│ let x$24 = x$16.z in -- #90 -//│ jump j$4(0,x$3,x$4) -- #89 -//│ B => -//│ let x$25 = x$2.y in -- #100 -//│ jump j$1(2,x$3,x$4) -- #99 -//│ C => -//│ let x$26 = x$2.z in -- #105 -//│ jump j$1(3,x$3,x$4) -- #104 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$36 = B(10) in -- #172 -//│ let x$37 = A(9,x$36) in -- #171 -//│ let x$38 = A(10,x$37) in -- #170 -//│ let* (x$39) = complex_foo(x$38) in -- #169 -//│ x$39 -- #168 -//│ ) -//│ }, -//│ let* (x$40) = bar() in -- #176 -//│ x$40 -- #175) //│ //│ Promoted: //│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index cf8ab42e06..d2dcf23909 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -13,50 +13,6 @@ fib(20) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fib, [n$0], -//│ 1, -//│ let x$0 = <(n$0,2) in -- #28 -//│ if x$0 -- #27 -//│ true => -//│ jump j$0(n$0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #26 -//│ let* (x$3) = fib(x$2) in -- #25 -//│ let x$4 = -(n$0,2) in -- #24 -//│ let* (x$5) = fib(x$4) in -- #23 -//│ let x$6 = +(x$3,x$5) in -- #22 -//│ jump j$0(x$6) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$7) = fib(20) in -- #34 -//│ x$7 -- #33) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fib, [n$0], -//│ 1, -//│ let x$0 = <(n$0,2) in -- #28 -//│ if x$0 -- #27 -//│ true => -//│ jump j$0(n$0) -- #5 -//│ false => -//│ let x$2 = -(n$0,1) in -- #26 -//│ let* (x$3) = fib(x$2) in -- #25 -//│ let x$4 = -(n$0,2) in -- #24 -//│ let* (x$5) = fib(x$4) in -- #23 -//│ let x$6 = +(x$3,x$5) in -- #22 -//│ jump j$0(x$6) -- #21 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ }, -//│ let* (x$7) = fib(20) in -- #34 -//│ x$7 -- #33) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { @@ -97,88 +53,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ let x$1 = ==(x$0,0) in -- #18 -//│ if x$1 -- #17 -//│ true => -//│ let x$3 = False() in -- #6 -//│ jump j$0(x$3) -- #5 -//│ false => -//│ let x$4 = -(x$0,1) in -- #16 -//│ let* (x$5) = even(x$4) in -- #15 -//│ jump j$0(x$5) -- #14 -//│ ) -//│ Def(1, j$0, [x$2], -//│ 1, -//│ x$2 -- #3 -//│ ) -//│ Def(2, even, [x$6], -//│ 1, -//│ let x$7 = ==(x$6,0) in -- #37 -//│ if x$7 -- #36 -//│ true => -//│ let x$9 = True() in -- #25 -//│ jump j$1(x$9) -- #24 -//│ false => -//│ let x$10 = -(x$6,1) in -- #35 -//│ let* (x$11) = odd(x$10) in -- #34 -//│ jump j$1(x$11) -- #33 -//│ ) -//│ Def(3, j$1, [x$8], -//│ 1, -//│ x$8 -- #22 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let* (x$12) = odd(10) in -- #43 -//│ x$12 -- #42 -//│ ) -//│ }, -//│ let* (x$13) = foo() in -- #47 -//│ x$13 -- #46) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ let x$1 = ==(x$0,0) in -- #18 -//│ if x$1 -- #17 -//│ true => -//│ let x$3 = False() in -- #6 -//│ jump j$0(x$3) -- #5 -//│ false => -//│ let x$4 = -(x$0,1) in -- #16 -//│ let* (x$5) = even(x$4) in -- #15 -//│ jump j$0(x$5) -- #14 -//│ ) -//│ Def(1, j$0, [x$2], -//│ 1, -//│ x$2 -- #3 -//│ ) -//│ Def(2, even, [x$6], -//│ 1, -//│ let x$7 = ==(x$6,0) in -- #37 -//│ if x$7 -- #36 -//│ true => -//│ let x$9 = True() in -- #25 -//│ jump j$1(x$9) -- #24 -//│ false => -//│ let x$10 = -(x$6,1) in -- #35 -//│ let* (x$11) = odd(x$10) in -- #34 -//│ jump j$1(x$11) -- #33 -//│ ) -//│ Def(3, j$1, [x$8], -//│ 1, -//│ x$8 -- #22 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let* (x$12) = odd(10) in -- #43 -//│ x$12 -- #42 -//│ ) -//│ }, -//│ let* (x$13) = foo() in -- #47 -//│ x$13 -- #46) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { @@ -243,86 +117,6 @@ main() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, not, [x$0], -//│ 1, -//│ if x$0 -- #8 -//│ true => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ false => -//│ let x$3 = True() in -- #7 -//│ jump j$0(x$3) -- #6 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, foo, [x$4], -//│ 1, -//│ if x$4 -- #30 -//│ true => -//│ let x$6 = A() in -- #13 -//│ jump j$1(x$6) -- #12 -//│ false => -//│ let* (x$7) = not(x$4) in -- #29 -//│ let* (x$8) = foo(x$7) in -- #28 -//│ let x$9 = B(x$8) in -- #27 -//│ jump j$1(x$9) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #10 -//│ ) -//│ Def(4, main, [], -//│ 1, -//│ let x$10 = False() in -- #37 -//│ let* (x$11) = foo(x$10) in -- #36 -//│ x$11 -- #35 -//│ ) -//│ }, -//│ let* (x$12) = main() in -- #41 -//│ x$12 -- #40) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, not, [x$0], -//│ 1, -//│ if x$0 -- #8 -//│ true => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ false => -//│ let x$3 = True() in -- #7 -//│ jump j$0(x$3) -- #6 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, foo, [x$4], -//│ 1, -//│ if x$4 -- #30 -//│ true => -//│ let x$6 = A() in -- #13 -//│ jump j$1(x$6) -- #12 -//│ false => -//│ let* (x$7) = not(x$4) in -- #29 -//│ let* (x$8) = foo(x$7) in -- #28 -//│ let x$9 = B(x$8) in -- #27 -//│ jump j$1(x$9) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #10 -//│ ) -//│ Def(4, main, [], -//│ 1, -//│ let x$10 = False() in -- #37 -//│ let* (x$11) = foo(x$10) in -- #36 -//│ x$11 -- #35 -//│ ) -//│ }, -//│ let* (x$12) = main() in -- #41 -//│ x$12 -- #40) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { @@ -399,6 +193,8 @@ main() //│ //│ //│ IR: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { //│ Def(0, aaa, [], //│ 1, @@ -468,270 +264,44 @@ main() //│ }, //│ let* (x$26) = main() in -- #86 //│ x$26 -- #85) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], +//│ +//│ Interpreted: +//│ 404 + + +:interpIR +class True +class False +class S(s) +class O +fun odd(x) = + if x is + O then False + S(s) then even(s) +fun even(x) = + if x is + O then True + S(s) then odd(s) +fun foo() = odd(S(S(S(O)))) +foo() +//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| +//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} +//│ +//│ +//│ IR: +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Def(0, odd, [x$0], //│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [], -//│ 1, -//│ let x$20 = False() in -- #82 -//│ let* (x$21) = foo(x$20) in -- #81 -//│ case x$21 of -- #80 -//│ A => -//│ let* (x$23) = aaa() in -- #71 -//│ jump j$2(x$23) -- #70 -//│ B => -//│ let x$24 = x$21.b in -- #79 -//│ let* (x$25) = bbb() in -- #78 -//│ jump j$2(x$25) -- #77 -//│ ) -//│ Def(7, j$2, [x$22], -//│ 1, -//│ x$22 -- #66 -//│ ) -//│ }, -//│ let* (x$26) = main() in -- #86 -//│ x$26 -- #85) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [], -//│ 1, -//│ let x$20 = False() in -- #82 -//│ let* (x$21) = foo(x$20) in -- #81 -//│ case x$21 of -- #80 -//│ A => -//│ let* (x$23) = aaa() in -- #71 -//│ jump j$2(x$23) -- #70 -//│ B => -//│ let x$24 = x$21.b in -- #79 -//│ let* (x$25) = bbb() in -- #78 -//│ jump j$2(x$25) -- #77 -//│ ) -//│ Def(7, j$2, [x$22], -//│ 1, -//│ x$22 -- #66 -//│ ) -//│ }, -//│ let* (x$26) = main() in -- #86 -//│ x$26 -- #85) -//│ -//│ Interpreted: -//│ 404 - - -:interpIR -class True -class False -class S(s) -class O -fun odd(x) = - if x is - O then False - S(s) then even(s) -fun even(x) = - if x is - O then True - S(s) then odd(s) -fun foo() = odd(S(S(S(O)))) -foo() -//│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| -//│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = O() in -- #50 -//│ let x$11 = S(x$10) in -- #49 -//│ let x$12 = S(x$11) in -- #48 -//│ let x$13 = S(x$12) in -- #47 -//│ let* (x$14) = odd(x$13) in -- #46 -//│ x$14 -- #45 -//│ ) -//│ }, -//│ let* (x$15) = foo() in -- #54 -//│ x$15 -- #53) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = O() in -- #50 -//│ let x$11 = S(x$10) in -- #49 -//│ let x$12 = S(x$11) in -- #48 -//│ let x$13 = S(x$12) in -- #47 -//│ let* (x$14) = odd(x$13) in -- #46 -//│ x$14 -- #45 -//│ ) -//│ }, -//│ let* (x$15) = foo() in -- #54 -//│ x$15 -- #53) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 +//│ case x$0 of -- #15 +//│ O => +//│ let x$2 = False() in -- #4 +//│ jump j$0(x$2) -- #3 +//│ S => +//│ let x$3 = x$0.s in -- #14 +//│ let* (x$4) = even(x$3) in -- #13 +//│ jump j$0(x$4) -- #12 //│ ) //│ Def(1, j$0, [x$1], //│ 1, @@ -789,122 +359,8 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #65 -//│ let* (x$17) = odd(x$16) in -- #64 -//│ x$17 -- #63 -//│ ) -//│ }, -//│ let* (x$18) = foo() in -- #69 -//│ x$18 -- #68) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #65 -//│ let* (x$17) = odd(x$16) in -- #64 -//│ x$17 -- #63 -//│ ) -//│ }, -//│ let* (x$18) = foo() in -- #69 -//│ x$18 -- #68) -//│ -//│ Promoted: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, @@ -987,124 +443,6 @@ foo() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 -//│ ) -//│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, mk, [n$0], -//│ 1, -//│ let x$10 = >(n$0,0) in -- #54 -//│ if x$10 -- #53 -//│ true => -//│ let x$12 = -(n$0,1) in -- #49 -//│ let* (x$13) = mk(x$12) in -- #48 -//│ let x$14 = S(x$13) in -- #47 -//│ jump j$2(x$14) -- #46 -//│ false => -//│ let x$15 = O() in -- #52 -//│ jump j$2(x$15) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #35 -//│ ) -//│ Def(6, foo, [], -//│ 1, -//│ let* (x$16) = mk(10) in -- #73 -//│ let x$17 = S(x$16) in -- #72 -//│ let x$18 = S(x$17) in -- #71 -//│ let* (x$19) = odd(x$18) in -- #70 -//│ x$19 -- #69 -//│ ) -//│ }, -//│ let* (x$20) = foo() in -- #77 -//│ x$20 -- #76) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { @@ -1194,158 +532,8 @@ main() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = >(10,0) in -- #52 -//│ if x$10 -- #51 -//│ true => -//│ let x$13 = O() in -- #47 -//│ let x$14 = S(x$13) in -- #46 -//│ jump j$2(x$14) -- #45 -//│ false => -//│ let x$15 = O() in -- #50 -//│ jump j$2(x$15) -- #49 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ let* (x$12) = odd(x$11) in -- #40 -//│ x$12 -- #39 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$16 = >(10,0) in -- #78 -//│ if x$16 -- #77 -//│ true => -//│ let x$18 = O() in -- #68 -//│ let x$19 = S(x$18) in -- #67 -//│ let* (x$20) = odd(x$19) in -- #66 -//│ jump j$3(x$20) -- #65 -//│ false => -//│ let x$21 = O() in -- #76 -//│ let* (x$22) = odd(x$21) in -- #75 -//│ jump j$3(x$22) -- #74 -//│ ) -//│ Def(7, j$3, [x$17], -//│ 1, -//│ x$17 -- #56 -//│ ) -//│ Def(8, main, [], -//│ 1, -//│ let* (x$23) = foo() in -- #86 -//│ let* (x$24) = bar() in -- #85 -//│ x$24 -- #84 -//│ ) -//│ }, -//│ let* (x$25) = main() in -- #90 -//│ x$25 -- #89) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { -//│ Def(0, odd, [x$0], -//│ 1, -//│ case x$0 of -- #15 -//│ O => -//│ let x$2 = False() in -- #4 -//│ jump j$0(x$2) -- #3 -//│ S => -//│ let x$3 = x$0.s in -- #14 -//│ let* (x$4) = even(x$3) in -- #13 -//│ jump j$0(x$4) -- #12 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #1 -//│ ) -//│ Def(2, even, [x$5], -//│ 1, -//│ case x$5 of -- #31 -//│ O => -//│ let x$7 = True() in -- #20 -//│ jump j$1(x$7) -- #19 -//│ S => -//│ let x$8 = x$5.s in -- #30 -//│ let* (x$9) = odd(x$8) in -- #29 -//│ jump j$1(x$9) -- #28 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #17 -//│ ) -//│ Def(4, foo, [], -//│ 1, -//│ let x$10 = >(10,0) in -- #52 -//│ if x$10 -- #51 -//│ true => -//│ let x$13 = O() in -- #47 -//│ let x$14 = S(x$13) in -- #46 -//│ jump j$2(x$14) -- #45 -//│ false => -//│ let x$15 = O() in -- #50 -//│ jump j$2(x$15) -- #49 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ let* (x$12) = odd(x$11) in -- #40 -//│ x$12 -- #39 -//│ ) -//│ Def(6, bar, [], -//│ 1, -//│ let x$16 = >(10,0) in -- #78 -//│ if x$16 -- #77 -//│ true => -//│ let x$18 = O() in -- #68 -//│ let x$19 = S(x$18) in -- #67 -//│ let* (x$20) = odd(x$19) in -- #66 -//│ jump j$3(x$20) -- #65 -//│ false => -//│ let x$21 = O() in -- #76 -//│ let* (x$22) = odd(x$21) in -- #75 -//│ jump j$3(x$22) -- #74 -//│ ) -//│ Def(7, j$3, [x$17], -//│ 1, -//│ x$17 -- #56 -//│ ) -//│ Def(8, main, [], -//│ 1, -//│ let* (x$23) = foo() in -- #86 -//│ let* (x$24) = bar() in -- #85 -//│ x$24 -- #84 -//│ ) -//│ }, -//│ let* (x$25) = main() in -- #90 -//│ x$25 -- #89) -//│ -//│ Promoted: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, @@ -1454,6 +642,8 @@ main(False) //│ //│ //│ IR: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { //│ Def(0, aaa, [], //│ 1, @@ -1523,325 +713,87 @@ main(False) //│ let x$25 = False() in -- #88 //│ let* (x$26) = main(x$25) in -- #87 //│ x$26 -- #86) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], +//│ +//│ Interpreted: +//│ 404 + + +:interpIR +class True +class False +class Cons(h, t) +class Nil +class Some(x) +class None +fun head_opt(l) = + if l is + Nil then None + Cons(h, t) then Some(h) +fun is_none(o) = + if o is + None then True + Some(x) then False +fun is_empty(l) = + is_none(head_opt(l)) +fun main() = + is_empty(Cons(1, Cons(2, Nil))) +main() +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} +//│ +//│ +//│ IR: +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Def(0, head_opt, [l$0], //│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 +//│ case l$0 of -- #17 +//│ Nil => +//│ let x$1 = None() in -- #4 +//│ jump j$0(x$1) -- #3 +//│ Cons => +//│ let x$2 = l$0.t in -- #16 +//│ let x$3 = l$0.h in -- #15 +//│ let x$4 = Some(x$3) in -- #14 +//│ jump j$0(x$4) -- #13 //│ ) -//│ Def(3, j$0, [x$11], +//│ Def(1, j$0, [x$0], //│ 1, -//│ x$11 -- #30 +//│ x$0 -- #1 //│ ) -//│ Def(4, foo, [x$14], +//│ Def(2, is_none, [o$0], //│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 +//│ case o$0 of -- #29 +//│ None => +//│ let x$6 = True() in -- #22 +//│ jump j$1(x$6) -- #21 +//│ Some => +//│ let x$7 = o$0.x in -- #28 +//│ let x$8 = False() in -- #27 +//│ jump j$1(x$8) -- #26 //│ ) -//│ Def(5, j$1, [x$15], +//│ Def(3, j$1, [x$5], //│ 1, -//│ x$15 -- #39 +//│ x$5 -- #19 //│ ) -//│ Def(6, main, [flag$0], +//│ Def(4, is_empty, [l$1], //│ 1, -//│ let* (x$20) = foo(flag$0) in -- #81 -//│ case x$20 of -- #80 -//│ A => -//│ let* (x$22) = aaa() in -- #71 -//│ jump j$2(x$22) -- #70 -//│ B => -//│ let x$23 = x$20.b in -- #79 -//│ let* (x$24) = bbb() in -- #78 -//│ jump j$2(x$24) -- #77 +//│ let* (x$9) = head_opt(l$1) in -- #40 +//│ let* (x$10) = is_none(x$9) in -- #39 +//│ x$10 -- #38 //│ ) -//│ Def(7, j$2, [x$21], +//│ Def(5, main, [], //│ 1, -//│ x$21 -- #66 +//│ let x$11 = Nil() in -- #59 +//│ let x$12 = Cons(2,x$11) in -- #58 +//│ let x$13 = Cons(1,x$12) in -- #57 +//│ let* (x$14) = is_empty(x$13) in -- #56 +//│ x$14 -- #55 //│ ) //│ }, -//│ let x$25 = False() in -- #88 -//│ let* (x$26) = main(x$25) in -- #87 -//│ x$26 -- #86) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { -//│ Def(0, aaa, [], -//│ 1, -//│ let x$0 = 1 in -- #17 -//│ let x$1 = 2 in -- #16 -//│ let x$2 = 3 in -- #15 -//│ let x$3 = 4 in -- #14 -//│ let x$4 = +(x$0,x$1) in -- #13 -//│ let x$5 = -(x$4,x$2) in -- #12 -//│ let x$6 = +(x$5,x$3) in -- #11 -//│ x$6 -- #10 -//│ ) -//│ Def(1, bbb, [], -//│ 1, -//│ let* (x$7) = aaa() in -- #28 -//│ let x$8 = *(x$7,100) in -- #27 -//│ let x$9 = +(x$8,4) in -- #26 -//│ x$9 -- #25 -//│ ) -//│ Def(2, not, [x$10], -//│ 1, -//│ if x$10 -- #37 -//│ true => -//│ let x$12 = False() in -- #33 -//│ jump j$0(x$12) -- #32 -//│ false => -//│ let x$13 = True() in -- #36 -//│ jump j$0(x$13) -- #35 -//│ ) -//│ Def(3, j$0, [x$11], -//│ 1, -//│ x$11 -- #30 -//│ ) -//│ Def(4, foo, [x$14], -//│ 1, -//│ if x$14 -- #59 -//│ true => -//│ let x$16 = A() in -- #42 -//│ jump j$1(x$16) -- #41 -//│ false => -//│ let* (x$17) = not(x$14) in -- #58 -//│ let* (x$18) = foo(x$17) in -- #57 -//│ let x$19 = B(x$18) in -- #56 -//│ jump j$1(x$19) -- #55 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #39 -//│ ) -//│ Def(6, main, [flag$0], -//│ 1, -//│ let* (x$20) = foo(flag$0) in -- #81 -//│ case x$20 of -- #80 -//│ A => -//│ let* (x$22) = aaa() in -- #71 -//│ jump j$2(x$22) -- #70 -//│ B => -//│ let x$23 = x$20.b in -- #79 -//│ let* (x$24) = bbb() in -- #78 -//│ jump j$2(x$24) -- #77 -//│ ) -//│ Def(7, j$2, [x$21], -//│ 1, -//│ x$21 -- #66 -//│ ) -//│ }, -//│ let x$25 = False() in -- #88 -//│ let* (x$26) = main(x$25) in -- #87 -//│ x$26 -- #86) -//│ -//│ Interpreted: -//│ 404 - - -:interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None -fun head_opt(l) = - if l is - Nil then None - Cons(h, t) then Some(h) -fun is_none(o) = - if o is - None then True - Some(x) then False -fun is_empty(l) = - is_none(head_opt(l)) -fun main() = - is_empty(Cons(1, Cons(2, Nil))) -main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #17 -//│ Nil => -//│ let x$1 = None() in -- #4 -//│ jump j$0(x$1) -- #3 -//│ Cons => -//│ let x$2 = l$0.t in -- #16 -//│ let x$3 = l$0.h in -- #15 -//│ let x$4 = Some(x$3) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #29 -//│ None => -//│ let x$6 = True() in -- #22 -//│ jump j$1(x$6) -- #21 -//│ Some => -//│ let x$7 = o$0.x in -- #28 -//│ let x$8 = False() in -- #27 -//│ jump j$1(x$8) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #19 -//│ ) -//│ Def(4, is_empty, [l$1], -//│ 1, -//│ let* (x$9) = head_opt(l$1) in -- #40 -//│ let* (x$10) = is_none(x$9) in -- #39 -//│ x$10 -- #38 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let x$11 = Nil() in -- #59 -//│ let x$12 = Cons(2,x$11) in -- #58 -//│ let x$13 = Cons(1,x$12) in -- #57 -//│ let* (x$14) = is_empty(x$13) in -- #56 -//│ x$14 -- #55 -//│ ) -//│ }, -//│ let* (x$15) = main() in -- #63 -//│ x$15 -- #62) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #17 -//│ Nil => -//│ let x$1 = None() in -- #4 -//│ jump j$0(x$1) -- #3 -//│ Cons => -//│ let x$2 = l$0.t in -- #16 -//│ let x$3 = l$0.h in -- #15 -//│ let x$4 = Some(x$3) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #29 -//│ None => -//│ let x$6 = True() in -- #22 -//│ jump j$1(x$6) -- #21 -//│ Some => -//│ let x$7 = o$0.x in -- #28 -//│ let x$8 = False() in -- #27 -//│ jump j$1(x$8) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #19 -//│ ) -//│ Def(4, is_empty, [l$1], -//│ 1, -//│ let* (x$9) = head_opt(l$1) in -- #40 -//│ let* (x$10) = is_none(x$9) in -- #39 -//│ x$10 -- #38 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let x$11 = Nil() in -- #59 -//│ let x$12 = Cons(2,x$11) in -- #58 -//│ let x$13 = Cons(1,x$12) in -- #57 -//│ let* (x$14) = is_empty(x$13) in -- #56 -//│ x$14 -- #55 -//│ ) -//│ }, -//│ let* (x$15) = main() in -- #63 -//│ x$15 -- #62) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #17 -//│ Nil => -//│ let x$1 = None() in -- #4 -//│ jump j$0(x$1) -- #3 -//│ Cons => -//│ let x$2 = l$0.t in -- #16 -//│ let x$3 = l$0.h in -- #15 -//│ let x$4 = Some(x$3) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #29 -//│ None => -//│ let x$6 = True() in -- #22 -//│ jump j$1(x$6) -- #21 -//│ Some => -//│ let x$7 = o$0.x in -- #28 -//│ let x$8 = False() in -- #27 -//│ jump j$1(x$8) -- #26 -//│ ) -//│ Def(3, j$1, [x$5], -//│ 1, -//│ x$5 -- #19 -//│ ) -//│ Def(4, is_empty, [l$1], -//│ 1, -//│ let* (x$9) = head_opt(l$1) in -- #40 -//│ let* (x$10) = is_none(x$9) in -- #39 -//│ x$10 -- #38 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let x$11 = Nil() in -- #59 -//│ let x$12 = Cons(2,x$11) in -- #58 -//│ let x$13 = Cons(1,x$12) in -- #57 -//│ let* (x$14) = is_empty(x$13) in -- #56 -//│ x$14 -- #55 -//│ ) -//│ }, -//│ let* (x$15) = main() in -- #63 -//│ x$15 -- #62) +//│ let* (x$15) = main() in -- #63 +//│ x$15 -- #62) //│ //│ Interpreted: //│ False() @@ -1873,136 +825,8 @@ main() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #42 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #41 -//│ let x$9 = l$0.h in -- #40 -//│ let x$10 = Some(x$9) in -- #39 -//│ jump j$1(x$10) -- #38 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #54 -//│ None => -//│ let x$12 = True() in -- #47 -//│ jump j$2(x$12) -- #46 -//│ Some => -//│ let x$13 = o$0.x in -- #53 -//│ let x$14 = False() in -- #52 -//│ jump j$2(x$14) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #44 -//│ ) -//│ Def(6, is_empty, [l$1], -//│ 1, -//│ let* (x$15) = head_opt(l$1) in -- #65 -//│ let* (x$16) = is_none(x$15) in -- #64 -//│ x$16 -- #63 -//│ ) -//│ Def(7, main, [], -//│ 1, -//│ let* (x$17) = mk_list(10) in -- #76 -//│ let* (x$18) = is_empty(x$17) in -- #75 -//│ x$18 -- #74 -//│ ) -//│ }, -//│ let* (x$19) = main() in -- #80 -//│ x$19 -- #79) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, head_opt, [l$0], -//│ 1, -//│ case l$0 of -- #42 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #41 -//│ let x$9 = l$0.h in -- #40 -//│ let x$10 = Some(x$9) in -- #39 -//│ jump j$1(x$10) -- #38 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, is_none, [o$0], -//│ 1, -//│ case o$0 of -- #54 -//│ None => -//│ let x$12 = True() in -- #47 -//│ jump j$2(x$12) -- #46 -//│ Some => -//│ let x$13 = o$0.x in -- #53 -//│ let x$14 = False() in -- #52 -//│ jump j$2(x$14) -- #51 -//│ ) -//│ Def(5, j$2, [x$11], -//│ 1, -//│ x$11 -- #44 -//│ ) -//│ Def(6, is_empty, [l$1], -//│ 1, -//│ let* (x$15) = head_opt(l$1) in -- #65 -//│ let* (x$16) = is_none(x$15) in -- #64 -//│ x$16 -- #63 -//│ ) -//│ Def(7, main, [], -//│ 1, -//│ let* (x$17) = mk_list(10) in -- #76 -//│ let* (x$18) = is_empty(x$17) in -- #75 -//│ x$18 -- #74 -//│ ) -//│ }, -//│ let* (x$19) = main() in -- #80 -//│ x$19 -- #79) -//│ -//│ Promoted: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { //│ Def(0, mk_list, [n$0], //│ 1, @@ -2095,114 +919,6 @@ main() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, last_opt, [l$0], -//│ 1, -//│ case l$0 of -- #59 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #58 -//│ let x$9 = l$0.h in -- #57 -//│ case x$8 of -- #56 -//│ Nil => -//│ let x$11 = Some(x$9) in -- #42 -//│ jump j$2(x$11) -- #41 -//│ Cons => -//│ let x$12 = x$8.t in -- #55 -//│ let x$13 = x$8.h in -- #54 -//│ let* (x$14) = last_opt(x$8) in -- #53 -//│ jump j$2(x$14) -- #52 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, j$2, [x$10], -//│ 1, -//│ jump j$1(x$10) -- #36 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let* (x$15) = mk_list(10) in -- #70 -//│ let* (x$16) = last_opt(x$15) in -- #69 -//│ x$16 -- #68 -//│ ) -//│ }, -//│ let* (x$17) = main() in -- #74 -//│ x$17 -- #73) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, mk_list, [n$0], -//│ 1, -//│ let x$0 = ==(n$0,0) in -- #24 -//│ if x$0 -- #23 -//│ true => -//│ let x$2 = Nil() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ false => -//│ let x$3 = -(n$0,1) in -- #22 -//│ let* (x$4) = mk_list(x$3) in -- #21 -//│ let x$5 = Cons(n$0,x$4) in -- #20 -//│ jump j$0(x$5) -- #19 -//│ ) -//│ Def(1, j$0, [x$1], -//│ 1, -//│ x$1 -- #3 -//│ ) -//│ Def(2, last_opt, [l$0], -//│ 1, -//│ case l$0 of -- #59 -//│ Nil => -//│ let x$7 = None() in -- #29 -//│ jump j$1(x$7) -- #28 -//│ Cons => -//│ let x$8 = l$0.t in -- #58 -//│ let x$9 = l$0.h in -- #57 -//│ case x$8 of -- #56 -//│ Nil => -//│ let x$11 = Some(x$9) in -- #42 -//│ jump j$2(x$11) -- #41 -//│ Cons => -//│ let x$12 = x$8.t in -- #55 -//│ let x$13 = x$8.h in -- #54 -//│ let* (x$14) = last_opt(x$8) in -- #53 -//│ jump j$2(x$14) -- #52 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #26 -//│ ) -//│ Def(4, j$2, [x$10], -//│ 1, -//│ jump j$1(x$10) -- #36 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let* (x$15) = mk_list(10) in -- #70 -//│ let* (x$16) = last_opt(x$15) in -- #69 -//│ x$16 -- #68 -//│ ) -//│ }, -//│ let* (x$17) = main() in -- #74 -//│ x$17 -- #73) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -2248,239 +964,59 @@ main() //│ ) //│ Def(4, j$2, [x$10], //│ 1, -//│ jump j$1(x$10) -- #36 -//│ ) -//│ Def(5, main, [], -//│ 1, -//│ let* (x$15) = mk_list(10) in -- #70 -//│ let* (x$16) = last_opt(x$15) in -- #69 -//│ x$16 -- #68 -//│ ) -//│ }, -//│ let* (x$17) = main() in -- #74 -//│ x$17 -- #73) -//│ -//│ Interpreted: -//│ Some(1) - -:interpIR -class True -class False -class Cons(h, t) -class Nil -class Some(x) -class None -fun is_some(o) = - if o is - Some(x) then True - None then False -fun e0(w) = - w + 8 + 9 + 10 -fun e1(a, c) = - a + 1 + 2 + 3 + 4 -fun e3(c) = - let m = 4 - let n = 5 - let p = 6 - let q = 7 - if c then m + n + p + q else m + n - p + q -fun e2(x) = - x + 12 + 13 + 14 -fun f(x) = - let c1 = is_some(x) - let z = e3(c1) - let w = if x is - Some(a) then e1(a, z) - None then e2(z) - e0(w) -fun main() = - f(Some(2)) + f(None) -main() -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, c,) => {+(+(+(+(a,)(1,),)(2,),)(3,),)(4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,c$0], -//│ 1, -//│ let x$7 = +(a$0,1) in -- #34 -//│ let x$8 = +(x$7,2) in -- #33 -//│ let x$9 = +(x$8,3) in -- #32 -//│ let x$10 = +(x$9,4) in -- #31 -//│ x$10 -- #30 -//│ ) -//│ Def(4, e3, [c$1], -//│ 1, -//│ let x$11 = 4 in -- #67 -//│ let x$12 = 5 in -- #66 -//│ let x$13 = 6 in -- #65 -//│ let x$14 = 7 in -- #64 -//│ if c$1 -- #63 -//│ true => -//│ let x$16 = +(x$11,x$12) in -- #51 -//│ let x$17 = +(x$16,x$13) in -- #50 -//│ let x$18 = +(x$17,x$14) in -- #49 -//│ jump j$1(x$18) -- #48 -//│ false => -//│ let x$19 = +(x$11,x$12) in -- #62 -//│ let x$20 = -(x$19,x$13) in -- #61 -//│ let x$21 = +(x$20,x$14) in -- #60 -//│ jump j$1(x$21) -- #59 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #40 -//│ ) -//│ Def(6, e2, [x$22], -//│ 1, -//│ let x$23 = +(x$22,12) in -- #77 -//│ let x$24 = +(x$23,13) in -- #76 -//│ let x$25 = +(x$24,14) in -- #75 -//│ x$25 -- #74 -//│ ) -//│ Def(7, f, [x$26], -//│ 1, -//│ let* (x$27) = is_some(x$26) in -- #117 -//│ let* (x$28) = e3(x$27) in -- #116 -//│ case x$26 of -- #115 -//│ Some => -//│ let x$31 = x$26.x in -- #107 -//│ let* (x$32) = e1(x$31,x$28) in -- #106 -//│ jump j$2(x$32) -- #105 -//│ None => -//│ let* (x$33) = e2(x$28) in -- #114 -//│ jump j$2(x$33) -- #113 -//│ ) -//│ Def(8, j$2, [x$29], -//│ 1, -//│ let* (x$30) = e0(x$29) in -- #95 -//│ x$30 -- #94 -//│ ) -//│ Def(9, main, [], -//│ 1, -//│ let x$34 = Some(2) in -- #136 -//│ let* (x$35) = f(x$34) in -- #135 -//│ let x$36 = None() in -- #134 -//│ let* (x$37) = f(x$36) in -- #133 -//│ let x$38 = +(x$35,x$37) in -- #132 -//│ x$38 -- #131 -//│ ) -//│ }, -//│ let* (x$39) = main() in -- #140 -//│ x$39 -- #139) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,c$0], -//│ 1, -//│ let x$7 = +(a$0,1) in -- #34 -//│ let x$8 = +(x$7,2) in -- #33 -//│ let x$9 = +(x$8,3) in -- #32 -//│ let x$10 = +(x$9,4) in -- #31 -//│ x$10 -- #30 -//│ ) -//│ Def(4, e3, [c$1], -//│ 1, -//│ let x$11 = 4 in -- #67 -//│ let x$12 = 5 in -- #66 -//│ let x$13 = 6 in -- #65 -//│ let x$14 = 7 in -- #64 -//│ if c$1 -- #63 -//│ true => -//│ let x$16 = +(x$11,x$12) in -- #51 -//│ let x$17 = +(x$16,x$13) in -- #50 -//│ let x$18 = +(x$17,x$14) in -- #49 -//│ jump j$1(x$18) -- #48 -//│ false => -//│ let x$19 = +(x$11,x$12) in -- #62 -//│ let x$20 = -(x$19,x$13) in -- #61 -//│ let x$21 = +(x$20,x$14) in -- #60 -//│ jump j$1(x$21) -- #59 -//│ ) -//│ Def(5, j$1, [x$15], -//│ 1, -//│ x$15 -- #40 -//│ ) -//│ Def(6, e2, [x$22], -//│ 1, -//│ let x$23 = +(x$22,12) in -- #77 -//│ let x$24 = +(x$23,13) in -- #76 -//│ let x$25 = +(x$24,14) in -- #75 -//│ x$25 -- #74 -//│ ) -//│ Def(7, f, [x$26], -//│ 1, -//│ let* (x$27) = is_some(x$26) in -- #117 -//│ let* (x$28) = e3(x$27) in -- #116 -//│ case x$26 of -- #115 -//│ Some => -//│ let x$31 = x$26.x in -- #107 -//│ let* (x$32) = e1(x$31,x$28) in -- #106 -//│ jump j$2(x$32) -- #105 -//│ None => -//│ let* (x$33) = e2(x$28) in -- #114 -//│ jump j$2(x$33) -- #113 -//│ ) -//│ Def(8, j$2, [x$29], -//│ 1, -//│ let* (x$30) = e0(x$29) in -- #95 -//│ x$30 -- #94 +//│ jump j$1(x$10) -- #36 //│ ) -//│ Def(9, main, [], +//│ Def(5, main, [], //│ 1, -//│ let x$34 = Some(2) in -- #136 -//│ let* (x$35) = f(x$34) in -- #135 -//│ let x$36 = None() in -- #134 -//│ let* (x$37) = f(x$36) in -- #133 -//│ let x$38 = +(x$35,x$37) in -- #132 -//│ x$38 -- #131 +//│ let* (x$15) = mk_list(10) in -- #70 +//│ let* (x$16) = last_opt(x$15) in -- #69 +//│ x$16 -- #68 //│ ) //│ }, -//│ let* (x$39) = main() in -- #140 -//│ x$39 -- #139) +//│ let* (x$17) = main() in -- #74 +//│ x$17 -- #73) +//│ +//│ Interpreted: +//│ Some(1) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +class Some(x) +class None +fun is_some(o) = + if o is + Some(x) then True + None then False +fun e0(w) = + w + 8 + 9 + 10 +fun e1(a, c) = + a + 1 + 2 + 3 + 4 +fun e3(c) = + let m = 4 + let n = 5 + let p = 6 + let q = 7 + if c then m + n + p + q else m + n - p + q +fun e2(x) = + x + 12 + 13 + 14 +fun f(x) = + let c1 = is_some(x) + let z = e3(c1) + let w = if x is + Some(a) then e1(a, z) + None then e2(z) + e0(w) +fun main() = + f(Some(2)) + f(None) +main() +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, c,) => {+(+(+(+(a,)(1,),)(2,),)(3,),)(4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} +//│ +//│ +//│ IR: //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { @@ -2615,202 +1151,6 @@ main() //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,z$0], -//│ 1, -//│ let x$7 = >(a$0,0) in -- #43 -//│ if x$7 -- #42 -//│ true => -//│ let x$9 = -(a$0,1) in -- #39 -//│ let x$10 = Some(x$9) in -- #38 -//│ let* (x$11) = f(x$10) in -- #37 -//│ jump j$1(x$11) -- #36 -//│ false => -//│ jump j$1(z$0) -- #41 -//│ ) -//│ Def(4, j$1, [x$8], -//│ 1, -//│ x$8 -- #25 -//│ ) -//│ Def(5, e3, [c$0], -//│ 1, -//│ let x$12 = 4 in -- #76 -//│ let x$13 = 5 in -- #75 -//│ let x$14 = 6 in -- #74 -//│ let x$15 = 7 in -- #73 -//│ if c$0 -- #72 -//│ true => -//│ let x$17 = +(x$12,x$13) in -- #60 -//│ let x$18 = +(x$17,x$14) in -- #59 -//│ let x$19 = +(x$18,x$15) in -- #58 -//│ jump j$2(x$19) -- #57 -//│ false => -//│ let x$20 = +(x$12,x$13) in -- #71 -//│ let x$21 = -(x$20,x$14) in -- #70 -//│ let x$22 = +(x$21,x$15) in -- #69 -//│ jump j$2(x$22) -- #68 -//│ ) -//│ Def(6, j$2, [x$16], -//│ 1, -//│ x$16 -- #49 -//│ ) -//│ Def(7, e2, [x$23], -//│ 1, -//│ let x$24 = +(x$23,12) in -- #86 -//│ let x$25 = +(x$24,13) in -- #85 -//│ let x$26 = +(x$25,14) in -- #84 -//│ x$26 -- #83 -//│ ) -//│ Def(8, f, [x$27], -//│ 1, -//│ let* (x$28) = is_some(x$27) in -- #126 -//│ let* (x$29) = e3(x$28) in -- #125 -//│ case x$27 of -- #124 -//│ Some => -//│ let x$32 = x$27.x in -- #116 -//│ let* (x$33) = e1(x$32,x$29) in -- #115 -//│ jump j$3(x$33) -- #114 -//│ None => -//│ let* (x$34) = e2(x$29) in -- #123 -//│ jump j$3(x$34) -- #122 -//│ ) -//│ Def(9, j$3, [x$30], -//│ 1, -//│ let* (x$31) = e0(x$30) in -- #104 -//│ x$31 -- #103 -//│ ) -//│ Def(10, main, [], -//│ 1, -//│ let x$35 = Some(2) in -- #145 -//│ let* (x$36) = f(x$35) in -- #144 -//│ let x$37 = None() in -- #143 -//│ let* (x$38) = f(x$37) in -- #142 -//│ let x$39 = +(x$36,x$38) in -- #141 -//│ x$39 -- #140 -//│ ) -//│ }, -//│ let* (x$40) = main() in -- #149 -//│ x$40 -- #148) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { -//│ Def(0, is_some, [o$0], -//│ 1, -//│ case o$0 of -- #11 -//│ Some => -//│ let x$1 = o$0.x in -- #7 -//│ let x$2 = True() in -- #6 -//│ jump j$0(x$2) -- #5 -//│ None => -//│ let x$3 = False() in -- #10 -//│ jump j$0(x$3) -- #9 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, e0, [w$0], -//│ 1, -//│ let x$4 = +(w$0,8) in -- #21 -//│ let x$5 = +(x$4,9) in -- #20 -//│ let x$6 = +(x$5,10) in -- #19 -//│ x$6 -- #18 -//│ ) -//│ Def(3, e1, [a$0,z$0], -//│ 1, -//│ let x$7 = >(a$0,0) in -- #43 -//│ if x$7 -- #42 -//│ true => -//│ let x$9 = -(a$0,1) in -- #39 -//│ let x$10 = Some(x$9) in -- #38 -//│ let* (x$11) = f(x$10) in -- #37 -//│ jump j$1(x$11) -- #36 -//│ false => -//│ jump j$1(z$0) -- #41 -//│ ) -//│ Def(4, j$1, [x$8], -//│ 1, -//│ x$8 -- #25 -//│ ) -//│ Def(5, e3, [c$0], -//│ 1, -//│ let x$12 = 4 in -- #76 -//│ let x$13 = 5 in -- #75 -//│ let x$14 = 6 in -- #74 -//│ let x$15 = 7 in -- #73 -//│ if c$0 -- #72 -//│ true => -//│ let x$17 = +(x$12,x$13) in -- #60 -//│ let x$18 = +(x$17,x$14) in -- #59 -//│ let x$19 = +(x$18,x$15) in -- #58 -//│ jump j$2(x$19) -- #57 -//│ false => -//│ let x$20 = +(x$12,x$13) in -- #71 -//│ let x$21 = -(x$20,x$14) in -- #70 -//│ let x$22 = +(x$21,x$15) in -- #69 -//│ jump j$2(x$22) -- #68 -//│ ) -//│ Def(6, j$2, [x$16], -//│ 1, -//│ x$16 -- #49 -//│ ) -//│ Def(7, e2, [x$23], -//│ 1, -//│ let x$24 = +(x$23,12) in -- #86 -//│ let x$25 = +(x$24,13) in -- #85 -//│ let x$26 = +(x$25,14) in -- #84 -//│ x$26 -- #83 -//│ ) -//│ Def(8, f, [x$27], -//│ 1, -//│ let* (x$28) = is_some(x$27) in -- #126 -//│ let* (x$29) = e3(x$28) in -- #125 -//│ case x$27 of -- #124 -//│ Some => -//│ let x$32 = x$27.x in -- #116 -//│ let* (x$33) = e1(x$32,x$29) in -- #115 -//│ jump j$3(x$33) -- #114 -//│ None => -//│ let* (x$34) = e2(x$29) in -- #123 -//│ jump j$3(x$34) -- #122 -//│ ) -//│ Def(9, j$3, [x$30], -//│ 1, -//│ let* (x$31) = e0(x$30) in -- #104 -//│ x$31 -- #103 -//│ ) -//│ Def(10, main, [], -//│ 1, -//│ let x$35 = Some(2) in -- #145 -//│ let* (x$36) = f(x$35) in -- #144 -//│ let x$37 = None() in -- #143 -//│ let* (x$38) = f(x$37) in -- #142 -//│ let x$39 = +(x$36,x$38) in -- #141 -//│ x$39 -- #140 -//│ ) -//│ }, -//│ let* (x$40) = main() in -- #149 -//│ x$40 -- #148) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 4d1506aca3..a3b1e3a0c8 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -15,6 +15,8 @@ fact(1, 5) //│ //│ //│ IR: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, @@ -35,6 +37,23 @@ fact(1, 5) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 //│ x$5 -- #29) +//│ +//│ Interpreted: +//│ 120 + +:interpIR +@tailrec +class True +class False +fun fact(acc, n) = + if n == 0 then acc + else fact(acc * n, n - 1) +fact(1, 5) +//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, @@ -56,48 +75,33 @@ fact(1, 5) //│ let* (x$5) = fact(1,5) in -- #30 //│ x$5 -- #29) //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { -//│ Def(0, fact, [acc$0,n$0], +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, j$0, [x$1], //│ 1, -//│ let x$0 = ==(n$0,0) in -- #22 -//│ if x$0 -- #21 +//│ x$1 -- #3 +//│ ) +//│ Def(2, fact_jp, [acc$0,n$0], +//│ 1, +//│ let x$0 = ==(n$0,0) in -- #36 +//│ if x$0 -- #35 //│ true => -//│ jump j$0(acc$0) -- #5 +//│ jump j$0(acc$0) -- #31 //│ false => -//│ let x$2 = *(acc$0,n$0) in -- #20 -//│ let x$3 = -(n$0,1) in -- #19 -//│ let* (x$4) = fact(x$2,x$3) in -- #18 -//│ jump j$0(x$4) -- #17 +//│ let x$2 = *(acc$0,n$0) in -- #34 +//│ let x$3 = -(n$0,1) in -- #33 +//│ jump fact_jp(x$2,x$3) -- #32 //│ ) -//│ Def(1, j$0, [x$1], +//│ Def(3, fact, [acc$0,n$0], //│ 1, -//│ x$1 -- #3 +//│ let* (r0) = fact_jp(acc$0,n$0) in -- #38 +//│ r0 -- #37 //│ ) //│ }, //│ let* (x$5) = fact(1,5) in -- #30 //│ x$5 -- #29) //│ -//│ Interpreted: -//│ 120 - -:interpIR -@tailrec -class True -class False -fun fact(acc, n) = - if n == 0 then acc - else fact(acc * n, n - 1) -fact(1, 5) -//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} -//│ -//│ -//│ IR: -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(fact)) -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(1, j$0, [x$1], @@ -144,9 +148,87 @@ fact(1, 5) //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let x$0 = >(n$0,0) in -- #32 +//│ if x$0 -- #31 +//│ true => +//│ let x$6 = -(n$0,1) in -- #28 +//│ jump j$0(x$6,acc$0,n$0) -- #27 +//│ false => +//│ jump j$0(0,acc$0,n$0) -- #30 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(j$1), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, fact, [acc$0,n$0], +//│ 1, +//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ r0 -- #59 +//│ ) +//│ Def(1, j$1, [x$3], +//│ 1, +//│ x$3 -- #7 +//│ ) +//│ Def(2, j$0, [x$1,acc$0,n$0], +//│ 1, +//│ let x$2 = <=(x$1,0) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ jump j$1(acc$0) -- #9 +//│ false => +//│ let x$4 = *(n$0,acc$0) in -- #21 +//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 +//│ jump j$1(x$5) -- #19 +//│ ) +//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ let scrut = ==(2,tailrecBranch$) in -- #57 +//│ if scrut -- #56 +//│ true => +//│ let x$2 = <=(j$0_x$1,0) in -- #55 +//│ if x$2 -- #54 +//│ true => +//│ jump j$1(j$0_acc$0) -- #51 +//│ false => +//│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 +//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ false => +//│ let x$0 = >(fact_n$0,0) in -- #50 +//│ if x$0 -- #49 +//│ true => +//│ let x$6 = -(fact_n$0,1) in -- #47 +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ false => +//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 +//│ ) +//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ }, +//│ let* (x$7) = fact(1,5) in -- #40 +//│ x$7 -- #39) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -215,6 +297,8 @@ g(6, 0) //│ //│ //│ IR: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, double, [x$0], //│ 1, @@ -259,6 +343,22 @@ g(6, 0) //│ }, //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) +//│ +//│ Interpreted: +//│ -12 + +:interpIR +class True +class False +fun double(x) = x * 2 +@tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) +@tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) +g(6, 0) +//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} +//│ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, double, [x$0], //│ 1, @@ -304,8 +404,9 @@ g(6, 0) //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Strongly Connected Tail Calls: +//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -313,16 +414,8 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let x$2 = ==(n$0,0) in -- #31 -//│ if x$2 -- #30 -//│ true => -//│ let* (x$4) = double(acc$0) in -- #14 -//│ jump j$0(x$4) -- #13 -//│ false => -//│ let x$5 = -(n$0,1) in -- #29 -//│ let x$6 = +(acc$0,1) in -- #28 -//│ let* (x$7) = g(x$5,x$6) in -- #27 -//│ jump j$0(x$7) -- #26 +//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], //│ 1, @@ -330,45 +423,47 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let x$8 = ==(m$0,0) in -- #62 -//│ if x$8 -- #61 -//│ true => -//│ let* (x$10) = double(acc$1) in -- #45 -//│ let x$11 = -(0,x$10) in -- #44 -//│ jump j$1(x$11) -- #43 -//│ false => -//│ let x$12 = -(m$0,1) in -- #60 -//│ let x$13 = +(acc$1,1) in -- #59 -//│ let* (x$14) = f(x$12,x$13) in -- #58 -//│ jump j$1(x$14) -- #57 +//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, //│ x$9 -- #35 //│ ) +//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ let scrut = ==(1,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$2 = ==(f_n$0,0) in -- #93 +//│ if x$2 -- #92 +//│ true => +//│ let* (x$4) = double(f_acc$0) in -- #88 +//│ jump j$0(x$4) -- #87 +//│ false => +//│ let x$5 = -(f_n$0,1) in -- #91 +//│ let x$6 = +(f_acc$0,1) in -- #90 +//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ false => +//│ let x$8 = ==(g_m$0,0) in -- #86 +//│ if x$8 -- #85 +//│ true => +//│ let* (x$10) = double(g_acc$1) in -- #81 +//│ let x$11 = -(0,x$10) in -- #80 +//│ jump j$1(x$11) -- #79 +//│ false => +//│ let x$12 = -(g_m$0,1) in -- #84 +//│ let x$13 = +(g_acc$1,1) in -- #83 +//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 +//│ ) +//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 //│ x$15 -- #69) //│ -//│ Interpreted: -//│ -12 - -:interpIR -class True -class False -fun double(x) = x * 2 -@tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) -@tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) -g(6, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} -//│ -//│ -//│ IR: -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], @@ -442,11 +537,27 @@ class False //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (x$0) = g(0,0) in -- #7 +//│ x$0 -- #6 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (x$1) = h(0,0,0,0) in -- #19 +//│ x$1 -- #18 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (x$2) = f(0,0,0) in -- #29 +//│ x$2 -- #28 +//│ ) +//│ }, +//│ 2 -- #30) //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) -//│ -//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, @@ -483,10 +594,48 @@ class False //│ ) //│ }, //│ 2 -- #30) - -:ce -class True -class False +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, f, [a$0,b$0,c$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ r0 -- #44 +//│ ) +//│ Def(1, g, [d$0,e$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ r0 -- #42 +//│ ) +//│ Def(2, h, [p$0,q$0,r$0,s$0], +//│ 1, +//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ r0 -- #40 +//│ ) +//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #38 +//│ if scrut -- #37 +//│ true => +//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ false => +//│ let scrut = ==(1,tailrecBranch$) in -- #36 +//│ if scrut -- #35 +//│ true => +//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ false => +//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 +//│ ) +//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ }, +//│ 2 -- #30) + +:ce +class True +class False fun hello() = @tailcall hello() @tailcall hello() @@ -497,19 +646,39 @@ hello() //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) //│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall //│ ╟── first call -//│ ║ l.657: @tailcall hello() +//│ ║ l.491: @tailcall hello() //│ ║ ^^^^^ //│ ╟── second call -//│ ║ l.658: @tailcall hello() +//│ ║ l.492: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.657: @tailcall hello() +//│ ║ l.491: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #8 +//│ let* (x$1) = @tailcall hello() in -- #7 +//│ 2 -- #6 +//│ ) +//│ }, +//│ let* (x$2) = hello() in -- #12 +//│ x$2 -- #11) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -535,12 +704,30 @@ hello() //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.706: @tailcall hello() +//│ ║ l.530: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, hello, [], +//│ 1, +//│ let* (x$0) = @tailcall hello() in -- #4 +//│ 2 -- #3 +//│ ) +//│ }, +//│ let* (x$1) = hello() in -- #8 +//│ x$1 -- #7) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { @@ -568,9 +755,95 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #27 +//│ Cons => +//│ let x$1 = xs$0.t in -- #23 +//│ let x$2 = xs$0.h in -- #22 +//│ let x$3 = +(x$2,1) in -- #21 +//│ let* (x$4) = @tailcall addOne(x$1) in -- #20 +//│ let x$5 = Cons(x$3,x$4) in -- #19 +//│ jump j$0(x$5) -- #18 +//│ Nil => +//│ let x$6 = Nil() in -- #26 +//│ jump j$0(x$6) -- #25 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 +//│ res -- #78 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #59 +//│ _IdContext => +//│ x -- #58 +//│ _Context => +//│ let field = ctx.field in -- #57 +//│ let ptr = ctx.ptr in -- #56 +//│ let _ = assign ptr.t := x in -- #55 +//│ let acc = ctx.acc in -- #54 +//│ acc -- #53 +//│ ) +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #65 +//│ let ctx2ptr = ctx2.ptr in -- #64 +//│ let ctx2field = ctx2.field in -- #63 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 +//│ ret -- #60 +//│ ) +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #91 +//│ Cons => +//│ let x$1 = xs$0.t in -- #87 +//│ let x$2 = xs$0.h in -- #86 +//│ let x$3 = +(x$2,1) in -- #85 +//│ let x$5 = Cons(x$3,0) in -- #84 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #83 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 +//│ jump addOne_modcons_jp(composed,x$1) -- #81 +//│ Nil => +//│ let x$6 = Nil() in -- #90 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 +//│ res -- #88 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 +//│ r0 -- #92 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #52 +//│ let x$8 = Cons(3,x$7) in -- #51 +//│ let x$9 = Cons(2,x$8) in -- #50 +//│ let x$10 = Cons(1,x$9) in -- #49 +//│ let* (x$11) = addOne(x$10) in -- #48 +//│ x$11 -- #47) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { @@ -657,50 +930,8 @@ a(S(S(S(Zero)))) //│ //│ //│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { -//│ Def(0, a, [n$0], -//│ 1, -//│ case n$0 of -- #23 -//│ S => -//│ let x$1 = n$0.x in -- #15 -//│ let* (x$2) = @tailcall b(x$1) in -- #14 -//│ let x$3 = S(x$2) in -- #13 -//│ jump j$0(x$3) -- #12 -//│ Zero => -//│ let x$4 = Zero() in -- #22 -//│ let x$5 = S(x$4) in -- #21 -//│ jump j$0(x$5) -- #20 -//│ ) -//│ Def(1, j$0, [x$0], -//│ 1, -//│ x$0 -- #1 -//│ ) -//│ Def(2, b, [n$1], -//│ 1, -//│ case n$1 of -- #55 -//│ S => -//│ let x$7 = n$1.x in -- #43 -//│ let* (x$8) = @tailcall a(x$7) in -- #42 -//│ let x$9 = S(x$8) in -- #41 -//│ let x$10 = S(x$9) in -- #40 -//│ jump j$1(x$10) -- #39 -//│ Zero => -//│ let x$11 = Zero() in -- #54 -//│ let x$12 = S(x$11) in -- #53 -//│ let x$13 = S(x$12) in -- #52 -//│ jump j$1(x$13) -- #51 -//│ ) -//│ Def(3, j$1, [x$6], -//│ 1, -//│ x$6 -- #25 -//│ ) -//│ }, -//│ let x$14 = Zero() in -- #74 -//│ let x$15 = S(x$14) in -- #73 -//│ let x$16 = S(x$15) in -- #72 -//│ let x$17 = S(x$16) in -- #71 -//│ let* (x$18) = a(x$17) in -- #70 -//│ x$18 -- #69) +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, @@ -746,7 +977,28 @@ a(S(S(S(Zero)))) //│ let* (x$18) = a(x$17) in -- #70 //│ x$18 -- #69) //│ -//│ Promoted: +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class True +class False +class Zero +class S(x) +@tailrec fun a(n) = + if n is + S(x) then S(@tailcall b(x)) + Zero then S(Zero) +@tailrec fun b(n) = + if n is + S(x) then S(S(@tailcall a(x))) + Zero then S(S(Zero)) +a(S(S(S(Zero)))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], //│ 1, @@ -792,33 +1044,8 @@ a(S(S(S(Zero)))) //│ let* (x$18) = a(x$17) in -- #70 //│ x$18 -- #69) //│ -//│ Interpreted: -//│ S(S(S(S(S(S(Zero())))))) - -:interpIR -class True -class False -class Zero -class S(x) -@tailrec fun a(n) = - if n is - S(x) then S(@tailcall b(x)) - Zero then S(Zero) -@tailrec fun b(n) = - if n is - S(x) then S(S(@tailcall a(x))) - Zero then S(S(Zero)) -a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} -//│ -//│ -//│ IR: -//│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) -//│ -//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, @@ -916,11 +1143,109 @@ a(S(S(S(Zero)))) //│ let* (x$18) = a(x$17) in -- #70 //│ x$18 -- #69) //│ -//│ Interpreted: -//│ S(S(S(S(S(S(Zero())))))) - -:interpIR -class True +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [n$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #117 +//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 +//│ res -- #115 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, b, [n$1], +//│ 1, +//│ let idCtx = _IdContext() in -- #103 +//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 +//│ res -- #101 +//│ ) +//│ Def(3, j$1, [x$6], +//│ 1, +//│ x$6 -- #25 +//│ ) +//│ Def(4, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #81 +//│ _IdContext => +//│ x -- #80 +//│ _Context => +//│ let field = ctx.field in -- #79 +//│ let ptr = ctx.ptr in -- #78 +//│ let _ = assign ptr.x := x in -- #77 +//│ let acc = ctx.acc in -- #76 +//│ acc -- #75 +//│ ) +//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #87 +//│ let ctx2ptr = ctx2.ptr in -- #86 +//│ let ctx2field = ctx2.field in -- #85 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 +//│ ret -- #82 +//│ ) +//│ Def(6, b_modcons, [ctx,n$1], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 +//│ r0 -- #155 +//│ ) +//│ Def(7, a_modcons, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 +//│ r0 -- #157 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #153 +//│ if scrut -- #152 +//│ true => +//│ case a_modcons_n$0 of -- #151 +//│ S => +//│ let x$1 = a_modcons_n$0.x in -- #146 +//│ let x$3 = S(0) in -- #145 +//│ let ctx2 = _Context(x$3,x$3,0) in -- #144 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 +//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 +//│ Zero => +//│ let x$4 = Zero() in -- #150 +//│ let x$5 = S(x$4) in -- #149 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 +//│ res -- #147 +//│ false => +//│ case b_modcons_n$1 of -- #141 +//│ S => +//│ let x$7 = b_modcons_n$1.x in -- #135 +//│ let x$9 = S(0) in -- #134 +//│ let x$10 = S(x$9) in -- #133 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #132 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 +//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 +//│ Zero => +//│ let x$11 = Zero() in -- #140 +//│ let x$12 = S(x$11) in -- #139 +//│ let x$13 = S(x$12) in -- #138 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 +//│ res -- #136 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 +//│ ) +//│ }, +//│ let x$14 = Zero() in -- #74 +//│ let x$15 = S(x$14) in -- #73 +//│ let x$16 = S(x$15) in -- #72 +//│ let x$17 = S(x$16) in -- #71 +//│ let* (x$18) = a(x$17) in -- #70 +//│ x$18 -- #69) +//│ +//│ Interpreted: +//│ S(S(S(S(S(S(Zero())))))) + +:interpIR +class True class False class Cons(h, t) class Nil @@ -939,9 +1264,95 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ case xs$0 of -- #30 +//│ Cons => +//│ let x$1 = xs$0.t in -- #26 +//│ let x$2 = xs$0.h in -- #25 +//│ let* (x$3) = @tailcall addOne(x$1) in -- #24 +//│ let x$4 = +(x$2,1) in -- #23 +//│ let x$5 = Cons(x$4,x$3) in -- #22 +//│ jump j$0(x$5) -- #21 +//│ Nil => +//│ let x$6 = Nil() in -- #29 +//│ jump j$0(x$6) -- #28 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, addOne, [xs$0], +//│ 1, +//│ let idCtx = _IdContext() in -- #83 +//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 +//│ res -- #81 +//│ ) +//│ Def(1, j$0, [x$0], +//│ 1, +//│ x$0 -- #1 +//│ ) +//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #62 +//│ _IdContext => +//│ x -- #61 +//│ _Context => +//│ let field = ctx.field in -- #60 +//│ let ptr = ctx.ptr in -- #59 +//│ let _ = assign ptr.t := x in -- #58 +//│ let acc = ctx.acc in -- #57 +//│ acc -- #56 +//│ ) +//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #68 +//│ let ctx2ptr = ctx2.ptr in -- #67 +//│ let ctx2field = ctx2.field in -- #66 +//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 +//│ ret -- #63 +//│ ) +//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ 1, +//│ case xs$0 of -- #94 +//│ Cons => +//│ let x$1 = xs$0.t in -- #90 +//│ let x$2 = xs$0.h in -- #89 +//│ let x$4 = +(x$2,1) in -- #88 +//│ let x$5 = Cons(x$4,0) in -- #87 +//│ let ctx2 = _Context(x$5,x$5,0) in -- #86 +//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 +//│ jump addOne_modcons_jp(composed,x$1) -- #84 +//│ Nil => +//│ let x$6 = Nil() in -- #93 +//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 +//│ res -- #91 +//│ ) +//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 +//│ r0 -- #95 +//│ ) +//│ }, +//│ let x$7 = Nil() in -- #55 +//│ let x$8 = Cons(3,x$7) in -- #54 +//│ let x$9 = Cons(2,x$8) in -- #53 +//│ let x$10 = Cons(1,x$9) in -- #52 +//│ let* (x$11) = addOne(x$10) in -- #51 +//│ x$11 -- #50) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { @@ -1032,11 +1443,61 @@ b(16) //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { +//│ Def(0, a, [x$0], +//│ 1, +//│ case x$0 of -- #46 +//│ Cons => +//│ let x$2 = x$0.n in -- #42 +//│ let x$3 = x$0.m in -- #41 +//│ let x$4 = <(x$3,0) in -- #40 +//│ if x$4 -- #39 +//│ true => +//│ let x$6 = Nil() in -- #19 +//│ let x$7 = Cons(-1,x$6) in -- #18 +//│ jump j$1(x$7) -- #17 +//│ false => +//│ let x$8 = *(x$3,4) in -- #38 +//│ let x$9 = -(x$3,2) in -- #37 +//│ let* (x$10) = b(x$9) in -- #36 +//│ let x$11 = Cons(x$8,x$10) in -- #35 +//│ jump j$1(x$11) -- #34 +//│ Nil => +//│ let x$12 = Nil() in -- #45 +//│ jump j$0(x$12) -- #44 +//│ ) +//│ Def(1, j$0, [x$1], +//│ 1, +//│ x$1 -- #1 +//│ ) +//│ Def(2, j$1, [x$5], +//│ 1, +//│ jump j$0(x$5) -- #10 +//│ ) +//│ Def(3, b, [n$0], +//│ 1, +//│ let x$13 = <=(n$0,0) in -- #75 +//│ if x$13 -- #74 +//│ true => +//│ let x$15 = Nil() in -- #59 +//│ let x$16 = Cons(0,x$15) in -- #58 +//│ jump j$2(x$16) -- #57 +//│ false => +//│ let x$17 = Nil() in -- #73 +//│ let x$18 = Cons(n$0,x$17) in -- #72 +//│ let* (x$19) = a(x$18) in -- #71 +//│ jump j$2(x$19) -- #70 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ }, +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) -//│ -//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, @@ -1141,79 +1602,137 @@ b(16) //│ let* (x$20) = b(16) in -- #81 //│ x$20 -- #80) //│ -//│ Interpreted: -//│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) - -:noTailRec -:interpIR -class True -class False -class Cons(h, t) -class Nil -fun foo(xs) = - if xs is - Cons(h, t) then - if h > 5 then foo(t) - else - val item = if h < 3 then -1 else 100 - Cons(item, Cons(h, foo(t))) - Nil then - Nil -foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { -//│ Def(0, foo, [xs$0], +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [x$0], //│ 1, -//│ case xs$0 of -- #54 -//│ Cons => -//│ let x$1 = xs$0.t in -- #50 -//│ let x$2 = xs$0.h in -- #49 -//│ let x$3 = >(x$2,5) in -- #48 -//│ if x$3 -- #47 -//│ true => -//│ let* (x$5) = foo(x$1) in -- #17 -//│ jump j$1(x$5) -- #16 -//│ false => -//│ let x$6 = <(x$2,3) in -- #46 -//│ if x$6 -- #45 -//│ true => -//│ jump j$2(-1,x$1,x$2) -- #42 -//│ false => -//│ jump j$2(100,x$1,x$2) -- #44 -//│ Nil => -//│ let x$11 = Nil() in -- #53 -//│ jump j$0(x$11) -- #52 +//│ let idCtx = _IdContext() in -- #129 +//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 +//│ res -- #127 //│ ) -//│ Def(1, j$0, [x$0], +//│ Def(1, j$0, [x$1], //│ 1, -//│ x$0 -- #1 +//│ x$1 -- #1 //│ ) -//│ Def(2, j$1, [x$4], +//│ Def(2, j$1, [x$5], //│ 1, -//│ jump j$0(x$4) -- #10 +//│ jump j$0(x$5) -- #10 //│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], +//│ Def(3, b, [n$0], //│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 +//│ let idCtx = _IdContext() in -- #107 +//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 +//│ res -- #105 +//│ ) +//│ Def(4, j$2, [x$14], +//│ 1, +//│ x$14 -- #50 +//│ ) +//│ Def(5, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #88 +//│ _IdContext => +//│ x -- #87 +//│ _Context => +//│ let field = ctx.field in -- #86 +//│ let ptr = ctx.ptr in -- #85 +//│ let _ = assign ptr.n := x in -- #84 +//│ let acc = ctx.acc in -- #83 +//│ acc -- #82 +//│ ) +//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #94 +//│ let ctx2ptr = ctx2.ptr in -- #93 +//│ let ctx2field = ctx2.field in -- #92 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 +//│ ret -- #89 +//│ ) +//│ Def(7, b_modcons, [ctx,n$0], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 +//│ r0 -- #169 +//│ ) +//│ Def(8, a_modcons, [ctx,x$0], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 +//│ r0 -- #171 +//│ ) +//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ let scrut = ==(8,tailrecBranch$) in -- #167 +//│ if scrut -- #166 +//│ true => +//│ case a_modcons_x$0 of -- #165 +//│ Cons => +//│ let x$2 = a_modcons_x$0.n in -- #161 +//│ let x$3 = a_modcons_x$0.m in -- #160 +//│ let x$4 = <(x$3,0) in -- #159 +//│ if x$4 -- #158 +//│ true => +//│ let x$6 = Nil() in -- #151 +//│ let x$7 = Cons(-1,x$6) in -- #150 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 +//│ res -- #148 +//│ false => +//│ let x$8 = *(x$3,4) in -- #157 +//│ let x$9 = -(x$3,2) in -- #156 +//│ let x$11 = Cons(x$8,0) in -- #155 +//│ let ctx2 = _Context(x$11,x$11,0) in -- #154 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 +//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 +//│ Nil => +//│ let x$12 = Nil() in -- #164 +//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 +//│ res -- #162 +//│ false => +//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 +//│ if x$13 -- #146 +//│ true => +//│ let x$15 = Nil() in -- #142 +//│ let x$16 = Cons(0,x$15) in -- #141 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 +//│ res -- #139 +//│ false => +//│ let x$17 = Nil() in -- #145 +//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 +//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 +//│ ) +//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 //│ ) //│ }, -//│ let x$12 = Nil() in -- #103 -//│ let x$13 = Cons(9,x$12) in -- #102 -//│ let x$14 = Cons(3,x$13) in -- #101 -//│ let x$15 = Cons(2,x$14) in -- #100 -//│ let x$16 = Cons(4,x$15) in -- #99 -//│ let x$17 = Cons(7,x$16) in -- #98 -//│ let x$18 = Cons(6,x$17) in -- #97 -//│ let x$19 = Cons(1,x$18) in -- #96 -//│ let* (x$20) = foo(x$19) in -- #95 -//│ x$20 -- #94) +//│ let* (x$20) = b(16) in -- #81 +//│ x$20 -- #80) +//│ +//│ Interpreted: +//│ Cons(64,Cons(56,Cons(48,Cons(40,Cons(32,Cons(24,Cons(16,Cons(8,Cons(0,Nil()))))))))) + +:noTailRec +:interpIR +class True +class False +class Cons(h, t) +class Nil +fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then foo(t) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ +//│ IR: +//│ +//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], //│ 1, @@ -1264,7 +1783,29 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let* (x$20) = foo(x$19) in -- #95 //│ x$20 -- #94) //│ -//│ Promoted: +//│ Interpreted: +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +:interpIR +class True +class False +class Cons(h, t) +class Nil +@tailrec fun foo(xs) = + if xs is + Cons(h, t) then + if h > 5 then foo(t) + else + val item = if h < 3 then -1 else 100 + Cons(item, Cons(h, foo(t))) + Nil then + Nil +foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], //│ 1, @@ -1315,34 +1856,8 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let* (x$20) = foo(x$19) in -- #95 //│ x$20 -- #94) //│ -//│ Interpreted: -//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) - -:interpIR -class True -class False -class Cons(h, t) -class Nil -@tailrec fun foo(xs) = - if xs is - Cons(h, t) then - if h > 5 then foo(t) - else - val item = if h < 3 then -1 else 100 - Cons(item, Cons(h, foo(t))) - Nil then - Nil -foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} -//│ -//│ -//│ IR: -//│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(foo)) -//│ -//│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, @@ -1438,70 +1953,228 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let* (x$20) = foo(x$19) in -- #95 //│ x$20 -- #94) //│ -//│ Interpreted: -//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) - -:ce -class True -class False -fun b() = - a() - a() -@tailrec -fun a() = - if 0 < 1 then a() - else b() -a() -//│ |#class| |True|↵|#class| |False|↵|#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} -//│ -//│ -//│ IR: -//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2048: @tailrec -//│ ║ ^^^^^^^ -//│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.2046: a() -//│ ╙── ^ -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(a, b)) -//│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { -//│ Def(0, b, [], +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Def(0, foo, [xs$0], //│ 1, -//│ let* (r0) = _a_b_opt(0) in -- #44 -//│ r0 -- #43 +//│ let idCtx = _IdContext() in -- #137 +//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 +//│ res -- #135 //│ ) -//│ Def(1, a, [], +//│ Def(1, j$0, [x$0], //│ 1, -//│ let* (r0) = _a_b_opt(1) in -- #42 -//│ r0 -- #41 +//│ x$0 -- #1 //│ ) -//│ Def(2, j$0, [x$3], +//│ Def(2, j$1, [x$4], //│ 1, -//│ x$3 -- #11 +//│ jump j$0(x$4) -- #10 //│ ) -//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ Def(3, j$2, [x$7,x$1,x$2], //│ 1, -//│ let scrut = ==(0,tailrecBranch$) in -- #39 -//│ if scrut -- #38 -//│ true => -//│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp(1) -- #36 -//│ false => -//│ let x$2 = <(0,1) in -- #35 -//│ if x$2 -- #34 -//│ true => -//│ jump _a_b_opt_jp(1) -- #32 -//│ false => -//│ jump _a_b_opt_jp(0) -- #33 +//│ let* (x$8) = foo(x$1) in -- #40 +//│ let x$9 = Cons(x$2,x$8) in -- #39 +//│ let x$10 = Cons(x$7,x$9) in -- #38 +//│ jump j$1(x$10) -- #37 //│ ) -//│ Def(4, _a_b_opt, [tailrecBranch$], +//│ Def(4, _foo_ctx_app, [ctx,x], //│ 1, -//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 -//│ ) +//│ case ctx of -- #110 +//│ _IdContext => +//│ x -- #109 +//│ _Context => +//│ let field = ctx.field in -- #108 +//│ let ptr = ctx.ptr in -- #107 +//│ let _ = assign ptr.t := x in -- #106 +//│ let acc = ctx.acc in -- #105 +//│ acc -- #104 +//│ ) +//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #116 +//│ let ctx2ptr = ctx2.ptr in -- #115 +//│ let ctx2field = ctx2.field in -- #114 +//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 +//│ ret -- #111 +//│ ) +//│ Def(7, foo_modcons, [ctx,xs$0], +//│ 1, +//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ r0 -- #172 +//│ ) +//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #170 +//│ if scrut -- #169 +//│ true => +//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 +//│ let ctx2 = _Context(x$10,x$9,0) in -- #166 +//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 +//│ false => +//│ case foo_modcons_xs$0 of -- #163 +//│ Cons => +//│ let x$1 = foo_modcons_xs$0.t in -- #159 +//│ let x$2 = foo_modcons_xs$0.h in -- #158 +//│ let x$3 = >(x$2,5) in -- #157 +//│ if x$3 -- #156 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 +//│ false => +//│ let x$6 = <(x$2,3) in -- #155 +//│ if x$6 -- #154 +//│ true => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 +//│ false => +//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 +//│ Nil => +//│ let x$11 = Nil() in -- #162 +//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 +//│ res -- #160 +//│ ) +//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ 1, +//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 +//│ ) +//│ }, +//│ let x$12 = Nil() in -- #103 +//│ let x$13 = Cons(9,x$12) in -- #102 +//│ let x$14 = Cons(3,x$13) in -- #101 +//│ let x$15 = Cons(2,x$14) in -- #100 +//│ let x$16 = Cons(4,x$15) in -- #99 +//│ let x$17 = Cons(7,x$16) in -- #98 +//│ let x$18 = Cons(6,x$17) in -- #97 +//│ let x$19 = Cons(1,x$18) in -- #96 +//│ let* (x$20) = foo(x$19) in -- #95 +//│ x$20 -- #94) +//│ +//│ Interpreted: +//│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) + +:ce +class True +class False +fun b() = + a() + a() +@tailrec +fun a() = + if 0 < 1 then a() + else b() +a() +//│ |#class| |True|↵|#class| |False|↵|#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} +//│ +//│ +//│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (x$0) = a() in -- #7 +//│ let* (x$1) = a() in -- #6 +//│ x$1 -- #5 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let x$2 = <(0,1) in -- #23 +//│ if x$2 -- #22 +//│ true => +//│ let* (x$4) = a() in -- #16 +//│ jump j$0(x$4) -- #15 +//│ false => +//│ let* (x$5) = b() in -- #21 +//│ jump j$0(x$5) -- #20 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.1450: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recursive through this call, which may not be a tail-call +//│ ║ l.1448: a() +//│ ╙── ^ +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(j$0), Set(a, b)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp(0) -- #33 +//│ ) +//│ Def(4, _a_b_opt, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #27 +//│ x$6 -- #26) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(0, b, [], +//│ 1, +//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ r0 -- #43 +//│ ) +//│ Def(1, a, [], +//│ 1, +//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ r0 -- #41 +//│ ) +//│ Def(2, j$0, [x$3], +//│ 1, +//│ x$3 -- #11 +//│ ) +//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ 1, +//│ let scrut = ==(0,tailrecBranch$) in -- #39 +//│ if scrut -- #38 +//│ true => +//│ let* (x$0) = a() in -- #37 +//│ jump _a_b_opt_jp(1) -- #36 +//│ false => +//│ let x$2 = <(0,1) in -- #35 +//│ if x$2 -- #34 +//│ true => +//│ jump _a_b_opt_jp(1) -- #32 +//│ false => +//│ jump _a_b_opt_jp(0) -- #33 +//│ ) +//│ Def(4, _a_b_opt, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ ) //│ }, //│ let* (x$6) = a() in -- #27 //│ x$6 -- #26) @@ -1520,15 +2193,124 @@ a() //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = @tailcall a() in -- #22 +//│ let* (x$3) = c() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let* (x$5) = b() in -- #32 +//│ let x$6 = A(x$5,1) in -- #31 +//│ x$6 -- #30 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2148: @tailrec +//│ ║ l.1513: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.2150: fun b() = A(@tailcall a(), c()) +//│ ║ l.1515: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(c, b, a)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #74 +//│ let* (res) = a_modcons(idCtx) in -- #73 +//│ res -- #72 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #66 +//│ let* (res) = b_modcons(idCtx) in -- #65 +//│ res -- #64 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #57 +//│ let* (res) = c_modcons(idCtx) in -- #56 +//│ res -- #55 +//│ ) +//│ Def(3, _c_b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #43 +//│ _IdContext => +//│ x -- #42 +//│ _Context => +//│ let field = ctx.field in -- #41 +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 +//│ ) +//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #49 +//│ let ctx2ptr = ctx2.ptr in -- #48 +//│ let ctx2field = ctx2.field in -- #47 +//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 +//│ ret -- #44 +//│ ) +//│ Def(5, c_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 +//│ r0 -- #97 +//│ ) +//│ Def(6, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 +//│ r0 -- #99 +//│ ) +//│ Def(7, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 +//│ r0 -- #101 +//│ ) +//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(7,tailrecBranch$) in -- #95 +//│ if scrut -- #94 +//│ true => +//│ let x$1 = A(0,1) in -- #91 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 +//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ false => +//│ let scrut = ==(6,tailrecBranch$) in -- #93 +//│ if scrut -- #92 +//│ true => +//│ let* (x$3) = c() in -- #87 +//│ let x$4 = A(0,x$3) in -- #86 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 +//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ false => +//│ let x$6 = A(0,1) in -- #82 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 +//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ ) +//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ ) +//│ }, +//│ let* (x$7) = a() in -- #36 +//│ x$7 -- #35) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { @@ -1631,9 +2413,103 @@ a() //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = a() in -- #22 +//│ let* (x$3) = c() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = a_modcons(idCtx) in -- #62 +//│ res -- #61 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #55 +//│ let* (res) = b_modcons(idCtx) in -- #54 +//│ res -- #53 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #40 +//│ _IdContext => +//│ x -- #39 +//│ _Context => +//│ let field = ctx.field in -- #38 +//│ let ptr = ctx.ptr in -- #37 +//│ let _ = assign ptr.a := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #46 +//│ let ctx2ptr = ctx2.ptr in -- #45 +//│ let ctx2field = ctx2.field in -- #44 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 +//│ ret -- #41 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 +//│ r0 -- #81 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 +//│ r0 -- #83 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #79 +//│ if scrut -- #78 +//│ true => +//│ let x$1 = A(0,1) in -- #77 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 +//│ false => +//│ let* (x$3) = c() in -- #73 +//│ let x$4 = A(0,x$3) in -- #72 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { @@ -1722,9 +2598,111 @@ a() //│ //│ //│ IR: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { +//│ Def(0, a, [], +//│ 1, +//│ let* (x$0) = b() in -- #9 +//│ let x$1 = A(x$0,1) in -- #8 +//│ x$1 -- #7 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let* (x$2) = c() in -- #22 +//│ let* (x$3) = a() in -- #21 +//│ let x$4 = A(x$2,x$3) in -- #20 +//│ x$4 -- #19 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Def(0, a, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #69 +//│ let* (res) = a_modcons(idCtx) in -- #68 +//│ res -- #67 +//│ ) +//│ Def(1, b, [], +//│ 1, +//│ let idCtx = _IdContext() in -- #61 +//│ let* (res) = b_modcons(idCtx) in -- #60 +//│ res -- #59 +//│ ) +//│ Def(2, c, [], +//│ 1, +//│ let x$5 = A(0,1) in -- #29 +//│ x$5 -- #28 +//│ ) +//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ 1, +//│ case ctx of -- #46 +//│ _IdContext => +//│ x -- #45 +//│ _Context => +//│ let field = ctx.field in -- #44 +//│ let scrut = ==(1,field) in -- #43 +//│ if scrut -- #42 +//│ true => +//│ let ptr = ctx.ptr in -- #41 +//│ let _ = assign ptr.a := x in -- #40 +//│ let acc = ctx.acc in -- #39 +//│ acc -- #38 +//│ false => +//│ let ptr = ctx.ptr in -- #37 +//│ let _ = assign ptr.b := x in -- #36 +//│ let acc = ctx.acc in -- #35 +//│ acc -- #34 +//│ ) +//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ 1, +//│ let ctx2acc = ctx2.acc in -- #52 +//│ let ctx2ptr = ctx2.ptr in -- #51 +//│ let ctx2field = ctx2.field in -- #50 +//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 +//│ ret -- #47 +//│ ) +//│ Def(5, b_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 +//│ r0 -- #87 +//│ ) +//│ Def(6, a_modcons, [ctx], +//│ 1, +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 +//│ r0 -- #89 +//│ ) +//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ let scrut = ==(6,tailrecBranch$) in -- #85 +//│ if scrut -- #84 +//│ true => +//│ let x$1 = A(0,1) in -- #83 +//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 +//│ false => +//│ let* (x$2) = c() in -- #79 +//│ let x$4 = A(x$2,0) in -- #78 +//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 +//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 +//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 +//│ ) +//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ 1, +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 +//│ ) +//│ }, +//│ let* (x$6) = a() in -- #33 +//│ x$6 -- #32) //│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 1bdc7ab79f..d94a09d27e 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -24,7 +24,7 @@ class IRDiffTestCompiler extends DiffTests { val gb = Builder(Fresh(), fnUid, classUid, tag) val graph_ = gb.buildGraph(unit) - if mode.noTailRecOpt then + if !mode.noTailRecOpt then output(graph_.toString()) val graph = if (!mode.noTailRecOpt) { @@ -37,7 +37,7 @@ class IRDiffTestCompiler extends DiffTests { graph_ } - if mode.noTailRecOpt then + if !mode.noTailRecOpt then output(graph.toString()) output("\nPromoted:") From 487279ede1d81d9db174e71ab69f439e5f17050e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Jun 2024 23:30:35 +0800 Subject: [PATCH 44/59] Fix grammar --- .../compiler/optimizer/TailRecOpt.scala | 2 +- compiler/shared/test/diff-ir/IRTailRec.mls | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 722b27318b..511fb0e453 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -969,7 +969,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag raise(ErrorReport( List( msg"function `${fn.name}` is not tail-recursive, but is marked as @tailrec" -> fn.loc, - msg"it could self-recursive through this call, which may not be a tail-call" -> call.loc + msg"it could self-recurse through this call, which may not be a tail-call" -> call.loc ), true, Diagnostic.Compilation) ) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index a3b1e3a0c8..1aab142c9f 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -658,13 +658,13 @@ hello() //│ x$2 -- #11) //│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall //│ ╟── first call -//│ ║ l.491: @tailcall hello() +//│ ║ l.640: @tailcall hello() //│ ║ ^^^^^ //│ ╟── second call -//│ ║ l.492: @tailcall hello() +//│ ║ l.641: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.491: @tailcall hello() +//│ ║ l.640: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: @@ -714,7 +714,7 @@ hello() //│ let* (x$1) = hello() in -- #8 //│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.530: @tailcall hello() +//│ ║ l.699: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: @@ -2094,10 +2094,10 @@ a() //│ let* (x$6) = a() in -- #27 //│ x$6 -- #26) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.1450: @tailrec +//│ ║ l.2061: @tailrec //│ ║ ^^^^^^^ -//│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.1448: a() +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.2059: a() //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: @@ -2217,10 +2217,10 @@ a() //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.1513: @tailrec +//│ ║ l.2186: @tailrec //│ ║ ^^^^^^^ -//│ ╟── it could self-recursive through this call, which may not be a tail-call -//│ ║ l.1515: fun b() = A(@tailcall a(), c()) +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.2188: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: From a4efc7a41cab82032eb5eb01305d834c1582e9ce Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 21:22:16 +0800 Subject: [PATCH 45/59] Purity check, fix join point bug --- .../scala/mlscript/compiler/ir/Builder.scala | 5 + .../compiler/optimizer/TailRecOpt.scala | 58 +- compiler/shared/test/diff-ir/IRTailRec.mls | 647 ++++++------------ 3 files changed, 266 insertions(+), 444 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index d7ba106188..8187869f28 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -342,5 +342,10 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres resolveDefnRef(main, defs, true) validate(main, defs) + + // TODO: should properly import built-in types + val clsWithBool = clsinfo + + ClassInfo(classUid.make, "True", List()) + + ClassInfo(classUid.make, "False", List()) Program(clsinfo, defs, main) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 511fb0e453..e37af36dfb 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -1,15 +1,13 @@ -package mlscript.compiler.optimizer +package mlscript +package compiler.optimizer -import mlscript.compiler.ir._ -import mlscript.compiler.ir.Node._ import scala.annotation.tailrec -import mlscript.IntLit -import mlscript.utils.shorthands.Bool -import mlscript.Diagnostic -import mlscript.ErrorReport -import mlscript.Message -import mlscript.Message.MessageContext -import mlscript.Loc + +import utils.shorthands._ +import Message.MessageContext + +import compiler.ir._ +import compiler.ir.Node._ // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): @@ -59,6 +57,27 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag def discoverJoinPointsCont(defn: Defn, acc: Set[Defn]) = discoverJoinPoints(defn.body, acc) + defn + // TODO: implement proper purity checking. This is a very simple purity check that only allows the last + // parameter of a mod cons call to be optimised. + private val pureCache: scala.collection.mutable.Map[Int, Bool] = scala.collection.mutable.Map[Int, Bool]() + private def isPure(node: Node): Bool = + pureCache.get(node.tag.inner) match + case None => + val ret = node match + case Jump(defn, args) => isIdentityJp(defn.expectDefn) + case _: LetCall => false + case Case(scrut, cases) => cases.foldLeft(true)((value, branch) => value && isPure(branch._2)) + case LetExpr(name, expr: Expr.AssignField, body) => false + case x: LetExpr => true + case Result(res) => true + pureCache.put(node.tag.inner, ret) + ret + + case Some(value) => value + + + + // do a DFS to discover join points @tailrec private def discoverJoinPoints(node: Node, acc: Set[Defn]): Set[Defn] = @@ -299,12 +318,19 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag Left(updatedMap + TailCallInfo(src, defn.expectDefn)) else + val restIsPure = isPure(body) letCallNode match case None => // OK, we may use this LetCall as the mod cons // For now, only optimize functions which return one value - if callInScc && defn.expectDefn.resultNum == 1 then + if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then searchOptCalls(body)(acc, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else + if isTailRec then + if !restIsPure then + raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) + else + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) + // Treat this as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) @@ -336,7 +362,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag searchOptCalls(body)(newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK else // only include mod cons calls that have one return value - if callInScc && defn.expectDefn.resultNum == 1 then + if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec @@ -345,6 +371,11 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)(y.loc)) searchOptCalls(body)(newMap, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else + if isTailRec then + if !restIsPure then + raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) + else + raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) // shadow all the variables in this letcall // Treat this as a normal call @@ -417,6 +448,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val defns = nodeMap.values.toSet val inital = Map[Int, Set[Defn]]() val joinPoints = defns.map(d => (d.defn.id -> discoverJoinPoints(d.defn.body, Set()))).toMap + val allJoinPoints = joinPoints.values.flatMap(x => x).toSet val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn, joinPoints(defn.defn.id))(acc)).withDefaultValue(Set()) var ctr = 0 @@ -479,7 +511,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag sccs = DefnGraph(scc, categorizedEdges, sccJoinPoints) :: sccs for v <- defns do - if (!v.visited) + if !allJoinPoints.contains(v.defn) && !v.visited then dfs(v) sccs diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 1aab142c9f..1f03b58df3 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -77,7 +77,7 @@ fact(1, 5) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 @@ -103,7 +103,7 @@ fact(1, 5) //│ x$5 -- #29) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 @@ -179,8 +179,8 @@ fact(1, 5) //│ x$7 -- #39) //│ //│ Strongly Connected Tail Calls: -//│ List(Set(j$0), Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ List(Set(j$1), Set(fact)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, //│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 @@ -190,17 +190,6 @@ fact(1, 5) //│ 1, //│ x$3 -- #7 //│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) //│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, //│ let scrut = ==(2,tailrecBranch$) in -- #57 @@ -231,7 +220,7 @@ fact(1, 5) //│ x$7 -- #39) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, //│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 @@ -241,17 +230,6 @@ fact(1, 5) //│ 1, //│ x$3 -- #7 //│ ) -//│ Def(2, j$0, [x$1,acc$0,n$0], -//│ 1, -//│ let x$2 = <=(x$1,0) in -- #23 -//│ if x$2 -- #22 -//│ true => -//│ jump j$1(acc$0) -- #9 -//│ false => -//│ let x$4 = *(n$0,acc$0) in -- #21 -//│ let* (x$5) = @tailcall fact(x$4,x$1) in -- #20 -//│ jump j$1(x$5) -- #19 -//│ ) //│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, //│ let scrut = ==(2,tailrecBranch$) in -- #57 @@ -406,7 +384,7 @@ g(6, 0) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -465,7 +443,7 @@ g(6, 0) //│ x$15 -- #69) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -558,7 +536,7 @@ class False //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, //│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 @@ -596,7 +574,7 @@ class False //│ 2 -- #30) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, //│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 @@ -656,20 +634,16 @@ hello() //│ }, //│ let* (x$2) = hello() in -- #12 //│ x$2 -- #11) -//│ ╔══[COMPILATION ERROR] multiple calls in the same branch marked with @tailcall -//│ ╟── first call -//│ ║ l.640: @tailcall hello() -//│ ║ ^^^^^ -//│ ╟── second call -//│ ║ l.641: @tailcall hello() +//│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure +//│ ║ l.618: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.640: @tailcall hello() +//│ ║ l.619: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #8 @@ -681,7 +655,7 @@ hello() //│ x$2 -- #11) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #8 @@ -714,12 +688,12 @@ hello() //│ let* (x$1) = hello() in -- #8 //│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.699: @tailcall hello() +//│ ║ l.673: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #4 @@ -730,7 +704,7 @@ hello() //│ x$1 -- #7) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #4 @@ -784,7 +758,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -846,7 +820,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #47) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -1046,7 +1020,7 @@ a(S(S(S(Zero)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 @@ -1144,7 +1118,7 @@ a(S(S(S(Zero)))) //│ x$18 -- #69) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 @@ -1293,7 +1267,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 @@ -1355,7 +1329,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #50) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 @@ -1498,7 +1472,7 @@ b(16) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 @@ -1603,7 +1577,7 @@ b(16) //│ x$20 -- #80) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 @@ -1794,15 +1768,15 @@ class Nil @tailrec fun foo(xs) = if xs is Cons(h, t) then - if h > 5 then foo(t) + if h > 5 then @tailcall foo(t) else val item = if h < 3 then -1 else 100 - Cons(item, Cons(h, foo(t))) + Cons(item, Cons(h, @tailcall foo(t))) Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: @@ -1816,7 +1790,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let x$3 = >(x$2,5) in -- #48 //│ if x$3 -- #47 //│ true => -//│ let* (x$5) = foo(x$1) in -- #17 +//│ let* (x$5) = @tailcall foo(x$1) in -- #17 //│ jump j$1(x$5) -- #16 //│ false => //│ let x$6 = <(x$2,3) in -- #46 @@ -1839,7 +1813,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ ) //│ Def(3, j$2, [x$7,x$1,x$2], //│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 +//│ let* (x$8) = @tailcall foo(x$1) in -- #40 //│ let x$9 = Cons(x$2,x$8) in -- #39 //│ let x$10 = Cons(x$7,x$9) in -- #38 //│ jump j$1(x$10) -- #37 @@ -1857,8 +1831,8 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ x$20 -- #94) //│ //│ Strongly Connected Tail Calls: -//│ List(Set(j$2), Set(j$1), Set(j$0), Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ List(Set(j$1), Set(j$0), Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 @@ -1873,13 +1847,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) //│ Def(4, _foo_ctx_app, [ctx,x], //│ 1, //│ case ctx of -- #110 @@ -1954,7 +1921,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ x$20 -- #94) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 @@ -1969,13 +1936,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(3, j$2, [x$7,x$1,x$2], -//│ 1, -//│ let* (x$8) = foo(x$1) in -- #40 -//│ let x$9 = Cons(x$2,x$8) in -- #39 -//│ let x$10 = Cons(x$7,x$9) in -- #38 -//│ jump j$1(x$10) -- #37 -//│ ) //│ Def(4, _foo_ctx_app, [ctx,x], //│ 1, //│ case ctx of -- #110 @@ -2094,15 +2054,15 @@ a() //│ let* (x$6) = a() in -- #27 //│ x$6 -- #26) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2061: @tailrec +//│ ║ l.2021: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2059: a() +//│ ║ l.2019: a() //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(a, b)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, //│ let* (r0) = _a_b_opt(0) in -- #44 @@ -2141,7 +2101,7 @@ a() //│ x$6 -- #26) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, //│ let* (r0) = _a_b_opt(0) in -- #44 @@ -2185,11 +2145,11 @@ class False class A(a, b) @tailrec fun a() = A(b(), 1) -fun b() = A(@tailcall a(), c()) +fun b() = A(c(), @tailcall a()) fun c() = A(b(), 1) a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(b(), 1,); a()} +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} //│ //│ //│ IR: @@ -2202,8 +2162,8 @@ a() //│ ) //│ Def(1, b, [], //│ 1, -//│ let* (x$2) = @tailcall a() in -- #22 -//│ let* (x$3) = c() in -- #21 +//│ let* (x$2) = c() in -- #22 +//│ let* (x$3) = @tailcall a() in -- #21 //│ let x$4 = A(x$2,x$3) in -- #20 //│ x$4 -- #19 //│ ) @@ -2217,199 +2177,216 @@ a() //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2186: @tailrec +//│ ║ l.2146: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2188: fun b() = A(@tailcall a(), c()) -//│ ╙── ^ +//│ ║ l.2148: fun b() = A(c(), @tailcall a()) +//│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(c, b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, -//│ let idCtx = _IdContext() in -- #74 -//│ let* (res) = a_modcons(idCtx) in -- #73 -//│ res -- #72 +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = a_modcons(idCtx) in -- #79 +//│ res -- #78 //│ ) //│ Def(1, b, [], //│ 1, -//│ let idCtx = _IdContext() in -- #66 -//│ let* (res) = b_modcons(idCtx) in -- #65 -//│ res -- #64 +//│ let idCtx = _IdContext() in -- #72 +//│ let* (res) = b_modcons(idCtx) in -- #71 +//│ res -- #70 //│ ) //│ Def(2, c, [], //│ 1, -//│ let idCtx = _IdContext() in -- #57 -//│ let* (res) = c_modcons(idCtx) in -- #56 -//│ res -- #55 +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = c_modcons(idCtx) in -- #62 +//│ res -- #61 //│ ) //│ Def(3, _c_b_a_ctx_app, [ctx,x], //│ 1, -//│ case ctx of -- #43 +//│ case ctx of -- #49 //│ _IdContext => -//│ x -- #42 +//│ x -- #48 //│ _Context => -//│ let field = ctx.field in -- #41 -//│ let ptr = ctx.ptr in -- #40 -//│ let _ = assign ptr.a := x in -- #39 -//│ let acc = ctx.acc in -- #38 -//│ acc -- #37 +//│ let field = ctx.field in -- #47 +//│ let scrut = ==(1,field) in -- #46 +//│ if scrut -- #45 +//│ true => +//│ let ptr = ctx.ptr in -- #44 +//│ let _ = assign ptr.b := x in -- #43 +//│ let acc = ctx.acc in -- #42 +//│ acc -- #41 +//│ false => +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 //│ ) //│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let ctx2acc = ctx2.acc in -- #49 -//│ let ctx2ptr = ctx2.ptr in -- #48 -//│ let ctx2field = ctx2.field in -- #47 -//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 -//│ ret -- #44 +//│ let ctx2acc = ctx2.acc in -- #55 +//│ let ctx2ptr = ctx2.ptr in -- #54 +//│ let ctx2field = ctx2.field in -- #53 +//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #52 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 +//│ ret -- #50 //│ ) //│ Def(5, c_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 -//│ r0 -- #97 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #104 +//│ r0 -- #103 //│ ) //│ Def(6, b_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 -//│ r0 -- #99 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #106 +//│ r0 -- #105 //│ ) //│ Def(7, a_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 -//│ r0 -- #101 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #108 +//│ r0 -- #107 //│ ) //│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #95 -//│ if scrut -- #94 +//│ let scrut = ==(7,tailrecBranch$) in -- #101 +//│ if scrut -- #100 //│ true => -//│ let x$1 = A(0,1) in -- #91 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 -//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ let x$1 = A(0,1) in -- #97 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 +//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #95 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #94 //│ false => -//│ let scrut = ==(6,tailrecBranch$) in -- #93 -//│ if scrut -- #92 +//│ let scrut = ==(6,tailrecBranch$) in -- #99 +//│ if scrut -- #98 //│ true => -//│ let* (x$3) = c() in -- #87 -//│ let x$4 = A(0,x$3) in -- #86 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 -//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ let* (x$2) = c() in -- #93 +//│ let x$4 = A(x$2,0) in -- #92 +//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 +//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #90 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #89 //│ false => -//│ let x$6 = A(0,1) in -- #82 -//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 -//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ let x$6 = A(0,1) in -- #88 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 +//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #86 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #85 //│ ) //│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #102 //│ ) //│ }, //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, -//│ let idCtx = _IdContext() in -- #74 -//│ let* (res) = a_modcons(idCtx) in -- #73 -//│ res -- #72 +//│ let idCtx = _IdContext() in -- #80 +//│ let* (res) = a_modcons(idCtx) in -- #79 +//│ res -- #78 //│ ) //│ Def(1, b, [], //│ 1, -//│ let idCtx = _IdContext() in -- #66 -//│ let* (res) = b_modcons(idCtx) in -- #65 -//│ res -- #64 +//│ let idCtx = _IdContext() in -- #72 +//│ let* (res) = b_modcons(idCtx) in -- #71 +//│ res -- #70 //│ ) //│ Def(2, c, [], //│ 1, -//│ let idCtx = _IdContext() in -- #57 -//│ let* (res) = c_modcons(idCtx) in -- #56 -//│ res -- #55 +//│ let idCtx = _IdContext() in -- #63 +//│ let* (res) = c_modcons(idCtx) in -- #62 +//│ res -- #61 //│ ) //│ Def(3, _c_b_a_ctx_app, [ctx,x], //│ 1, -//│ case ctx of -- #43 +//│ case ctx of -- #49 //│ _IdContext => -//│ x -- #42 +//│ x -- #48 //│ _Context => -//│ let field = ctx.field in -- #41 -//│ let ptr = ctx.ptr in -- #40 -//│ let _ = assign ptr.a := x in -- #39 -//│ let acc = ctx.acc in -- #38 -//│ acc -- #37 +//│ let field = ctx.field in -- #47 +//│ let scrut = ==(1,field) in -- #46 +//│ if scrut -- #45 +//│ true => +//│ let ptr = ctx.ptr in -- #44 +//│ let _ = assign ptr.b := x in -- #43 +//│ let acc = ctx.acc in -- #42 +//│ acc -- #41 +//│ false => +//│ let ptr = ctx.ptr in -- #40 +//│ let _ = assign ptr.a := x in -- #39 +//│ let acc = ctx.acc in -- #38 +//│ acc -- #37 //│ ) //│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], //│ 1, -//│ let ctx2acc = ctx2.acc in -- #49 -//│ let ctx2ptr = ctx2.ptr in -- #48 -//│ let ctx2field = ctx2.field in -- #47 -//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #46 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #45 -//│ ret -- #44 +//│ let ctx2acc = ctx2.acc in -- #55 +//│ let ctx2ptr = ctx2.ptr in -- #54 +//│ let ctx2field = ctx2.field in -- #53 +//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #52 +//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 +//│ ret -- #50 //│ ) //│ Def(5, c_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #98 -//│ r0 -- #97 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #104 +//│ r0 -- #103 //│ ) //│ Def(6, b_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #100 -//│ r0 -- #99 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #106 +//│ r0 -- #105 //│ ) //│ Def(7, a_modcons, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #102 -//│ r0 -- #101 +//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #108 +//│ r0 -- #107 //│ ) //│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ let scrut = ==(7,tailrecBranch$) in -- #95 -//│ if scrut -- #94 +//│ let scrut = ==(7,tailrecBranch$) in -- #101 +//│ if scrut -- #100 //│ true => -//│ let x$1 = A(0,1) in -- #91 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #90 -//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #89 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #88 +//│ let x$1 = A(0,1) in -- #97 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #96 +//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #95 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #94 //│ false => -//│ let scrut = ==(6,tailrecBranch$) in -- #93 -//│ if scrut -- #92 +//│ let scrut = ==(6,tailrecBranch$) in -- #99 +//│ if scrut -- #98 //│ true => -//│ let* (x$3) = c() in -- #87 -//│ let x$4 = A(0,x$3) in -- #86 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #85 -//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #84 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #83 +//│ let* (x$2) = c() in -- #93 +//│ let x$4 = A(x$2,0) in -- #92 +//│ let ctx2 = _Context(x$4,x$4,1) in -- #91 +//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #90 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #89 //│ false => -//│ let x$6 = A(0,1) in -- #82 -//│ let ctx2 = _Context(x$6,x$6,0) in -- #81 -//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #80 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #79 +//│ let x$6 = A(0,1) in -- #88 +//│ let ctx2 = _Context(x$6,x$6,0) in -- #87 +//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #86 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #85 //│ ) //│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #96 +//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #102 //│ ) //│ }, //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) +// TODO: Purity check class True class False class A(a, b) @tailrec fun a() = A(b(), 1) -fun b() = A(a(), c()) +fun b() = A(@tailcall a(), c()) fun c() = A(0, 1) a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(a(), c(),); fun c = () => A(0, 1,); a()} +//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| +//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} //│ //│ //│ IR: @@ -2422,7 +2399,7 @@ a() //│ ) //│ Def(1, b, [], //│ 1, -//│ let* (x$2) = a() in -- #22 +//│ let* (x$2) = @tailcall a() in -- #22 //│ let* (x$3) = c() in -- #21 //│ let x$4 = A(x$2,x$3) in -- #20 //│ x$4 -- #19 @@ -2435,21 +2412,30 @@ a() //│ }, //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) +//│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure +//│ ║ l.2385: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ +//│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec +//│ ║ l.2383: @tailrec +//│ ║ ^^^^^^^ +//│ ╟── it could self-recurse through this call, which may not be a tail-call +//│ ║ l.2385: fun b() = A(@tailcall a(), c()) +//│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, -//│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = a_modcons(idCtx) in -- #62 -//│ res -- #61 +//│ let idCtx = _IdContext() in -- #62 +//│ let* (res) = a_modcons(idCtx) in -- #61 +//│ res -- #60 //│ ) //│ Def(1, b, [], //│ 1, -//│ let idCtx = _IdContext() in -- #55 -//│ let* (res) = b_modcons(idCtx) in -- #54 -//│ res -- #53 +//│ let idCtx = _IdContext() in -- #54 +//│ let* (res) = b_modcons(idCtx) in -- #53 +//│ res -- #52 //│ ) //│ Def(2, c, [], //│ 1, @@ -2479,51 +2465,51 @@ a() //│ ) //│ Def(5, b_modcons, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 -//│ r0 -- #81 +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #81 +//│ r0 -- #80 //│ ) //│ Def(6, a_modcons, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 -//│ r0 -- #83 +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #83 +//│ r0 -- #82 //│ ) //│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #79 -//│ if scrut -- #78 +//│ let scrut = ==(6,tailrecBranch$) in -- #78 +//│ if scrut -- #77 //│ true => -//│ let x$1 = A(0,1) in -- #77 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 +//│ let x$1 = A(0,1) in -- #76 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #74 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #73 //│ false => -//│ let* (x$3) = c() in -- #73 -//│ let x$4 = A(0,x$3) in -- #72 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 +//│ let* (x$2) = @tailcall a() in -- #72 +//│ let* (x$3) = c() in -- #71 +//│ let x$4 = A(x$2,x$3) in -- #70 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$4) in -- #69 +//│ res -- #68 //│ ) //│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #79 //│ ) //│ }, //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, -//│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = a_modcons(idCtx) in -- #62 -//│ res -- #61 +//│ let idCtx = _IdContext() in -- #62 +//│ let* (res) = a_modcons(idCtx) in -- #61 +//│ res -- #60 //│ ) //│ Def(1, b, [], //│ 1, -//│ let idCtx = _IdContext() in -- #55 -//│ let* (res) = b_modcons(idCtx) in -- #54 -//│ res -- #53 +//│ let idCtx = _IdContext() in -- #54 +//│ let* (res) = b_modcons(idCtx) in -- #53 +//│ res -- #52 //│ ) //│ Def(2, c, [], //│ 1, @@ -2553,234 +2539,33 @@ a() //│ ) //│ Def(5, b_modcons, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #82 -//│ r0 -- #81 -//│ ) -//│ Def(6, a_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #84 -//│ r0 -- #83 -//│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #79 -//│ if scrut -- #78 -//│ true => -//│ let x$1 = A(0,1) in -- #77 -//│ let ctx2 = _Context(x$1,x$1,0) in -- #76 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #75 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #74 -//│ false => -//│ let* (x$3) = c() in -- #73 -//│ let x$4 = A(0,x$3) in -- #72 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #71 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #70 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #69 -//│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #80 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) - -class True -class False -class A(a, b) -@tailrec -fun a() = A(b(), 1) -fun b() = A(c(), a()) -fun c() = A(0, 1) -a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), a(),); fun c = () => A(0, 1,); a()} -//│ -//│ -//│ IR: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { -//│ Def(0, a, [], -//│ 1, -//│ let* (x$0) = b() in -- #9 -//│ let x$1 = A(x$0,1) in -- #8 -//│ x$1 -- #7 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let* (x$2) = c() in -- #22 -//│ let* (x$3) = a() in -- #21 -//│ let x$4 = A(x$2,x$3) in -- #20 -//│ x$4 -- #19 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) -//│ -//│ Strongly Connected Tail Calls: -//│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #69 -//│ let* (res) = a_modcons(idCtx) in -- #68 -//│ res -- #67 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #61 -//│ let* (res) = b_modcons(idCtx) in -- #60 -//│ res -- #59 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #46 -//│ _IdContext => -//│ x -- #45 -//│ _Context => -//│ let field = ctx.field in -- #44 -//│ let scrut = ==(1,field) in -- #43 -//│ if scrut -- #42 -//│ true => -//│ let ptr = ctx.ptr in -- #41 -//│ let _ = assign ptr.a := x in -- #40 -//│ let acc = ctx.acc in -- #39 -//│ acc -- #38 -//│ false => -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.b := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #52 -//│ let ctx2ptr = ctx2.ptr in -- #51 -//│ let ctx2field = ctx2.field in -- #50 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 -//│ ret -- #47 -//│ ) -//│ Def(5, b_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 -//│ r0 -- #87 -//│ ) -//│ Def(6, a_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 -//│ r0 -- #89 -//│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #85 -//│ if scrut -- #84 -//│ true => -//│ let x$1 = A(0,1) in -- #83 -//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 -//│ false => -//│ let* (x$2) = c() in -- #79 -//│ let x$4 = A(x$2,0) in -- #78 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 -//│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 -//│ ) -//│ }, -//│ let* (x$6) = a() in -- #33 -//│ x$6 -- #32) -//│ -//│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { -//│ Def(0, a, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #69 -//│ let* (res) = a_modcons(idCtx) in -- #68 -//│ res -- #67 -//│ ) -//│ Def(1, b, [], -//│ 1, -//│ let idCtx = _IdContext() in -- #61 -//│ let* (res) = b_modcons(idCtx) in -- #60 -//│ res -- #59 -//│ ) -//│ Def(2, c, [], -//│ 1, -//│ let x$5 = A(0,1) in -- #29 -//│ x$5 -- #28 -//│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], -//│ 1, -//│ case ctx of -- #46 -//│ _IdContext => -//│ x -- #45 -//│ _Context => -//│ let field = ctx.field in -- #44 -//│ let scrut = ==(1,field) in -- #43 -//│ if scrut -- #42 -//│ true => -//│ let ptr = ctx.ptr in -- #41 -//│ let _ = assign ptr.a := x in -- #40 -//│ let acc = ctx.acc in -- #39 -//│ acc -- #38 -//│ false => -//│ let ptr = ctx.ptr in -- #37 -//│ let _ = assign ptr.b := x in -- #36 -//│ let acc = ctx.acc in -- #35 -//│ acc -- #34 -//│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], -//│ 1, -//│ let ctx2acc = ctx2.acc in -- #52 -//│ let ctx2ptr = ctx2.ptr in -- #51 -//│ let ctx2field = ctx2.field in -- #50 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #49 -//│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #48 -//│ ret -- #47 -//│ ) -//│ Def(5, b_modcons, [ctx], -//│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #88 -//│ r0 -- #87 +//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #81 +//│ r0 -- #80 //│ ) //│ Def(6, a_modcons, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #90 -//│ r0 -- #89 +//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #83 +//│ r0 -- #82 //│ ) //│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ let scrut = ==(6,tailrecBranch$) in -- #85 -//│ if scrut -- #84 +//│ let scrut = ==(6,tailrecBranch$) in -- #78 +//│ if scrut -- #77 //│ true => -//│ let x$1 = A(0,1) in -- #83 -//│ let ctx2 = _Context(x$1,x$1,1) in -- #82 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #81 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #80 +//│ let x$1 = A(0,1) in -- #76 +//│ let ctx2 = _Context(x$1,x$1,0) in -- #75 +//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #74 +//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #73 //│ false => -//│ let* (x$2) = c() in -- #79 -//│ let x$4 = A(x$2,0) in -- #78 -//│ let ctx2 = _Context(x$4,x$4,0) in -- #77 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #76 -//│ jump _b_modcons_a_modcons_opt_jp(6,b_modcons_ctx,composed) -- #75 +//│ let* (x$2) = @tailcall a() in -- #72 +//│ let* (x$3) = c() in -- #71 +//│ let x$4 = A(x$2,x$3) in -- #70 +//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$4) in -- #69 +//│ res -- #68 //│ ) //│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], //│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #86 +//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #79 //│ ) //│ }, //│ let* (x$6) = a() in -- #33 From 0c8dec1c4934a8f224d7c68e2085c359e770ff58 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 21:33:44 +0800 Subject: [PATCH 46/59] Build in true/false class in IR --- .../scala/mlscript/compiler/ir/Builder.scala | 10 +- compiler/shared/test/diff-ir/IR.mls | 26 ++- compiler/shared/test/diff-ir/IRComplex.mls | 6 +- compiler/shared/test/diff-ir/IRRec.mls | 46 +++-- compiler/shared/test/diff-ir/IRTailRec.mls | 184 +++++++----------- 5 files changed, 119 insertions(+), 153 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 8187869f28..dfb7acc8d8 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -307,7 +307,10 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres import scala.collection.mutable.{ HashSet => MutHSet } - val cls = grouped.getOrElse(0, Nil).map(buildClassInfo) + val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" amd "False" at some pointgrouped.getOrElse(0, Nil).map(buildClassInfo) + :: ClassInfo(classUid.make, "False", List()) + :: grouped.getOrElse(0, Nil).map(buildClassInfo) + cls.foldLeft(Set.empty)(checkDuplicateField(_, _)) val clsinfo = cls.toSet @@ -342,10 +345,5 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres resolveDefnRef(main, defs, true) validate(main, defs) - - // TODO: should properly import built-in types - val clsWithBool = clsinfo - + ClassInfo(classUid.make, "True", List()) - + ClassInfo(classUid.make, "False", List()) Program(clsinfo, defs, main) diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 9181242f9b..6a93ae6524 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -17,7 +17,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, mktup2, [x$0,y$0], //│ 1, //│ let* (x$1) = mktup(x$0,y$0) in -- #7 @@ -55,7 +55,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [pair$0], //│ 1, //│ case pair$0 of -- #16 @@ -105,7 +105,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, silly, [pair$0], //│ 1, //│ let x$0 = 0 in -- #29 @@ -163,7 +163,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], //│ 1, //│ let x$0 = 2 in -- #15 @@ -208,7 +208,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], //│ 1, //│ let x$0 = 0 in -- #15 @@ -256,7 +256,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Left, [x]),ClassInfo(1, Right, [y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Left, [x]),ClassInfo(3, Right, [y])}, { //│ Def(0, foo, [a$0,b$0], //│ 1, //│ case a$0 of -- #36 @@ -298,15 +298,13 @@ bar() //│ 2 :interpIR -class True -class False class Pair(x, y) fun foo(a) = a.x + a.y fun bar() = foo(Pair(1, 0)) bar() -//│ |#class| |True|↵|#class| |False|↵|#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| -//│ Parsed: {class True {}; class False {}; class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} +//│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| +//│ Parsed: {class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} //│ //│ //│ IR: @@ -350,7 +348,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, C1, [x,y]),ClassInfo(1, C2, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, C1, [x,y]),ClassInfo(3, C2, [z])}, { //│ Def(0, foo, [a$0], //│ 1, //│ case a$0 of -- #15 @@ -401,7 +399,7 @@ baz() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [a$0,b$0], //│ 1, //│ let x$0 = a$0.x in -- #21 @@ -451,7 +449,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, Pair, [x,y])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [], //│ 1, //│ let x$0 = Pair(0,1) in -- #10 @@ -484,7 +482,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, S, [s]),ClassInfo(1, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, foo, [], //│ 1, //│ let x$0 = O() in -- #10 diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index 8a3e4182e4..59f3497175 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -26,7 +26,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, A, [x,y,z]),ClassInfo(1, B, [m,n])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [x,y,z]),ClassInfo(3, B, [m,n])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ case t$0 of -- #63 @@ -117,7 +117,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ let x$0 = +(1,2) in -- #140 @@ -256,7 +256,7 @@ bar() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, A, [w,x]),ClassInfo(1, B, [y]),ClassInfo(2, C, [z])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], //│ 1, //│ let x$0 = +(1,2) in -- #150 diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index d2dcf23909..df8bad56fd 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -15,7 +15,7 @@ fib(20) //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, fib, [n$0], //│ 1, //│ let x$0 = <(n$0,2) in -- #28 @@ -55,7 +55,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ let x$1 = ==(x$0,0) in -- #18 @@ -119,7 +119,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, not, [x$0], //│ 1, //│ if x$0 -- #8 @@ -195,7 +195,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], //│ 1, //│ let x$0 = 1 in -- #17 @@ -291,7 +291,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -361,7 +361,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -420,7 +420,8 @@ foo() //│ x$18 -- #68) //│ //│ Interpreted: -//│ False() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR class True @@ -445,7 +446,7 @@ foo() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -506,7 +507,8 @@ foo() //│ x$20 -- #76) //│ //│ Interpreted: -//│ False() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR class True @@ -534,7 +536,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], //│ 1, //│ case x$0 of -- #15 @@ -611,7 +613,8 @@ main() //│ x$25 -- #89) //│ //│ Interpreted: -//│ True() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected :interpIR class True @@ -644,7 +647,7 @@ main(False) //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, []),ClassInfo(3, B, [b])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], //│ 1, //│ let x$0 = 1 in -- #17 @@ -745,7 +748,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, head_opt, [l$0], //│ 1, //│ case l$0 of -- #17 @@ -827,7 +830,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], //│ 1, //│ let x$0 = ==(n$0,0) in -- #24 @@ -893,7 +896,8 @@ main() //│ x$19 -- #79) //│ //│ Interpreted: -//│ False() +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR class True @@ -921,7 +925,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], //│ 1, //│ let x$0 = ==(n$0,0) in -- #24 @@ -977,7 +981,8 @@ main() //│ x$17 -- #73) //│ //│ Interpreted: -//│ Some(1) +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(1, False, []) expected :interpIR class True @@ -1019,7 +1024,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], //│ 1, //│ case o$0 of -- #11 @@ -1153,7 +1158,7 @@ main() //│ IR: //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, Some, [x]),ClassInfo(5, None, [])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], //│ 1, //│ case o$0 of -- #11 @@ -1253,4 +1258,5 @@ main() //│ x$40 -- #148) //│ //│ Interpreted: -//│ 179 +//│ +//│ IR Processing Failed: can not find the matched case, ClassInfo(0, True, []) expected diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 1f03b58df3..6ba58ff647 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -4,14 +4,12 @@ :noTailRec :interpIR -class True -class False fun fact(acc, n) = if n == 0 then acc else fact(acc * n, n - 1) fact(1, 5) -//│ |#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ |#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ //│ //│ IR: @@ -43,14 +41,12 @@ fact(1, 5) :interpIR @tailrec -class True -class False fun fact(acc, n) = if n == 0 then acc else fact(acc * n, n - 1) fact(1, 5) -//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} +//│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ //│ //│ IR: @@ -77,7 +73,7 @@ fact(1, 5) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 @@ -103,7 +99,7 @@ fact(1, 5) //│ x$5 -- #29) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(1, j$0, [x$1], //│ 1, //│ x$1 -- #3 @@ -133,8 +129,6 @@ fact(1, 5) :interpIR @tailrec -class True -class False fun fact(acc, n) = val x = if n > 0 then n - 1 else 0 @@ -143,8 +137,8 @@ fun fact(acc, n) = else @tailcall fact(n * acc, x) fact(1, 5) -//│ |@|tailrec|↵|#class| |True|↵|#class| |False|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| -//│ Parsed: {class True {}; class False {}; fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} +//│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| +//│ Parsed: {fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ //│ //│ IR: @@ -180,7 +174,7 @@ fact(1, 5) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(fact)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, //│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 @@ -220,7 +214,7 @@ fact(1, 5) //│ x$7 -- #39) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, //│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 @@ -264,14 +258,12 @@ fact(1, 5) :noTailRec :interpIR -class True -class False fun double(x) = x * 2 fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(6, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} +//│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: @@ -326,14 +318,12 @@ g(6, 0) //│ -12 :interpIR -class True -class False fun double(x) = x * 2 @tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) @tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(6, 0) -//│ |#class| |True|↵|#class| |False|↵|#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| -//│ Parsed: {class True {}; class False {}; fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} +//│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| +//│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: @@ -384,7 +374,7 @@ g(6, 0) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -443,7 +433,7 @@ g(6, 0) //│ x$15 -- #69) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, double, [x$0], //│ 1, //│ let x$1 = *(x$0,2) in -- #3 @@ -504,14 +494,12 @@ g(6, 0) //│ Interpreted: //│ -12 -class True -class False @tailrec fun f(a, b, c) = g(0, 0) @tailrec fun g(d, e) = h(0, 0, 0, 0) @tailrec fun h(p, q, r, s) = f(0, 0, 0) 2 -//│ |#class| |True|↵|#class| |False|↵|@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | -//│ Parsed: {class True {}; class False {}; fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} +//│ |@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | +//│ Parsed: {fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} //│ //│ //│ IR: @@ -536,7 +524,7 @@ class False //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, //│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 @@ -574,7 +562,7 @@ class False //│ 2 -- #30) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, //│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 @@ -612,15 +600,13 @@ class False //│ 2 -- #30) :ce -class True -class False fun hello() = @tailcall hello() @tailcall hello() 2 hello() -//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | -//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} +//│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} //│ //│ //│ IR: @@ -635,15 +621,15 @@ hello() //│ let* (x$2) = hello() in -- #12 //│ x$2 -- #11) //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.618: @tailcall hello() +//│ ║ l.604: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.619: @tailcall hello() +//│ ║ l.605: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #8 @@ -655,7 +641,7 @@ hello() //│ x$2 -- #11) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #8 @@ -667,14 +653,12 @@ hello() //│ x$2 -- #11) :ce -class True -class False fun hello() = @tailcall hello() 2 hello() -//│ |#class| |True|↵|#class| |False|↵|#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | -//│ Parsed: {class True {}; class False {}; fun hello = () => {@tailcall hello(); 2}; hello()} +//│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | +//│ Parsed: {fun hello = () => {@tailcall hello(); 2}; hello()} //│ //│ //│ IR: @@ -688,12 +672,12 @@ hello() //│ let* (x$1) = hello() in -- #8 //│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.673: @tailcall hello() +//│ ║ l.657: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #4 @@ -704,7 +688,7 @@ hello() //│ x$1 -- #7) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, hello, [], //│ 1, //│ let* (x$0) = @tailcall hello() in -- #4 @@ -715,8 +699,6 @@ hello() //│ x$1 -- #7) :interpIR -class True -class False class Cons(h, t) class Nil @tailrec fun addOne(xs) = @@ -724,8 +706,8 @@ class Nil Cons(h, t) then Cons(h + 1, @tailcall addOne(t)) Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: @@ -758,7 +740,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -820,7 +802,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #47) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -886,8 +868,6 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) :noTailRec :interpIR -class True -class False class Zero class S(x) fun a(n) = @@ -899,8 +879,8 @@ fun b(n) = S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ |#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: @@ -955,8 +935,6 @@ a(S(S(S(Zero)))) //│ S(S(S(S(S(S(Zero())))))) :interpIR -class True -class False class Zero class S(x) @tailrec fun a(n) = @@ -968,8 +946,8 @@ class S(x) S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} +//│ |#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| +//│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: @@ -1020,7 +998,7 @@ a(S(S(S(Zero)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 @@ -1118,7 +1096,7 @@ a(S(S(S(Zero)))) //│ x$18 -- #69) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 @@ -1219,8 +1197,6 @@ a(S(S(S(Zero)))) //│ S(S(S(S(S(S(Zero())))))) :interpIR -class True -class False class Cons(h, t) class Nil @tailrec fun addOne(xs) = @@ -1233,8 +1209,8 @@ class Nil Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: @@ -1267,7 +1243,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 @@ -1329,7 +1305,7 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ x$11 -- #50) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 @@ -1394,8 +1370,6 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Cons(2,Cons(3,Cons(4,Nil()))) :interpIR -class True -class False class Nil class Cons(m, n) @tailrec fun a(x) = @@ -1412,8 +1386,8 @@ class Cons(m, n) else a(Cons(n, Nil)) b(16) -//│ |#class| |True|↵|#class| |False|↵|#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| -//│ Parsed: {class True {}; class False {}; class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} +//│ |#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| +//│ Parsed: {class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} //│ //│ //│ IR: @@ -1472,7 +1446,7 @@ b(16) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 @@ -1577,7 +1551,7 @@ b(16) //│ x$20 -- #80) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n]),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 @@ -1686,8 +1660,6 @@ b(16) :noTailRec :interpIR -class True -class False class Cons(h, t) class Nil fun foo(xs) = @@ -1700,8 +1672,8 @@ fun foo(xs) = Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: @@ -1761,8 +1733,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) :interpIR -class True -class False class Cons(h, t) class Nil @tailrec fun foo(xs) = @@ -1775,8 +1745,8 @@ class Nil Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) -//│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| -//│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} +//│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| +//│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: @@ -1832,7 +1802,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(foo)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 @@ -1921,7 +1891,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ x$20 -- #94) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(6, _IdContext, []),ClassInfo(7, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 @@ -2013,8 +1983,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) :ce -class True -class False fun b() = a() a() @@ -2023,8 +1991,8 @@ fun a() = if 0 < 1 then a() else b() a() -//│ |#class| |True|↵|#class| |False|↵|#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} +//│ |#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| +//│ Parsed: {fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} //│ //│ //│ IR: @@ -2054,15 +2022,15 @@ a() //│ let* (x$6) = a() in -- #27 //│ x$6 -- #26) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2021: @tailrec +//│ ║ l.1989: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2019: a() +//│ ║ l.1987: a() //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(a, b)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, //│ let* (r0) = _a_b_opt(0) in -- #44 @@ -2101,7 +2069,7 @@ a() //│ x$6 -- #26) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(4, _IdContext, []),ClassInfo(5, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, //│ let* (r0) = _a_b_opt(0) in -- #44 @@ -2140,16 +2108,14 @@ a() //│ x$6 -- #26) :ce -class True -class False class A(a, b) @tailrec fun a() = A(b(), 1) fun b() = A(c(), @tailcall a()) fun c() = A(b(), 1) a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} +//│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| +//│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} //│ //│ //│ IR: @@ -2177,15 +2143,15 @@ a() //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2146: @tailrec +//│ ║ l.2112: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2148: fun b() = A(c(), @tailcall a()) +//│ ║ l.2114: fun b() = A(c(), @tailcall a()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(c, b, a)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -2281,7 +2247,7 @@ a() //│ x$7 -- #35) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #80 @@ -2377,16 +2343,14 @@ a() //│ x$7 -- #35) // TODO: Purity check -class True -class False class A(a, b) @tailrec fun a() = A(b(), 1) fun b() = A(@tailcall a(), c()) fun c() = A(0, 1) a() -//│ |#class| |True|↵|#class| |False|↵|#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| -//│ Parsed: {class True {}; class False {}; class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} +//│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| +//│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} //│ //│ //│ IR: @@ -2413,18 +2377,18 @@ a() //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.2385: fun b() = A(@tailcall a(), c()) +//│ ║ l.2349: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2383: @tailrec +//│ ║ l.2347: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2385: fun b() = A(@tailcall a(), c()) +//│ ║ l.2349: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #62 @@ -2498,7 +2462,7 @@ a() //│ x$6 -- #32) //│ //│ Promoted: -//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(5, _IdContext, []),ClassInfo(6, _Context, [acc,ptr,field])}, { +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b]),ClassInfo(3, _IdContext, []),ClassInfo(4, _Context, [acc,ptr,field])}, { //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #62 From 1ea98f12df3a3e30330719680e06cf9fe9a255f3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 21:36:24 +0800 Subject: [PATCH 47/59] restore nuscratch changes --- shared/src/test/diff/nu/NuScratch.mls | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/src/test/diff/nu/NuScratch.mls b/shared/src/test/diff/nu/NuScratch.mls index 34d9bdfef7..539c23787c 100644 --- a/shared/src/test/diff/nu/NuScratch.mls +++ b/shared/src/test/diff/nu/NuScratch.mls @@ -1 +1,3 @@ :NewDefs + + From abcadfe88fa1e9da78f4c26726fa057ec4641e06 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 22:00:58 +0800 Subject: [PATCH 48/59] Ensure no function name clashes --- .../compiler/optimizer/TailRecOpt.scala | 27 +- compiler/shared/test/diff-ir/IRTailRec.mls | 680 +++++++++--------- 2 files changed, 356 insertions(+), 351 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index e37af36dfb..d734d4609a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -583,12 +583,14 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get - // CONOTEXT APPLICATION - + // CONTEXT APPLICATION + val mergedNames = defns.foldLeft("")(_ + "_" + _.name) - val ctxAppName = mergedNames + "_ctx_app" - val ctxCompName = mergedNames + "_ctx_comp" + val ctxAppId = fnUid.make + val ctxAppName = mergedNames + "_ctx_app$" + ctxAppId + val ctxCompId = fnUid.make + val ctxCompName = mergedNames + "_ctx_comp$" + ctxCompId // map integers to classes and fields which will be assigned to val classIdMap = classes.map(c => c.id -> c).toMap @@ -649,7 +651,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag ) ).attachTag(tag) - val appDefn = Defn(fnUid.make, ctxAppName, List(appCtxName, appValName), 1, appNode, false) + val appDefn = Defn(ctxAppId, ctxAppName, List(appCtxName, appValName), 1, appNode, false) // CONTEXT COMPOSITION val cmpCtx1Name = Name("ctx1") @@ -690,7 +692,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag ).attachTag(tag) ).attachTag(tag) - val cmpDefn = Defn(fnUid.make, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode, false) + val cmpDefn = Defn(ctxCompId, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode, false) // We use tags to identify nodes // a bit hacky but it's the most elegant way @@ -790,7 +792,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag def rewriteDefn(d: Defn): Defn = val transformed = transformNode(d.body) - Defn(fnUid.make, d.name + "_modcons", Name("ctx") :: d.params, d.resultNum, transformed, d.isTailRec) + val id = fnUid.make + Defn(id, d.name + "_modcons$" + id, Name("ctx") :: d.params, d.resultNum, transformed, d.isTailRec) // returns (new defn, mod cons defn) // where new defn has the same signature and ids as the original, but immediately calls the mod cons defn @@ -915,8 +918,10 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // TODO: This works fine for now, but ideally should find a way to guarantee the new // name is unique - val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt" - val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp" + val newId = fnUid.make + val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt$" + newId + val jpId = fnUid.make + val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp$" + jpId val newDefnRef = DefnRef(Right(newName)) val jpDefnRef = DefnRef(Right(jpName)) @@ -978,10 +983,10 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val newNode = makeSwitch(trName, valsAndNodes.tail, valsAndNodes.head._2)(trueClass, falseClass) - val jpDefn = Defn(fnUid.make, jpName, stackFrame, resultNum, newNode, false) + val jpDefn = Defn(jpId, jpName, stackFrame, resultNum, newNode, false) val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) - val newDefn = Defn(fnUid.make, newName, stackFrame, resultNum, jmp, defnsNoJp.find { _.isTailRec }.isDefined ) + val newDefn = Defn(newId, newName, stackFrame, resultNum, jmp, defnsNoJp.find { _.isTailRec }.isDefined ) jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 6ba58ff647..a3ceab66ba 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -177,14 +177,18 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,0,0,0) in -- #60 //│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], //│ 1, //│ x$3 -- #7 //│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, //│ let scrut = ==(2,tailrecBranch$) in -- #57 //│ if scrut -- #56 @@ -195,19 +199,15 @@ fact(1, 5) //│ jump j$1(j$0_acc$0) -- #51 //│ false => //│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 //│ false => //│ let x$0 = >(fact_n$0,0) in -- #50 //│ if x$0 -- #49 //│ true => //│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 //│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 //│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 @@ -217,14 +217,18 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt(0,acc$0,n$0,0,0,0) in -- #60 +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,0,0,0) in -- #60 //│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], //│ 1, //│ x$3 -- #7 //│ ) -//│ Def(3, _fact_j$0_opt_jp, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ Def(3, _fact_j$0_opt$3, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], +//│ 1, +//│ jump _fact_j$0_opt_jp$4(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ ) +//│ Def(4, _fact_j$0_opt_jp$4, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], //│ 1, //│ let scrut = ==(2,tailrecBranch$) in -- #57 //│ if scrut -- #56 @@ -235,19 +239,15 @@ fact(1, 5) //│ jump j$1(j$0_acc$0) -- #51 //│ false => //│ let x$4 = *(j$0_n$0,j$0_acc$0) in -- #53 -//│ jump _fact_j$0_opt_jp(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 +//│ jump _fact_j$0_opt_jp$4(0,x$4,j$0_x$1,j$0_x$1,j$0_acc$0,j$0_n$0) -- #52 //│ false => //│ let x$0 = >(fact_n$0,0) in -- #50 //│ if x$0 -- #49 //│ true => //│ let x$6 = -(fact_n$0,1) in -- #47 -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,x$6,fact_acc$0,fact_n$0) -- #46 //│ false => -//│ jump _fact_j$0_opt_jp(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 -//│ ) -//│ Def(4, _fact_j$0_opt, [tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0], -//│ 1, -//│ jump _fact_j$0_opt_jp(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$1,j$0_acc$0,j$0_n$0) -- #58 +//│ jump _fact_j$0_opt_jp$4(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #48 //│ ) //│ }, //│ let* (x$7) = fact(1,5) in -- #40 @@ -382,7 +382,7 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ let* (r0) = _g_f_opt$5(1,0,0,n$0,acc$0) in -- #100 //│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], @@ -391,14 +391,18 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,0,0) in -- #98 //│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, //│ x$9 -- #35 //│ ) -//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) +//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, //│ let scrut = ==(1,tailrecBranch$) in -- #95 //│ if scrut -- #94 @@ -411,7 +415,7 @@ g(6, 0) //│ false => //│ let x$5 = -(f_n$0,1) in -- #91 //│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 //│ false => //│ let x$8 = ==(g_m$0,0) in -- #86 //│ if x$8 -- #85 @@ -422,11 +426,7 @@ g(6, 0) //│ false => //│ let x$12 = -(g_m$0,1) in -- #84 //│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 -//│ ) -//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -441,7 +441,7 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt(1,0,0,n$0,acc$0) in -- #100 +//│ let* (r0) = _g_f_opt$5(1,0,0,n$0,acc$0) in -- #100 //│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], @@ -450,14 +450,18 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt(3,m$0,acc$1,0,0) in -- #98 +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,0,0) in -- #98 //│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], //│ 1, //│ x$9 -- #35 //│ ) -//│ Def(5, _g_f_opt_jp, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ Def(5, _g_f_opt$5, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], +//│ 1, +//│ jump _g_f_opt_jp$6(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ ) +//│ Def(6, _g_f_opt_jp$6, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], //│ 1, //│ let scrut = ==(1,tailrecBranch$) in -- #95 //│ if scrut -- #94 @@ -470,7 +474,7 @@ g(6, 0) //│ false => //│ let x$5 = -(f_n$0,1) in -- #91 //│ let x$6 = +(f_acc$0,1) in -- #90 -//│ jump _g_f_opt_jp(3,x$5,x$6,f_n$0,f_acc$0) -- #89 +//│ jump _g_f_opt_jp$6(3,x$5,x$6,f_n$0,f_acc$0) -- #89 //│ false => //│ let x$8 = ==(g_m$0,0) in -- #86 //│ if x$8 -- #85 @@ -481,11 +485,7 @@ g(6, 0) //│ false => //│ let x$12 = -(g_m$0,1) in -- #84 //│ let x$13 = +(g_acc$1,1) in -- #83 -//│ jump _g_f_opt_jp(1,g_m$0,g_acc$1,x$12,x$13) -- #82 -//│ ) -//│ Def(6, _g_f_opt, [tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0], -//│ 1, -//│ jump _g_f_opt_jp(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #96 +//│ jump _g_f_opt_jp$6(1,g_m$0,g_acc$1,x$12,x$13) -- #82 //│ ) //│ }, //│ let* (x$15) = g(6,0) in -- #70 @@ -527,36 +527,36 @@ g(6, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ let* (r0) = _h_g_f_opt$3(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 //│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ let* (r0) = _h_g_f_opt$3(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 //│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 //│ r0 -- #40 //│ ) -//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, //│ let scrut = ==(0,tailrecBranch$) in -- #38 //│ if scrut -- #37 //│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 //│ false => //│ let scrut = ==(1,tailrecBranch$) in -- #36 //│ if scrut -- #35 //│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 //│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 //│ ) //│ }, //│ 2 -- #30) @@ -565,36 +565,36 @@ g(6, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ let* (r0) = _h_g_f_opt$3(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 //│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ let* (r0) = _h_g_f_opt$3(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 //│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 //│ r0 -- #40 //│ ) -//│ Def(3, _h_g_f_opt_jp, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], +//│ 1, +//│ jump _h_g_f_opt_jp$4(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ ) +//│ Def(4, _h_g_f_opt_jp$4, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], //│ 1, //│ let scrut = ==(0,tailrecBranch$) in -- #38 //│ if scrut -- #37 //│ true => -//│ jump _h_g_f_opt_jp(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 +//│ jump _h_g_f_opt_jp$4(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #34 //│ false => //│ let scrut = ==(1,tailrecBranch$) in -- #36 //│ if scrut -- #35 //│ true => -//│ jump _h_g_f_opt_jp(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 +//│ jump _h_g_f_opt_jp$4(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #33 //│ false => -//│ jump _h_g_f_opt_jp(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 -//│ ) -//│ Def(4, _h_g_f_opt, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], -//│ 1, -//│ jump _h_g_f_opt_jp(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #39 +//│ jump _h_g_f_opt_jp$4(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #32 //│ ) //│ }, //│ 2 -- #30) @@ -744,14 +744,14 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 //│ res -- #78 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ Def(2, _addOne_ctx_app$2, [ctx,x], //│ 1, //│ case ctx of -- #59 //│ _IdContext => @@ -763,16 +763,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let acc = ctx.acc in -- #54 //│ acc -- #53 //│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #65 //│ let ctx2ptr = ctx2.ptr in -- #64 //│ let ctx2field = ctx2.field in -- #63 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 //│ ret -- #60 //│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], //│ 1, //│ case xs$0 of -- #91 //│ Cons => @@ -781,16 +781,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let x$3 = +(x$2,1) in -- #85 //│ let x$5 = Cons(x$3,0) in -- #84 //│ let ctx2 = _Context(x$5,x$5,0) in -- #83 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 -//│ jump addOne_modcons_jp(composed,x$1) -- #81 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 //│ Nil => //│ let x$6 = Nil() in -- #90 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 //│ res -- #88 //│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ Def(6, addOne_modcons$4, [ctx,xs$0], //│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 //│ r0 -- #92 //│ ) //│ }, @@ -806,14 +806,14 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #79 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #79 //│ res -- #78 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ Def(2, _addOne_ctx_app$2, [ctx,x], //│ 1, //│ case ctx of -- #59 //│ _IdContext => @@ -825,16 +825,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let acc = ctx.acc in -- #54 //│ acc -- #53 //│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #65 //│ let ctx2ptr = ctx2.ptr in -- #64 //│ let ctx2field = ctx2.field in -- #63 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #62 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #62 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #61 //│ ret -- #60 //│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], //│ 1, //│ case xs$0 of -- #91 //│ Cons => @@ -843,16 +843,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let x$3 = +(x$2,1) in -- #85 //│ let x$5 = Cons(x$3,0) in -- #84 //│ let ctx2 = _Context(x$5,x$5,0) in -- #83 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #82 -//│ jump addOne_modcons_jp(composed,x$1) -- #81 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #82 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #81 //│ Nil => //│ let x$6 = Nil() in -- #90 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #89 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #89 //│ res -- #88 //│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ Def(6, addOne_modcons$4, [ctx,xs$0], //│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #93 +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #93 //│ r0 -- #92 //│ ) //│ }, @@ -1002,7 +1002,7 @@ a(S(S(S(Zero)))) //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 -//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 +//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 //│ res -- #115 //│ ) //│ Def(1, j$0, [x$0], @@ -1012,14 +1012,14 @@ a(S(S(S(Zero)))) //│ Def(2, b, [n$1], //│ 1, //│ let idCtx = _IdContext() in -- #103 -//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 +//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 //│ res -- #101 //│ ) //│ Def(3, j$1, [x$6], //│ 1, //│ x$6 -- #25 //│ ) -//│ Def(4, _b_a_ctx_app, [ctx,x], +//│ Def(4, _b_a_ctx_app$4, [ctx,x], //│ 1, //│ case ctx of -- #81 //│ _IdContext => @@ -1031,62 +1031,62 @@ a(S(S(S(Zero)))) //│ let acc = ctx.acc in -- #76 //│ acc -- #75 //│ ) -//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #87 //│ let ctx2ptr = ctx2.ptr in -- #86 //│ let ctx2field = ctx2.field in -- #85 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 +//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 //│ ret -- #82 //│ ) -//│ Def(6, b_modcons, [ctx,n$1], +//│ Def(6, b_modcons$6, [ctx,n$1], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,0,0) in -- #156 //│ r0 -- #155 //│ ) -//│ Def(7, a_modcons, [ctx,n$0], +//│ Def(7, a_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx,n$0) in -- #158 //│ r0 -- #157 //│ ) -//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 +//│ ) +//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], //│ 1, //│ let scrut = ==(7,tailrecBranch$) in -- #153 //│ if scrut -- #152 //│ true => -//│ case a_modcons_n$0 of -- #151 +//│ case a_modcons$7_n$0 of -- #151 //│ S => -//│ let x$1 = a_modcons_n$0.x in -- #146 +//│ let x$1 = a_modcons$7_n$0.x in -- #146 //│ let x$3 = S(0) in -- #145 //│ let ctx2 = _Context(x$3,x$3,0) in -- #144 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 -//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 +//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 //│ Zero => //│ let x$4 = Zero() in -- #150 //│ let x$5 = S(x$4) in -- #149 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 +//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 //│ res -- #147 //│ false => -//│ case b_modcons_n$1 of -- #141 +//│ case b_modcons$6_n$1 of -- #141 //│ S => -//│ let x$7 = b_modcons_n$1.x in -- #135 +//│ let x$7 = b_modcons$6_n$1.x in -- #135 //│ let x$9 = S(0) in -- #134 //│ let x$10 = S(x$9) in -- #133 //│ let ctx2 = _Context(x$10,x$9,0) in -- #132 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 -//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 +//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 //│ Zero => //│ let x$11 = Zero() in -- #140 //│ let x$12 = S(x$11) in -- #139 //│ let x$13 = S(x$12) in -- #138 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 +//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 //│ res -- #136 //│ ) -//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 -//│ ) //│ }, //│ let x$14 = Zero() in -- #74 //│ let x$15 = S(x$14) in -- #73 @@ -1100,7 +1100,7 @@ a(S(S(S(Zero)))) //│ Def(0, a, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #117 -//│ let* (res) = a_modcons(idCtx,n$0) in -- #116 +//│ let* (res) = a_modcons$7(idCtx,n$0) in -- #116 //│ res -- #115 //│ ) //│ Def(1, j$0, [x$0], @@ -1110,14 +1110,14 @@ a(S(S(S(Zero)))) //│ Def(2, b, [n$1], //│ 1, //│ let idCtx = _IdContext() in -- #103 -//│ let* (res) = b_modcons(idCtx,n$1) in -- #102 +//│ let* (res) = b_modcons$6(idCtx,n$1) in -- #102 //│ res -- #101 //│ ) //│ Def(3, j$1, [x$6], //│ 1, //│ x$6 -- #25 //│ ) -//│ Def(4, _b_a_ctx_app, [ctx,x], +//│ Def(4, _b_a_ctx_app$4, [ctx,x], //│ 1, //│ case ctx of -- #81 //│ _IdContext => @@ -1129,62 +1129,62 @@ a(S(S(S(Zero)))) //│ let acc = ctx.acc in -- #76 //│ acc -- #75 //│ ) -//│ Def(5, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(5, _b_a_ctx_comp$5, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #87 //│ let ctx2ptr = ctx2.ptr in -- #86 //│ let ctx2field = ctx2.field in -- #85 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #84 +//│ let* (newAcc) = _b_a_ctx_app$4(ctx1,ctx2acc) in -- #84 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #83 //│ ret -- #82 //│ ) -//│ Def(6, b_modcons, [ctx,n$1], +//│ Def(6, b_modcons$6, [ctx,n$1], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,ctx,n$1,0,0) in -- #156 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,0,0) in -- #156 //│ r0 -- #155 //│ ) -//│ Def(7, a_modcons, [ctx,n$0], +//│ Def(7, a_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,0,0,ctx,n$0) in -- #158 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx,n$0) in -- #158 //│ r0 -- #157 //│ ) -//│ Def(8, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], +//│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], +//│ 1, +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #154 +//│ ) +//│ Def(9, _b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], //│ 1, //│ let scrut = ==(7,tailrecBranch$) in -- #153 //│ if scrut -- #152 //│ true => -//│ case a_modcons_n$0 of -- #151 +//│ case a_modcons$7_n$0 of -- #151 //│ S => -//│ let x$1 = a_modcons_n$0.x in -- #146 +//│ let x$1 = a_modcons$7_n$0.x in -- #146 //│ let x$3 = S(0) in -- #145 //│ let ctx2 = _Context(x$3,x$3,0) in -- #144 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #143 -//│ jump _b_modcons_a_modcons_opt_jp(6,composed,x$1,a_modcons_ctx,a_modcons_n$0) -- #142 +//│ let* (composed) = _b_a_ctx_comp$5(a_modcons$7_ctx,ctx2) in -- #143 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(6,composed,x$1,a_modcons$7_ctx,a_modcons$7_n$0) -- #142 //│ Zero => //│ let x$4 = Zero() in -- #150 //│ let x$5 = S(x$4) in -- #149 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$5) in -- #148 +//│ let* (res) = _b_a_ctx_app$4(a_modcons$7_ctx,x$5) in -- #148 //│ res -- #147 //│ false => -//│ case b_modcons_n$1 of -- #141 +//│ case b_modcons$6_n$1 of -- #141 //│ S => -//│ let x$7 = b_modcons_n$1.x in -- #135 +//│ let x$7 = b_modcons$6_n$1.x in -- #135 //│ let x$9 = S(0) in -- #134 //│ let x$10 = S(x$9) in -- #133 //│ let ctx2 = _Context(x$10,x$9,0) in -- #132 -//│ let* (composed) = _b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #131 -//│ jump _b_modcons_a_modcons_opt_jp(7,b_modcons_ctx,b_modcons_n$1,composed,x$7) -- #130 +//│ let* (composed) = _b_a_ctx_comp$5(b_modcons$6_ctx,ctx2) in -- #131 +//│ jump _b_modcons$6_a_modcons$7_opt_jp$9(7,b_modcons$6_ctx,b_modcons$6_n$1,composed,x$7) -- #130 //│ Zero => //│ let x$11 = Zero() in -- #140 //│ let x$12 = S(x$11) in -- #139 //│ let x$13 = S(x$12) in -- #138 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$13) in -- #137 +//│ let* (res) = _b_a_ctx_app$4(b_modcons$6_ctx,x$13) in -- #137 //│ res -- #136 //│ ) -//│ Def(9, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$1,a_modcons_ctx,a_modcons_n$0) -- #154 -//│ ) //│ }, //│ let x$14 = Zero() in -- #74 //│ let x$15 = S(x$14) in -- #73 @@ -1247,14 +1247,14 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 //│ res -- #81 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ Def(2, _addOne_ctx_app$2, [ctx,x], //│ 1, //│ case ctx of -- #62 //│ _IdContext => @@ -1266,16 +1266,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let acc = ctx.acc in -- #57 //│ acc -- #56 //│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #68 //│ let ctx2ptr = ctx2.ptr in -- #67 //│ let ctx2field = ctx2.field in -- #66 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 //│ ret -- #63 //│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], //│ 1, //│ case xs$0 of -- #94 //│ Cons => @@ -1284,16 +1284,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let x$4 = +(x$2,1) in -- #88 //│ let x$5 = Cons(x$4,0) in -- #87 //│ let ctx2 = _Context(x$5,x$5,0) in -- #86 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 -//│ jump addOne_modcons_jp(composed,x$1) -- #84 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 //│ Nil => //│ let x$6 = Nil() in -- #93 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 //│ res -- #91 //│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ Def(6, addOne_modcons$4, [ctx,xs$0], //│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 //│ r0 -- #95 //│ ) //│ }, @@ -1309,14 +1309,14 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ Def(0, addOne, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #83 -//│ let* (res) = addOne_modcons(idCtx,xs$0) in -- #82 +//│ let* (res) = addOne_modcons$4(idCtx,xs$0) in -- #82 //│ res -- #81 //│ ) //│ Def(1, j$0, [x$0], //│ 1, //│ x$0 -- #1 //│ ) -//│ Def(2, _addOne_ctx_app, [ctx,x], +//│ Def(2, _addOne_ctx_app$2, [ctx,x], //│ 1, //│ case ctx of -- #62 //│ _IdContext => @@ -1328,16 +1328,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let acc = ctx.acc in -- #57 //│ acc -- #56 //│ ) -//│ Def(3, _addOne_ctx_comp, [ctx1,ctx2], +//│ Def(3, _addOne_ctx_comp$3, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #68 //│ let ctx2ptr = ctx2.ptr in -- #67 //│ let ctx2field = ctx2.field in -- #66 -//│ let* (newAcc) = _addOne_ctx_app(ctx1,ctx2acc) in -- #65 +//│ let* (newAcc) = _addOne_ctx_app$2(ctx1,ctx2acc) in -- #65 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #64 //│ ret -- #63 //│ ) -//│ Def(5, addOne_modcons_jp, [ctx,xs$0], +//│ Def(5, addOne_modcons$4_jp, [ctx,xs$0], //│ 1, //│ case xs$0 of -- #94 //│ Cons => @@ -1346,16 +1346,16 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ let x$4 = +(x$2,1) in -- #88 //│ let x$5 = Cons(x$4,0) in -- #87 //│ let ctx2 = _Context(x$5,x$5,0) in -- #86 -//│ let* (composed) = _addOne_ctx_comp(ctx,ctx2) in -- #85 -//│ jump addOne_modcons_jp(composed,x$1) -- #84 +//│ let* (composed) = _addOne_ctx_comp$3(ctx,ctx2) in -- #85 +//│ jump addOne_modcons$4_jp(composed,x$1) -- #84 //│ Nil => //│ let x$6 = Nil() in -- #93 -//│ let* (res) = _addOne_ctx_app(ctx,x$6) in -- #92 +//│ let* (res) = _addOne_ctx_app$2(ctx,x$6) in -- #92 //│ res -- #91 //│ ) -//│ Def(6, addOne_modcons, [ctx,xs$0], +//│ Def(6, addOne_modcons$4, [ctx,xs$0], //│ 1, -//│ let* (r0) = addOne_modcons_jp(ctx,xs$0) in -- #96 +//│ let* (r0) = addOne_modcons$4_jp(ctx,xs$0) in -- #96 //│ r0 -- #95 //│ ) //│ }, @@ -1450,7 +1450,7 @@ b(16) //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 -//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 +//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 //│ res -- #127 //│ ) //│ Def(1, j$0, [x$1], @@ -1464,14 +1464,14 @@ b(16) //│ Def(3, b, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #107 -//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 +//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 //│ res -- #105 //│ ) //│ Def(4, j$2, [x$14], //│ 1, //│ x$14 -- #50 //│ ) -//│ Def(5, _b_a_ctx_app, [ctx,x], +//│ Def(5, _b_a_ctx_app$5, [ctx,x], //│ 1, //│ case ctx of -- #88 //│ _IdContext => @@ -1483,68 +1483,68 @@ b(16) //│ let acc = ctx.acc in -- #83 //│ acc -- #82 //│ ) -//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #94 //│ let ctx2ptr = ctx2.ptr in -- #93 //│ let ctx2field = ctx2.field in -- #92 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 +//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 //│ ret -- #89 //│ ) -//│ Def(7, b_modcons, [ctx,n$0], +//│ Def(7, b_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,0,0) in -- #170 //│ r0 -- #169 //│ ) -//│ Def(8, a_modcons, [ctx,x$0], +//│ Def(8, a_modcons$8, [ctx,x$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,0,0,ctx,x$0) in -- #172 //│ r0 -- #171 //│ ) -//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 +//│ ) +//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], //│ 1, //│ let scrut = ==(8,tailrecBranch$) in -- #167 //│ if scrut -- #166 //│ true => -//│ case a_modcons_x$0 of -- #165 +//│ case a_modcons$8_x$0 of -- #165 //│ Cons => -//│ let x$2 = a_modcons_x$0.n in -- #161 -//│ let x$3 = a_modcons_x$0.m in -- #160 +//│ let x$2 = a_modcons$8_x$0.n in -- #161 +//│ let x$3 = a_modcons$8_x$0.m in -- #160 //│ let x$4 = <(x$3,0) in -- #159 //│ if x$4 -- #158 //│ true => //│ let x$6 = Nil() in -- #151 //│ let x$7 = Cons(-1,x$6) in -- #150 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 //│ res -- #148 //│ false => //│ let x$8 = *(x$3,4) in -- #157 //│ let x$9 = -(x$3,2) in -- #156 //│ let x$11 = Cons(x$8,0) in -- #155 //│ let ctx2 = _Context(x$11,x$11,0) in -- #154 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 -//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 +//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 //│ Nil => //│ let x$12 = Nil() in -- #164 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 //│ res -- #162 //│ false => -//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 +//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 //│ if x$13 -- #146 //│ true => //│ let x$15 = Nil() in -- #142 //│ let x$16 = Cons(0,x$15) in -- #141 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 +//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 //│ res -- #139 //│ false => //│ let x$17 = Nil() in -- #145 -//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 -//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 -//│ ) -//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 +//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 //│ ) //│ }, //│ let* (x$20) = b(16) in -- #81 @@ -1555,7 +1555,7 @@ b(16) //│ Def(0, a, [x$0], //│ 1, //│ let idCtx = _IdContext() in -- #129 -//│ let* (res) = a_modcons(idCtx,x$0) in -- #128 +//│ let* (res) = a_modcons$8(idCtx,x$0) in -- #128 //│ res -- #127 //│ ) //│ Def(1, j$0, [x$1], @@ -1569,14 +1569,14 @@ b(16) //│ Def(3, b, [n$0], //│ 1, //│ let idCtx = _IdContext() in -- #107 -//│ let* (res) = b_modcons(idCtx,n$0) in -- #106 +//│ let* (res) = b_modcons$7(idCtx,n$0) in -- #106 //│ res -- #105 //│ ) //│ Def(4, j$2, [x$14], //│ 1, //│ x$14 -- #50 //│ ) -//│ Def(5, _b_a_ctx_app, [ctx,x], +//│ Def(5, _b_a_ctx_app$5, [ctx,x], //│ 1, //│ case ctx of -- #88 //│ _IdContext => @@ -1588,68 +1588,68 @@ b(16) //│ let acc = ctx.acc in -- #83 //│ acc -- #82 //│ ) -//│ Def(6, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(6, _b_a_ctx_comp$6, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #94 //│ let ctx2ptr = ctx2.ptr in -- #93 //│ let ctx2field = ctx2.field in -- #92 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #91 +//│ let* (newAcc) = _b_a_ctx_app$5(ctx1,ctx2acc) in -- #91 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 //│ ret -- #89 //│ ) -//│ Def(7, b_modcons, [ctx,n$0], +//│ Def(7, b_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(7,ctx,n$0,0,0) in -- #170 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,0,0) in -- #170 //│ r0 -- #169 //│ ) -//│ Def(8, a_modcons, [ctx,x$0], +//│ Def(8, a_modcons$8, [ctx,x$0], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(8,0,0,ctx,x$0) in -- #172 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,0,0,ctx,x$0) in -- #172 //│ r0 -- #171 //│ ) -//│ Def(9, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], +//│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], +//│ 1, +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0) -- #168 +//│ ) +//│ Def(10, _b_modcons$7_a_modcons$8_opt_jp$10, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], //│ 1, //│ let scrut = ==(8,tailrecBranch$) in -- #167 //│ if scrut -- #166 //│ true => -//│ case a_modcons_x$0 of -- #165 +//│ case a_modcons$8_x$0 of -- #165 //│ Cons => -//│ let x$2 = a_modcons_x$0.n in -- #161 -//│ let x$3 = a_modcons_x$0.m in -- #160 +//│ let x$2 = a_modcons$8_x$0.n in -- #161 +//│ let x$3 = a_modcons$8_x$0.m in -- #160 //│ let x$4 = <(x$3,0) in -- #159 //│ if x$4 -- #158 //│ true => //│ let x$6 = Nil() in -- #151 //│ let x$7 = Cons(-1,x$6) in -- #150 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$7) in -- #149 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$7) in -- #149 //│ res -- #148 //│ false => //│ let x$8 = *(x$3,4) in -- #157 //│ let x$9 = -(x$3,2) in -- #156 //│ let x$11 = Cons(x$8,0) in -- #155 //│ let ctx2 = _Context(x$11,x$11,0) in -- #154 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #153 -//│ jump _b_modcons_a_modcons_opt_jp(7,composed,x$9,a_modcons_ctx,a_modcons_x$0) -- #152 +//│ let* (composed) = _b_a_ctx_comp$6(a_modcons$8_ctx,ctx2) in -- #153 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(7,composed,x$9,a_modcons$8_ctx,a_modcons$8_x$0) -- #152 //│ Nil => //│ let x$12 = Nil() in -- #164 -//│ let* (res) = _b_a_ctx_app(a_modcons_ctx,x$12) in -- #163 +//│ let* (res) = _b_a_ctx_app$5(a_modcons$8_ctx,x$12) in -- #163 //│ res -- #162 //│ false => -//│ let x$13 = <=(b_modcons_n$0,0) in -- #147 +//│ let x$13 = <=(b_modcons$7_n$0,0) in -- #147 //│ if x$13 -- #146 //│ true => //│ let x$15 = Nil() in -- #142 //│ let x$16 = Cons(0,x$15) in -- #141 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$16) in -- #140 +//│ let* (res) = _b_a_ctx_app$5(b_modcons$7_ctx,x$16) in -- #140 //│ res -- #139 //│ false => //│ let x$17 = Nil() in -- #145 -//│ let x$18 = Cons(b_modcons_n$0,x$17) in -- #144 -//│ jump _b_modcons_a_modcons_opt_jp(8,b_modcons_ctx,b_modcons_n$0,b_modcons_ctx,x$18) -- #143 -//│ ) -//│ Def(10, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,b_modcons_n$0,a_modcons_ctx,a_modcons_x$0) -- #168 +//│ let x$18 = Cons(b_modcons$7_n$0,x$17) in -- #144 +//│ jump _b_modcons$7_a_modcons$8_opt_jp$10(8,b_modcons$7_ctx,b_modcons$7_n$0,b_modcons$7_ctx,x$18) -- #143 //│ ) //│ }, //│ let* (x$20) = b(16) in -- #81 @@ -1806,7 +1806,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 -//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 +//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 //│ res -- #135 //│ ) //│ Def(1, j$0, [x$0], @@ -1817,7 +1817,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(4, _foo_ctx_app, [ctx,x], +//│ Def(4, _foo_ctx_app$4, [ctx,x], //│ 1, //│ case ctx of -- #110 //│ _IdContext => @@ -1829,55 +1829,55 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let acc = ctx.acc in -- #105 //│ acc -- #104 //│ ) -//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], +//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #116 //│ let ctx2ptr = ctx2.ptr in -- #115 //│ let ctx2field = ctx2.field in -- #114 -//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 +//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 //│ ret -- #111 //│ ) -//│ Def(7, foo_modcons, [ctx,xs$0], +//│ Def(7, foo_modcons$7, [ctx,xs$0], //│ 1, -//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,0,0,0,0) in -- #173 //│ r0 -- #172 //│ ) -//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 +//│ ) +//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], //│ 1, //│ let scrut = ==(6,tailrecBranch$) in -- #170 //│ if scrut -- #169 //│ true => -//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 -//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 +//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 //│ let ctx2 = _Context(x$10,x$9,0) in -- #166 -//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 +//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 //│ false => -//│ case foo_modcons_xs$0 of -- #163 +//│ case foo_modcons$7_xs$0 of -- #163 //│ Cons => -//│ let x$1 = foo_modcons_xs$0.t in -- #159 -//│ let x$2 = foo_modcons_xs$0.h in -- #158 +//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 +//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 //│ let x$3 = >(x$2,5) in -- #157 //│ if x$3 -- #156 //│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 //│ false => //│ let x$6 = <(x$2,3) in -- #155 //│ if x$6 -- #154 //│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 //│ false => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 //│ Nil => //│ let x$11 = Nil() in -- #162 -//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 +//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 //│ res -- #160 //│ ) -//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], -//│ 1, -//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 -//│ ) //│ }, //│ let x$12 = Nil() in -- #103 //│ let x$13 = Cons(9,x$12) in -- #102 @@ -1895,7 +1895,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ Def(0, foo, [xs$0], //│ 1, //│ let idCtx = _IdContext() in -- #137 -//│ let* (res) = foo_modcons(idCtx,xs$0) in -- #136 +//│ let* (res) = foo_modcons$7(idCtx,xs$0) in -- #136 //│ res -- #135 //│ ) //│ Def(1, j$0, [x$0], @@ -1906,7 +1906,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ 1, //│ jump j$0(x$4) -- #10 //│ ) -//│ Def(4, _foo_ctx_app, [ctx,x], +//│ Def(4, _foo_ctx_app$4, [ctx,x], //│ 1, //│ case ctx of -- #110 //│ _IdContext => @@ -1918,55 +1918,55 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ let acc = ctx.acc in -- #105 //│ acc -- #104 //│ ) -//│ Def(5, _foo_ctx_comp, [ctx1,ctx2], +//│ Def(5, _foo_ctx_comp$5, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #116 //│ let ctx2ptr = ctx2.ptr in -- #115 //│ let ctx2field = ctx2.field in -- #114 -//│ let* (newAcc) = _foo_ctx_app(ctx1,ctx2acc) in -- #113 +//│ let* (newAcc) = _foo_ctx_app$4(ctx1,ctx2acc) in -- #113 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #112 //│ ret -- #111 //│ ) -//│ Def(7, foo_modcons, [ctx,xs$0], +//│ Def(7, foo_modcons$7, [ctx,xs$0], //│ 1, -//│ let* (r0) = _foo_modcons_j$2_modcons_opt(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,0,0,0,0) in -- #173 //│ r0 -- #172 //│ ) -//│ Def(8, _foo_modcons_j$2_modcons_opt_jp, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], +//│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], +//│ 1, +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #171 +//│ ) +//│ Def(9, _foo_modcons$7_j$2_modcons$6_opt_jp$9, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], //│ 1, //│ let scrut = ==(6,tailrecBranch$) in -- #170 //│ if scrut -- #169 //│ true => -//│ let x$9 = Cons(j$2_modcons_x$2,0) in -- #168 -//│ let x$10 = Cons(j$2_modcons_x$7,x$9) in -- #167 +//│ let x$9 = Cons(j$2_modcons$6_x$2,0) in -- #168 +//│ let x$10 = Cons(j$2_modcons$6_x$7,x$9) in -- #167 //│ let ctx2 = _Context(x$10,x$9,0) in -- #166 -//│ let* (composed) = _foo_ctx_comp(j$2_modcons_ctx,ctx2) in -- #165 -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,composed,j$2_modcons_x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #164 +//│ let* (composed) = _foo_ctx_comp$5(j$2_modcons$6_ctx,ctx2) in -- #165 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,composed,j$2_modcons$6_x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #164 //│ false => -//│ case foo_modcons_xs$0 of -- #163 +//│ case foo_modcons$7_xs$0 of -- #163 //│ Cons => -//│ let x$1 = foo_modcons_xs$0.t in -- #159 -//│ let x$2 = foo_modcons_xs$0.h in -- #158 +//│ let x$1 = foo_modcons$7_xs$0.t in -- #159 +//│ let x$2 = foo_modcons$7_xs$0.h in -- #158 //│ let x$3 = >(x$2,5) in -- #157 //│ if x$3 -- #156 //│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(7,foo_modcons_ctx,x$1,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #151 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(7,foo_modcons$7_ctx,x$1,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2) -- #151 //│ false => //│ let x$6 = <(x$2,3) in -- #155 //│ if x$6 -- #154 //│ true => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,-1,x$1,x$2) -- #152 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,-1,x$1,x$2) -- #152 //│ false => -//│ jump _foo_modcons_j$2_modcons_opt_jp(6,foo_modcons_ctx,foo_modcons_xs$0,foo_modcons_ctx,100,x$1,x$2) -- #153 +//│ jump _foo_modcons$7_j$2_modcons$6_opt_jp$9(6,foo_modcons$7_ctx,foo_modcons$7_xs$0,foo_modcons$7_ctx,100,x$1,x$2) -- #153 //│ Nil => //│ let x$11 = Nil() in -- #162 -//│ let* (res) = _foo_ctx_app(foo_modcons_ctx,x$11) in -- #161 +//│ let* (res) = _foo_ctx_app$4(foo_modcons$7_ctx,x$11) in -- #161 //│ res -- #160 //│ ) -//│ Def(9, _foo_modcons_j$2_modcons_opt, [tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2], -//│ 1, -//│ jump _foo_modcons_j$2_modcons_opt_jp(tailrecBranch$,foo_modcons_ctx,foo_modcons_xs$0,j$2_modcons_ctx,j$2_modcons_x$7,j$2_modcons_x$1,j$2_modcons_x$2) -- #171 -//│ ) //│ }, //│ let x$12 = Nil() in -- #103 //│ let x$13 = Cons(9,x$12) in -- #102 @@ -2033,36 +2033,36 @@ a() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, -//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ let* (r0) = _a_b_opt$3(0) in -- #44 //│ r0 -- #43 //│ ) //│ Def(1, a, [], //│ 1, -//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ let* (r0) = _a_b_opt$3(1) in -- #42 //│ r0 -- #41 //│ ) //│ Def(2, j$0, [x$3], //│ 1, //│ x$3 -- #11 //│ ) -//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ Def(3, _a_b_opt$3, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 +//│ ) +//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], //│ 1, //│ let scrut = ==(0,tailrecBranch$) in -- #39 //│ if scrut -- #38 //│ true => //│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp(1) -- #36 +//│ jump _a_b_opt_jp$4(1) -- #36 //│ false => //│ let x$2 = <(0,1) in -- #35 //│ if x$2 -- #34 //│ true => -//│ jump _a_b_opt_jp(1) -- #32 +//│ jump _a_b_opt_jp$4(1) -- #32 //│ false => -//│ jump _a_b_opt_jp(0) -- #33 -//│ ) -//│ Def(4, _a_b_opt, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ jump _a_b_opt_jp$4(0) -- #33 //│ ) //│ }, //│ let* (x$6) = a() in -- #27 @@ -2072,36 +2072,36 @@ a() //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, b, [], //│ 1, -//│ let* (r0) = _a_b_opt(0) in -- #44 +//│ let* (r0) = _a_b_opt$3(0) in -- #44 //│ r0 -- #43 //│ ) //│ Def(1, a, [], //│ 1, -//│ let* (r0) = _a_b_opt(1) in -- #42 +//│ let* (r0) = _a_b_opt$3(1) in -- #42 //│ r0 -- #41 //│ ) //│ Def(2, j$0, [x$3], //│ 1, //│ x$3 -- #11 //│ ) -//│ Def(3, _a_b_opt_jp, [tailrecBranch$], +//│ Def(3, _a_b_opt$3, [tailrecBranch$], +//│ 1, +//│ jump _a_b_opt_jp$4(tailrecBranch$) -- #40 +//│ ) +//│ Def(4, _a_b_opt_jp$4, [tailrecBranch$], //│ 1, //│ let scrut = ==(0,tailrecBranch$) in -- #39 //│ if scrut -- #38 //│ true => //│ let* (x$0) = a() in -- #37 -//│ jump _a_b_opt_jp(1) -- #36 +//│ jump _a_b_opt_jp$4(1) -- #36 //│ false => //│ let x$2 = <(0,1) in -- #35 //│ if x$2 -- #34 //│ true => -//│ jump _a_b_opt_jp(1) -- #32 +//│ jump _a_b_opt_jp$4(1) -- #32 //│ false => -//│ jump _a_b_opt_jp(0) -- #33 -//│ ) -//│ Def(4, _a_b_opt, [tailrecBranch$], -//│ 1, -//│ jump _a_b_opt_jp(tailrecBranch$) -- #40 +//│ jump _a_b_opt_jp$4(0) -- #33 //│ ) //│ }, //│ let* (x$6) = a() in -- #27 @@ -2155,22 +2155,22 @@ a() //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = a_modcons(idCtx) in -- #79 +//│ let* (res) = a_modcons$7(idCtx) in -- #79 //│ res -- #78 //│ ) //│ Def(1, b, [], //│ 1, //│ let idCtx = _IdContext() in -- #72 -//│ let* (res) = b_modcons(idCtx) in -- #71 +//│ let* (res) = b_modcons$6(idCtx) in -- #71 //│ res -- #70 //│ ) //│ Def(2, c, [], //│ 1, //│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = c_modcons(idCtx) in -- #62 +//│ let* (res) = c_modcons$5(idCtx) in -- #62 //│ res -- #61 //│ ) -//│ Def(3, _c_b_a_ctx_app, [ctx,x], +//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], //│ 1, //│ case ctx of -- #49 //│ _IdContext => @@ -2190,39 +2190,43 @@ a() //│ let acc = ctx.acc in -- #38 //│ acc -- #37 //│ ) -//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], +//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #55 //│ let ctx2ptr = ctx2.ptr in -- #54 //│ let ctx2field = ctx2.field in -- #53 -//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #52 +//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 //│ ret -- #50 //│ ) -//│ Def(5, c_modcons, [ctx], +//│ Def(5, c_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #104 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,0,0) in -- #104 //│ r0 -- #103 //│ ) -//│ Def(6, b_modcons, [ctx], +//│ Def(6, b_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #106 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,0,ctx,0) in -- #106 //│ r0 -- #105 //│ ) -//│ Def(7, a_modcons, [ctx], +//│ Def(7, a_modcons$7, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #108 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx) in -- #108 //│ r0 -- #107 //│ ) -//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 +//│ ) +//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], //│ 1, //│ let scrut = ==(7,tailrecBranch$) in -- #101 //│ if scrut -- #100 //│ true => //│ let x$1 = A(0,1) in -- #97 //│ let ctx2 = _Context(x$1,x$1,0) in -- #96 -//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #95 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #94 +//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 //│ false => //│ let scrut = ==(6,tailrecBranch$) in -- #99 //│ if scrut -- #98 @@ -2230,17 +2234,13 @@ a() //│ let* (x$2) = c() in -- #93 //│ let x$4 = A(x$2,0) in -- #92 //│ let ctx2 = _Context(x$4,x$4,1) in -- #91 -//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #90 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #89 +//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 //│ false => //│ let x$6 = A(0,1) in -- #88 //│ let ctx2 = _Context(x$6,x$6,0) in -- #87 -//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #86 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #85 -//│ ) -//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #102 +//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 //│ ) //│ }, //│ let* (x$7) = a() in -- #36 @@ -2251,22 +2251,22 @@ a() //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #80 -//│ let* (res) = a_modcons(idCtx) in -- #79 +//│ let* (res) = a_modcons$7(idCtx) in -- #79 //│ res -- #78 //│ ) //│ Def(1, b, [], //│ 1, //│ let idCtx = _IdContext() in -- #72 -//│ let* (res) = b_modcons(idCtx) in -- #71 +//│ let* (res) = b_modcons$6(idCtx) in -- #71 //│ res -- #70 //│ ) //│ Def(2, c, [], //│ 1, //│ let idCtx = _IdContext() in -- #63 -//│ let* (res) = c_modcons(idCtx) in -- #62 +//│ let* (res) = c_modcons$5(idCtx) in -- #62 //│ res -- #61 //│ ) -//│ Def(3, _c_b_a_ctx_app, [ctx,x], +//│ Def(3, _c_b_a_ctx_app$3, [ctx,x], //│ 1, //│ case ctx of -- #49 //│ _IdContext => @@ -2286,39 +2286,43 @@ a() //│ let acc = ctx.acc in -- #38 //│ acc -- #37 //│ ) -//│ Def(4, _c_b_a_ctx_comp, [ctx1,ctx2], +//│ Def(4, _c_b_a_ctx_comp$4, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #55 //│ let ctx2ptr = ctx2.ptr in -- #54 //│ let ctx2field = ctx2.field in -- #53 -//│ let* (newAcc) = _c_b_a_ctx_app(ctx1,ctx2acc) in -- #52 +//│ let* (newAcc) = _c_b_a_ctx_app$3(ctx1,ctx2acc) in -- #52 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #51 //│ ret -- #50 //│ ) -//│ Def(5, c_modcons, [ctx], +//│ Def(5, c_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(5,ctx,0,0) in -- #104 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,0,0) in -- #104 //│ r0 -- #103 //│ ) -//│ Def(6, b_modcons, [ctx], +//│ Def(6, b_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(6,0,ctx,0) in -- #106 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,0,ctx,0) in -- #106 //│ r0 -- #105 //│ ) -//│ Def(7, a_modcons, [ctx], +//│ Def(7, a_modcons$7, [ctx], //│ 1, -//│ let* (r0) = _c_modcons_b_modcons_a_modcons_opt(7,0,0,ctx) in -- #108 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx) in -- #108 //│ r0 -- #107 //│ ) -//│ Def(8, _c_modcons_b_modcons_a_modcons_opt_jp, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], +//│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], +//│ 1, +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx) -- #102 +//│ ) +//│ Def(9, _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], //│ 1, //│ let scrut = ==(7,tailrecBranch$) in -- #101 //│ if scrut -- #100 //│ true => //│ let x$1 = A(0,1) in -- #97 //│ let ctx2 = _Context(x$1,x$1,0) in -- #96 -//│ let* (composed) = _c_b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #95 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #94 +//│ let* (composed) = _c_b_a_ctx_comp$4(a_modcons$7_ctx,ctx2) in -- #95 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #94 //│ false => //│ let scrut = ==(6,tailrecBranch$) in -- #99 //│ if scrut -- #98 @@ -2326,17 +2330,13 @@ a() //│ let* (x$2) = c() in -- #93 //│ let x$4 = A(x$2,0) in -- #92 //│ let ctx2 = _Context(x$4,x$4,1) in -- #91 -//│ let* (composed) = _c_b_a_ctx_comp(b_modcons_ctx,ctx2) in -- #90 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(7,c_modcons_ctx,b_modcons_ctx,composed) -- #89 +//│ let* (composed) = _c_b_a_ctx_comp$4(b_modcons$6_ctx,ctx2) in -- #90 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(7,c_modcons$5_ctx,b_modcons$6_ctx,composed) -- #89 //│ false => //│ let x$6 = A(0,1) in -- #88 //│ let ctx2 = _Context(x$6,x$6,0) in -- #87 -//│ let* (composed) = _c_b_a_ctx_comp(c_modcons_ctx,ctx2) in -- #86 -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(6,c_modcons_ctx,composed,a_modcons_ctx) -- #85 -//│ ) -//│ Def(9, _c_modcons_b_modcons_a_modcons_opt, [tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _c_modcons_b_modcons_a_modcons_opt_jp(tailrecBranch$,c_modcons_ctx,b_modcons_ctx,a_modcons_ctx) -- #102 +//│ let* (composed) = _c_b_a_ctx_comp$4(c_modcons$5_ctx,ctx2) in -- #86 +//│ jump _c_modcons$5_b_modcons$6_a_modcons$7_opt_jp$9(6,c_modcons$5_ctx,composed,a_modcons$7_ctx) -- #85 //│ ) //│ }, //│ let* (x$7) = a() in -- #36 @@ -2392,13 +2392,13 @@ a() //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #62 -//│ let* (res) = a_modcons(idCtx) in -- #61 +//│ let* (res) = a_modcons$6(idCtx) in -- #61 //│ res -- #60 //│ ) //│ Def(1, b, [], //│ 1, //│ let idCtx = _IdContext() in -- #54 -//│ let* (res) = b_modcons(idCtx) in -- #53 +//│ let* (res) = b_modcons$5(idCtx) in -- #53 //│ res -- #52 //│ ) //│ Def(2, c, [], @@ -2406,7 +2406,7 @@ a() //│ let x$5 = A(0,1) in -- #29 //│ x$5 -- #28 //│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ Def(3, _b_a_ctx_app$3, [ctx,x], //│ 1, //│ case ctx of -- #40 //│ _IdContext => @@ -2418,45 +2418,45 @@ a() //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #46 //│ let ctx2ptr = ctx2.ptr in -- #45 //│ let ctx2field = ctx2.field in -- #44 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 +//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 //│ ret -- #41 //│ ) -//│ Def(5, b_modcons, [ctx], +//│ Def(5, b_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #81 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,0) in -- #81 //│ r0 -- #80 //│ ) -//│ Def(6, a_modcons, [ctx], +//│ Def(6, a_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #83 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,0,ctx) in -- #83 //│ r0 -- #82 //│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 +//│ ) +//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], //│ 1, //│ let scrut = ==(6,tailrecBranch$) in -- #78 //│ if scrut -- #77 //│ true => //│ let x$1 = A(0,1) in -- #76 //│ let ctx2 = _Context(x$1,x$1,0) in -- #75 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #74 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #73 +//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 //│ false => //│ let* (x$2) = @tailcall a() in -- #72 //│ let* (x$3) = c() in -- #71 //│ let x$4 = A(x$2,x$3) in -- #70 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$4) in -- #69 +//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 //│ res -- #68 //│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #79 -//│ ) //│ }, //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) @@ -2466,13 +2466,13 @@ a() //│ Def(0, a, [], //│ 1, //│ let idCtx = _IdContext() in -- #62 -//│ let* (res) = a_modcons(idCtx) in -- #61 +//│ let* (res) = a_modcons$6(idCtx) in -- #61 //│ res -- #60 //│ ) //│ Def(1, b, [], //│ 1, //│ let idCtx = _IdContext() in -- #54 -//│ let* (res) = b_modcons(idCtx) in -- #53 +//│ let* (res) = b_modcons$5(idCtx) in -- #53 //│ res -- #52 //│ ) //│ Def(2, c, [], @@ -2480,7 +2480,7 @@ a() //│ let x$5 = A(0,1) in -- #29 //│ x$5 -- #28 //│ ) -//│ Def(3, _b_a_ctx_app, [ctx,x], +//│ Def(3, _b_a_ctx_app$3, [ctx,x], //│ 1, //│ case ctx of -- #40 //│ _IdContext => @@ -2492,45 +2492,45 @@ a() //│ let acc = ctx.acc in -- #35 //│ acc -- #34 //│ ) -//│ Def(4, _b_a_ctx_comp, [ctx1,ctx2], +//│ Def(4, _b_a_ctx_comp$4, [ctx1,ctx2], //│ 1, //│ let ctx2acc = ctx2.acc in -- #46 //│ let ctx2ptr = ctx2.ptr in -- #45 //│ let ctx2field = ctx2.field in -- #44 -//│ let* (newAcc) = _b_a_ctx_app(ctx1,ctx2acc) in -- #43 +//│ let* (newAcc) = _b_a_ctx_app$3(ctx1,ctx2acc) in -- #43 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #42 //│ ret -- #41 //│ ) -//│ Def(5, b_modcons, [ctx], +//│ Def(5, b_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(5,ctx,0) in -- #81 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,0) in -- #81 //│ r0 -- #80 //│ ) -//│ Def(6, a_modcons, [ctx], +//│ Def(6, a_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _b_modcons_a_modcons_opt(6,0,ctx) in -- #83 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,0,ctx) in -- #83 //│ r0 -- #82 //│ ) -//│ Def(7, _b_modcons_a_modcons_opt_jp, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], +//│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], +//│ 1, +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx) -- #79 +//│ ) +//│ Def(8, _b_modcons$5_a_modcons$6_opt_jp$8, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], //│ 1, //│ let scrut = ==(6,tailrecBranch$) in -- #78 //│ if scrut -- #77 //│ true => //│ let x$1 = A(0,1) in -- #76 //│ let ctx2 = _Context(x$1,x$1,0) in -- #75 -//│ let* (composed) = _b_a_ctx_comp(a_modcons_ctx,ctx2) in -- #74 -//│ jump _b_modcons_a_modcons_opt_jp(5,composed,a_modcons_ctx) -- #73 +//│ let* (composed) = _b_a_ctx_comp$4(a_modcons$6_ctx,ctx2) in -- #74 +//│ jump _b_modcons$5_a_modcons$6_opt_jp$8(5,composed,a_modcons$6_ctx) -- #73 //│ false => //│ let* (x$2) = @tailcall a() in -- #72 //│ let* (x$3) = c() in -- #71 //│ let x$4 = A(x$2,x$3) in -- #70 -//│ let* (res) = _b_a_ctx_app(b_modcons_ctx,x$4) in -- #69 +//│ let* (res) = _b_a_ctx_app$3(b_modcons$5_ctx,x$4) in -- #69 //│ res -- #68 //│ ) -//│ Def(8, _b_modcons_a_modcons_opt, [tailrecBranch$,b_modcons_ctx,a_modcons_ctx], -//│ 1, -//│ jump _b_modcons_a_modcons_opt_jp(tailrecBranch$,b_modcons_ctx,a_modcons_ctx) -- #79 -//│ ) //│ }, //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) From ea8d442f683e39dfd0493b885532fe84f40edf17 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 22:33:38 +0800 Subject: [PATCH 49/59] Document, add undefined literal and address remaining todos --- .../main/scala/mlscript/compiler/ir/IR.scala | 8 ++- .../scala/mlscript/compiler/ir/Interp.scala | 3 +- .../compiler/optimizer/TailRecOpt.scala | 13 ++-- compiler/shared/test/diff-ir/IRTailRec.mls | 64 +++++++++---------- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 30816156ca..652d4be20a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -15,6 +15,9 @@ import scala.collection.immutable.SortedSet final case class IRError(message: String) extends Exception(message) +class Undefined: + override def toString = "undefined" + case class Program( classes: Set[ClassInfo], defs: Set[Defn], @@ -96,7 +99,7 @@ private def show_args(args: Ls[TrivialExpr]) = args map (_.show) mkString "," enum Expr: case Ref(name: Name) extends Expr, TrivialExpr - case Literal(lit: Lit) extends Expr, TrivialExpr + case Literal(lit: Lit | Undefined) extends Expr, TrivialExpr case CtorApp(name: ClassInfo, args: Ls[TrivialExpr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) @@ -109,6 +112,7 @@ enum Expr: def toDocument: Document = this match case Ref(s) => s.toString |> raw + case Literal(_: Undefined) => "undefined" |> raw case Literal(IntLit(lit)) => s"$lit" |> raw case Literal(DecLit(lit)) => s"$lit" |> raw case Literal(StrLit(lit)) => s"$lit" |> raw @@ -258,7 +262,7 @@ case class DefnLocMarker(val defn: Str, val marker: LocMarker): enum LocMarker: case MRef(name: Str) - case MLit(lit: Lit) + case MLit(lit: Lit | Undefined) case MCtorApp(name: ClassInfo, args: Ls[LocMarker]) case MSelect(name: Str, cls: ClassInfo, field: Str) case MBasicOp(name: Str, args: Ls[LocMarker]) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 016ab09938..7836f44cb6 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -38,7 +38,7 @@ class Interpreter(verbose: Bool): private enum Expr: case Ref(name: Name) - case Literal(lit: Lit) + case Literal(lit: Lit | Undefined) case CtorApp(name: ClassInfo, var args: Ls[Expr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[Expr]) @@ -51,6 +51,7 @@ class Interpreter(verbose: Bool): def document: Document = this match case Ref(Name(s)) => s |> raw + case Literal(_: Undefined) => "undefined" |> raw case Literal(IntLit(lit)) => s"$lit" |> raw case Literal(DecLit(lit)) => s"$lit" |> raw case Literal(StrLit(lit)) => s"$lit" |> raw diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index d734d4609a..c4d20fcff8 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -846,6 +846,8 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get + // undefined for dummy values + val dummyVal = Expr.Literal(Undefined()) // join points need to be rewritten. For now, just combine them into the rest of the function. They will be inlined anyways val defns = component.nodes ++ component.joinPoints @@ -916,8 +918,6 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames - // TODO: This works fine for now, but ideally should find a way to guarantee the new - // name is unique val newId = fnUid.make val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt$" + newId val jpId = fnUid.make @@ -946,15 +946,16 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) - // Tail calls to another function in the component will be replaced with a tail call + // Tail calls to another function in the component will be replaced with a call // to the merged function + // i.e. for mutually tailrec functions f(a, b) and g(c, d), + // f's body will be replaced with a call f_g(a, b, *, *), where * is a dummy value def transformDefn(defn: Defn): Defn = - // TODO: Figure out how to substitute variables with dummy variables. val info = defnInfoMap(defn.id) val start = - stackFrame.take(info.stackFrameIdx).drop(1).map { _ => Expr.Literal(IntLit(0)) } // we drop tailrecBranch and replace it with the defn id - val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => Expr.Literal(IntLit(0)) } + stackFrame.take(info.stackFrameIdx).drop(1).map { _ => dummyVal } // we drop tailrecBranch and replace it with the defn id + val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => dummyVal } val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end // We use a let call instead of a jump to avoid newDefn from being turned into a join point, diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index a3ceab66ba..580b119ca1 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -177,7 +177,7 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,0,0,0) in -- #60 +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 //│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], @@ -217,7 +217,7 @@ fact(1, 5) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, fact, [acc$0,n$0], //│ 1, -//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,0,0,0) in -- #60 +//│ let* (r0) = _fact_j$0_opt$3(0,acc$0,n$0,undefined,undefined,undefined) in -- #60 //│ r0 -- #59 //│ ) //│ Def(1, j$1, [x$3], @@ -382,7 +382,7 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt$5(1,0,0,n$0,acc$0) in -- #100 +//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 //│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], @@ -391,7 +391,7 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,0,0) in -- #98 +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 //│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], @@ -441,7 +441,7 @@ g(6, 0) //│ ) //│ Def(1, f, [n$0,acc$0], //│ 1, -//│ let* (r0) = _g_f_opt$5(1,0,0,n$0,acc$0) in -- #100 +//│ let* (r0) = _g_f_opt$5(1,undefined,undefined,n$0,acc$0) in -- #100 //│ r0 -- #99 //│ ) //│ Def(2, j$0, [x$3], @@ -450,7 +450,7 @@ g(6, 0) //│ ) //│ Def(3, g, [m$0,acc$1], //│ 1, -//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,0,0) in -- #98 +//│ let* (r0) = _g_f_opt$5(3,m$0,acc$1,undefined,undefined) in -- #98 //│ r0 -- #97 //│ ) //│ Def(4, j$1, [x$9], @@ -527,17 +527,17 @@ g(6, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 //│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 //│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 //│ r0 -- #40 //│ ) //│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], @@ -565,17 +565,17 @@ g(6, 0) //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { //│ Def(0, f, [a$0,b$0,c$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(0,0,0,0,0,0,0,a$0,b$0,c$0) in -- #45 +//│ let* (r0) = _h_g_f_opt$3(0,undefined,undefined,undefined,undefined,undefined,undefined,a$0,b$0,c$0) in -- #45 //│ r0 -- #44 //│ ) //│ Def(1, g, [d$0,e$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(1,0,0,0,0,d$0,e$0,0,0,0) in -- #43 +//│ let* (r0) = _h_g_f_opt$3(1,undefined,undefined,undefined,undefined,d$0,e$0,undefined,undefined,undefined) in -- #43 //│ r0 -- #42 //│ ) //│ Def(2, h, [p$0,q$0,r$0,s$0], //│ 1, -//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,0,0,0,0,0) in -- #41 +//│ let* (r0) = _h_g_f_opt$3(2,p$0,q$0,r$0,s$0,undefined,undefined,undefined,undefined,undefined) in -- #41 //│ r0 -- #40 //│ ) //│ Def(3, _h_g_f_opt$3, [tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0], @@ -1042,12 +1042,12 @@ a(S(S(S(Zero)))) //│ ) //│ Def(6, b_modcons$6, [ctx,n$1], //│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,0,0) in -- #156 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 //│ r0 -- #155 //│ ) //│ Def(7, a_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx,n$0) in -- #158 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 //│ r0 -- #157 //│ ) //│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], @@ -1140,12 +1140,12 @@ a(S(S(S(Zero)))) //│ ) //│ Def(6, b_modcons$6, [ctx,n$1], //│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,0,0) in -- #156 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(6,ctx,n$1,undefined,undefined) in -- #156 //│ r0 -- #155 //│ ) //│ Def(7, a_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx,n$0) in -- #158 +//│ let* (r0) = _b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx,n$0) in -- #158 //│ r0 -- #157 //│ ) //│ Def(8, _b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,b_modcons$6_ctx,b_modcons$6_n$1,a_modcons$7_ctx,a_modcons$7_n$0], @@ -1494,12 +1494,12 @@ b(16) //│ ) //│ Def(7, b_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,0,0) in -- #170 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 //│ r0 -- #169 //│ ) //│ Def(8, a_modcons$8, [ctx,x$0], //│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,0,0,ctx,x$0) in -- #172 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 //│ r0 -- #171 //│ ) //│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], @@ -1599,12 +1599,12 @@ b(16) //│ ) //│ Def(7, b_modcons$7, [ctx,n$0], //│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,0,0) in -- #170 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(7,ctx,n$0,undefined,undefined) in -- #170 //│ r0 -- #169 //│ ) //│ Def(8, a_modcons$8, [ctx,x$0], //│ 1, -//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,0,0,ctx,x$0) in -- #172 +//│ let* (r0) = _b_modcons$7_a_modcons$8_opt$9(8,undefined,undefined,ctx,x$0) in -- #172 //│ r0 -- #171 //│ ) //│ Def(9, _b_modcons$7_a_modcons$8_opt$9, [tailrecBranch$,b_modcons$7_ctx,b_modcons$7_n$0,a_modcons$8_ctx,a_modcons$8_x$0], @@ -1840,7 +1840,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ ) //│ Def(7, foo_modcons$7, [ctx,xs$0], //│ 1, -//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 //│ r0 -- #172 //│ ) //│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], @@ -1929,7 +1929,7 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ ) //│ Def(7, foo_modcons$7, [ctx,xs$0], //│ 1, -//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,0,0,0,0) in -- #173 +//│ let* (r0) = _foo_modcons$7_j$2_modcons$6_opt$8(7,ctx,xs$0,undefined,undefined,undefined,undefined) in -- #173 //│ r0 -- #172 //│ ) //│ Def(8, _foo_modcons$7_j$2_modcons$6_opt$8, [tailrecBranch$,foo_modcons$7_ctx,foo_modcons$7_xs$0,j$2_modcons$6_ctx,j$2_modcons$6_x$7,j$2_modcons$6_x$1,j$2_modcons$6_x$2], @@ -2201,17 +2201,17 @@ a() //│ ) //│ Def(5, c_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,0,0) in -- #104 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 //│ r0 -- #103 //│ ) //│ Def(6, b_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,0,ctx,0) in -- #106 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 //│ r0 -- #105 //│ ) //│ Def(7, a_modcons$7, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx) in -- #108 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 //│ r0 -- #107 //│ ) //│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], @@ -2297,17 +2297,17 @@ a() //│ ) //│ Def(5, c_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,0,0) in -- #104 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(5,ctx,undefined,undefined) in -- #104 //│ r0 -- #103 //│ ) //│ Def(6, b_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,0,ctx,0) in -- #106 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(6,undefined,ctx,undefined) in -- #106 //│ r0 -- #105 //│ ) //│ Def(7, a_modcons$7, [ctx], //│ 1, -//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,0,0,ctx) in -- #108 +//│ let* (r0) = _c_modcons$5_b_modcons$6_a_modcons$7_opt$8(7,undefined,undefined,ctx) in -- #108 //│ r0 -- #107 //│ ) //│ Def(8, _c_modcons$5_b_modcons$6_a_modcons$7_opt$8, [tailrecBranch$,c_modcons$5_ctx,b_modcons$6_ctx,a_modcons$7_ctx], @@ -2429,12 +2429,12 @@ a() //│ ) //│ Def(5, b_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,0) in -- #81 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 //│ r0 -- #80 //│ ) //│ Def(6, a_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,0,ctx) in -- #83 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 //│ r0 -- #82 //│ ) //│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], @@ -2503,12 +2503,12 @@ a() //│ ) //│ Def(5, b_modcons$5, [ctx], //│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,0) in -- #81 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(5,ctx,undefined) in -- #81 //│ r0 -- #80 //│ ) //│ Def(6, a_modcons$6, [ctx], //│ 1, -//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,0,ctx) in -- #83 +//│ let* (r0) = _b_modcons$5_a_modcons$6_opt$7(6,undefined,ctx) in -- #83 //│ r0 -- #82 //│ ) //│ Def(7, _b_modcons$5_a_modcons$6_opt$7, [tailrecBranch$,b_modcons$5_ctx,a_modcons$6_ctx], From 0125b1e28418a70bd355c9c1b492f24797a70a0a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 5 Jun 2024 22:37:14 +0800 Subject: [PATCH 50/59] use unitlit instead of a new literal type for undefined --- .../shared/main/scala/mlscript/compiler/ir/IR.scala | 12 ++++-------- .../main/scala/mlscript/compiler/ir/Interp.scala | 3 +-- .../mlscript/compiler/optimizer/TailRecOpt.scala | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala index 652d4be20a..a84f48fd36 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/IR.scala @@ -15,9 +15,6 @@ import scala.collection.immutable.SortedSet final case class IRError(message: String) extends Exception(message) -class Undefined: - override def toString = "undefined" - case class Program( classes: Set[ClassInfo], defs: Set[Defn], @@ -99,7 +96,7 @@ private def show_args(args: Ls[TrivialExpr]) = args map (_.show) mkString "," enum Expr: case Ref(name: Name) extends Expr, TrivialExpr - case Literal(lit: Lit | Undefined) extends Expr, TrivialExpr + case Literal(lit: Lit) extends Expr, TrivialExpr case CtorApp(name: ClassInfo, args: Ls[TrivialExpr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) @@ -112,11 +109,10 @@ enum Expr: def toDocument: Document = this match case Ref(s) => s.toString |> raw - case Literal(_: Undefined) => "undefined" |> raw case Literal(IntLit(lit)) => s"$lit" |> raw case Literal(DecLit(lit)) => s"$lit" |> raw case Literal(StrLit(lit)) => s"$lit" |> raw - case Literal(UnitLit(lit)) => s"$lit" |> raw + case Literal(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw case CtorApp(ClassInfo(_, name, _), args) => raw(name) <#> raw("(") <#> raw(args |> show_args) <#> raw(")") case Select(s, _, fld) => @@ -262,7 +258,7 @@ case class DefnLocMarker(val defn: Str, val marker: LocMarker): enum LocMarker: case MRef(name: Str) - case MLit(lit: Lit | Undefined) + case MLit(lit: Lit) case MCtorApp(name: ClassInfo, args: Ls[LocMarker]) case MSelect(name: Str, cls: ClassInfo, field: Str) case MBasicOp(name: Str, args: Ls[LocMarker]) @@ -301,7 +297,7 @@ enum LocMarker: case MLit(IntLit(lit)) => s"$lit" |> raw case MLit(DecLit(lit)) => s"$lit" |> raw case MLit(StrLit(lit)) => s"$lit" |> raw - case MLit(UnitLit(lit)) => s"$lit" |> raw + case MLit(UnitLit(lit)) => (if lit then "undefined" else "null") |> raw case _ => raw("...") def show = s"$tag-" + toDocument.print diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala index 7836f44cb6..016ab09938 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala @@ -38,7 +38,7 @@ class Interpreter(verbose: Bool): private enum Expr: case Ref(name: Name) - case Literal(lit: Lit | Undefined) + case Literal(lit: Lit) case CtorApp(name: ClassInfo, var args: Ls[Expr]) case Select(name: Name, cls: ClassInfo, field: Str) case BasicOp(name: Str, args: Ls[Expr]) @@ -51,7 +51,6 @@ class Interpreter(verbose: Bool): def document: Document = this match case Ref(Name(s)) => s |> raw - case Literal(_: Undefined) => "undefined" |> raw case Literal(IntLit(lit)) => s"$lit" |> raw case Literal(DecLit(lit)) => s"$lit" |> raw case Literal(StrLit(lit)) => s"$lit" |> raw diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index c4d20fcff8..0f5a2b38a2 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -847,7 +847,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag val trueClass = classes.find(c => c.ident == "True").get val falseClass = classes.find(c => c.ident == "False").get // undefined for dummy values - val dummyVal = Expr.Literal(Undefined()) + val dummyVal = Expr.Literal(UnitLit(true)) // join points need to be rewritten. For now, just combine them into the rest of the function. They will be inlined anyways val defns = component.nodes ++ component.joinPoints From dd8f2ae4689fd2c4608323e136561dac55c8149c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Jun 2024 16:12:32 +0800 Subject: [PATCH 51/59] Document @tailcall and @tailrec --- .../compiler/optimizer/TailRecOpt.scala | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 0f5a2b38a2..7cfa4ca59a 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -9,6 +9,62 @@ import Message.MessageContext import compiler.ir._ import compiler.ir.Node._ +/* + +DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec + +@tailcall: Used to annotate specific function calls. Calls annotated with @tailcall +must be tail calls or tail modulo-cons calls. These calls may be optimized to not +consume additional stack space. If such an optimization is not possible, then the +compiler will throw an error. + +If there are multiple possible candidates for tail modulo-cons calls in a single +branch of an expression, then @tailcall can be uesd to indicate which one will be +optimized. For instance in + +fun foo() = + A(foo(), bar()) + +we can use @tailcall to annotate the call foo() or bar(). If a call other than the +last call is annotated with @tailcall, then the remaining functions must be pure +to ensure that reordering computations does not change the result. + +If bar() is impure but you still want to optimize the call foo(), then you can do + +fun foo() = + let b = bar() + let a = @tailcall foo() + A(a, b) + +because here, you are taking responsibility for the reordering of the computations. + +@tailrec: Used to annotate functions. When this annotation is used on a function, say +@tailrec fun foo(), the compiler will ensure no sequence of recursive calls back to foo() +consume stack space, i.e. they are all tail calls. Note that a call to foo() may +consume an arbitrary amount of stack space as long as foo() is only consuming finite +stack space. For example, + +@tailrec fun foo() = bar() +fun bar() = + bar() + bar() + +is valid. However, + +@tailrec fun foo() = bar() +fun bar() = + foo() + bar() + +is invalid. If we swap the position of foo() and bar() in the body of bar, it is still invalid. + +Equivalently, if fun foo() is annotated with @tailrec, let S be the largest strongly +connected component in the call-graph of the program that contains foo. Then an error +will be thrown if and only if all edges (calls) connecting the nodes of the strongly +connected component are tail calls or tail modulo-cons calls. + +*/ + // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) From 398bd9ec4b21003975b97165894b5327ce9d3d4d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 17 Jun 2024 15:05:12 +0800 Subject: [PATCH 52/59] properly raise errors in IR builder --- .../scala/mlscript/compiler/ir/Builder.scala | 27 ++- .../compiler/optimizer/TailRecOpt.scala | 10 +- compiler/shared/test/diff-ir/IRTailRec.mls | 163 ++++++++++++++++++ .../test/scala/mlscript/compiler/TestIR.scala | 2 +- 4 files changed, 190 insertions(+), 12 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index dfb7acc8d8..66ecd1d3a3 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -4,11 +4,12 @@ import mlscript.compiler.optimizer.FreeVarAnalysis import mlscript.utils.shorthands._ import mlscript.utils._ import mlscript._ +import mlscript.Message._ import collection.mutable.ListBuffer final val ops = Set("+", "-", "*", "/", ">", "<", ">=", "<=", "!=", "==") -final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt): +final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): import Node._ import Expr._ @@ -79,8 +80,13 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres val v = fresh.make ann match - case Some(Var(nme)) if nme == "tailcall" => - LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + case Some(ann @ Var(nme)) => + if nme == "tailcall" then + LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) + else + if nme == "tailrec" then + raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) + LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case Some(_) => node |> unexpectedNode case None => LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) @@ -139,6 +145,14 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case App(f, xs @ Tup(_)) => buildLetCall(f, xs, None) case Ann(ann, App(f, xs @ Tup(_))) => buildLetCall(f, xs, Some(ann)) + case Ann(ann @ Var(name), recv) => + if name == "tailcall" then + raise(ErrorReport(List(msg"@tailcall may only be used to annotate function calls" -> ann.toLoc), true, Diagnostic.Compilation)) + else if name == "tailrec" then + raise(ErrorReport(List(msg"@tailrec may only be used to annotate functions" -> ann.toLoc), true, Diagnostic.Compilation)) + + buildResultFromTerm(recv)(k) + case Let(false, Var(name), rhs, body) => buildBinding(name, rhs, body)(k) @@ -255,7 +269,12 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres } val names = strs map (fresh.make(_)) given Ctx = ctx.copy(nameCtx = ctx.nameCtx ++ (strs zip names)) - val trAnn = nfd.annotations.find { case Var("tailrec") => true; case _ => false } + val trAnn = nfd.annotations.find { + case Var("tailrec") => true + case ann @ Var("tailcall") => + raise(ErrorReport(List(msg"@tailcall is for annotating function calls; try @tailrec instead" -> ann.toLoc), true, Diagnostic.Compilation)) + false + case _ => false } Defn( fnUid.make, diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 7cfa4ca59a..d475a6c7a5 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -235,8 +235,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag case _ => returnNone case Case(scrut, cases) => Right(cases.map(_._2)) - case x: LetExpr => - val LetExpr(name, expr, body) = x + case x @ LetExpr(name, expr, body) => expr match // Check if this let binding references the mod cons call. case Expr.Ref(name) => @@ -253,8 +252,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag shadowAndCont(body, name) // OK case Expr.Literal(lit) => shadowAndCont(body, name) // OK - case y: Expr.CtorApp => - val Expr.CtorApp(clsInfo, ctorArgs) = y + case y @ Expr.CtorApp(clsInfo, ctorArgs) => // if expr is a constructor with a call to some function as a parameter letCallNode match case None => shadowAndCont(body, name) // OK @@ -342,9 +340,7 @@ class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diag // If this assignment overwrites the mod cons value, forget it if containingCtors.contains(assignee) then invalidateAndCont(body) else searchOptCalls(body) - case x: LetCall => - val LetCall(names, defn, args, isTailRec, body) = x - + case x @ LetCall(names, defn, args, isTailRec, body) => val callInScc = scc.contains(defn.expectDefn) // Only deal with calls in the scc diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index 580b119ca1..ed7f0bbf15 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -2534,3 +2534,166 @@ a() //│ }, //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) + +:ce +@tailcall 1 +//│ |@|tailcall| |1| +//│ Parsed: {@tailcall 1} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] @tailcall may only be used to annotate function calls +//│ ║ l.2539: @tailcall 1 +//│ ╙── ^^^^^^^^ +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) + +:ce +@tailrec 1 +//│ |@|tailrec| |1| +//│ Parsed: {@tailrec 1} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] @tailrec may only be used to annotate functions +//│ ║ l.2567: @tailrec 1 +//│ ╙── ^^^^^^^ +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Strongly Connected Tail Calls: +//│ List() +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ +//│ }, +//│ 1 -- #0) + +:ce +fun foo() = + @tailrec foo() +foo() +//│ |#fun| |foo|(||)| |#=|→|@|tailrec| |foo|(||)|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {@tailrec foo()}; foo()} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] @tailrec is for annotating functions; try @tailcall instead +//│ ║ l.2596: @tailrec foo() +//│ ╙── ^^^^^^^ +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let* (x$0) = foo() in -- #3 +//│ x$0 -- #2 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) + +:ce +@tailcall +fun foo() = + foo() +foo() +//│ |@|tailcall|↵|#fun| |foo|(||)| |#=|→|foo|(||)|←|↵|foo|(||)| +//│ Parsed: {fun foo = () => {foo()}; foo()} +//│ +//│ +//│ IR: +//│ ╔══[COMPILATION ERROR] @tailcall is for annotating function calls; try @tailrec instead +//│ ║ l.2648: @tailcall +//│ ╙── ^^^^^^^^ +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { +//│ Def(0, foo, [], +//│ 1, +//│ let* (x$0) = foo() in -- #3 +//│ x$0 -- #2 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Strongly Connected Tail Calls: +//│ List(Set(foo)) +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) +//│ +//│ Promoted: +//│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, _IdContext, []),ClassInfo(3, _Context, [acc,ptr,field])}, { +//│ Def(1, foo_jp, [], +//│ 1, +//│ jump foo_jp() -- #8 +//│ ) +//│ Def(2, foo, [], +//│ 1, +//│ let* (r0) = foo_jp() in -- #10 +//│ r0 -- #9 +//│ ) +//│ }, +//│ let* (x$1) = foo() in -- #7 +//│ x$1 -- #6) diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index d94a09d27e..bbff20f515 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -21,7 +21,7 @@ class IRDiffTestCompiler extends DiffTests { val classUid = FreshInt() val tag = FreshInt() - val gb = Builder(Fresh(), fnUid, classUid, tag) + val gb = Builder(Fresh(), fnUid, classUid, tag, raise) val graph_ = gb.buildGraph(unit) if !mode.noTailRecOpt then From 4f93b76fe874a0ebfa57fd5f0950218019bb8df3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 17 Jun 2024 15:08:21 +0800 Subject: [PATCH 53/59] fix some documentation --- .../scala/mlscript/compiler/optimizer/TailRecOpt.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index d475a6c7a5..1405e45765 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -14,7 +14,7 @@ import compiler.ir.Node._ DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec @tailcall: Used to annotate specific function calls. Calls annotated with @tailcall -must be tail calls or tail modulo-cons calls. These calls may be optimized to not +must be tail calls or tail modulo-cons calls. These calls must be optimized to not consume additional stack space. If such an optimization is not possible, then the compiler will throw an error. @@ -39,9 +39,9 @@ fun foo() = because here, you are taking responsibility for the reordering of the computations. @tailrec: Used to annotate functions. When this annotation is used on a function, say -@tailrec fun foo(), the compiler will ensure no sequence of recursive calls back to foo() -consume stack space, i.e. they are all tail calls. Note that a call to foo() may -consume an arbitrary amount of stack space as long as foo() is only consuming finite +@tailrec fun foo(), the compiler will ensure no sequence of direct recursive calls back +to foo() consume stack space, i.e. they are all tail calls. Note that a call to foo() +may consume an arbitrary amount of stack space as long as foo() is only consuming finite stack space. For example, @tailrec fun foo() = bar() @@ -60,7 +60,7 @@ is invalid. If we swap the position of foo() and bar() in the body of bar, it is Equivalently, if fun foo() is annotated with @tailrec, let S be the largest strongly connected component in the call-graph of the program that contains foo. Then an error -will be thrown if and only if all edges (calls) connecting the nodes of the strongly +will be thrown unless all edges (calls) connecting the nodes of the strongly connected component are tail calls or tail modulo-cons calls. */ From f479ecc96132ef73f227187753c545b1f07ffafa Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 17 Jun 2024 15:13:33 +0800 Subject: [PATCH 54/59] remove bad copy paste --- compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 66ecd1d3a3..02c82e1e35 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -326,7 +326,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres import scala.collection.mutable.{ HashSet => MutHSet } - val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" amd "False" at some pointgrouped.getOrElse(0, Nil).map(buildClassInfo) + val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" amd "False" at some point :: ClassInfo(classUid.make, "False", List()) :: grouped.getOrElse(0, Nil).map(buildClassInfo) From 49a499360957d167ab05a74c0f9b35ad82011491 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 17 Jun 2024 15:13:45 +0800 Subject: [PATCH 55/59] fix typo --- compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 02c82e1e35..ac111b5c3e 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -326,7 +326,7 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres import scala.collection.mutable.{ HashSet => MutHSet } - val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" amd "False" at some point + val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" and "False" at some point :: ClassInfo(classUid.make, "False", List()) :: grouped.getOrElse(0, Nil).map(buildClassInfo) From 875484cb90007824df91760477307d7d0619b25d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 17 Jun 2024 15:14:40 +0800 Subject: [PATCH 56/59] Improve todo comment --- compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index ac111b5c3e..5dbfd46de9 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -326,7 +326,8 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres import scala.collection.mutable.{ HashSet => MutHSet } - val cls = ClassInfo(classUid.make, "True", List()) // TODO: add "True" and "False" at some point + // TODO: properly add prelude classes such as "True" and "False" rather than this hacky method + val cls = ClassInfo(classUid.make, "True", List()) :: ClassInfo(classUid.make, "False", List()) :: grouped.getOrElse(0, Nil).map(buildClassInfo) From 92039eaaaabb849a5f70a7acf11ef5ca79d79850 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 18 Jun 2024 21:27:19 +0800 Subject: [PATCH 57/59] respond to code review --- .../scala/mlscript/compiler/ir/Builder.scala | 8 +++--- .../test/scala/mlscript/compiler/TestIR.scala | 25 ++++++++----------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala index 5dbfd46de9..c80a698f6f 100644 --- a/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala +++ b/compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala @@ -81,10 +81,10 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres ann match case Some(ann @ Var(nme)) => - if nme == "tailcall" then + if nme === "tailcall" then LetCall(List(v), DefnRef(Right(g.str)), args, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) else - if nme == "tailrec" then + if nme === "tailrec" then raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) LetCall(List(v), DefnRef(Right(g.str)), args, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case Some(_) => node |> unexpectedNode @@ -146,9 +146,9 @@ final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: Fres case Ann(ann, App(f, xs @ Tup(_))) => buildLetCall(f, xs, Some(ann)) case Ann(ann @ Var(name), recv) => - if name == "tailcall" then + if name === "tailcall" then raise(ErrorReport(List(msg"@tailcall may only be used to annotate function calls" -> ann.toLoc), true, Diagnostic.Compilation)) - else if name == "tailrec" then + else if name === "tailrec" then raise(ErrorReport(List(msg"@tailrec may only be used to annotate functions" -> ann.toLoc), true, Diagnostic.Compilation)) buildResultFromTerm(recv)(k) diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index bbff20f515..1801469457 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -1,13 +1,10 @@ -package mlscript.compiler - +package mlscript +package compiler import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import scala.collection.mutable.StringBuilder -import mlscript.{DiffTests, ModeType, TypingUnit} -import mlscript.compiler.ir.{Interpreter, Fresh, FreshInt, Builder} import mlscript.compiler.optimizer.TailRecOpt -import mlscript.Diagnostic class IRDiffTestCompiler extends DiffTests { import IRDiffTestCompiler.* @@ -27,15 +24,15 @@ class IRDiffTestCompiler extends DiffTests { if !mode.noTailRecOpt then output(graph_.toString()) - val graph = if (!mode.noTailRecOpt) { - val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) - val (g, comps) = tailRecOpt.run_debug(graph_) - output("\nStrongly Connected Tail Calls:") - output(comps.toString) - g - } else { - graph_ - } + val graph = if !mode.noTailRecOpt + then + val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) + val (g, comps) = tailRecOpt.run_debug(graph_) + output("\nStrongly Connected Tail Calls:") + output(comps.toString) + g + else + graph_ if !mode.noTailRecOpt then output(graph.toString()) From 22d4f47211418e2966abbc7cff5d4d496b1bfbdf Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 18 Jun 2024 21:29:26 +0800 Subject: [PATCH 58/59] Improve test format --- compiler/shared/test/diff-ir/IR.mls | 33 --------- compiler/shared/test/diff-ir/IRComplex.mls | 9 --- compiler/shared/test/diff-ir/IRRec.mls | 42 ----------- compiler/shared/test/diff-ir/IRTailRec.mls | 74 ++++++------------- .../test/scala/mlscript/compiler/TestIR.scala | 6 +- 5 files changed, 25 insertions(+), 139 deletions(-) diff --git a/compiler/shared/test/diff-ir/IR.mls b/compiler/shared/test/diff-ir/IR.mls index 6a93ae6524..b5b44bbcf9 100644 --- a/compiler/shared/test/diff-ir/IR.mls +++ b/compiler/shared/test/diff-ir/IR.mls @@ -13,9 +13,6 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |mktup2|(|x|,| |y|)| |#=| |mktup|(|x|,| |y|)|↵|#fun| |mktup|(|x|,| |y|)| |#=| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|mktup2|(|1|,| |2|)|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun mktup2 = (x, y,) => mktup(x, y,); fun mktup = (x, y,) => Pair(x, y,); fun foo = () => {mktup2(1, 2,)}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, mktup2, [x$0,y$0], @@ -51,9 +48,6 @@ bar() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|pair|)| |#=|→|#if| |pair| |is|→|Pair|(|x|,| |y|)| |#then| |Pair|(|x|,| |y|)|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |2|)|)|←|↵|bar|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = (pair,) => {if pair is ‹(Pair(x, y,)) then Pair(x, y,)›}; fun bar = () => {foo(Pair(1, 2,),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [pair$0], @@ -101,9 +95,6 @@ foo() //│ |#class| |Pair|(|x|,| |y|)| |{||}|↵|#fun| |silly|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#let| |n| |#=| |#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then|→|#if| |pair| |is|→|Pair| |(|x3|,| |x4|)| |#then| |x3| |+| |1|←|←|←|↵|n| |+| |1|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |silly|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun silly = (pair,) => {let _ = 0; let n = if pair is ‹(Pair(x1, x2,)) then {if pair is ‹(Pair(x3, x4,)) then +(x3,)(1,)›}›; +(n,)(1,)}; fun foo = () => {let a = Pair(0, 1,); let b = silly(a,); b}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, silly, [pair$0], @@ -159,9 +150,6 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |c| |#=| |2|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x1| |+| |c|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |inc_fst|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let c = 2; if pair is ‹(Pair(x1, x2,)) then +(x1,)(c,)›}; fun foo = () => {let a = Pair(0, 1,); let b = inc_fst(a,); b}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], @@ -204,9 +192,6 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |inc_fst|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x2| |+| |1|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |b| |#=| |inc_fst|(|Pair|(|0|,| |1|)|)|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun inc_fst = (pair,) => {let _ = 0; if pair is ‹(Pair(x1, x2,)) then +(x2,)(1,)›}; fun foo = () => {let b = inc_fst(Pair(0, 1,),); b}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, inc_fst, [pair$0], @@ -252,9 +237,6 @@ bar() //│ |#class| |Left|(|x|)|↵|#class| |Right|(|y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |t| |#=| |#if| |a| |is|→|Left|(|x|)| |#then| |Left|(|x| |+| |1|)|↵|Right|(|y|)| |#then| |Right|(|b|)|←|↵|#if| |t| |is|→|Left|(|x|)| |#then| |x|↵|Right|(|y|)| |#then| |y|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Right|(|2|)|,| |2|)|←|↵|bar|(||)| //│ Parsed: {class Left(x,) {}; class Right(y,) {}; fun foo = (a, b,) => {let t = if a is ‹(Left(x,)) then Left(+(x,)(1,),); (Right(y,)) then Right(b,)›; if t is ‹(Left(x,)) then x; (Right(y,)) then y›}; fun bar = () => {foo(Right(2,), 2,)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Left, [x]),ClassInfo(3, Right, [y])}, { //│ Def(0, foo, [a$0,b$0], @@ -306,9 +288,6 @@ bar() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|)| |#=| |a|.x| |+| |a|.y|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = (a,) => +((a).x,)((a).y,); fun bar = () => {foo(Pair(1, 0,),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [a$0], @@ -344,9 +323,6 @@ bar() //│ |#class| |C1|(|x|,| |y|)|↵|#class| |C2|(|z|)|↵|#fun| |foo|(|a|)| |#=| |#if| |a| |is|→|C1|(|x|,| |y|)| |#then| |x|↵|C2|(|z|)| |#then| |z|←|↵|#fun| |bar|(||)| |#=|→|foo|(|C1|(|0|,| |1|)|)|←|↵|bar|(||)| //│ Parsed: {class C1(x, y,) {}; class C2(z,) {}; fun foo = (a,) => if a is ‹(C1(x, y,)) then x; (C2(z,)) then z›; fun bar = () => {foo(C1(0, 1,),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, C1, [x,y]),ClassInfo(3, C2, [z])}, { //│ Def(0, foo, [a$0], @@ -395,9 +371,6 @@ baz() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(|a|,| |b|)| |#=|→|#let| |x1| |#=| |a|.x|↵|#let| |y1| |#=| |a|.y|↵|#let| |x2| |#=| |b|.x|↵|#let| |y2| |#=| |b|.y|↵|x1| |+| |y1| |+| |x2| |+| |y2|←|↵|#fun| |bar|(|c|)| |#=|→|foo|(|Pair|(|0|,| |1|)|,| |c|)|↵|foo|(|c|,| |Pair|(|2|,| |3|)|)|↵|foo|(|Pair|(|0|,| |1|)|,| |Pair|(|2|,| |3|)|)|←|↵|#fun| |baz|(||)| |#=|→|bar|(|Pair|(|4|,|5|)|)|←|↵|baz|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = (a, b,) => {let x1 = (a).x; let y1 = (a).y; let x2 = (b).x; let y2 = (b).y; +(+(+(x1,)(y1,),)(x2,),)(y2,)}; fun bar = (c,) => {foo(Pair(0, 1,), c,); foo(c, Pair(2, 3,),); foo(Pair(0, 1,), Pair(2, 3,),)}; fun baz = () => {bar(Pair(4, 5,),)}; baz()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [a$0,b$0], @@ -445,9 +418,6 @@ foo() //│ |#class| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|#let| |p| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |p|.x|↵|b|←|↵|foo|(||)| //│ Parsed: {class Pair(x, y,) {}; fun foo = () => {let p = Pair(0, 1,); let b = (p).x; b}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Pair, [x,y])}, { //│ Def(0, foo, [], @@ -478,9 +448,6 @@ foo() //│ |#class| |S|(|s|)|↵|#class| |O|↵|#fun| |foo|(||)| |#=|→|bar|(|S|(|O|)|)|←|↵|#fun| |bar|(|x|)| |#=|→|baz|(|x|)|←|↵|#fun| |baz|(|x|)| |#=|→|#if| |x| |is|→|S|(|s|)| |#then| |s|↵|O| |#then| |x|←|←|↵|foo|(||)| //│ Parsed: {class S(s,) {}; class O {}; fun foo = () => {bar(S(O,),)}; fun bar = (x,) => {baz(x,)}; fun baz = (x,) => {if x is ‹(S(s,)) then s; (O) then x›}; foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, S, [s]),ClassInfo(3, O, [])}, { //│ Def(0, foo, [], diff --git a/compiler/shared/test/diff-ir/IRComplex.mls b/compiler/shared/test/diff-ir/IRComplex.mls index 59f3497175..e6df8a88ec 100644 --- a/compiler/shared/test/diff-ir/IRComplex.mls +++ b/compiler/shared/test/diff-ir/IRComplex.mls @@ -22,9 +22,6 @@ bar() //│ |#class| |A|(|x|,| |y|,| |z|)|↵|#class| |B|(|m|,| |n|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |r| |#=| |#if| |t| |is|→|A|(|x|,| |y|,| |z|)| |#then| |x| |+| |y| |*| |z|↵|B|(|m|,| |n|)| |#then| |m| |-| |n|←|↵|#let| |s| |#=| |B|(|1|,| |2|)|↵|#let| |u| |#=| |#if| |s| |is|→|A|(|x|,| |y|,| |z|)| |#then| |3|↵|B|(|m|,| |n|)| |#then| |4|←|↵|r| |+| |u|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|6|,| |7|,| |8|)|)|↵|complex_foo|(|B|(|9|,| |10|)|)|←|↵|bar|(||)| //│ Parsed: {class A(x, y, z,) {}; class B(m, n,) {}; fun complex_foo = (t,) => {let r = if t is ‹(A(x, y, z,)) then +(x,)(*(y,)(z,),); (B(m, n,)) then -(m,)(n,)›; let s = B(1, 2,); let u = if s is ‹(A(x, y, z,)) then 3; (B(m, n,)) then 4›; +(r,)(u,)}; fun bar = () => {complex_foo(A(6, 7, 8,),); complex_foo(B(9, 10,),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [x,y,z]),ClassInfo(3, B, [m,n])}, { //│ Def(0, complex_foo, [t$0], @@ -113,9 +110,6 @@ bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |y|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then y; (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], @@ -252,9 +246,6 @@ bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |A|(|x|,| |C|(|0|)|)|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1,)(2,); let b = *(1,)(2,); let x = if t is ‹(A(x, y,)) then A(x, C(0,),); (B(x,)) then B(+(x,)(b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x,)(a,),)(b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [w,x]),ClassInfo(3, B, [y]),ClassInfo(4, C, [z])}, { //│ Def(0, complex_foo, [t$0], diff --git a/compiler/shared/test/diff-ir/IRRec.mls b/compiler/shared/test/diff-ir/IRRec.mls index df8bad56fd..a56f53a9e7 100644 --- a/compiler/shared/test/diff-ir/IRRec.mls +++ b/compiler/shared/test/diff-ir/IRRec.mls @@ -11,9 +11,6 @@ fib(20) //│ |#class| |True|↵|#class| |False|↵|#fun| |fib|(|n|)| |#=| |#if| |n| |<| |2| |#then| |n| |#else| |fib|(|n|-|1|)| |+| |fib|(|n|-|2|)|↵|fib|(|20|)| //│ Parsed: {class True {}; class False {}; fun fib = (n,) => if (<(n,)(2,)) then n else +(fib(-(n,)(1,),),)(fib(-(n,)(2,),),); fib(20,)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, fib, [n$0], @@ -51,9 +48,6 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#fun| |odd|(|x|)| |#=| |#if| |x| |==| |0| |#then| |False| |#else| |even|(|x|-|1|)|↵|#fun| |even|(|x|)| |#=| |#if| |x| |==| |0| |#then| |True| |#else| |odd|(|x|-|1|)|↵|#fun| |foo|(||)| |#=| |odd|(|10|)|↵|foo|(||)| //│ Parsed: {class True {}; class False {}; fun odd = (x,) => if (==(x,)(0,)) then False else even(-(x,)(1,),); fun even = (x,) => if (==(x,)(0,)) then True else odd(-(x,)(1,),); fun foo = () => odd(10,); foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, [])}, { //│ Def(0, odd, [x$0], @@ -115,9 +109,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |A|↵|#class| |B|(|b|)|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=| |foo|(|False|)|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class A {}; class B(b,) {}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => foo(False,); main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, not, [x$0], @@ -191,9 +182,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A|→|#else| |B|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=|→|#let| |x| |#=| |foo|(|False|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = () => {let x = foo(False,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], @@ -287,9 +275,6 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], @@ -357,9 +342,6 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|mk|(|10|)|)|↵|foo|(||)| | //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(mk(10,),); foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], @@ -442,9 +424,6 @@ foo() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n,)(0,)) then S(mk(-(n,)(1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], @@ -532,9 +511,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |S|(|s|)|↵|#class| |O|↵|#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|#if| |10| |>| |0| |#then| |S|(|O|)| |#else| |O|)|↵|#fun| |bar|(||)| |#=| |#if| |10| |>| |0| |#then| |odd|(|S|(|O|)|)| |#else| |odd|(|O|)|↵|#fun| |main|(||)| |#=|→|foo|(||)|↵|bar|(||)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class S(s,) {}; class O {}; fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(if (>(10,)(0,)) then S(O,) else O,); fun bar = () => if (>(10,)(0,)) then odd(S(O,),) else odd(O,); fun main = () => {foo(); bar()}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, S, [s]),ClassInfo(5, O, [])}, { //│ Def(0, odd, [x$0], @@ -643,9 +619,6 @@ main(False) //│ |#class| |True|↵|#class| |False|↵|#class| |A|(||)|↵|#class| |B|(|b|)|↵|#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |A| |#else| |B|(|foo|(|not|(|x|)|)|)|←|↵|#fun| |main|(|flag|)| |#=|→|#let| |x| |#=| |foo|(|flag|)|↵|#if| |x| |is|→|A| |#then| |aaa|(||)|↵|B|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(|False|)| //│ Parsed: {class True {}; class False {}; class A() {}; class B(b,) {}; fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m,)(n,),)(p,),)(q,)}; fun bbb = () => {let x = aaa(); +(*(x,)(100,),)(4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then A else B(foo(not(x,),),)}; fun main = (flag,) => {let x = foo(flag,); if x is ‹(A) then aaa(); (B(b1,)) then bbb()›}; main(False,)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, A, []),ClassInfo(5, B, [b])}, { //│ Def(0, aaa, [], @@ -744,9 +717,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, head_opt, [l$0], @@ -826,9 +796,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], @@ -921,9 +888,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |last_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then|→|#if| |t| |is|→|Nil| |#then| |Some|(|h|)|↵|Cons|(|h2|,| |t2|)| |#then| |last_opt|(|t|)|←|←|←|←|↵|#fun| |main|(||)| |#=|→|last_opt|(|mk_list|(|10|)|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun mk_list = (n,) => {if (==(n,)(0,)) then Nil else Cons(n, mk_list(-(n,)(1,),),)}; fun last_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then {if t is ‹(Nil) then Some(h,); (Cons(h2, t2,)) then last_opt(t,)›}›}; fun main = () => {last_opt(mk_list(10,),)}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, mk_list, [n$0], @@ -1020,9 +984,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, c,) => {+(+(+(+(a,)(1,),)(2,),)(3,),)(4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], @@ -1154,9 +1115,6 @@ main() //│ |#class| |True|↵|#class| |False|↵|#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#class| |Some|(|x|)|↵|#class| |None|↵|#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |z|)| |#=|→|#if| |a| |>| |0| |#then| |f|(|Some|(|a| |-| |1|)|)| |#else| |z|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {class True {}; class False {}; class Cons(h, t,) {}; class Nil {}; class Some(x,) {}; class None {}; fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w,)(8,),)(9,),)(10,)}; fun e1 = (a, z,) => {if (>(a,)(0,)) then f(Some(-(a,)(1,),),) else z}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m,)(n,),)(p,),)(q,) else +(-(+(m,)(n,),)(p,),)(q,)}; fun e2 = (x,) => {+(+(+(x,)(12,),)(13,),)(14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),),)(f(None,),)}; main()} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, True, []),ClassInfo(3, False, []),ClassInfo(4, Cons, [h,t]),ClassInfo(5, Nil, []),ClassInfo(6, Some, [x]),ClassInfo(7, None, [])}, { //│ Def(0, is_some, [o$0], diff --git a/compiler/shared/test/diff-ir/IRTailRec.mls b/compiler/shared/test/diff-ir/IRTailRec.mls index ed7f0bbf15..4979546b13 100644 --- a/compiler/shared/test/diff-ir/IRTailRec.mls +++ b/compiler/shared/test/diff-ir/IRTailRec.mls @@ -11,9 +11,6 @@ fact(1, 5) //│ |#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, fact, [acc$0,n$0], @@ -48,7 +45,6 @@ fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, fact, [acc$0,n$0], @@ -140,7 +136,6 @@ fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, fact, [acc$0,n$0], @@ -265,9 +260,6 @@ g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, double, [x$0], @@ -325,7 +317,6 @@ g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, double, [x$0], @@ -501,7 +492,6 @@ g(6, 0) //│ |@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | //│ Parsed: {fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, f, [a$0,b$0,c$0], @@ -608,7 +598,6 @@ hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, hello, [], @@ -621,10 +610,10 @@ hello() //│ let* (x$2) = hello() in -- #12 //│ x$2 -- #11) //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.604: @tailcall hello() +//│ ║ l.598: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.605: @tailcall hello() +//│ ║ l.599: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: @@ -660,7 +649,6 @@ hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); 2}; hello()} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, hello, [], @@ -672,7 +660,7 @@ hello() //│ let* (x$1) = hello() in -- #8 //│ x$1 -- #7) //│ ╔══[COMPILATION ERROR] not a tail call -//│ ║ l.657: @tailcall hello() +//│ ║ l.651: @tailcall hello() //│ ╙── ^^^^^ //│ //│ Strongly Connected Tail Calls: @@ -709,7 +697,6 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, addOne, [xs$0], @@ -882,9 +869,6 @@ a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], @@ -949,7 +933,6 @@ a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Zero, []),ClassInfo(3, S, [x])}, { //│ Def(0, a, [n$0], @@ -1212,7 +1195,6 @@ addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, addOne, [xs$0], @@ -1389,7 +1371,6 @@ b(16) //│ |#class| |Nil|↵|#class| |Cons|(|m|,| |n|)|↵|@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| //│ Parsed: {class Nil {}; class Cons(m, n,) {}; fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Nil, []),ClassInfo(3, Cons, [m,n])}, { //│ Def(0, a, [x$0], @@ -1675,9 +1656,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| //│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ -//│ -//│ IR: -//│ //│ Promoted: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], @@ -1748,7 +1726,6 @@ foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ |#class| |Cons|(|h|,| |t|)|↵|#class| |Nil|↵|@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| //│ Parsed: {class Cons(h, t,) {}; class Nil {}; fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, Cons, [h,t]),ClassInfo(3, Nil, [])}, { //│ Def(0, foo, [xs$0], @@ -1994,7 +1971,6 @@ a() //│ |#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| //│ Parsed: {fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, b, [], @@ -2022,10 +1998,10 @@ a() //│ let* (x$6) = a() in -- #27 //│ x$6 -- #26) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.1989: @tailrec +//│ ║ l.1977: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.1987: a() +//│ ║ l.1975: a() //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: @@ -2117,7 +2093,6 @@ a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { //│ Def(0, a, [], @@ -2143,10 +2118,10 @@ a() //│ let* (x$7) = a() in -- #36 //│ x$7 -- #35) //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2112: @tailrec +//│ ║ l.2100: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2114: fun b() = A(c(), @tailcall a()) +//│ ║ l.2102: fun b() = A(c(), @tailcall a()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: @@ -2352,7 +2327,6 @@ a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} //│ -//│ //│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, []),ClassInfo(2, A, [a,b])}, { //│ Def(0, a, [], @@ -2377,13 +2351,13 @@ a() //│ let* (x$6) = a() in -- #33 //│ x$6 -- #32) //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure -//│ ║ l.2349: fun b() = A(@tailcall a(), c()) +//│ ║ l.2337: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec -//│ ║ l.2347: @tailrec +//│ ║ l.2335: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call -//│ ║ l.2349: fun b() = A(@tailcall a(), c()) +//│ ║ l.2337: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ //│ Strongly Connected Tail Calls: @@ -2539,12 +2513,11 @@ a() @tailcall 1 //│ |@|tailcall| |1| //│ Parsed: {@tailcall 1} -//│ -//│ -//│ IR: //│ ╔══[COMPILATION ERROR] @tailcall may only be used to annotate function calls -//│ ║ l.2539: @tailcall 1 +//│ ║ l.2527: @tailcall 1 //│ ╙── ^^^^^^^^ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ //│ }, @@ -2567,12 +2540,11 @@ a() @tailrec 1 //│ |@|tailrec| |1| //│ Parsed: {@tailrec 1} -//│ -//│ -//│ IR: //│ ╔══[COMPILATION ERROR] @tailrec may only be used to annotate functions -//│ ║ l.2567: @tailrec 1 +//│ ║ l.2555: @tailrec 1 //│ ╙── ^^^^^^^ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ //│ }, @@ -2597,12 +2569,11 @@ fun foo() = foo() //│ |#fun| |foo|(||)| |#=|→|@|tailrec| |foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {@tailrec foo()}; foo()} -//│ -//│ -//│ IR: //│ ╔══[COMPILATION ERROR] @tailrec is for annotating functions; try @tailcall instead -//│ ║ l.2596: @tailrec foo() +//│ ║ l.2584: @tailrec foo() //│ ╙── ^^^^^^^ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, foo, [], //│ 1, @@ -2651,12 +2622,11 @@ fun foo() = foo() //│ |@|tailcall|↵|#fun| |foo|(||)| |#=|→|foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {foo()}; foo()} -//│ -//│ -//│ IR: //│ ╔══[COMPILATION ERROR] @tailcall is for annotating function calls; try @tailrec instead -//│ ║ l.2648: @tailcall +//│ ║ l.2636: @tailcall //│ ╙── ^^^^^^^^ +//│ +//│ IR: //│ Program({ClassInfo(0, True, []),ClassInfo(1, False, [])}, { //│ Def(0, foo, [], //│ 1, diff --git a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala index 1801469457..94dcacae44 100644 --- a/compiler/shared/test/scala/mlscript/compiler/TestIR.scala +++ b/compiler/shared/test/scala/mlscript/compiler/TestIR.scala @@ -13,7 +13,6 @@ class IRDiffTestCompiler extends DiffTests { val outputBuilder = StringBuilder() if (mode.useIR || mode.irVerbose) try - output("\n\nIR:") val fnUid = FreshInt() val classUid = FreshInt() val tag = FreshInt() @@ -22,10 +21,11 @@ class IRDiffTestCompiler extends DiffTests { val graph_ = gb.buildGraph(unit) if !mode.noTailRecOpt then + output("\nIR:") output(graph_.toString()) - val graph = if !mode.noTailRecOpt - then + val graph = + if !mode.noTailRecOpt then val tailRecOpt = new TailRecOpt(fnUid, classUid, tag, raise) val (g, comps) = tailRecOpt.run_debug(graph_) output("\nStrongly Connected Tail Calls:") From d0923c3715378d2df3fbff6f4d393fe83115567a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 18 Jun 2024 21:45:46 +0800 Subject: [PATCH 59/59] re-run tests to fix line numbers --- shared/src/test/diff/codegen/TrickyWiths.mls | 22 ++++++++--------- .../test/diff/ecoop23/PolymorphicVariants.mls | 16 ++++++------- .../diff/ecoop23/SimpleRegionDSL_annot.mls | 24 +++++++++---------- .../test/diff/ecoop23/SimpleRegionDSL_raw.mls | 22 ++++++++--------- .../diff/pretyper/ucs/patterns/Literals.mls | 20 ++++++++-------- .../pretyper/ucs/patterns/SimpleTuple.mls | 14 +++++------ 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/shared/src/test/diff/codegen/TrickyWiths.mls b/shared/src/test/diff/codegen/TrickyWiths.mls index 9f572112b5..28081a27e3 100644 --- a/shared/src/test/diff/codegen/TrickyWiths.mls +++ b/shared/src/test/diff/codegen/TrickyWiths.mls @@ -121,13 +121,13 @@ f { a = 12; b = 34 } :e f { a = "oops"; b = 34 } //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.229: f { a = "oops"; b = 34 } +//│ ║ l.122: f { a = "oops"; b = 34 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` -//│ ║ l.229: f { a = "oops"; b = 34 } +//│ ║ l.122: f { a = "oops"; b = 34 } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.202: def f x = x with { a = x.a + 1 } +//│ ║ l.113: def f x = x with { a = x.a + 1 } //│ ╙── ^^^ //│ res: error | {a: int, b: 34} //│ = { a: 'oops1', b: 34 } @@ -157,19 +157,19 @@ r = a2 with { x = 1 } :e r : (A2 \ x) & {x: 1} //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.303: r : (A2 \ x) & {x: 1} +//│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ║ ^ //│ ╟── application of type `A2` does not match type `{x: string}` -//│ ║ l.285: a2 = A2{x="a"} +//│ ║ l.147: a2 = A2{x="a"} //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: string}` -//│ ║ l.303: r : (A2 \ x) & {x: 1} +//│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ║ ^ //│ ╟── Note: constraint arises from record type: -//│ ║ l.271: class A2: { x: string } +//│ ║ l.144: class A2: { x: string } //│ ║ ^^^^^^^^^^^^^ //│ ╟── from intersection type: -//│ ║ l.303: r : (A2 \ x) & {x: 1} +//│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: A2\x & {x: 1} //│ = A2 { x: 1 } @@ -177,13 +177,13 @@ r : (A2 \ x) & {x: 1} :e // * Field removal in negative position (see above) r2 = r : (A2 \ y) & {x: 1} //│ ╔══[ERROR] Type mismatch in type ascription: -//│ ║ l.317: r2 = r : (A2 \ y) & {x: 1} +//│ ║ l.178: r2 = r : (A2 \ y) & {x: 1} //│ ║ ^ //│ ╟── integer literal of type `1` is not an instance of type `string` -//│ ║ l.286: r = a2 with { x = 1 } +//│ ║ l.148: r = a2 with { x = 1 } //│ ║ ^ //│ ╟── Note: constraint arises from type reference: -//│ ║ l.271: class A2: { x: string } +//│ ║ l.144: class A2: { x: string } //│ ╙── ^^^^^^ //│ r2: A2\y & {x: 1} //│ = A2 { x: 1 } diff --git a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls index 28c5cf2f3d..65427a13a4 100644 --- a/shared/src/test/diff/ecoop23/PolymorphicVariants.mls +++ b/shared/src/test/diff/ecoop23/PolymorphicVariants.mls @@ -171,16 +171,16 @@ Test2.eval(Cons(["a", Numb(1)], Nil), Var("a")) :e Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.367: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` -//│ ║ l.367: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.260: if v is +//│ ║ l.125: if v is //│ ║ ^ //│ ╟── from reference: -//│ ║ l.281: let vv = map_expr(eta, v) +//│ ║ l.135: let vv = map_expr(eta, v) //│ ╙── ^ //│ Abs[Var] | Numb | Var | error //│ res @@ -233,16 +233,16 @@ module Test3 extends EvalVar, EvalLambda, EvalExpr :e Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.486: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` -//│ ║ l.486: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) +//│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.260: if v is +//│ ║ l.125: if v is //│ ║ ^ //│ ╟── from reference: -//│ ║ l.281: let vv = map_expr(eta, v) +//│ ║ l.135: let vv = map_expr(eta, v) //│ ╙── ^ //│ Abs[Var] | error | 'a //│ where diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls index aa77b53590..a186ce40c1 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls @@ -180,16 +180,16 @@ mixin Text { :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.174: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) +//│ ║ l.172: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.172: Outside(a) then concat("outside a region of size ", toString(this.size(a))) +//│ ║ l.170: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.175: Translate then concat("a translated region of size ", toString(this.size(e))) +//│ ║ l.173: Translate then concat("a translated region of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.173: Union then concat("the union of two regions of size ", toString(this.size(e))) +//│ ║ l.171: Union then concat("the union of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str @@ -289,10 +289,10 @@ IsUnivIsEmpty.isEmpty(circles) class Foo() IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.292: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Foo` does not match type `BaseLang[RegionLang] | ExtLang[RegionLang]` -//│ ║ l.292: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) +//│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.88: type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] @@ -417,16 +417,16 @@ Lang.size(mk(100)) :re Lang.contains(mk(100), Vector(0, 0)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.420: Lang.contains(mk(100), Vector(0, 0)) +//│ ║ l.418: Lang.contains(mk(100), Vector(0, 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` -//│ ║ l.350: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: -//│ ║ l.136: type BaseRegionLang = BaseLang[BaseRegionLang] +//│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] //│ ╙── ^^^^^^^^^^^^^^ //│ error | false | true //│ res @@ -437,16 +437,16 @@ Lang.contains(mk(100), Vector(0, 0)) :re Lang.text(mk(100)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.440: Lang.text(mk(100)) +//│ ║ l.438: Lang.text(mk(100)) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` -//│ ║ l.350: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: -//│ ║ l.136: type BaseRegionLang = BaseLang[BaseRegionLang] +//│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] //│ ╙── ^^^^^^^^^^^^^^ //│ Str | error //│ res diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls index 4347276b34..8850b910c5 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls @@ -168,16 +168,16 @@ mixin Text { :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.322: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) +//│ ║ l.160: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.320: Outside(a) then concat("outside a region of size ", toString(this.size(a))) +//│ ║ l.158: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.323: Translate then concat("a translated region of size ", toString(this.size(e))) +//│ ║ l.161: Translate then concat("a translated region of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` -//│ ║ l.321: Union then concat("the union of two regions of size ", toString(this.size(e))) +//│ ║ l.159: Union then concat("the union of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str @@ -399,13 +399,13 @@ Lang.size(mk(100)) :re Lang.contains(mk(100), Vector(0, 0)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.834: Lang.contains(mk(100), Vector(0, 0)) +//│ ║ l.400: Lang.contains(mk(100), Vector(0, 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` -//│ ║ l.669: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.215: if a is +//│ ║ l.113: if a is //│ ║ ^ //│ ╟── from field selection: //│ ║ l.13: class Outside[out Region](a: Region) @@ -422,16 +422,16 @@ Lang.contains(mk(100), Vector(0, 0)) :re Lang.text(mk(100)) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.865: Lang.text(mk(100)) +//│ ║ l.423: Lang.text(mk(100)) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` -//│ ║ l.669: _ then Scale(Vector(0, 0), mk(n)) +//│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `Circle | Intersect[?Region4] | Outside[?Region5] | Translate[?Region6] | Union[?Region7]` -//│ ║ l.865: Lang.text(mk(100)) +//│ ║ l.423: Lang.text(mk(100)) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.318: if e is +//│ ║ l.156: if e is //│ ╙── ^ //│ Str | error //│ res diff --git a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls index 7c9289bca1..1b89a4b196 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/Literals.mls @@ -71,22 +71,22 @@ fun mix(x) = if x is :e [mix(false), mix(None)] //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.123: [mix(false), mix(None)] -//│ ║ ^^^^^^^^^^ +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` does not match type `0 | Some[?T] | true` -//│ ║ l.123: [mix(false), mix(None)] -//│ ║ ^^^^^ +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.91: fun mix(x) = if x is +//│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.123: [mix(false), mix(None)] -//│ ║ ^^^^^^^^^ +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^^^^^^ //│ ╟── reference of type `None` does not match type `0 | Some[?T] | true` -//│ ║ l.123: [mix(false), mix(None)] -//│ ║ ^^^^ +//│ ║ l.72: [mix(false), mix(None)] +//│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: -//│ ║ l.91: fun mix(x) = if x is +//│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ["Some" | "true" | "zero" | error, "Some" | "true" | "zero" | error] //│ res diff --git a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls index e06f333b7d..57d37d53cd 100644 --- a/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls +++ b/shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls @@ -112,10 +112,10 @@ fun not_working(x) = else 0 //│ ╔══[WARNING] this case is unreachable -//│ ║ l.122: 0 +//│ ║ l.113: 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch -//│ ║ l.120: a + b + c +//│ ║ l.111: a + b + c //│ ╙── ^^^^^^^^^ //│ fun not_working: {0: Int, 1: Int, 2: Int} -> Int @@ -127,18 +127,18 @@ not_working([1, 2, 3]) :e not_working([1, 2]) //│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.157: not_working([1, 2]) +//│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `{0: 1, 1: 2}` does not have field '2' -//│ ║ l.157: not_working([1, 2]) +//│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: -//│ ║ l.118: if x is +//│ ║ l.109: if x is //│ ║ ^^^^ -//│ ║ l.119: [a, b, c] then +//│ ║ l.110: [a, b, c] then //│ ║ ^^^^^^^^^^^^ //│ ╟── from reference: -//│ ║ l.118: if x is +//│ ║ l.109: if x is //│ ╙── ^ //│ Int | error //│ res