From d42cd710dbb05b0343cc800aa30b260e940e3673 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:21:24 +0800 Subject: [PATCH 001/303] wip --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 18 +- .../scala/hkmc2/codegen/Deforestation.scala | 295 ++++++++++++++++++ 2 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 60b605af02..762e52f99d 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -68,6 +68,14 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsb = new JSBuilder with JSBuilderArgNumSanityChecks(instrument = false) val le = low.program(blk) + + if showLoweredTree.isSet then + output(s"Lowered:") + output(le.showAsTree) + + val deforest = new Deforest + deforest(le) + val nestedScp = baseScp.nest val je = nestedScp.givenIn: jsb.program(le, N, wd) @@ -84,12 +92,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsb = new JSBuilder with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) val le = low.program(blk) - if showLoweredTree.isSet then - output(s"Lowered:") - output(le.showAsTree) - if ppLoweredTree.isSet then - output(s"Pretty Lowered:") - output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) + + // if ppLoweredTree.isSet then + // output(s"Pretty Lowered:") + // output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) // * Note that the codegen scope is not in sync with curCtx in terms of its `this` symbol. // * We do not nest TopLevelSymbol in codegen `Scope`s diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala new file mode 100644 index 0000000000..767f5f8c2f --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -0,0 +1,295 @@ +package hkmc2 +package codegen + +import semantics.* +import semantics.Elaborator.State +import syntax.{Literal, Tree} +import utils.{TL, tl} +import mlscript.utils.*, shorthands.* +import scala.collection.mutable + +type StratVar +type StratVarId = Uid[StratVar] + +enum ProdStrat: + case Ctor(ctor: ClassSymbol | ModuleSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) + case ProdFun(l: Ls[ConsStrat], r: ProdStrat) + case ProdVar(uid: StratVarId, name: Str = "") extends ProdStrat with StratVarTrait(uid, name) + case NoProd + + +enum ConsStrat: + case Dtor(scrut: Value.Ref) + case FieldSel(field: Tree.Ident, consVar: ConsVar, sym: Opt[TermSymbol]) + case ConsFun(l: Ls[ProdStrat], r: ConsStrat) + case ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) + case NoCons + +trait StratVarTrait(uid: StratVarId, name: Str): + this: ProdStrat.ProdVar | ConsStrat.ConsVar => + lazy val asProdStrat = ProdStrat.ProdVar(uid, name) + lazy val asConsStrat = ConsStrat.ConsVar(uid, name) + +class Deforest(using TL, Raise, Elaborator.State): + import ProdStrat.* + import ConsStrat.* + + object StratVarId extends Uid.Handler[StratVar] + + def apply(p: Program) = + processBlock(p.main) + constraints.foreach(println) + println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") + resolveConstraints + + println("upper:") + upperBounds.foreach(u => println("\t" + u)) + println("lower:") + lowerBounds.foreach(l => println("\t" + l)) + println("ctor -> dtor:") + ctorDests.foreach(l => println("\t" + l._1 + " ===> " + l._2)) + println("dtor -> ctor:") + dtorSources.foreach(l => println("\t" + l._1 + " ===> " + l._2)) + + + + val vuid = StratVarId.State() + + var constraints: Ls[ProdStrat -> ConsStrat] = Nil + + val symToStrat = mutable.Map.empty[Symbol, ProdStrat.ProdVar] + // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols + // TODO: ctor as a function? + def getStratOfSym(s: Symbol) = + // s.asMod.fold( + s match + case _: BuiltinSymbol => NoProd + case _: TopLevelSymbol => NoProd + case _: BlockMemberSymbol => symToStrat(s) + case _: LocalSymbol => symToStrat(s) + case _: FlowSymbol => symToStrat(s) + // )(m => Ctor(m, Map.empty)) + + // def getClsFields(s: ClassSymbol) = s.tree.paramLists.head.fields.map { + // case Tree.InfixApp(id: Tree.Ident, kw, rhs) => id + // case id: Tree.Ident => id + // } + def getClsFields(s: ClassSymbol) = s.tree.clsParams + + def freshVar(nme: String = ""): ProdVar -> ConsVar = + val newId = vuid.nextUid + ProdVar(newId, nme) -> ConsVar(newId, nme) + + def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c + + def processBlock(b: Block)(using inArm: Option[ProdStrat -> (ClassSymbol | ModuleSymbol)] = N): ProdStrat = b match + case Match(scrut, arms, dflt, rest) => + val scrutStrat = processResult(scrut) + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then + arms.foreach { case (Case.Cls(s, _), body) => + constrain(scrutStrat, Dtor(scrut)) + processBlock(body)(using S(scrutStrat -> s)) + } + else + arms.map{ case (_, armBody) => processBlock(armBody) } + // TODO: dflt? + dflt.map(processBlock) + processBlock(rest) + + case Return(res, implct) => processResult(res) + case Assign(lhs, rhs, rest) => + symToStrat.get(lhs) match + case None => + val lhsTpeVar = freshVar(lhs.nme) + constrain(processResult(rhs), lhsTpeVar._2) + symToStrat += lhs -> lhsTpeVar._1 + case Some(v) => + constrain(processResult(rhs), v.asConsStrat) + processBlock(rest) + case Begin(sub, rest) => + processBlock(sub) + processBlock(rest) + case Define(defn, rest) => + defn match + case FunDefn(sym, params, body) => NoProd // TODO: + case ValDefn(owner, k, sym, rhs) => NoProd // TODO: + case ClsLikeDefn(sym, k, parentSym, methods, privateFields, publicFields, preCtor, ctor) => NoProd + + case End(msg) => NoProd + case Throw(exc) => NoProd + case AssignField(lhs, nme, rhs, rest) => ??? + case Label(label, body, rest) => ??? + case Break(label) => ??? + case Continue(label) => ??? + case TryBlock(sub, finallyDo, rest) => ??? + + def constrFun(params: Ls[Param], body: Block) = + val paramSyms = params.map{ case Param(_, sym, _) => sym } + val paramStrats = paramSyms.map{ sym => freshVar(sym.name) } + symToStrat.addAll(paramSyms.zip(paramStrats.map(_._1))) + val res = freshVar() + constrain(processBlock(body), res._2) + ProdFun(paramStrats.map(_._2), res._1) + + def processResult(r: Result)(using inArm: Option[ProdStrat -> (ClassSymbol | ModuleSymbol)]): ProdStrat = r match + case c@Call(f, args) => + val argsTpe = args.map { case Arg(false, value) => + processResult(value) + } + f match + case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match + case Some(s) => + val clsFields = getClsFields(s) + Ctor(s, clsFields.zip(argsTpe).toMap, c) + case _ => + val pStrat = processResult(p) + val tpeVar = freshVar() + constrain(pStrat, FieldSel(nme, tpeVar._2, N)) + val appRes = freshVar() + constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) + appRes._1 + case Value.Ref(l) => + l.asCls match + case Some(s) => + val clsFields = getClsFields(s) + Ctor(s, clsFields.zip(argsTpe).toMap, c) + case _ => + val appRes = freshVar() + constrain(getStratOfSym(l), ConsFun(argsTpe, appRes._2)) + appRes._1 + case lam@Value.Lam(params, body) => + val funTpe = processResult(lam) + val appRes = freshVar() + constrain(funTpe, ConsFun(argsTpe, appRes._2)) + appRes._1 + + case Value.This(sym) => ??? + case Value.Lit(lit) => ??? + case Value.Arr(elems) => ??? + + case Instantiate(cls, args) => + // TODO: + freshVar()._1 + + case sel@Select(p, nme) => sel.symbol match + case Some(s) if s.asMod.isDefined => + Ctor(s.asMod.get, Map.empty, sel) + case _ => + val pStrat = processResult(p) + inArm match + case Some(armP -> clsSym) if armP === pStrat => + assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) + val tpeVar = freshVar() + constrain(pStrat, FieldSel(nme, tpeVar._2, sel.symbol.map(_.asInstanceOf[TermSymbol]))) + tpeVar._1 + case _ => + val tpeVar = freshVar() + constrain(pStrat, FieldSel(nme, tpeVar._2, N)) + tpeVar._1 + + case Value.Ref(l) => getStratOfSym(l) + case Value.This(sym) => ??? + case Value.Lit(lit) => NoProd + case Value.Lam(ParamList(_, params, N), body) => + constrFun(params, body) + case Value.Arr(elems) => ??? + + + val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) + val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) + + val ctorDests = mutable.Map.empty[Call | Select, Ls[Value.Ref]].withDefaultValue(Nil) + val dtorSources = mutable.Map.empty[Value.Ref, Ls[Call | Select]].withDefaultValue(Nil) + + def resolveConstraints: Unit = + + def handle(c: ProdStrat -> ConsStrat)(using cache: mutable.Set[ProdStrat -> ConsStrat]): Unit = + val prod = c._1 + val cons = c._2 + + if cache(c) then return () + + cache += c + + (prod, cons) match + case (Ctor(ctor, args, expr), Dtor(scrut)) => + ctorDests += expr -> (scrut :: ctorDests(expr)) + dtorSources += scrut -> (expr :: dtorSources(scrut)) + // TODO: keep track of this ctor to dtor + case (Ctor(ctor, args, _), FieldSel(field, consVar, clsSym)) => + if clsSym.isDefined then + args.get(clsSym.get).map(p => handle(p -> consVar)) + else + args.find(a => a._1.id == field).map(p => handle(p._2 -> consVar)) + case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? + case (_, ConsVar(uid, name)) => + lowerBounds += uid -> (prod :: lowerBounds(uid)) + upperBounds(uid).foreach(c => handle(prod -> c)) + case (ProdVar(uid, name), _) => + upperBounds += uid -> (cons :: upperBounds(uid)) + lowerBounds(uid).foreach(p => handle(p -> cons)) + case (Ctor(ctor, args, _), NoCons) => () + case (ProdFun(l, r), Dtor(cls)) => ??? + case (ProdFun(l, r), FieldSel(field, consVar, _)) => ??? + case (ProdFun(lp, rp), ConsFun(lc, rc)) => + lc.zip(lp).foreach(handle) + handle(rp, rc) + case (ProdFun(l, r), NoCons) => () + case (NoProd, Dtor(cls)) => () + case (NoProd, FieldSel(field, consVar, _)) => () + case (NoProd, ConsFun(l, r)) => () + case (NoProd, NoCons) => () + + constraints.foreach(c => handle(c)(using mutable.Set.empty)) + + + + def rewrite(p: Program) = ??? + + def rewriteBlock(b: Block): Block = b match + case Match(scrut, arms, dflt, rest) => + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then + ??? // TODO: dtor, check strat + else + Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) + case Return(res, implct) => Return(rewriteResult(res), implct) + case Assign(lhs, rhs, rest) => Assign(lhs, rewriteResult(rhs), rewriteBlock(rest)) + case Begin(sub, rest) => Begin(rewriteBlock(sub), rewriteBlock(rest)) + case d@Define(defn, rest) => + // TODO: + ??? + + + case End(msg) => End(msg) + case Throw(exc) => Throw(rewriteResult(exc)) + + case AssignField(lhs, nme, rhs, rest) => ??? + case Label(label, body, rest) => ??? + case Break(label) => Break(label) + case Continue(label) => ??? + case TryBlock(sub, finallyDo, rest) => ??? + + + def rewriteResult(r: Result): Result = r match + case c@Call(f, args) => + f match + case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match + case None => c + case Some(c) => ??? // TODO: ctor, check strat + case Value.Ref(l) => l.asCls match + case None => c + case Some(c) => ??? // TODO: ctor, check strat + case Value.Lam(params, body) => + Call(Value.Lam(params, rewriteBlock(body)), args)(c.isMlsFun) + case Instantiate(cls, args) => r + case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match + case None => s + case Some(value) => ??? // TODO: ctor, check strat + + case Value.Ref(l) => Value.Ref(l) + case Value.This(sym) => Value.This(sym) + case Value.Lit(lit) => Value.Lit(lit) + case Value.Lam(params, body) => Value.Lam(params, rewriteBlock(body)) + case Value.Arr(elems) => Value.Arr(elems) + + \ No newline at end of file From 374f2f20e872c3cada3f2666b202becd95b5a1ae Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:39:07 +0800 Subject: [PATCH 002/303] wip --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 139 ++++++++++++----- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../scala/hkmc2/codegen/Deforestation.scala | 140 +++++++++++++----- .../main/scala/hkmc2/codegen/Lowering.scala | 2 +- .../main/scala/hkmc2/semantics/Symbol.scala | 16 +- 5 files changed, 210 insertions(+), 89 deletions(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 762e52f99d..0ee7a7580c 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -69,46 +69,21 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: with JSBuilderArgNumSanityChecks(instrument = false) val le = low.program(blk) - if showLoweredTree.isSet then - output(s"Lowered:") - output(le.showAsTree) val deforest = new Deforest - deforest(le) + val deforestRes = deforest(le) + + if showLoweredTree.isSet then + output(s"Lowered:") + output(deforestRes.showAsTree) val nestedScp = baseScp.nest val je = nestedScp.givenIn: - jsb.program(le, N, wd) + jsb.program(deforestRes, N, wd) val jsStr = je.stripBreaks.mkString(100) output(s"JS (unsanitized):") output(jsStr) - if js.isSet && !showingJSYieldedCompileError then - val low = ltl.givenIn: - new codegen.Lowering - with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) - with codegen.LoweringTraceLog(traceJS.isSet) - with codegen.LoweringHandler(handler.isSet) - given Elaborator.Ctx = curCtx - val jsb = new JSBuilder - with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) - val le = low.program(blk) - - // if ppLoweredTree.isSet then - // output(s"Pretty Lowered:") - // output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) - // * Note that the codegen scope is not in sync with curCtx in terms of its `this` symbol. - // * We do not nest TopLevelSymbol in codegen `Scope`s - // * to avoid needlessly generating new variable names in separate blocks. - val nestedScp = baseScp.nest - // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed - - val je = nestedScp.givenIn: - jsb.program(le, N, wd) - val jsStr = je.stripBreaks.mkString(100) - if showSanitizedJS.isSet then - output(s"JS:") - output(jsStr) def mkQuery(prefix: Str, jsStr: Str) = import hkmc2.Message.MessageContext val queryStr = jsStr.replaceAll("\n", " ") @@ -148,17 +123,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: source = Diagnostic.Source.Runtime)) if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") - - if traceJS.isSet then - host.execute( - "globalThis.Predef.TraceLogger.enabled = true; " + - "globalThis.Predef.TraceLogger.resetIndent(0)") - mkQuery("", jsStr) - if traceJS.isSet then - host.execute("globalThis.Predef.TraceLogger.enabled = false") - import Elaborator.Ctx.* def definedValues = curCtx.env.iterator.flatMap: case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => @@ -175,5 +141,98 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsStr = je.stripBreaks.mkString(100) mkQuery(s"$nme ", jsStr) + // if js.isSet && !showingJSYieldedCompileError then + // val low = ltl.givenIn: + // new codegen.Lowering + // with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) + // with codegen.LoweringTraceLog(traceJS.isSet) + // with codegen.LoweringHandler(handler.isSet) + // given Elaborator.Ctx = curCtx + // val jsb = new JSBuilder + // with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) + // val le = low.program(blk) + + // // if ppLoweredTree.isSet then + // // output(s"Pretty Lowered:") + // // output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) + + // // * Note that the codegen scope is not in sync with curCtx in terms of its `this` symbol. + // // * We do not nest TopLevelSymbol in codegen `Scope`s + // // * to avoid needlessly generating new variable names in separate blocks. + // val nestedScp = baseScp.nest + // // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed + + // val je = nestedScp.givenIn: + // jsb.program(le, N, wd) + // val jsStr = je.stripBreaks.mkString(100) + // if showSanitizedJS.isSet then + // output(s"JS:") + // output(jsStr) + // def mkQuery(prefix: Str, jsStr: Str) = + // import hkmc2.Message.MessageContext + // val queryStr = jsStr.replaceAll("\n", " ") + // val (reply, stderr) = host.query(queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) + // reply match + // case ReplHost.Result(content, stdout) => + // if silent.isUnset then + // stdout match + // case None | Some("") => + // case Some(str) => + // str.splitSane('\n').foreach: line => + // output(s"> ${line}") + // content match + // case "undefined" => + // case "null" => + // case _ => + // expect.get match + // case S(expected) if content != expected => raise: + // ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, + // source = Diagnostic.Source.Runtime) + // case _ => output(s"$prefix= ${content}") + // case ReplHost.Empty => + // case ReplHost.Unexecuted(message) => ??? + // case ReplHost.Error(isSyntaxError, message, otherOutputs) => + // if otherOutputs.nonEmpty then + // otherOutputs.splitSane('\n').foreach: line => + // output(s"> ${line}") + + // if (isSyntaxError) then + // // If there is a syntax error in the generated code, + // // it should be a code generation error. + // raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, + // source = Diagnostic.Source.Compilation)) + // else + // // Otherwise, it is considered a simple runtime error. + // raise(ErrorReport(msg"${message}" -> N :: Nil, + // source = Diagnostic.Source.Runtime)) + // if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") + + + // if traceJS.isSet then + // host.execute( + // "globalThis.Predef.TraceLogger.enabled = true; " + + // "globalThis.Predef.TraceLogger.resetIndent(0)") + + // mkQuery("", jsStr) + + // if traceJS.isSet then + // host.execute("globalThis.Predef.TraceLogger.enabled = false") + + // import Elaborator.Ctx.* + // def definedValues = curCtx.env.iterator.flatMap: + // case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => + // e.symbol match + // case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts)) + // case S(ts: BlockMemberSymbol) + // if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts)) + // case _ => N + // case _ => N + // definedValues.toSeq.sortBy(_._1).foreach: (nme, sym) => + // val le = codegen.Return(codegen.Value.Ref(sym), implct = true) + // val je = nestedScp.givenIn: + // jsb.block(le) + // val jsStr = je.stripBreaks.mkString(100) + // mkQuery(s"$nme ", jsStr) + diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 1c7fc4fc77..209bb0cde1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -68,7 +68,7 @@ end Block sealed abstract class BlockTail extends Block case class Match( - scrut: Path, + scrut: Value.Ref, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 767f5f8c2f..e77309b1ac 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -19,7 +19,8 @@ enum ProdStrat: enum ConsStrat: - case Dtor(scrut: Value.Ref) + // case Destruct + case Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) case FieldSel(field: Tree.Ident, consVar: ConsVar, sym: Opt[TermSymbol]) case ConsFun(l: Ls[ProdStrat], r: ConsStrat) case ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) @@ -30,6 +31,32 @@ trait StratVarTrait(uid: StratVarId, name: Str): lazy val asProdStrat = ProdStrat.ProdVar(uid, name) lazy val asConsStrat = ConsStrat.ConsVar(uid, name) +extension (b: Block) + // TODO: similar to Block.mapTail? + def mapRes(f: Result => Block): Block = b match + case Return(res, implct) => f(res) + case Assign(lhs, rhs, rest: End) => Assign(lhs, rhs, f(Value.Ref(lhs))) + case Assign(lhs, rhs, rest) => Assign(lhs, rhs, rest.mapRes(f)) + case Define(defn, rest) => Define(defn, rest.mapRes(f)) + case Match(scrut, arms, dflt, rest) => ??? + case Throw(exc) => ??? + case Label(label, body, rest) => ??? + case Break(label) => ??? + case Continue(label) => ??? + case Begin(sub, rest) => ??? + case TryBlock(sub, finallyDo, rest) => ??? + case AssignField(_, _, _, _) => ??? + case HandleBlock(lhs, res, cls, handlers, body, rest) => ??? + case HandleBlockReturn(res) => ??? + case End(msg) => ??? + + def replaceAssignments(args: List[Path]): Block = args match + case head :: tail => b match + case Assign(lhs, rhs, rest) => Assign(lhs, head, rest.replaceAssignments(tail)) + case Nil => b + + + class Deforest(using TL, Raise, Elaborator.State): import ProdStrat.* import ConsStrat.* @@ -38,19 +65,21 @@ class Deforest(using TL, Raise, Elaborator.State): def apply(p: Program) = processBlock(p.main) - constraints.foreach(println) - println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // constraints.foreach(println) + // println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") resolveConstraints println("upper:") upperBounds.foreach(u => println("\t" + u)) println("lower:") lowerBounds.foreach(l => println("\t" + l)) - println("ctor -> dtor:") - ctorDests.foreach(l => println("\t" + l._1 + " ===> " + l._2)) - println("dtor -> ctor:") - dtorSources.foreach(l => println("\t" + l._1 + " ===> " + l._2)) + // println("ctor -> dtor:") + // ctorDests.foreach(l => println("\t" + l._1.toString() + " ===> " + l._2.size)) + // println("dtor -> ctor:") + // dtorSources.foreach(l => println("\t" + l._1.toString().take(20) + " ===> " + l._2.toString().take(20))) + + rewrite(p) val vuid = StratVarId.State() @@ -87,7 +116,7 @@ class Deforest(using TL, Raise, Elaborator.State): val scrutStrat = processResult(scrut) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.foreach { case (Case.Cls(s, _), body) => - constrain(scrutStrat, Dtor(scrut)) + constrain(scrutStrat, Dtor(scrut, arms)) processBlock(body)(using S(scrutStrat -> s)) } else @@ -111,7 +140,10 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(rest) case Define(defn, rest) => defn match - case FunDefn(sym, params, body) => NoProd // TODO: + case FunDefn(sym, params, body) => + val param = params.head match + case ParamList(flags, params, restParam) => params + constrFun(param, body) // TODO: handle mutiple param list case ValDefn(owner, k, sym, rhs) => NoProd // TODO: case ClsLikeDefn(sym, k, parentSym, methods, privateFields, publicFields, preCtor, ctor) => NoProd @@ -198,7 +230,7 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[Call | Select, Ls[Value.Ref]].withDefaultValue(Nil) + val ctorDests = mutable.Map.empty[Call | Select, Map[Value.Ref, Ls[Case -> Block]]].withDefaultValue(Map.empty) val dtorSources = mutable.Map.empty[Value.Ref, Ls[Call | Select]].withDefaultValue(Nil) def resolveConstraints: Unit = @@ -212,8 +244,11 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (Ctor(ctor, args, expr), Dtor(scrut)) => - ctorDests += expr -> (scrut :: ctorDests(expr)) + case (Ctor(ctor, args, expr), Dtor(scrut, arms)) => + ctorDests += expr -> (ctorDests(expr).updatedWith(scrut){ + case None => Some(arms) + case Some(v) => Some(arms ::: v) + }) dtorSources += scrut -> (expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor case (Ctor(ctor, args, _), FieldSel(field, consVar, clsSym)) => @@ -229,13 +264,13 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds += uid -> (cons :: upperBounds(uid)) lowerBounds(uid).foreach(p => handle(p -> cons)) case (Ctor(ctor, args, _), NoCons) => () - case (ProdFun(l, r), Dtor(cls)) => ??? + case (ProdFun(l, r), Dtor(cls, _)) => ??? case (ProdFun(l, r), FieldSel(field, consVar, _)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () - case (NoProd, Dtor(cls)) => () + case (NoProd, Dtor(cls, _)) => () case (NoProd, FieldSel(field, consVar, _)) => () case (NoProd, ConsFun(l, r)) => () case (NoProd, NoCons) => () @@ -244,24 +279,33 @@ class Deforest(using TL, Raise, Elaborator.State): - def rewrite(p: Program) = ??? + def rewrite(p: Program) = + Program( + p.imports, + rewriteBlock(p.main) + ) def rewriteBlock(b: Block): Block = b match - case Match(scrut, arms, dflt, rest) => + case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then - ??? // TODO: dtor, check strat + println(dtorSources.contains(scrut)) + // TODO: + rest match + case End(msg) => Return(scrut, true) + case _ => rest else Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) - case Return(res, implct) => Return(rewriteResult(res), implct) - case Assign(lhs, rhs, rest) => Assign(lhs, rewriteResult(rhs), rewriteBlock(rest)) + case Return(res, implct) => + rewriteResult(res)(r => Return(r, implct)) + case Assign(lhs, rhs, rest) => + rewriteResult(rhs)(r => Assign(lhs, r, rewriteBlock(rest))) case Begin(sub, rest) => Begin(rewriteBlock(sub), rewriteBlock(rest)) case d@Define(defn, rest) => - // TODO: - ??? - - + defn match + case FunDefn(sym, params, body) => Define(FunDefn(sym, params, rewriteBlock(body)), rewriteBlock(rest)) + case _ => d case End(msg) => End(msg) - case Throw(exc) => Throw(rewriteResult(exc)) + case Throw(exc) => rewriteResult(exc)(Throw.apply) case AssignField(lhs, nme, rhs, rest) => ??? case Label(label, body, rest) => ??? @@ -270,26 +314,44 @@ class Deforest(using TL, Raise, Elaborator.State): case TryBlock(sub, finallyDo, rest) => ??? - def rewriteResult(r: Result): Result = r match - case c@Call(f, args) => + def rewriteResult(r: Result)(k: Result => Block): Block = r match + case call@Call(f, args) => f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case None => c - case Some(c) => ??? // TODO: ctor, check strat + case None => k(call) + case Some(c) => + ctorDests(call).headOption match + case None => k(call) + case Some(dest) => + val body = dest._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + println(call.toString() + " ----> " + body) + body.replaceAssignments(args.map(a => a.value)).mapRes(k) case Value.Ref(l) => l.asCls match - case None => c - case Some(c) => ??? // TODO: ctor, check strat + case None => k(call) + case Some(c) => + val body = ctorDests(call).head._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + println(call.toString() + " ----> " + body) + body.replaceAssignments(args.map(a => a.value)).mapRes(k) case Value.Lam(params, body) => - Call(Value.Lam(params, rewriteBlock(body)), args)(c.isMlsFun) - case Instantiate(cls, args) => r + k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) + case Instantiate(cls, args) => k(r) case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match - case None => s - case Some(value) => ??? // TODO: ctor, check strat + case None => k(s) + case Some(mod) => + ctorDests.get(s) match + case None => + k(s) + case Some(dests) => + val body = dests.head._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get._2 + println(mod.toString + " ----> " + body) + + body.mapRes(k) + - case Value.Ref(l) => Value.Ref(l) - case Value.This(sym) => Value.This(sym) - case Value.Lit(lit) => Value.Lit(lit) - case Value.Lam(params, body) => Value.Lam(params, rewriteBlock(body)) - case Value.Arr(elems) => Value.Arr(elems) + case Value.Ref(l) => k(Value.Ref(l)) + case Value.This(sym) => k(Value.This(sym)) + case Value.Lit(lit) => k(Value.Lit(lit)) + case Value.Lam(params, body) => k(Value.Lam(params, rewriteBlock(body))) + case Value.Arr(elems) => k(Value.Arr(elems)) \ No newline at end of file diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 1611cb106e..c63574545b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -241,7 +241,7 @@ class Lowering(using TL, Raise, Elaborator.State): subTerm(scrut): sr => tl.log(s"Binding scrut $scrut to $sr ${summon[Subst].map}") // val cse = - def mkMatch(cse: Case -> Block) = Match(sr, cse :: Nil, + def mkMatch(cse: Case -> Block) = Match((sr.asInstanceOf[Value.Ref]), cse :: Nil, S(go(restSplit, topLevel = true)), End() ) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index 5e8cb10a91..c0aae25637 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -63,7 +63,7 @@ class FlowSymbol(label: Str)(using State) extends Symbol: val outFlows2: mutable.Buffer[Consumer] = mutable.Buffer.empty val inFlows: mutable.Buffer[ConcreteProd] = mutable.Buffer.empty override def toString: Str = - label + State.dbgUid(uid) + label + State.dbgUid(uid) + "---(flowSym)" sealed trait LocalSymbol extends Symbol @@ -77,7 +77,7 @@ abstract class BlockLocalSymbol(name: Str)(using State) extends FlowSymbol(name) class TempSymbol(val trm: Opt[Term], dbgNme: Str = "tmp")(using State) extends BlockLocalSymbol(dbgNme): val nameHints: MutSet[Str] = MutSet.empty override def toLoc: Option[Loc] = trm.flatMap(_.toLoc) - override def toString: Str = s"$$${super.toString}" + override def toString: Str = s"$$${super.toString}---(tmpSym)" // * When instantiating forall-qualified TVs, we need to duplicate the information @@ -86,7 +86,7 @@ class TempSymbol(val trm: Opt[Term], dbgNme: Str = "tmp")(using State) extends B class InstSymbol(val origin: Symbol)(using State) extends LocalSymbol: override def nme: Str = origin.nme override def toLoc: Option[Loc] = origin.toLoc - override def toString: Str = origin.toString + override def toString: Str = origin.toString + "---(instSym)" class VarSymbol(val id: Ident)(using State) extends BlockLocalSymbol(id.name) with NamedSymbol: @@ -97,7 +97,7 @@ class BuiltinSymbol (val nme: Str, val binary: Bool, val unary: Bool, val nullary: Bool, val functionLike: Bool)(using State) extends Symbol: def toLoc: Option[Loc] = N - override def toString: Str = s"builtin:$nme${State.dbgUid(uid)}" + override def toString: Str = s"builtin:$nme${State.dbgUid(uid)}---(builtin)" /** This is the outside-facing symbol associated to a possibly-overloaded @@ -124,7 +124,7 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree])(using State) modTree.isDefined || trmTree.isDefined || clsTree.exists(_.paramLists.nonEmpty) override def toString: Str = - s"member:$nme${State.dbgUid(uid)}" + s"member:$nme${State.dbgUid(uid)}---(blkMem)" override val isGetter: Bool = // TODO: this should be checked based on a special syntax for getter trmImplTree.exists(t => t.k === Fun && t.paramLists.isEmpty) @@ -143,7 +143,7 @@ class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.I def nme: Str = id.name def name: Str = nme def toLoc: Option[Loc] = id.toLoc - override def toString: Str = s"${owner.getOrElse("")}.${id.name}" + override def toString: Str = s"${owner.getOrElse("")}.${id.name}---(termSym)" sealed trait CtorSymbol extends Symbol @@ -190,7 +190,7 @@ class ModuleSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) class TypeAliasSymbol(val id: Tree.Ident)(using State) extends MemberSymbol[TypeDef]: def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of type alias here - override def toString: Str = s"module:${id.name}${State.dbgUid(uid)}" + override def toString: Str = s"typeAlias:${id.name}${State.dbgUid(uid)}" class PatternSymbol(val id: Tree.Ident)(using State) extends MemberSymbol[PatternDef] with CtorSymbol with InnerSymbol: @@ -202,6 +202,6 @@ class TopLevelSymbol(blockNme: Str)(using State) extends MemberSymbol[ModuleDef] with InnerSymbol: def nme = blockNme def toLoc: Option[Loc] = N - override def toString: Str = s"globalThis:$blockNme${State.dbgUid(uid)}" + override def toString: Str = s"globalThis:$blockNme${State.dbgUid(uid)}---(topLvl)" From 738df06eed3f845d0fadd8dbb0e4553e024fbe8c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:01:24 +0800 Subject: [PATCH 003/303] wip --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 158 +++++++----------- 1 file changed, 56 insertions(+), 102 deletions(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 0ee7a7580c..e1bcf3c355 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -26,6 +26,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val noSanityCheck = NullaryCommand("noSanityCheck") val traceJS = NullaryCommand("traceJS") val handler = NullaryCommand("handler") + val deforestFlag = NullaryCommand("deforest") val expect = Command("expect"): ln => ln.trim @@ -69,21 +70,59 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: with JSBuilderArgNumSanityChecks(instrument = false) val le = low.program(blk) - - val deforest = new Deforest - val deforestRes = deforest(le) - if showLoweredTree.isSet then output(s"Lowered:") - output(deforestRes.showAsTree) - + output(le.showAsTree) + val nestedScp = baseScp.nest val je = nestedScp.givenIn: - jsb.program(deforestRes, N, wd) + jsb.program(le, N, wd) val jsStr = je.stripBreaks.mkString(100) output(s"JS (unsanitized):") output(jsStr) + + if deforestFlag.isSet then + val deforest = new Deforest + output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") + val deforestRes = deforest(le) + if showLoweredTree.isSet then + output(deforestRes.showAsTree) + val nestedScp = baseScp.nest + val je = nestedScp.givenIn: + jsb.program(deforestRes, N, wd) + output("==== JS (deforested): ====") + output(je.stripBreaks.mkString(100)) + output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") + if js.isSet && !showingJSYieldedCompileError then + val low = ltl.givenIn: + new codegen.Lowering + with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) + with codegen.LoweringTraceLog(traceJS.isSet) + with codegen.LoweringHandler(handler.isSet) + given Elaborator.Ctx = curCtx + val jsb = new JSBuilder + with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) + val le = low.program(blk) + if showLoweredTree.isSet then + output(s"Lowered:") + output(le.showAsTree) + if ppLoweredTree.isSet then + output(s"Pretty Lowered:") + output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) + + // * Note that the codegen scope is not in sync with curCtx in terms of its `this` symbol. + // * We do not nest TopLevelSymbol in codegen `Scope`s + // * to avoid needlessly generating new variable names in separate blocks. + val nestedScp = baseScp.nest + // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed + + val je = nestedScp.givenIn: + jsb.program(le, N, wd) + val jsStr = je.stripBreaks.mkString(100) + if showSanitizedJS.isSet then + output(s"JS:") + output(jsStr) def mkQuery(prefix: Str, jsStr: Str) = import hkmc2.Message.MessageContext val queryStr = jsStr.replaceAll("\n", " ") @@ -123,8 +162,17 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: source = Diagnostic.Source.Runtime)) if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") + + if traceJS.isSet then + host.execute( + "globalThis.Predef.TraceLogger.enabled = true; " + + "globalThis.Predef.TraceLogger.resetIndent(0)") + mkQuery("", jsStr) + if traceJS.isSet then + host.execute("globalThis.Predef.TraceLogger.enabled = false") + import Elaborator.Ctx.* def definedValues = curCtx.env.iterator.flatMap: case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => @@ -141,98 +189,4 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsStr = je.stripBreaks.mkString(100) mkQuery(s"$nme ", jsStr) - // if js.isSet && !showingJSYieldedCompileError then - // val low = ltl.givenIn: - // new codegen.Lowering - // with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) - // with codegen.LoweringTraceLog(traceJS.isSet) - // with codegen.LoweringHandler(handler.isSet) - // given Elaborator.Ctx = curCtx - // val jsb = new JSBuilder - // with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) - // val le = low.program(blk) - - // // if ppLoweredTree.isSet then - // // output(s"Pretty Lowered:") - // // output(Printer.mkDocument(le)(using summon[Raise], baseScp.nest).toString) - - // // * Note that the codegen scope is not in sync with curCtx in terms of its `this` symbol. - // // * We do not nest TopLevelSymbol in codegen `Scope`s - // // * to avoid needlessly generating new variable names in separate blocks. - // val nestedScp = baseScp.nest - // // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed - - // val je = nestedScp.givenIn: - // jsb.program(le, N, wd) - // val jsStr = je.stripBreaks.mkString(100) - // if showSanitizedJS.isSet then - // output(s"JS:") - // output(jsStr) - // def mkQuery(prefix: Str, jsStr: Str) = - // import hkmc2.Message.MessageContext - // val queryStr = jsStr.replaceAll("\n", " ") - // val (reply, stderr) = host.query(queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) - // reply match - // case ReplHost.Result(content, stdout) => - // if silent.isUnset then - // stdout match - // case None | Some("") => - // case Some(str) => - // str.splitSane('\n').foreach: line => - // output(s"> ${line}") - // content match - // case "undefined" => - // case "null" => - // case _ => - // expect.get match - // case S(expected) if content != expected => raise: - // ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, - // source = Diagnostic.Source.Runtime) - // case _ => output(s"$prefix= ${content}") - // case ReplHost.Empty => - // case ReplHost.Unexecuted(message) => ??? - // case ReplHost.Error(isSyntaxError, message, otherOutputs) => - // if otherOutputs.nonEmpty then - // otherOutputs.splitSane('\n').foreach: line => - // output(s"> ${line}") - - // if (isSyntaxError) then - // // If there is a syntax error in the generated code, - // // it should be a code generation error. - // raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, - // source = Diagnostic.Source.Compilation)) - // else - // // Otherwise, it is considered a simple runtime error. - // raise(ErrorReport(msg"${message}" -> N :: Nil, - // source = Diagnostic.Source.Runtime)) - // if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") - - - // if traceJS.isSet then - // host.execute( - // "globalThis.Predef.TraceLogger.enabled = true; " + - // "globalThis.Predef.TraceLogger.resetIndent(0)") - - // mkQuery("", jsStr) - - // if traceJS.isSet then - // host.execute("globalThis.Predef.TraceLogger.enabled = false") - - // import Elaborator.Ctx.* - // def definedValues = curCtx.env.iterator.flatMap: - // case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => - // e.symbol match - // case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts)) - // case S(ts: BlockMemberSymbol) - // if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts)) - // case _ => N - // case _ => N - // definedValues.toSeq.sortBy(_._1).foreach: (nme, sym) => - // val le = codegen.Return(codegen.Value.Ref(sym), implct = true) - // val je = nestedScp.givenIn: - // jsb.block(le) - // val jsStr = je.stripBreaks.mkString(100) - // mkQuery(s"$nme ", jsStr) - - - + \ No newline at end of file From 80be404976ca26d8eb14b30572c1efe581e6c23f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:11:05 +0800 Subject: [PATCH 004/303] wip --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 89 +++++++++---------- .../main/scala/hkmc2/semantics/Symbol.scala | 18 ++-- 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index e1bcf3c355..97b4d5d503 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -51,6 +51,46 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: override def run(): Unit = try super.run() finally if hostCreated then host.terminate() + + def mkQuery(prefix: Str, jsStr: Str)(using Raise) = + import hkmc2.Message.MessageContext + val queryStr = jsStr.replaceAll("\n", " ") + val (reply, stderr) = host.query(queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) + reply match + case ReplHost.Result(content, stdout) => + if silent.isUnset then + stdout match + case None | Some("") => + case Some(str) => + str.splitSane('\n').foreach: line => + output(s"> ${line}") + content match + case "undefined" => + case "null" => + case _ => + expect.get match + case S(expected) if content != expected => raise: + ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, + source = Diagnostic.Source.Runtime) + case _ => output(s"$prefix= ${content}") + case ReplHost.Empty => + case ReplHost.Unexecuted(message) => ??? + case ReplHost.Error(isSyntaxError, message, otherOutputs) => + if otherOutputs.nonEmpty then + otherOutputs.splitSane('\n').foreach: line => + output(s"> ${line}") + + if (isSyntaxError) then + // If there is a syntax error in the generated code, + // it should be a code generation error. + raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, + source = Diagnostic.Source.Compilation)) + else + // Otherwise, it is considered a simple runtime error. + raise(ErrorReport(msg"${message}" -> N :: Nil, + source = Diagnostic.Source.Runtime)) + if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") + override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Raise): Unit = super.processTerm(blk, inImport) val outerRaise: Raise = summon @@ -69,11 +109,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsb = new JSBuilder with JSBuilderArgNumSanityChecks(instrument = false) val le = low.program(blk) - - if showLoweredTree.isSet then - output(s"Lowered:") - output(le.showAsTree) - val nestedScp = baseScp.nest val je = nestedScp.givenIn: jsb.program(le, N, wd) @@ -91,7 +126,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val je = nestedScp.givenIn: jsb.program(deforestRes, N, wd) output("==== JS (deforested): ====") - output(je.stripBreaks.mkString(100)) + + val jsStr = je.stripBreaks.mkString(100) + output(jsStr) + mkQuery("", jsStr) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet && !showingJSYieldedCompileError then @@ -123,45 +161,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if showSanitizedJS.isSet then output(s"JS:") output(jsStr) - def mkQuery(prefix: Str, jsStr: Str) = - import hkmc2.Message.MessageContext - val queryStr = jsStr.replaceAll("\n", " ") - val (reply, stderr) = host.query(queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) - reply match - case ReplHost.Result(content, stdout) => - if silent.isUnset then - stdout match - case None | Some("") => - case Some(str) => - str.splitSane('\n').foreach: line => - output(s"> ${line}") - content match - case "undefined" => - case "null" => - case _ => - expect.get match - case S(expected) if content != expected => raise: - ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, - source = Diagnostic.Source.Runtime) - case _ => output(s"$prefix= ${content}") - case ReplHost.Empty => - case ReplHost.Unexecuted(message) => ??? - case ReplHost.Error(isSyntaxError, message, otherOutputs) => - if otherOutputs.nonEmpty then - otherOutputs.splitSane('\n').foreach: line => - output(s"> ${line}") - - if (isSyntaxError) then - // If there is a syntax error in the generated code, - // it should be a code generation error. - raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, - source = Diagnostic.Source.Compilation)) - else - // Otherwise, it is considered a simple runtime error. - raise(ErrorReport(msg"${message}" -> N :: Nil, - source = Diagnostic.Source.Runtime)) - if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") - if traceJS.isSet then host.execute( diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index c0aae25637..3a90f72c93 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -63,7 +63,7 @@ class FlowSymbol(label: Str)(using State) extends Symbol: val outFlows2: mutable.Buffer[Consumer] = mutable.Buffer.empty val inFlows: mutable.Buffer[ConcreteProd] = mutable.Buffer.empty override def toString: Str = - label + State.dbgUid(uid) + "---(flowSym)" + label + State.dbgUid(uid) sealed trait LocalSymbol extends Symbol @@ -77,7 +77,7 @@ abstract class BlockLocalSymbol(name: Str)(using State) extends FlowSymbol(name) class TempSymbol(val trm: Opt[Term], dbgNme: Str = "tmp")(using State) extends BlockLocalSymbol(dbgNme): val nameHints: MutSet[Str] = MutSet.empty override def toLoc: Option[Loc] = trm.flatMap(_.toLoc) - override def toString: Str = s"$$${super.toString}---(tmpSym)" + override def toString: Str = s"$$${super.toString}" // * When instantiating forall-qualified TVs, we need to duplicate the information @@ -86,7 +86,7 @@ class TempSymbol(val trm: Opt[Term], dbgNme: Str = "tmp")(using State) extends B class InstSymbol(val origin: Symbol)(using State) extends LocalSymbol: override def nme: Str = origin.nme override def toLoc: Option[Loc] = origin.toLoc - override def toString: Str = origin.toString + "---(instSym)" + override def toString: Str = origin.toString class VarSymbol(val id: Ident)(using State) extends BlockLocalSymbol(id.name) with NamedSymbol: @@ -97,7 +97,7 @@ class BuiltinSymbol (val nme: Str, val binary: Bool, val unary: Bool, val nullary: Bool, val functionLike: Bool)(using State) extends Symbol: def toLoc: Option[Loc] = N - override def toString: Str = s"builtin:$nme${State.dbgUid(uid)}---(builtin)" + override def toString: Str = s"builtin:$nme${State.dbgUid(uid)}" /** This is the outside-facing symbol associated to a possibly-overloaded @@ -124,7 +124,7 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree])(using State) modTree.isDefined || trmTree.isDefined || clsTree.exists(_.paramLists.nonEmpty) override def toString: Str = - s"member:$nme${State.dbgUid(uid)}---(blkMem)" + s"member:$nme${State.dbgUid(uid)}" override val isGetter: Bool = // TODO: this should be checked based on a special syntax for getter trmImplTree.exists(t => t.k === Fun && t.paramLists.isEmpty) @@ -143,7 +143,7 @@ class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.I def nme: Str = id.name def name: Str = nme def toLoc: Option[Loc] = id.toLoc - override def toString: Str = s"${owner.getOrElse("")}.${id.name}---(termSym)" + override def toString: Str = s"${owner.getOrElse("")}.${id.name}" sealed trait CtorSymbol extends Symbol @@ -190,7 +190,7 @@ class ModuleSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) class TypeAliasSymbol(val id: Tree.Ident)(using State) extends MemberSymbol[TypeDef]: def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of type alias here - override def toString: Str = s"typeAlias:${id.name}${State.dbgUid(uid)}" + override def toString: Str = s"module:${id.name}${State.dbgUid(uid)}" class PatternSymbol(val id: Tree.Ident)(using State) extends MemberSymbol[PatternDef] with CtorSymbol with InnerSymbol: @@ -202,6 +202,4 @@ class TopLevelSymbol(blockNme: Str)(using State) extends MemberSymbol[ModuleDef] with InnerSymbol: def nme = blockNme def toLoc: Option[Loc] = N - override def toString: Str = s"globalThis:$blockNme${State.dbgUid(uid)}---(topLvl)" - - + override def toString: Str = s"globalThis:$blockNme${State.dbgUid(uid)}" \ No newline at end of file From 7d72fa562f71966032d37dc37bbe635793a3e363 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:01:25 +0800 Subject: [PATCH 005/303] wip --- hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 97b4d5d503..2c4fa8e487 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -119,8 +119,14 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if deforestFlag.isSet then val deforest = new Deforest output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") + if showLoweredTree.isSet then + output("==== Non-inserted lowered tree ====") + output(le.showAsTree) + val deforestRes = deforest(le) + if showLoweredTree.isSet then + output("\n==== deforested tree ====") output(deforestRes.showAsTree) val nestedScp = baseScp.nest val je = nestedScp.givenIn: From 7f34cb218278a2070205c55a21aec0f036a1a00d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 11 Jan 2025 22:24:35 +0800 Subject: [PATCH 006/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 103 ++++++++++++------ 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e77309b1ac..5d9ed3ce5b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -10,9 +10,10 @@ import scala.collection.mutable type StratVar type StratVarId = Uid[StratVar] +type ClsOrModSymbol = ClassSymbol | ModuleSymbol enum ProdStrat: - case Ctor(ctor: ClassSymbol | ModuleSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) + case Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) case ProdFun(l: Ls[ConsStrat], r: ProdStrat) case ProdVar(uid: StratVarId, name: Str = "") extends ProdStrat with StratVarTrait(uid, name) case NoProd @@ -28,6 +29,9 @@ enum ConsStrat: trait StratVarTrait(uid: StratVarId, name: Str): this: ProdStrat.ProdVar | ConsStrat.ConsVar => + + val filter = mutable.Map.empty[ProdStrat.ProdVar, Ls[ClsOrModSymbol]].withDefaultValue(Nil) + lazy val asProdStrat = ProdStrat.ProdVar(uid, name) lazy val asConsStrat = ConsStrat.ConsVar(uid, name) @@ -35,7 +39,7 @@ extension (b: Block) // TODO: similar to Block.mapTail? def mapRes(f: Result => Block): Block = b match case Return(res, implct) => f(res) - case Assign(lhs, rhs, rest: End) => Assign(lhs, rhs, f(Value.Ref(lhs))) + case Assign(lhs, rhs, rest: End) => f(rhs) case Assign(lhs, rhs, rest) => Assign(lhs, rhs, rest.mapRes(f)) case Define(defn, rest) => Define(defn, rest.mapRes(f)) case Match(scrut, arms, dflt, rest) => ??? @@ -90,14 +94,12 @@ class Deforest(using TL, Raise, Elaborator.State): // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols // TODO: ctor as a function? def getStratOfSym(s: Symbol) = - // s.asMod.fold( - s match - case _: BuiltinSymbol => NoProd - case _: TopLevelSymbol => NoProd - case _: BlockMemberSymbol => symToStrat(s) - case _: LocalSymbol => symToStrat(s) - case _: FlowSymbol => symToStrat(s) - // )(m => Ctor(m, Map.empty)) + s match + case _: BuiltinSymbol => NoProd + case _: TopLevelSymbol => NoProd + case _: BlockMemberSymbol => symToStrat(s) // For `fun` and `let` only, not classes or modules? + case _: LocalSymbol => symToStrat(s) + case _: FlowSymbol => symToStrat(s) // def getClsFields(s: ClassSymbol) = s.tree.paramLists.head.fields.map { // case Tree.InfixApp(id: Tree.Ident, kw, rhs) => id @@ -105,25 +107,44 @@ class Deforest(using TL, Raise, Elaborator.State): // } def getClsFields(s: ClassSymbol) = s.tree.clsParams - def freshVar(nme: String = ""): ProdVar -> ConsVar = + def freshVar(nme: String = "")(using filter: Opt[ProdVar -> ClsOrModSymbol] = N): ProdVar -> ConsVar = val newId = vuid.nextUid - ProdVar(newId, nme) -> ConsVar(newId, nme) + val p: ProdVar = ProdVar(newId, nme) + val c: ConsVar = ConsVar(newId, nme) + filter.foreach{ case v -> cls => + p.filter.updateWith(v){ + case None => Some(cls :: Nil) + case Some(value) => Some(cls :: value) + } + c.filter.updateWith(v){ + case None => Some(cls :: Nil) + case Some(value) => Some(cls :: value) + } + } + p -> c def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c - def processBlock(b: Block)(using inArm: Option[ProdStrat -> (ClassSymbol | ModuleSymbol)] = N): ProdStrat = b match + def processBlock(b: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N): ProdStrat = b match case Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then - arms.foreach { case (Case.Cls(s, _), body) => + val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then + arms.map { case (Case.Cls(s, _), body) => constrain(scrutStrat, Dtor(scrut, arms)) - processBlock(body)(using S(scrutStrat -> s)) + // TODO: fix this "asInstanceOf"? + processBlock(body)(using S(scrutStrat.asInstanceOf[ProdStrat.ProdVar] -> s)) } else arms.map{ case (_, armBody) => processBlock(armBody) } // TODO: dflt? - dflt.map(processBlock) - processBlock(rest) + val dfltRes = dflt.map(processBlock) + rest match + case End(msg) => + val matchRes = freshVar() + armsRes.appendedAll(dfltRes).foreach: r => + constrain(r, matchRes._2) + matchRes._1 + case _ => processBlock(rest) case Return(res, implct) => processResult(res) case Assign(lhs, rhs, rest) => @@ -143,10 +164,14 @@ class Deforest(using TL, Raise, Elaborator.State): case FunDefn(sym, params, body) => val param = params.head match case ParamList(flags, params, restParam) => params - constrFun(param, body) // TODO: handle mutiple param list + val funStrat = constrFun(param, body) // TODO: handle mutiple param list + val funSymStratVar = freshVar(sym.nme) + symToStrat += sym -> funSymStratVar._1 + constrain(funStrat, funSymStratVar._2) + funSymStratVar._1 case ValDefn(owner, k, sym, rhs) => NoProd // TODO: case ClsLikeDefn(sym, k, parentSym, methods, privateFields, publicFields, preCtor, ctor) => NoProd - + processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd case AssignField(lhs, nme, rhs, rest) => ??? @@ -155,7 +180,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Continue(label) => ??? case TryBlock(sub, finallyDo, rest) => ??? - def constrFun(params: Ls[Param], body: Block) = + def constrFun(params: Ls[Param], body: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => freshVar(sym.name) } symToStrat.addAll(paramSyms.zip(paramStrats.map(_._1))) @@ -163,30 +188,36 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(processBlock(body), res._2) ProdFun(paramStrats.map(_._2), res._1) - def processResult(r: Result)(using inArm: Option[ProdStrat -> (ClassSymbol | ModuleSymbol)]): ProdStrat = r match + def processResult(r: Result)(using inArm: Option[ProdVar -> ClsOrModSymbol]): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => processResult(value) } f match - case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case Some(s) => - val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c) - case _ => - val pStrat = processResult(p) - val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, N)) - val appRes = freshVar() - constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) - appRes._1 + case s@Select(p, nme) => + s.symbol.map(_.asCls) match + case None => + val pStrat = processResult(p) + val tpeVar = freshVar() + constrain(pStrat, FieldSel(nme, tpeVar._2, N)) + val appRes = freshVar() + constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) + appRes._1 + case Some(None) => + val funSym = s.symbol.get + val appRes = freshVar("call_" + funSym.nme + "_res") + constrain(getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) + appRes._1 + case Some(Some(s)) => + val clsFields = getClsFields(s) + Ctor(s, clsFields.zip(argsTpe).toMap, c) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) Ctor(s, clsFields.zip(argsTpe).toMap, c) - case _ => - val appRes = freshVar() + case _ => // then it is a function + val appRes = freshVar("call_" + l.nme + "_res") constrain(getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => @@ -291,7 +322,7 @@ class Deforest(using TL, Raise, Elaborator.State): println(dtorSources.contains(scrut)) // TODO: rest match - case End(msg) => Return(scrut, true) + case End(msg) => Return(scrut, false) // TODO: true or false? case _ => rest else Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) From 837dfc3d899a8aec8dcd8c51cc09845085de00ee Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 12 Jan 2025 01:30:15 +0800 Subject: [PATCH 007/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 7 +- .../src/test/mlscript/deforest/simple.mls | 179 ++++++++++++++++++ 2 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/simple.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5d9ed3ce5b..8dd31eb0fe 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -162,11 +162,11 @@ class Deforest(using TL, Raise, Elaborator.State): case Define(defn, rest) => defn match case FunDefn(sym, params, body) => + val funSymStratVar = freshVar(sym.nme) + symToStrat += sym -> funSymStratVar._1 val param = params.head match case ParamList(flags, params, restParam) => params val funStrat = constrFun(param, body) // TODO: handle mutiple param list - val funSymStratVar = freshVar(sym.nme) - symToStrat += sym -> funSymStratVar._1 constrain(funStrat, funSymStratVar._2) funSymStratVar._1 case ValDefn(owner, k, sym, rhs) => NoProd // TODO: @@ -318,8 +318,7 @@ class Deforest(using TL, Raise, Elaborator.State): def rewriteBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then - println(dtorSources.contains(scrut)) + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && dtorSources.contains(scrut) then // TODO: rest match case End(msg) => Return(scrut, false) // TODO: true or false? diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls new file mode 100644 index 0000000000..8c84fc2ade --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -0,0 +1,179 @@ +:js + +object A +object B +class AA(x) +class BB(x) + +// :sjs +// :deforest +// let x = if true then A else B +// if x is +// A then 1 +// B then 2 + + +// :sjs +// :deforest +// let x = if true then AA(11) else BB(22) +// if x is +// AA(x) then x +// BB(x) then x + +:sjs +:deforest +fun g(x) = if true then AA(11) else BB(22) +fun c(x) = if x is + AA(x) then x + BB(x) then x +c(g(true)) +//│ JS (unsanitized): +//│ let tmp; +//│ function g(x) { +//│ let scrut; +//│ scrut = true; +//│ if (scrut) { +//│ return globalThis.AA(11); +//│ } else { +//│ return globalThis.BB(22); +//│ } +//│ } +//│ function c(x) { +//│ let param0, x1, param01, x2; +//│ if (x instanceof globalThis.AA.class) { +//│ param01 = x.x; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ if (x instanceof globalThis.BB.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ tmp = this.g(true); +//│ this.c(tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let tmp; +//│ function g(x) { +//│ let scrut, param0, x1, param01, x2; +//│ scrut = true; +//│ if (scrut) { +//│ param01 = 11; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ param0 = 22; +//│ x1 = param0; +//│ return x1; +//│ } +//│ } +//│ function c(x) { +//│ return x; +//│ } +//│ tmp = this.g(true); +//│ this.c(tmp) +//│ = 11 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 11 + + +// TODO: +// :sjs +// :lot +// let s = if true then AA(A) else BB(B) +// if s is +// // AA then s.x // NOTE: this s.x should only get A as its lower bound +// // BB then s.x // NOTE: this s.x should only get A as its lower bound +// BB(x) then x // NOTE: this s.x should only get B as its lower bound + + + +// TODO: +// :sjs +// let s = AA(A) +// if s is +// AA then s.x + + + + + + + + +object Nil +class Cons(h, t) + +:sjs +:deforest +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(ls) = if ls is + Nil then Nil + Cons(h, t) then Cons(h + 4, map(t)) +map(enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, tmp1, tmp2; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ return globalThis.Cons(a, tmp2); +//│ } else { +//│ return globalThis.Nil; +//│ } +//│ } +//│ function map(ls) { +//│ let param0, param1, h, t, tmp1, tmp2; +//│ if (ls instanceof globalThis.Nil.class) { +//│ return globalThis.Nil; +//│ } else { +//│ if (ls instanceof globalThis.Cons.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp1 = h + 4; +//│ tmp2 = globalThis.map(t); +//│ return globalThis.Cons(tmp1, tmp2); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map(tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ param0 = a; +//│ param1 = tmp2; +//│ h = param0; +//│ t = param1; +//│ tmp3 = h + 4; +//│ tmp4 = globalThis.map(t); +//│ return globalThis.Cons(tmp3, tmp4); +//│ } else { +//│ return globalThis.Nil; +//│ } +//│ } +//│ function map(ls) { +//│ return ls; +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map(tmp) +//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } From 78364b4873edcc11a591e3760de927404a03672e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:01:23 +0800 Subject: [PATCH 008/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 95 ++++++++++++------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8dd31eb0fe..2b249c5903 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -12,28 +12,38 @@ type StratVar type StratVarId = Uid[StratVar] type ClsOrModSymbol = ClassSymbol | ModuleSymbol -enum ProdStrat: - case Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) - case ProdFun(l: Ls[ConsStrat], r: ProdStrat) - case ProdVar(uid: StratVarId, name: Str = "") extends ProdStrat with StratVarTrait(uid, name) - case NoProd +sealed abstract class Strat +sealed abstract class ProdStrat extends Strat -enum ConsStrat: - // case Destruct - case Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) - case FieldSel(field: Tree.Ident, consVar: ConsVar, sym: Opt[TermSymbol]) - case ConsFun(l: Ls[ProdStrat], r: ConsStrat) - case ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) - case NoCons +sealed abstract class ConsStrat extends Strat -trait StratVarTrait(uid: StratVarId, name: Str): - this: ProdStrat.ProdVar | ConsStrat.ConsVar => +// enum ProdStrat: +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) extends ProdStrat +case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat +case class ProdVar(uid: StratVarId, name: Str = "") extends ProdStrat with StratVarTrait(uid, name) +case object NoProd extends ProdStrat + + +// enum ConsStrat: +case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat +case class FieldSel(field: Tree.Ident, consVar: ConsVar, sym: Opt[TermSymbol]) extends ConsStrat with FieldSelTrait +case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat +case class ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) +case object NoCons extends ConsStrat + +trait FieldSelTrait: + this: FieldSel => + val filter = mutable.Map.empty[ProdVar, Ls[ClsOrModSymbol]].withDefaultValue(Nil) - val filter = mutable.Map.empty[ProdStrat.ProdVar, Ls[ClsOrModSymbol]].withDefaultValue(Nil) + def updateFilter(p: ProdVar, c: Ls[ClsOrModSymbol]) = + filter += p -> (c ::: filter(p)) + +trait StratVarTrait(uid: StratVarId, name: Str): + this: ProdVar | ConsVar => - lazy val asProdStrat = ProdStrat.ProdVar(uid, name) - lazy val asConsStrat = ConsStrat.ConsVar(uid, name) + lazy val asProdStrat = ProdVar(uid, name) + lazy val asConsStrat = ConsVar(uid, name) extension (b: Block) // TODO: similar to Block.mapTail? @@ -62,8 +72,8 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): - import ProdStrat.* - import ConsStrat.* + // import ProdStrat.* + // import ConsStrat.* object StratVarId extends Uid.Handler[StratVar] @@ -90,7 +100,7 @@ class Deforest(using TL, Raise, Elaborator.State): var constraints: Ls[ProdStrat -> ConsStrat] = Nil - val symToStrat = mutable.Map.empty[Symbol, ProdStrat.ProdVar] + val symToStrat = mutable.Map.empty[Symbol, ProdVar] // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols // TODO: ctor as a function? def getStratOfSym(s: Symbol) = @@ -111,16 +121,16 @@ class Deforest(using TL, Raise, Elaborator.State): val newId = vuid.nextUid val p: ProdVar = ProdVar(newId, nme) val c: ConsVar = ConsVar(newId, nme) - filter.foreach{ case v -> cls => - p.filter.updateWith(v){ - case None => Some(cls :: Nil) - case Some(value) => Some(cls :: value) - } - c.filter.updateWith(v){ - case None => Some(cls :: Nil) - case Some(value) => Some(cls :: value) - } - } + // filter.foreach{ case v -> cls => + // p.filter.updateWith(v){ + // case None => Some(cls :: Nil) + // case Some(value) => Some(cls :: value) + // } + // c.filter.updateWith(v){ + // case None => Some(cls :: Nil) + // case Some(value) => Some(cls :: value) + // } + // } p -> c def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c @@ -132,7 +142,7 @@ class Deforest(using TL, Raise, Elaborator.State): arms.map { case (Case.Cls(s, _), body) => constrain(scrutStrat, Dtor(scrut, arms)) // TODO: fix this "asInstanceOf"? - processBlock(body)(using S(scrutStrat.asInstanceOf[ProdStrat.ProdVar] -> s)) + processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -243,7 +253,9 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(armP -> clsSym) if armP === pStrat => assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, sel.symbol.map(_.asInstanceOf[TermSymbol]))) + val selStrat = FieldSel(nme, tpeVar._2, sel.symbol.map(_.asInstanceOf[TermSymbol])) + selStrat.updateFilter(armP, clsSym :: Nil) + constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() @@ -288,12 +300,25 @@ class Deforest(using TL, Raise, Elaborator.State): else args.find(a => a._1.id == field).map(p => handle(p._2 -> consVar)) case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? + + case (p@ProdVar(uid, name), _) => + upperBounds += uid -> (cons :: upperBounds(uid)) + lowerBounds(uid).foreach{ l => + (l, cons) match + case (l: ProdVar, sel@FieldSel(field, consVar, sym)) => + sel.updateFilter(l, sel.filter(p)) + handle(l -> cons) + case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, sym)) => + if sel.filter(p).contains(ctor) then + handle(l -> cons) + else + () + case _ => handle(p -> cons) + } + // lowerBounds(uid).foreach(p => handle(p -> cons)) case (_, ConsVar(uid, name)) => lowerBounds += uid -> (prod :: lowerBounds(uid)) upperBounds(uid).foreach(c => handle(prod -> c)) - case (ProdVar(uid, name), _) => - upperBounds += uid -> (cons :: upperBounds(uid)) - lowerBounds(uid).foreach(p => handle(p -> cons)) case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls, _)) => ??? case (ProdFun(l, r), FieldSel(field, consVar, _)) => ??? From 84582c825f492f925caaa7e25aad2c9058196225 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 14 Jan 2025 02:54:24 +0800 Subject: [PATCH 009/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 25 ++- .../src/test/mlscript/deforest/simple.mls | 161 ++++-------------- 2 files changed, 46 insertions(+), 140 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 2b249c5903..3bcd5f10c3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -52,7 +52,8 @@ extension (b: Block) case Assign(lhs, rhs, rest: End) => f(rhs) case Assign(lhs, rhs, rest) => Assign(lhs, rhs, rest.mapRes(f)) case Define(defn, rest) => Define(defn, rest.mapRes(f)) - case Match(scrut, arms, dflt, rest) => ??? + // case Match(scrut, arms, dflt, e: End) => Match(scrut, arms) + // case Match(scrut, arms, dflt, rest) => Match(scrut, arms, dflt, rest.mapRes(f)) case Throw(exc) => ??? case Label(label, body, rest) => ??? case Break(label) => ??? @@ -87,6 +88,7 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds.foreach(u => println("\t" + u)) println("lower:") lowerBounds.foreach(l => println("\t" + l)) + println("==============================") // println("ctor -> dtor:") // ctorDests.foreach(l => println("\t" + l._1.toString() + " ===> " + l._2.size)) @@ -295,10 +297,10 @@ class Deforest(using TL, Raise, Elaborator.State): dtorSources += scrut -> (expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor case (Ctor(ctor, args, _), FieldSel(field, consVar, clsSym)) => - if clsSym.isDefined then - args.get(clsSym.get).map(p => handle(p -> consVar)) - else - args.find(a => a._1.id == field).map(p => handle(p._2 -> consVar)) + // if clsSym.isDefined then + // args.get(clsSym.get).map(p => handle(p -> consVar)) + // else + args.find(a => a._1.id == field).map(p => handle(p._2 -> consVar)) case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? case (p@ProdVar(uid, name), _) => @@ -313,7 +315,7 @@ class Deforest(using TL, Raise, Elaborator.State): handle(l -> cons) else () - case _ => handle(p -> cons) + case _ => handle(l -> cons) } // lowerBounds(uid).foreach(p => handle(p -> cons)) case (_, ConsVar(uid, name)) => @@ -323,6 +325,7 @@ class Deforest(using TL, Raise, Elaborator.State): case (ProdFun(l, r), Dtor(cls, _)) => ??? case (ProdFun(l, r), FieldSel(field, consVar, _)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => + println(s">>>>>>>>>>>>>>>>>>>>>>>> $prod ->>> $cons <<<<<<<<<<<<<<<<<<<<<<<") lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () @@ -346,7 +349,7 @@ class Deforest(using TL, Raise, Elaborator.State): if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && dtorSources.contains(scrut) then // TODO: rest match - case End(msg) => Return(scrut, false) // TODO: true or false? + case End(msg) => Return(scrut, true) // TODO: true or false? case _ => rest else Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) @@ -375,12 +378,18 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => k(call) case Some(c) => + assert(ctorDests(call).size == 1) ctorDests(call).headOption match case None => k(call) case Some(dest) => val body = dest._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 println(call.toString() + " ----> " + body) - body.replaceAssignments(args.map(a => a.value)).mapRes(k) + + val newArgs = args.map(_ => TempSymbol(N)) + args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => + rewriteResult(a.value): r => + Assign(tmp, r, rest) + } case Value.Ref(l) => l.asCls match case None => k(call) case Some(c) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 8c84fc2ade..1493b4db19 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -15,71 +15,29 @@ class BB(x) // :sjs // :deforest -// let x = if true then AA(11) else BB(22) +// let x = if true then AA(A) else BB(B) // if x is // AA(x) then x // BB(x) then x -:sjs -:deforest -fun g(x) = if true then AA(11) else BB(22) -fun c(x) = if x is - AA(x) then x - BB(x) then x -c(g(true)) -//│ JS (unsanitized): -//│ let tmp; -//│ function g(x) { -//│ let scrut; -//│ scrut = true; -//│ if (scrut) { -//│ return globalThis.AA(11); -//│ } else { -//│ return globalThis.BB(22); -//│ } -//│ } -//│ function c(x) { -//│ let param0, x1, param01, x2; -//│ if (x instanceof globalThis.AA.class) { -//│ param01 = x.x; -//│ x2 = param01; -//│ return x2; -//│ } else { -//│ if (x instanceof globalThis.BB.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ return x1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ tmp = this.g(true); -//│ this.c(tmp) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let tmp; -//│ function g(x) { -//│ let scrut, param0, x1, param01, x2; -//│ scrut = true; -//│ if (scrut) { -//│ param01 = 11; -//│ x2 = param01; -//│ return x2; -//│ } else { -//│ param0 = 22; -//│ x1 = param0; -//│ return x1; -//│ } -//│ } -//│ function c(x) { -//│ return x; -//│ } -//│ tmp = this.g(true); -//│ this.c(tmp) -//│ = 11 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 11 +// :sjs +// :deforest +// fun f(a) = if a is +// A then 1 +// B then 2 +// let x = if true then AA(A) else BB(B) +// if x is +// AA(x) then f(x) +// BB(x) then f(x) + + +// :sjs +// :deforest +// fun g(x) = if true then AA(11) else BB(22) +// fun c(x) = if x is +// AA(x) then x +// BB(x) then x +// c(g(true)) // TODO: @@ -106,74 +64,13 @@ c(g(true)) -object Nil -class Cons(h, t) - -:sjs -:deforest -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(ls) = if ls is - Nil then Nil - Cons(h, t) then Cons(h + 4, map(t)) -map(enumFromTo(1, 4)) -//│ JS (unsanitized): -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ return globalThis.Cons(a, tmp2); -//│ } else { -//│ return globalThis.Nil; -//│ } -//│ } -//│ function map(ls) { -//│ let param0, param1, h, t, tmp1, tmp2; -//│ if (ls instanceof globalThis.Nil.class) { -//│ return globalThis.Nil; -//│ } else { -//│ if (ls instanceof globalThis.Cons.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp1 = h + 4; -//│ tmp2 = globalThis.map(t); -//│ return globalThis.Cons(tmp1, tmp2); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map(tmp) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ param0 = a; -//│ param1 = tmp2; -//│ h = param0; -//│ t = param1; -//│ tmp3 = h + 4; -//│ tmp4 = globalThis.map(t); -//│ return globalThis.Cons(tmp3, tmp4); -//│ } else { -//│ return globalThis.Nil; -//│ } -//│ } -//│ function map(ls) { -//│ return ls; -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map(tmp) -//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } +// object Nil +// class Cons(h, t) + +// :sjs +// :deforest +// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +// fun map(ls) = if ls is +// Nil then Nil +// Cons(h, t) then Cons(h + 4, map(t)) +// map(enumFromTo(1, 4)) From c49b902417ff99dbc6762a1a48cd355b03630fc7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:01:30 +0800 Subject: [PATCH 010/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 3bcd5f10c3..43fd72953f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -27,7 +27,7 @@ case object NoProd extends ProdStrat // enum ConsStrat: case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat -case class FieldSel(field: Tree.Ident, consVar: ConsVar, sym: Opt[TermSymbol]) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) case object NoCons extends ConsStrat @@ -211,7 +211,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, N)) + constrain(pStrat, FieldSel(nme, tpeVar._2)) val appRes = freshVar() constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -253,15 +253,15 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) inArm match case Some(armP -> clsSym) if armP === pStrat => - assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) + // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() - val selStrat = FieldSel(nme, tpeVar._2, sel.symbol.map(_.asInstanceOf[TermSymbol])) + val selStrat = FieldSel(nme, tpeVar._2) selStrat.updateFilter(armP, clsSym :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, N)) + constrain(pStrat, FieldSel(nme, tpeVar._2)) tpeVar._1 case Value.Ref(l) => getStratOfSym(l) @@ -296,7 +296,7 @@ class Deforest(using TL, Raise, Elaborator.State): }) dtorSources += scrut -> (expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor - case (Ctor(ctor, args, _), FieldSel(field, consVar, clsSym)) => + case (Ctor(ctor, args, _), FieldSel(field, consVar)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else @@ -307,10 +307,10 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds += uid -> (cons :: upperBounds(uid)) lowerBounds(uid).foreach{ l => (l, cons) match - case (l: ProdVar, sel@FieldSel(field, consVar, sym)) => + case (l: ProdVar, sel@FieldSel(field, consVar)) => sel.updateFilter(l, sel.filter(p)) handle(l -> cons) - case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, sym)) => + case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar)) => if sel.filter(p).contains(ctor) then handle(l -> cons) else @@ -323,14 +323,14 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds(uid).foreach(c => handle(prod -> c)) case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls, _)) => ??? - case (ProdFun(l, r), FieldSel(field, consVar, _)) => ??? + case (ProdFun(l, r), FieldSel(field, consVar)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => println(s">>>>>>>>>>>>>>>>>>>>>>>> $prod ->>> $cons <<<<<<<<<<<<<<<<<<<<<<<") lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () case (NoProd, Dtor(cls, _)) => () - case (NoProd, FieldSel(field, consVar, _)) => () + case (NoProd, FieldSel(field, consVar)) => () case (NoProd, ConsFun(l, r)) => () case (NoProd, NoCons) => () From 50475fc1ace5e54894d0cd18ab42b94756518fb2 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 15 Jan 2025 01:40:20 +0800 Subject: [PATCH 011/303] wip --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 43fd72953f..ab7cca5dcb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -252,7 +252,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => val pStrat = processResult(p) inArm match - case Some(armP -> clsSym) if armP === pStrat => + case Some(armP -> clsSym) => // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() val selStrat = FieldSel(nme, tpeVar._2) From 960438d4676ed216520aa0e7c8f7f531c343c03d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:41:20 +0800 Subject: [PATCH 012/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 71 +++- .../src/test/mlscript/deforest/simple.mls | 389 +++++++++++++++--- 2 files changed, 391 insertions(+), 69 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ab7cca5dcb..8c46891e98 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -27,7 +27,7 @@ case object NoProd extends ProdStrat // enum ConsStrat: case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat -case class FieldSel(field: Tree.Ident, consVar: ConsVar) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar, expr: Select) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) case object NoCons extends ConsStrat @@ -45,6 +45,20 @@ trait StratVarTrait(uid: StratVarId, name: Str): lazy val asProdStrat = ProdVar(uid, name) lazy val asConsStrat = ConsVar(uid, name) +extension (r: Result) + def replaceSelect(using p: Set[Select], args: Map[Tree.Ident, Path]): Result = r match + case c@Call(f, args) => Call(f, args.map{case Arg(spread, value) => Arg(spread, value.replaceSelect.asInstanceOf[Path])})(c.isMlsFun) + case sel@Select(path, nme) => + if p.contains(sel) then args(nme) else sel + case _ => r + // case Value.Ref(l) => r + // case Instantiate(cls, args) => ??? + // case Value.This(sym) => ??? + // case Value.Lit(lit) => ??? + // case Value.Lam(params, body) => ??? + // case Value.Arr(elems) => ??? + + extension (b: Block) // TODO: similar to Block.mapTail? def mapRes(f: Result => Block): Block = b match @@ -65,6 +79,24 @@ extension (b: Block) case HandleBlockReturn(res) => ??? case End(msg) => ??? + def replaceSelect(using p: Set[Select], args: Map[Tree.Ident, Path]): Block = b match + case Assign(lhs, rhs, rest) => Assign(lhs, rhs.replaceSelect, rest.replaceSelect) + case Return(res, implct) => Return(res.replaceSelect, implct) + case Match(scrut, arms, dflt, rest) => ??? + case _ => b + // case Throw(exc) => ??? + // case Label(label, body, rest) => ??? + // case Break(label) => ??? + // case Continue(label) => ??? + // case Begin(sub, rest) => ??? + // case TryBlock(sub, finallyDo, rest) => ??? + // case AssignField(_, _, _, _) => ??? + // case Define(defn, rest) => ??? + // case HandleBlock(lhs, res, cls, handlers, body, rest) => ??? + // case HandleBlockReturn(res) => ??? + // case End(msg) => ??? + + def replaceAssignments(args: List[Path]): Block = args match case head :: tail => b match case Assign(lhs, rhs, rest) => Assign(lhs, head, rest.replaceAssignments(tail)) @@ -211,7 +243,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)) + constrain(pStrat, FieldSel(nme, tpeVar._2, s)) val appRes = freshVar() constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -252,16 +284,16 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => val pStrat = processResult(p) inArm match - case Some(armP -> clsSym) => + case Some(armP -> clsSym) if armP === pStrat => // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() - val selStrat = FieldSel(nme, tpeVar._2) + val selStrat = FieldSel(nme, tpeVar._2, sel) selStrat.updateFilter(armP, clsSym :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)) + constrain(pStrat, FieldSel(nme, tpeVar._2, sel)) tpeVar._1 case Value.Ref(l) => getStratOfSym(l) @@ -278,6 +310,8 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorDests = mutable.Map.empty[Call | Select, Map[Value.Ref, Ls[Case -> Block]]].withDefaultValue(Map.empty) val dtorSources = mutable.Map.empty[Value.Ref, Ls[Call | Select]].withDefaultValue(Nil) + val rewritingSel = mutable.Set.empty[Select] + def resolveConstraints: Unit = def handle(c: ProdStrat -> ConsStrat)(using cache: mutable.Set[ProdStrat -> ConsStrat]): Unit = @@ -296,21 +330,24 @@ class Deforest(using TL, Raise, Elaborator.State): }) dtorSources += scrut -> (expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor - case (Ctor(ctor, args, _), FieldSel(field, consVar)) => + case (Ctor(ctor, args, _), FieldSel(field, consVar, sel)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) - // else - args.find(a => a._1.id == field).map(p => handle(p._2 -> consVar)) + // else + args.find(a => a._1.id == field).map(p => + rewritingSel.add(sel) + handle(p._2 -> consVar) + ) case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? case (p@ProdVar(uid, name), _) => upperBounds += uid -> (cons :: upperBounds(uid)) lowerBounds(uid).foreach{ l => (l, cons) match - case (l: ProdVar, sel@FieldSel(field, consVar)) => + case (l: ProdVar, sel@FieldSel(field, consVar, selExpr)) => sel.updateFilter(l, sel.filter(p)) handle(l -> cons) - case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar)) => + case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, selExpr)) => if sel.filter(p).contains(ctor) then handle(l -> cons) else @@ -323,14 +360,14 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds(uid).foreach(c => handle(prod -> c)) case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls, _)) => ??? - case (ProdFun(l, r), FieldSel(field, consVar)) => ??? + case (ProdFun(l, r), FieldSel(field, consVar, sel)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => println(s">>>>>>>>>>>>>>>>>>>>>>>> $prod ->>> $cons <<<<<<<<<<<<<<<<<<<<<<<") lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () case (NoProd, Dtor(cls, _)) => () - case (NoProd, FieldSel(field, consVar)) => () + case (NoProd, FieldSel(field, consVar, sel)) => () case (NoProd, ConsFun(l, r)) => () case (NoProd, NoCons) => () @@ -386,7 +423,13 @@ class Deforest(using TL, Raise, Elaborator.State): println(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) - args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => + // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => + // rewriteResult(a.value): r => + // Assign(tmp, r, rest) + // } + val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap + + args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => rewriteResult(a.value): r => Assign(tmp, r, rest) } @@ -395,7 +438,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(c) => val body = ctorDests(call).head._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 println(call.toString() + " ----> " + body) - body.replaceAssignments(args.map(a => a.value)).mapRes(k) + body.replaceAssignments(args.map(a => a.value)).mapRes(k) // TODO: case Value.Lam(params, body) => k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) case Instantiate(cls, args) => k(r) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 1493b4db19..a7e773d2f7 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -5,72 +5,351 @@ object B class AA(x) class BB(x) -// :sjs -// :deforest -// let x = if true then A else B -// if x is -// A then 1 -// B then 2 +:sjs +:deforest +let x = if true then A else B +if x is + A then 1 + B then 2 +//│ JS (unsanitized): +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = this.A; +//│ } else { +//│ tmp = this.B; +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ if (scrut1 instanceof this.A.class) { +//│ 1 +//│ } else { +//│ if (scrut1 instanceof this.B.class) { +//│ 2 +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = 1; +//│ } else { +//│ tmp = 2; +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ scrut1 +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ x = A { class: [class A] } -// :sjs -// :deforest -// let x = if true then AA(A) else BB(B) -// if x is -// AA(x) then x -// BB(x) then x +:sjs +:deforest +let x = if true then AA(A) else BB(B) +if x is + AA(x) then x + BB(x) then x +//│ JS (unsanitized): +//│ let scrut, scrut1, param0, x, param01, x1, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = this.AA(this.A); +//│ } else { +//│ tmp = this.BB(this.B); +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ if (scrut1 instanceof this.AA.class) { +//│ param01 = scrut1.x; +//│ x1 = param01; +//│ x1 +//│ } else { +//│ if (scrut1 instanceof this.BB.class) { +//│ param0 = scrut1.x; +//│ x = param0; +//│ x +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let scrut, scrut1, param0, x, param01, x1, tmp, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = this.A; +//│ param01 = tmp1; +//│ x1 = param01; +//│ tmp = x1; +//│ } else { +//│ tmp2 = this.B; +//│ param0 = tmp2; +//│ x = param0; +//│ tmp = x; +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ scrut1 +//│ = A { class: [class A] } +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A { class: [class A] } +//│ x = AA { x: A { class: [class A] } } -// :sjs -// :deforest -// fun f(a) = if a is -// A then 1 -// B then 2 -// let x = if true then AA(A) else BB(B) -// if x is -// AA(x) then f(x) -// BB(x) then f(x) +:sjs +:deforest +fun f(a) = if a is + A then 1 + B then 2 +let x = if true then AA(A) else BB(B) +if x is + AA(x) then f(x) + BB(x) then f(x) +//│ JS (unsanitized): +//│ let scrut, scrut1, param0, x, param01, x1, tmp; +//│ function f(a) { +//│ if (a instanceof globalThis.A.class) { +//│ return 1; +//│ } else { +//│ if (a instanceof globalThis.B.class) { +//│ return 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ scrut = true; +//│ if (scrut) { +//│ tmp = this.AA(this.A); +//│ } else { +//│ tmp = this.BB(this.B); +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ if (scrut1 instanceof this.AA.class) { +//│ param01 = scrut1.x; +//│ x1 = param01; +//│ this.f(x1) +//│ } else { +//│ if (scrut1 instanceof this.BB.class) { +//│ param0 = scrut1.x; +//│ x = param0; +//│ this.f(x) +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let scrut, scrut1, param0, x, param01, x1, tmp, tmp1, tmp2; +//│ function f(a) { +//│ a +//│ } +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = 1; +//│ param01 = tmp1; +//│ x1 = param01; +//│ tmp = this.f(x1); +//│ } else { +//│ tmp2 = 2; +//│ param0 = tmp2; +//│ x = param0; +//│ tmp = this.f(x); +//│ } +//│ this.x = tmp; +//│ scrut1 = this.x; +//│ scrut1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ x = AA { x: A { class: [class A] } } -// :sjs -// :deforest -// fun g(x) = if true then AA(11) else BB(22) -// fun c(x) = if x is -// AA(x) then x -// BB(x) then x -// c(g(true)) +:sjs +:deforest +fun g(x) = if true then AA(11) else BB(22) +fun c(x) = if x is + AA(x) then x + BB(x) then x +c(g(true)) +//│ JS (unsanitized): +//│ let tmp; +//│ function g(x) { +//│ let scrut; +//│ scrut = true; +//│ if (scrut) { +//│ return globalThis.AA(11); +//│ } else { +//│ return globalThis.BB(22); +//│ } +//│ } +//│ function c(x) { +//│ let param0, x1, param01, x2; +//│ if (x instanceof globalThis.AA.class) { +//│ param01 = x.x; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ if (x instanceof globalThis.BB.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ tmp = this.g(true); +//│ this.c(tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let tmp; +//│ function g(x) { +//│ let scrut, param0, x1, param01, x2, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = 11; +//│ param01 = tmp1; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ tmp2 = 22; +//│ param0 = tmp2; +//│ x1 = param0; +//│ return x1; +//│ } +//│ } +//│ function c(x) { +//│ x +//│ } +//│ tmp = this.g(true); +//│ this.c(tmp) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 11 -// TODO: -// :sjs -// :lot -// let s = if true then AA(A) else BB(B) -// if s is -// // AA then s.x // NOTE: this s.x should only get A as its lower bound -// // BB then s.x // NOTE: this s.x should only get A as its lower bound -// BB(x) then x // NOTE: this s.x should only get B as its lower bound +:sjs +:deforest +let s = if true then AA(A) else BB(B) +if s is + AA then s.x // NOTE: this s.x should only get A as its lower bound + BB then s.x // NOTE: this s.x should only get A as its lower bound +//│ JS (unsanitized): +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = this.AA(this.A); +//│ } else { +//│ tmp = this.BB(this.B); +//│ } +//│ this.s = tmp; +//│ scrut1 = this.s; +//│ if (scrut1 instanceof this.AA.class) { +//│ this.s.x +//│ } else { +//│ if (scrut1 instanceof this.BB.class) { +//│ this.s.x +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let scrut, scrut1, tmp, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = this.A; +//│ tmp = tmp1; +//│ } else { +//│ tmp2 = this.B; +//│ tmp = tmp2; +//│ } +//│ this.s = tmp; +//│ scrut1 = this.s; +//│ scrut1 +//│ = A { class: [class A] } +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A { class: [class A] } +//│ s = AA { x: A { class: [class A] } } -// TODO: -// :sjs -// let s = AA(A) -// if s is -// AA then s.x +object Nil +class Cons(h, t) - - - - -// object Nil -// class Cons(h, t) - -// :sjs -// :deforest -// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -// fun map(ls) = if ls is -// Nil then Nil -// Cons(h, t) then Cons(h + 4, map(t)) -// map(enumFromTo(1, 4)) +:sjs +:deforest +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(ls) = if ls is + Nil then Nil + Cons(h, t) then Cons(h + 4, map(t)) +map(enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, tmp1, tmp2; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ return globalThis.Cons(a, tmp2); +//│ } else { +//│ return globalThis.Nil; +//│ } +//│ } +//│ function map(ls) { +//│ let param0, param1, h, t, tmp1, tmp2; +//│ if (ls instanceof globalThis.Nil.class) { +//│ return globalThis.Nil; +//│ } else { +//│ if (ls instanceof globalThis.Cons.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp1 = h + 4; +//│ tmp2 = globalThis.map(t); +//│ return globalThis.Cons(tmp1, tmp2); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map(tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ tmp5 = a; +//│ tmp6 = tmp2; +//│ param0 = tmp5; +//│ param1 = tmp6; +//│ h = param0; +//│ t = param1; +//│ tmp3 = h + 4; +//│ tmp4 = globalThis.map(t); +//│ return globalThis.Cons(tmp3, tmp4); +//│ } else { +//│ return globalThis.Nil; +//│ } +//│ } +//│ function map(ls) { +//│ ls +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map(tmp) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } From 45d7ed4366a40fa97bd7c810e99c83821af77598 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 19 Jan 2025 13:04:18 +0800 Subject: [PATCH 013/303] wip --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 10 ++++-- .../scala/hkmc2/codegen/Deforestation.scala | 36 +++++++++---------- .../src/test/mlscript/deforest/simple.mls | 5 +-- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 2c4fa8e487..a17441c464 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -27,6 +27,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val traceJS = NullaryCommand("traceJS") val handler = NullaryCommand("handler") val deforestFlag = NullaryCommand("deforest") + val deforestInfo = NullaryCommand("deforestInfo") val expect = Command("expect"): ln => ln.trim @@ -41,6 +42,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: override def doTrace = showRepl.isSet override def emitDbg(str: String): Unit = output(str) + val deforestTL = new TraceLogger: + override def doTrace: Bool = deforestInfo.isSet + override def emitDbg(str: String): Unit = output(str) + lazy val host = hostCreated = true given TL = replTL @@ -117,10 +122,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(jsStr) if deforestFlag.isSet then - val deforest = new Deforest + val deforest = new Deforest(using deforestTL) output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") if showLoweredTree.isSet then - output("==== Non-inserted lowered tree ====") + output("\n==== Non-inserted lowered tree ====") output(le.showAsTree) val deforestRes = deforest(le) @@ -128,6 +133,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if showLoweredTree.isSet then output("\n==== deforested tree ====") output(deforestRes.showAsTree) + output("\n") val nestedScp = baseScp.nest val je = nestedScp.givenIn: jsb.program(deforestRes, N, wd) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8c46891e98..6286adc50a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -112,20 +112,20 @@ class Deforest(using TL, Raise, Elaborator.State): def apply(p: Program) = processBlock(p.main) - // constraints.foreach(println) - // println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") + // constraints.foreach(tl.log) + // tl.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") resolveConstraints - println("upper:") - upperBounds.foreach(u => println("\t" + u)) - println("lower:") - lowerBounds.foreach(l => println("\t" + l)) - println("==============================") + tl.log("upper:") + upperBounds.foreach(u => tl.log("\t" + u)) + tl.log("lower:") + lowerBounds.foreach(l => tl.log("\t" + l)) + tl.log("-----------------------------------------") - // println("ctor -> dtor:") - // ctorDests.foreach(l => println("\t" + l._1.toString() + " ===> " + l._2.size)) - // println("dtor -> ctor:") - // dtorSources.foreach(l => println("\t" + l._1.toString().take(20) + " ===> " + l._2.toString().take(20))) + // tl.log("ctor -> dtor:") + // ctorDests.foreach(l => tl.log("\t" + l._1.toString() + " ===> " + l._2.size)) + // tl.log("dtor -> ctor:") + // dtorSources.foreach(l => tl.log("\t" + l._1.toString().take(20) + " ===> " + l._2.toString().take(20))) rewrite(p) @@ -362,7 +362,6 @@ class Deforest(using TL, Raise, Elaborator.State): case (ProdFun(l, r), Dtor(cls, _)) => ??? case (ProdFun(l, r), FieldSel(field, consVar, sel)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => - println(s">>>>>>>>>>>>>>>>>>>>>>>> $prod ->>> $cons <<<<<<<<<<<<<<<<<<<<<<<") lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () @@ -420,7 +419,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => k(call) case Some(dest) => val body = dest._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - println(call.toString() + " ----> " + body) + tl.log(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => @@ -435,10 +434,7 @@ class Deforest(using TL, Raise, Elaborator.State): } case Value.Ref(l) => l.asCls match case None => k(call) - case Some(c) => - val body = ctorDests(call).head._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - println(call.toString() + " ----> " + body) - body.replaceAssignments(args.map(a => a.value)).mapRes(k) // TODO: + case Some(c) => ??? // TODO: case Value.Lam(params, body) => k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) case Instantiate(cls, args) => k(r) @@ -449,10 +445,10 @@ class Deforest(using TL, Raise, Elaborator.State): case None => k(s) case Some(dests) => - val body = dests.head._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get._2 - println(mod.toString + " ----> " + body) + val body = dests.map(d => d._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get._2) + tl.log(mod.toString + " ----> " + body) - body.mapRes(k) + body.head.mapRes(k) case Value.Ref(l) => k(Value.Ref(l)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index a7e773d2f7..a7f2eb76de 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -232,12 +232,13 @@ c(g(true)) //│ = 11 +// FIXME: shouldn't fuse this... :sjs :deforest let s = if true then AA(A) else BB(B) if s is - AA then s.x // NOTE: this s.x should only get A as its lower bound - BB then s.x // NOTE: this s.x should only get A as its lower bound + AA then s.x + BB then s.x //│ JS (unsanitized): //│ let scrut, scrut1, tmp; //│ scrut = true; From d76a91b2027f0ded436e6b46f22d0b91e8e87a7e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:10:24 +0800 Subject: [PATCH 014/303] wip --- .../src/test/mlscript/deforest/simple.mls | 328 ++++++++++-------- 1 file changed, 175 insertions(+), 153 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index a7f2eb76de..5071f7f5c0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -7,110 +7,120 @@ class BB(x) :sjs :deforest -let x = if true then A else B -if x is - A then 1 - B then 2 +fun test() = + let x = if true then A else B + if x is + A then 1 + B then 2 +test() //│ JS (unsanitized): -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut) { -//│ tmp = this.A; -//│ } else { -//│ tmp = this.B; -//│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ if (scrut1 instanceof this.A.class) { -//│ 1 -//│ } else { -//│ if (scrut1 instanceof this.B.class) { -//│ 2 +//│ function test() { +//│ let x, scrut, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = globalThis.A; //│ } else { -//│ throw new this.Error("match error"); +//│ tmp = globalThis.B; +//│ } +//│ x = tmp; +//│ if (x instanceof globalThis.A.class) { +//│ return 1; +//│ } else { +//│ if (x instanceof globalThis.B.class) { +//│ return 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ } //│ } +//│ this.test() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut) { -//│ tmp = 1; -//│ } else { -//│ tmp = 2; +//│ function test() { +//│ let x, scrut, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = 1; +//│ } else { +//│ tmp = 2; +//│ } +//│ x = tmp; +//│ x //│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ scrut1 -//│ = 1 +//│ this.test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 -//│ x = A { class: [class A] } :sjs :deforest -let x = if true then AA(A) else BB(B) -if x is - AA(x) then x - BB(x) then x +fun test() = + let x = if true then AA(A) else BB(B) + if x is + AA(x) then x + BB(x) then x +test() //│ JS (unsanitized): -//│ let scrut, scrut1, param0, x, param01, x1, tmp; -//│ scrut = true; -//│ if (scrut) { -//│ tmp = this.AA(this.A); -//│ } else { -//│ tmp = this.BB(this.B); -//│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ if (scrut1 instanceof this.AA.class) { -//│ param01 = scrut1.x; -//│ x1 = param01; -//│ x1 -//│ } else { -//│ if (scrut1 instanceof this.BB.class) { -//│ param0 = scrut1.x; -//│ x = param0; -//│ x +//│ function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = globalThis.AA(globalThis.A); //│ } else { -//│ throw new this.Error("match error"); +//│ tmp = globalThis.BB(globalThis.B); +//│ } +//│ x = tmp; +//│ if (x instanceof globalThis.AA.class) { +//│ param01 = x.x; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ if (x instanceof globalThis.BB.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ } //│ } +//│ this.test() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let scrut, scrut1, param0, x, param01, x1, tmp, tmp1, tmp2; -//│ scrut = true; -//│ if (scrut) { -//│ tmp1 = this.A; -//│ param01 = tmp1; -//│ x1 = param01; -//│ tmp = x1; -//│ } else { -//│ tmp2 = this.B; -//│ param0 = tmp2; -//│ x = param0; -//│ tmp = x; +//│ function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = globalThis.A; +//│ param01 = tmp1; +//│ x2 = param01; +//│ tmp = x2; +//│ } else { +//│ tmp2 = globalThis.B; +//│ param0 = tmp2; +//│ x1 = param0; +//│ tmp = x1; +//│ } +//│ x = tmp; +//│ x //│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ scrut1 -//│ = A { class: [class A] } +//│ this.test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A { class: [class A] } -//│ x = AA { x: A { class: [class A] } } + :sjs :deforest fun f(a) = if a is A then 1 B then 2 -let x = if true then AA(A) else BB(B) -if x is - AA(x) then f(x) - BB(x) then f(x) +fun test() = + let x = if true then AA(A) else BB(B) + if x is + AA(x) then f(x) + BB(x) then f(x) +test() //│ JS (unsanitized): -//│ let scrut, scrut1, param0, x, param01, x1, tmp; //│ function f(a) { //│ if (a instanceof globalThis.A.class) { //│ return 1; @@ -122,112 +132,124 @@ if x is //│ } //│ } //│ } -//│ scrut = true; -//│ if (scrut) { -//│ tmp = this.AA(this.A); -//│ } else { -//│ tmp = this.BB(this.B); -//│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ if (scrut1 instanceof this.AA.class) { -//│ param01 = scrut1.x; -//│ x1 = param01; -//│ this.f(x1) -//│ } else { -//│ if (scrut1 instanceof this.BB.class) { -//│ param0 = scrut1.x; -//│ x = param0; -//│ this.f(x) -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ } -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let scrut, scrut1, param0, x, param01, x1, tmp, tmp1, tmp2; -//│ function f(a) { -//│ a -//│ } -//│ scrut = true; -//│ if (scrut) { -//│ tmp1 = 1; -//│ param01 = tmp1; -//│ x1 = param01; -//│ tmp = this.f(x1); -//│ } else { -//│ tmp2 = 2; -//│ param0 = tmp2; -//│ x = param0; -//│ tmp = this.f(x); -//│ } -//│ this.x = tmp; -//│ scrut1 = this.x; -//│ scrut1 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 -//│ x = AA { x: A { class: [class A] } } - - -:sjs -:deforest -fun g(x) = if true then AA(11) else BB(22) -fun c(x) = if x is - AA(x) then x - BB(x) then x -c(g(true)) -//│ JS (unsanitized): -//│ let tmp; -//│ function g(x) { -//│ let scrut; +//│ function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp; //│ scrut = true; //│ if (scrut) { -//│ return globalThis.AA(11); +//│ tmp = globalThis.AA(globalThis.A); //│ } else { -//│ return globalThis.BB(22); +//│ tmp = globalThis.BB(globalThis.B); //│ } -//│ } -//│ function c(x) { -//│ let param0, x1, param01, x2; +//│ x = tmp; //│ if (x instanceof globalThis.AA.class) { //│ param01 = x.x; //│ x2 = param01; -//│ return x2; +//│ return globalThis.f(x2); //│ } else { //│ if (x instanceof globalThis.BB.class) { //│ param0 = x.x; //│ x1 = param0; -//│ return x1; +//│ return globalThis.f(x1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } -//│ tmp = this.g(true); -//│ this.c(tmp) +//│ this.test() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let tmp; -//│ function g(x) { -//│ let scrut, param0, x1, param01, x2, tmp1, tmp2; +//│ function f(a) { +//│ a +//│ } +//│ function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut) { -//│ tmp1 = 11; +//│ tmp1 = 1; //│ param01 = tmp1; //│ x2 = param01; -//│ return x2; +//│ tmp = globalThis.f(x2); //│ } else { -//│ tmp2 = 22; +//│ tmp2 = 2; //│ param0 = tmp2; //│ x1 = param0; -//│ return x1; +//│ tmp = globalThis.f(x1); //│ } -//│ } -//│ function c(x) { +//│ x = tmp; //│ x //│ } -//│ tmp = this.g(true); -//│ this.c(tmp) +//│ this.test() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 + + +:sjs +:deforest +fun test() = + fun g(x) = if true then AA(11) else BB(22) + fun c(x) = if x is + AA(x) then x + BB(x) then x + c(g(true)) +test() +//│ JS (unsanitized): +//│ function test() { +//│ let tmp; +//│ function g(x) { +//│ let scrut; +//│ scrut = true; +//│ if (scrut) { +//│ return globalThis.AA(11); +//│ } else { +//│ return globalThis.BB(22); +//│ } +//│ } +//│ function c(x) { +//│ let param0, x1, param01, x2; +//│ if (x instanceof globalThis.AA.class) { +//│ param01 = x.x; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ if (x instanceof globalThis.BB.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ tmp = g(true); +//│ return c(tmp); +//│ } +//│ this.test() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ function test() { +//│ let tmp; +//│ function g(x) { +//│ let scrut, param0, x1, param01, x2, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = 11; +//│ param01 = tmp1; +//│ x2 = param01; +//│ return x2; +//│ } else { +//│ tmp2 = 22; +//│ param0 = tmp2; +//│ x1 = param0; +//│ return x1; +//│ } +//│ } +//│ function c(x) { +//│ x +//│ } +//│ tmp = g(true); +//│ return c(tmp); +//│ } +//│ this.test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 From 476511ee70b1668d4a317b4a241168c293f1683a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:00:10 +0800 Subject: [PATCH 015/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 26 ++++++++++++++++--- .../src/test/mlscript/deforest/simple.mls | 17 +++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6286adc50a..b11544ade3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -102,6 +102,23 @@ extension (b: Block) case Assign(lhs, rhs, rest) => Assign(lhs, head, rest.replaceAssignments(tail)) case Nil => b + def hasImplctRet: Boolean = b match + case Match(scrut, arms, dflt, rest) => arms.map(a => a._2).appendedAll(dflt).exists(b => b.hasImplctRet) + case Return(res, implct) => implct + case Assign(lhs, rhs, rest) => rest.hasImplctRet + case Define(defn, rest) => rest.hasImplctRet + case End(msg) => false + case _ => false + // case Throw(exc) => + // case Label(label, body, rest) => + // case Break(label) => + // case Continue(label) => + // case Begin(sub, rest) => + // case TryBlock(sub, finallyDo, rest) => + // case AssignField(symbol) => + // case HandleBlock(lhs, res, cls, handlers, body, rest) => + // case HandleBlockReturn(res) => + class Deforest(using TL, Raise, Elaborator.State): @@ -385,7 +402,7 @@ class Deforest(using TL, Raise, Elaborator.State): if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && dtorSources.contains(scrut) then // TODO: rest match - case End(msg) => Return(scrut, true) // TODO: true or false? + case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? case _ => rest else Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) @@ -414,7 +431,7 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => k(call) case Some(c) => - assert(ctorDests(call).size == 1) + assert(ctorDests(call).size == 1, s"$call has more than one destination") ctorDests(call).headOption match case None => k(call) case Some(dest) => @@ -445,10 +462,11 @@ class Deforest(using TL, Raise, Elaborator.State): case None => k(s) case Some(dests) => - val body = dests.map(d => d._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get._2) + val body = dests.map(d => d._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get) + assert(body.size == 1, s"$s has more than one destination") tl.log(mod.toString + " ----> " + body) - body.head.mapRes(k) + body.head._2.mapRes(k) case Value.Ref(l) => k(Value.Ref(l)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 5071f7f5c0..3972598258 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -45,9 +45,10 @@ test() //│ tmp = 2; //│ } //│ x = tmp; -//│ x +//│ return x; //│ } //│ this.test() +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -102,9 +103,10 @@ test() //│ tmp = x1; //│ } //│ x = tmp; -//│ x +//│ return x; //│ } //│ this.test() +//│ = A { class: [class A] } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A { class: [class A] } @@ -159,7 +161,7 @@ test() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ function f(a) { -//│ a +//│ return a; //│ } //│ function test() { //│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; @@ -176,9 +178,10 @@ test() //│ tmp = globalThis.f(x1); //│ } //│ x = tmp; -//│ x +//│ return x; //│ } //│ this.test() +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -244,12 +247,13 @@ test() //│ } //│ } //│ function c(x) { -//│ x +//│ return x; //│ } //│ tmp = g(true); //│ return c(tmp); //│ } //│ this.test() +//│ = 11 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 @@ -370,9 +374,10 @@ map(enumFromTo(1, 4)) //│ } //│ } //│ function map(ls) { -//│ ls +//│ return ls; //│ } //│ tmp = this.enumFromTo(1, 4); //│ this.map(tmp) +//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } From c8382087db5b5ef560a7d03ec9d1bef49b61f3a7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:01:14 +0800 Subject: [PATCH 016/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 82 ++++++++++++++----- .../src/test/mlscript/deforest/simple.mls | 19 +++-- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b11544ade3..7f3901bcaf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -324,10 +324,9 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[Call | Select, Map[Value.Ref, Ls[Case -> Block]]].withDefaultValue(Map.empty) + val ctorDests = mutable.Map.empty[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])].withDefaultValue(Map.empty -> Nil) val dtorSources = mutable.Map.empty[Value.Ref, Ls[Call | Select]].withDefaultValue(Nil) - val rewritingSel = mutable.Set.empty[Select] def resolveConstraints: Unit = @@ -341,18 +340,28 @@ class Deforest(using TL, Raise, Elaborator.State): (prod, cons) match case (Ctor(ctor, args, expr), Dtor(scrut, arms)) => - ctorDests += expr -> (ctorDests(expr).updatedWith(scrut){ - case None => Some(arms) - case Some(v) => Some(arms ::: v) - }) + ctorDests.updateWith(expr){ + case Some(d -> s) => + Some( + (d.updatedWith(scrut){ + case None => Some(arms) + case Some(v) => Some(arms ::: v) + }) -> s + ) + case None => Some(Map(scrut -> arms) -> Nil) + } dtorSources += scrut -> (expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor - case (Ctor(ctor, args, _), FieldSel(field, consVar, sel)) => + case (Ctor(ctor, args, expr), FieldSel(field, consVar, sel)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else + ctorDests.updateWith(expr){ + case Some(d -> s) => Some(d -> (sel :: s)) + case None => Some(Map.empty -> (sel :: Nil)) + } args.find(a => a._1.id == field).map(p => - rewritingSel.add(sel) + // rewritingSel.add(sel) handle(p._2 -> consVar) ) case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? @@ -388,9 +397,43 @@ class Deforest(using TL, Raise, Elaborator.State): case (NoProd, NoCons) => () constraints.foreach(c => handle(c)(using mutable.Set.empty)) - + + def filteredCtorDests: Map[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = + val res = mutable.Map.empty[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] + ctorDests.foreach { case (ctor, dests) => + val (dtors, sels) = dests + val filteredDtor = { + if dtors.size == 0 && sels.size == 1 then Some(sels.head) + else if dtors.size == 0 && sels.size > 1 then + None + // throw Error("more than one consumer") TODO: + else if dtors.size > 1 then + None + // throw Error("more than one consumer") TODO: + else if dtors.size == 1 then + val Value.Ref(scrut) = dtors.head._1 + if sels.forall{ case Select(Value.Ref(l), nme) => l == scrut; case _ => false } then + Some(dtors.head._1 -> (dtors.head._2 -> sels)) + else + None + // throw Error("more than one consumer") TODO: + else ??? + } + res.updateWith(ctor){_ => filteredDtor} + } + res.toMap + + def rewritingSel = filteredCtorDests.values.flatMap { + case Select(p, nme) => None // TODO: + case scrut -> (_ -> sels) => sels + } + + def filteredDtors = filteredCtorDests.values.collect { + case (scrut -> _) => scrut + }.toSet + def rewrite(p: Program) = Program( p.imports, @@ -399,7 +442,7 @@ class Deforest(using TL, Raise, Elaborator.State): def rewriteBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && dtorSources.contains(scrut) then + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut) then // TODO: rest match case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? @@ -431,11 +474,12 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => k(call) case Some(c) => - assert(ctorDests(call).size == 1, s"$call has more than one destination") - ctorDests(call).headOption match + // assert(ctorDests(call).size == 1, s"$call has more than one destination") + filteredCtorDests.get(call) match case None => k(call) - case Some(dest) => - val body = dest._2.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + case Some(Select(p, nme)) => ??? + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 tl.log(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) @@ -458,15 +502,15 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match case None => k(s) case Some(mod) => - ctorDests.get(s) match + filteredCtorDests.get(s) match case None => k(s) - case Some(dests) => - val body = dests.map(d => d._2.find{ case (Case.Cls(m, _) -> body) => m === mod }.get) - assert(body.size == 1, s"$s has more than one destination") + case Some(Select(p, nme)) => ??? // TODO: + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) - body.head._2.mapRes(k) + body._2.mapRes(k) case Value.Ref(l) => k(Value.Ref(l)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 3972598258..5949e99567 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -258,7 +258,6 @@ test() //│ = 11 -// FIXME: shouldn't fuse this... :sjs :deforest let s = if true then AA(A) else BB(B) @@ -286,18 +285,24 @@ if s is //│ } //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let scrut, scrut1, tmp, tmp1, tmp2; +//│ let scrut, scrut1, tmp; //│ scrut = true; //│ if (scrut) { -//│ tmp1 = this.A; -//│ tmp = tmp1; +//│ tmp = this.AA(this.A); //│ } else { -//│ tmp2 = this.B; -//│ tmp = tmp2; +//│ tmp = this.BB(this.B); //│ } //│ this.s = tmp; //│ scrut1 = this.s; -//│ scrut1 +//│ if (scrut1 instanceof this.AA.class) { +//│ this.s.x +//│ } else { +//│ if (scrut1 instanceof this.BB.class) { +//│ this.s.x +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ } //│ = A { class: [class A] } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A { class: [class A] } From 342db56251e6eb8a8a94fc45c220ab9de23a5cf9 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:24:00 +0800 Subject: [PATCH 017/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7f3901bcaf..329a61fab0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -7,6 +7,7 @@ import syntax.{Literal, Tree} import utils.{TL, tl} import mlscript.utils.*, shorthands.* import scala.collection.mutable +import hkmc2.syntax.Keyword.__ type StratVar type StratVarId = Uid[StratVar] @@ -374,16 +375,25 @@ class Deforest(using TL, Raise, Elaborator.State): sel.updateFilter(l, sel.filter(p)) handle(l -> cons) case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, selExpr)) => - if sel.filter(p).contains(ctor) then + if sel.filter.get(p).forall(_.contains(ctor)) then handle(l -> cons) else () case _ => handle(l -> cons) } - // lowerBounds(uid).foreach(p => handle(p -> cons)) - case (_, ConsVar(uid, name)) => + case (_, c@ConsVar(uid, name)) => lowerBounds += uid -> (prod :: lowerBounds(uid)) - upperBounds(uid).foreach(c => handle(prod -> c)) + upperBounds(uid).foreach { u => + (prod, u) match + case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, selExpr)) => + if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then + handle(prod -> u) + else + () + case (_: ProdVar, _) => ??? // unreachable, should be handled above + case _ => handle(prod -> u) + } + // upperBounds(uid).foreach(c => handle(prod -> c)) case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls, _)) => ??? case (ProdFun(l, r), FieldSel(field, consVar, sel)) => ??? From ad91803fd31fce33498188aad7dedc2da9408da9 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 21 Jan 2025 22:38:24 +0800 Subject: [PATCH 018/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 329a61fab0..8056d2fe68 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -7,7 +7,6 @@ import syntax.{Literal, Tree} import utils.{TL, tl} import mlscript.utils.*, shorthands.* import scala.collection.mutable -import hkmc2.syntax.Keyword.__ type StratVar type StratVarId = Uid[StratVar] @@ -326,7 +325,7 @@ class Deforest(using TL, Raise, Elaborator.State): val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) val ctorDests = mutable.Map.empty[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])].withDefaultValue(Map.empty -> Nil) - val dtorSources = mutable.Map.empty[Value.Ref, Ls[Call | Select]].withDefaultValue(Nil) + val dtorSources = mutable.Map.empty[Value.Ref | Select, Ls[Call | Select]].withDefaultValue(Nil) def resolveConstraints: Unit = @@ -361,6 +360,10 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(d -> s) => Some(d -> (sel :: s)) case None => Some(Map.empty -> (sel :: Nil)) } + dtorSources.updateWith(sel){ + case None => Some(expr :: Nil) + case Some(value) => Some(expr :: value) + } args.find(a => a._1.id == field).map(p => // rewritingSel.add(sel) handle(p._2 -> consVar) @@ -409,26 +412,63 @@ class Deforest(using TL, Raise, Elaborator.State): constraints.foreach(c => handle(c)(using mutable.Set.empty)) + // ======== after resolving constraints ====== + + def resolveClashes = + type CtorToDtor = Map[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])] + type DtorToCtor = Map[Value.Ref | Select, Ls[Call | Select]] + + def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[Call | Select]): CtorToDtor -> DtorToCtor = + if rm.isEmpty then + ctorDests -> dtorSources + else + val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) + removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[Value.Ref | Select]{ case (mat -> sels) => mat.keySet ++ sels }.toSet) + + def removeDtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[Value.Ref | Select]): CtorToDtor -> DtorToCtor = + if rm.isEmpty then + ctorDests -> dtorSources + else + val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) + removeCtor(ctorDests, newDtorSources, toDelete.values.flatten.toSet) + + val ctorToDtor = ctorDests.toMap + val dtorToCtor = dtorSources.toMap + + removeCtor( + ctorToDtor, + dtorToCtor, + ctorToDtor.filterNot { case (_, dests) => + val (dtors, sels) = dests + (dtors.size == 0 && sels.size == 1) + || (dtors.size == 1 && { + val Value.Ref(scrut) = dtors.head._1 + sels.forall { case Select(Value.Ref(l), nme) => l == scrut; case _ => false } + }) + }.keySet + ) + + def filteredCtorDests: Map[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = val res = mutable.Map.empty[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] - ctorDests.foreach { case (ctor, dests) => + resolveClashes._1.foreach { case (ctor, dests) => val (dtors, sels) = dests val filteredDtor = { if dtors.size == 0 && sels.size == 1 then Some(sels.head) else if dtors.size == 0 && sels.size > 1 then + throw Error("more than one consumer") None - // throw Error("more than one consumer") TODO: else if dtors.size > 1 then + throw Error("more than one consumer") None - // throw Error("more than one consumer") TODO: else if dtors.size == 1 then val Value.Ref(scrut) = dtors.head._1 if sels.forall{ case Select(Value.Ref(l), nme) => l == scrut; case _ => false } then Some(dtors.head._1 -> (dtors.head._2 -> sels)) else + throw Error("more than one consumer") None - // throw Error("more than one consumer") TODO: else ??? } res.updateWith(ctor){_ => filteredDtor} From c2ecb6ebd4012149376a1881c9e28b7911f1348f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:02:04 +0800 Subject: [PATCH 019/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 98 +++++++++---------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8056d2fe68..1085eb9a16 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,18 +18,22 @@ sealed abstract class ProdStrat extends Strat sealed abstract class ConsStrat extends Strat +class StratVarState(val uid: StratVarId, val name: Str = ""): + lazy val asProdStrat = ProdVar(this) + lazy val asConsStrat = ConsVar(this) + // enum ProdStrat: -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: Call | Select) extends ProdStrat +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: Call | Select) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat -case class ProdVar(uid: StratVarId, name: Str = "") extends ProdStrat with StratVarTrait(uid, name) +case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat // enum ConsStrat: case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat -case class FieldSel(field: Tree.Ident, consVar: ConsVar, expr: Select) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: Select) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat -case class ConsVar(uid: StratVarId, name: Str = "") extends ConsStrat with StratVarTrait(uid, name) +case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat trait FieldSelTrait: @@ -39,11 +43,12 @@ trait FieldSelTrait: def updateFilter(p: ProdVar, c: Ls[ClsOrModSymbol]) = filter += p -> (c ::: filter(p)) -trait StratVarTrait(uid: StratVarId, name: Str): +trait StratVarTrait(stratState: StratVarState): this: ProdVar | ConsVar => - lazy val asProdStrat = ProdVar(uid, name) - lazy val asConsStrat = ConsVar(uid, name) + lazy val asProdStrat = stratState.asProdStrat + lazy val asConsStrat = stratState.asConsStrat + lazy val uid = stratState.uid extension (r: Result) def replaceSelect(using p: Set[Select], args: Map[Tree.Ident, Path]): Result = r match @@ -129,8 +134,6 @@ class Deforest(using TL, Raise, Elaborator.State): def apply(p: Program) = processBlock(p.main) - // constraints.foreach(tl.log) - // tl.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") resolveConstraints tl.log("upper:") @@ -162,26 +165,13 @@ class Deforest(using TL, Raise, Elaborator.State): case _: LocalSymbol => symToStrat(s) case _: FlowSymbol => symToStrat(s) - // def getClsFields(s: ClassSymbol) = s.tree.paramLists.head.fields.map { - // case Tree.InfixApp(id: Tree.Ident, kw, rhs) => id - // case id: Tree.Ident => id - // } def getClsFields(s: ClassSymbol) = s.tree.clsParams def freshVar(nme: String = "")(using filter: Opt[ProdVar -> ClsOrModSymbol] = N): ProdVar -> ConsVar = val newId = vuid.nextUid - val p: ProdVar = ProdVar(newId, nme) - val c: ConsVar = ConsVar(newId, nme) - // filter.foreach{ case v -> cls => - // p.filter.updateWith(v){ - // case None => Some(cls :: Nil) - // case Some(value) => Some(cls :: value) - // } - // c.filter.updateWith(v){ - // case None => Some(cls :: Nil) - // case Some(value) => Some(cls :: value) - // } - // } + val s = StratVarState(newId, nme) + val p: ProdVar = ProdVar(s) + val c: ConsVar = ConsVar(s) p -> c def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c @@ -260,7 +250,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, s)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(s)) val appRes = freshVar() constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -271,12 +261,12 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c) + Ctor(s, clsFields.zip(argsTpe).toMap)(c) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c) + Ctor(s, clsFields.zip(argsTpe).toMap)(c) case _ => // then it is a function val appRes = freshVar("call_" + l.nme + "_res") constrain(getStratOfSym(l), ConsFun(argsTpe, appRes._2)) @@ -297,20 +287,20 @@ class Deforest(using TL, Raise, Elaborator.State): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asMod.isDefined => - Ctor(s.asMod.get, Map.empty, sel) + Ctor(s.asMod.get, Map.empty)(sel) case _ => val pStrat = processResult(p) inArm match case Some(armP -> clsSym) if armP === pStrat => // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() - val selStrat = FieldSel(nme, tpeVar._2, sel) + val selStrat = FieldSel(nme, tpeVar._2)(sel) selStrat.updateFilter(armP, clsSym :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2, sel)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(sel)) tpeVar._1 case Value.Ref(l) => getStratOfSym(l) @@ -339,8 +329,8 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (Ctor(ctor, args, expr), Dtor(scrut, arms)) => - ctorDests.updateWith(expr){ + case (ctorStrat@Ctor(ctor, args), Dtor(scrut, arms)) => + ctorDests.updateWith(ctorStrat.expr){ case Some(d -> s) => Some( (d.updatedWith(scrut){ @@ -350,45 +340,45 @@ class Deforest(using TL, Raise, Elaborator.State): ) case None => Some(Map(scrut -> arms) -> Nil) } - dtorSources += scrut -> (expr :: dtorSources(scrut)) + dtorSources += scrut -> (ctorStrat.expr :: dtorSources(scrut)) // TODO: keep track of this ctor to dtor - case (Ctor(ctor, args, expr), FieldSel(field, consVar, sel)) => + case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else - ctorDests.updateWith(expr){ - case Some(d -> s) => Some(d -> (sel :: s)) - case None => Some(Map.empty -> (sel :: Nil)) + ctorDests.updateWith(ctorStrat.expr){ + case Some(d -> s) => Some(d -> (selDtor.expr :: s)) + case None => Some(Map.empty -> (selDtor.expr :: Nil)) } - dtorSources.updateWith(sel){ - case None => Some(expr :: Nil) - case Some(value) => Some(expr :: value) + dtorSources.updateWith(selDtor.expr){ + case None => Some(ctorStrat.expr :: Nil) + case Some(value) => Some(ctorStrat.expr :: value) } args.find(a => a._1.id == field).map(p => // rewritingSel.add(sel) handle(p._2 -> consVar) ) - case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? + case (Ctor(ctor, args), ConsFun(l, r)) => ??? - case (p@ProdVar(uid, name), _) => - upperBounds += uid -> (cons :: upperBounds(uid)) - lowerBounds(uid).foreach{ l => + case (p: ProdVar, _) => + upperBounds += p.uid -> (cons :: upperBounds(p.uid)) + lowerBounds(p.uid).foreach{ l => (l, cons) match - case (l: ProdVar, sel@FieldSel(field, consVar, selExpr)) => + case (l: ProdVar, sel@FieldSel(field, consVar)) => sel.updateFilter(l, sel.filter(p)) handle(l -> cons) - case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, selExpr)) => + case (Ctor(ctor, args), sel@FieldSel(field, consVar)) => if sel.filter.get(p).forall(_.contains(ctor)) then handle(l -> cons) else () case _ => handle(l -> cons) } - case (_, c@ConsVar(uid, name)) => - lowerBounds += uid -> (prod :: lowerBounds(uid)) - upperBounds(uid).foreach { u => + case (_, c: ConsVar) => + lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) + upperBounds(c.uid).foreach { u => (prod, u) match - case (Ctor(ctor, args, expr), sel@FieldSel(field, consVar, selExpr)) => + case (Ctor(ctor, args), sel@FieldSel(field, consVar)) => if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then handle(prod -> u) else @@ -397,15 +387,15 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => handle(prod -> u) } // upperBounds(uid).foreach(c => handle(prod -> c)) - case (Ctor(ctor, args, _), NoCons) => () + case (Ctor(ctor, args), NoCons) => () case (ProdFun(l, r), Dtor(cls, _)) => ??? - case (ProdFun(l, r), FieldSel(field, consVar, sel)) => ??? + case (ProdFun(l, r), FieldSel(field, consVar)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () case (NoProd, Dtor(cls, _)) => () - case (NoProd, FieldSel(field, consVar, sel)) => () + case (NoProd, FieldSel(field, consVar)) => () case (NoProd, ConsFun(l, r)) => () case (NoProd, NoCons) => () From e37fb12dbd5cc327cb2ca4fe372a94eb57ae62e6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:10:24 +0800 Subject: [PATCH 020/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1085eb9a16..c087b8147c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -22,6 +22,17 @@ class StratVarState(val uid: StratVarId, val name: Str = ""): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) +object StratVarState: + private object StateHandler extends Uid.Handler[StratVar] + private val vuid = StateHandler.State() + + def freshVar(nme: String = "") = + val newId = vuid.nextUid + val s = StratVarState(newId, nme) + val p = s.asProdStrat + val c = s.asConsStrat + p -> c + // enum ProdStrat: case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: Call | Select) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat @@ -127,10 +138,6 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): - // import ProdStrat.* - // import ConsStrat.* - - object StratVarId extends Uid.Handler[StratVar] def apply(p: Program) = processBlock(p.main) @@ -150,7 +157,6 @@ class Deforest(using TL, Raise, Elaborator.State): rewrite(p) - val vuid = StratVarId.State() var constraints: Ls[ProdStrat -> ConsStrat] = Nil @@ -167,13 +173,8 @@ class Deforest(using TL, Raise, Elaborator.State): def getClsFields(s: ClassSymbol) = s.tree.clsParams - def freshVar(nme: String = "")(using filter: Opt[ProdVar -> ClsOrModSymbol] = N): ProdVar -> ConsVar = - val newId = vuid.nextUid - val s = StratVarState(newId, nme) - val p: ProdVar = ProdVar(s) - val c: ConsVar = ConsVar(s) - p -> c - + import StratVarState.freshVar + def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def processBlock(b: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N): ProdStrat = b match From 85cf293d6e6d06515cc76c3f225eac8a552c5047 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:43:01 +0800 Subject: [PATCH 021/303] wip --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c087b8147c..1ce3c5d59b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -23,10 +23,8 @@ class StratVarState(val uid: StratVarId, val name: Str = ""): lazy val asConsStrat = ConsVar(this) object StratVarState: - private object StateHandler extends Uid.Handler[StratVar] - private val vuid = StateHandler.State() - def freshVar(nme: String = "") = + def freshVar(nme: String = "")(using vuid: Uid.Handler[StratVar]#State) = val newId = vuid.nextUid val s = StratVarState(newId, nme) val p = s.asProdStrat @@ -173,6 +171,8 @@ class Deforest(using TL, Raise, Elaborator.State): def getClsFields(s: ClassSymbol) = s.tree.clsParams + object StratVarUidHandler extends Uid.Handler[StratVar]() + given Uid.Handler[StratVar]#State = StratVarUidHandler.State() import StratVarState.freshVar def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c From faeb52dd09f8e597c0ed691b2815b93772c2bdb8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 22 Jan 2025 22:45:11 +0800 Subject: [PATCH 022/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 30 ++- .../src/test/mlscript/deforest/simple.mls | 190 +++++++++++++++++- 2 files changed, 210 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1ce3c5d59b..f9ee1b3327 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -21,6 +21,8 @@ sealed abstract class ConsStrat extends Strat class StratVarState(val uid: StratVarId, val name: Str = ""): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) + + override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" object StratVarState: @@ -152,6 +154,11 @@ class Deforest(using TL, Raise, Elaborator.State): // tl.log("dtor -> ctor:") // dtorSources.foreach(l => tl.log("\t" + l._1.toString().take(20) + " ===> " + l._2.toString().take(20))) + tl.log("ctor -> dtor") + resolveClashes._1.foreach(u => tl.log("\t" + u)) + tl.log("dtor -> ctor") + resolveClashes._2.foreach(l => tl.log("\t" + l)) + rewrite(p) @@ -405,7 +412,7 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== - def resolveClashes = + lazy val resolveClashes = type CtorToDtor = Map[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])] type DtorToCtor = Map[Value.Ref | Select, Ls[Call | Select]] @@ -441,7 +448,7 @@ class Deforest(using TL, Raise, Elaborator.State): - def filteredCtorDests: Map[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = + lazy val filteredCtorDests: Map[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = val res = mutable.Map.empty[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] resolveClashes._1.foreach { case (ctor, dests) => val (dtors, sels) = dests @@ -466,12 +473,12 @@ class Deforest(using TL, Raise, Elaborator.State): } res.toMap - def rewritingSel = filteredCtorDests.values.flatMap { + lazy val rewritingSel = filteredCtorDests.values.flatMap { case Select(p, nme) => None // TODO: case scrut -> (_ -> sels) => sels } - def filteredDtors = filteredCtorDests.values.collect { + lazy val filteredDtors = filteredCtorDests.values.collect { case (scrut -> _) => scrut }.toSet @@ -513,11 +520,20 @@ class Deforest(using TL, Raise, Elaborator.State): case call@Call(f, args) => f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case None => k(call) + case None => + k(call) // TODO: args need to be rewritten case Some(c) => // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call) match - case None => k(call) + case None => + val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? + val tmpSym = TempSymbol(N) + v -> tmpSym + } + newArgSyms.foldRight( + k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) + ){ case (arg, sym) -> rest => + rewriteResult(arg)(r => Assign(sym, r, rest)) } case Some(Select(p, nme)) => ??? case Some(scrut -> (arms, sels)) => val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 @@ -535,7 +551,7 @@ class Deforest(using TL, Raise, Elaborator.State): Assign(tmp, r, rest) } case Value.Ref(l) => l.asCls match - case None => k(call) + case None => k(call) // TODO: args need to be rewritten case Some(c) => ??? // TODO: case Value.Lam(params, body) => k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 5949e99567..3e363138b8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -186,6 +186,85 @@ test() //│ = 1 + +:sjs +:deforest +fun f(a) = if a is + A then 1 + B then 2 +fun test() = + let x = if true then AA(A) else BB(B) + let y = x + if y is + AA then f(x.x) + BB then f(x.x) +test() +//│ JS (unsanitized): +//│ function f(a) { +//│ if (a instanceof globalThis.A.class) { +//│ return 1; +//│ } else { +//│ if (a instanceof globalThis.B.class) { +//│ return 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ function test() { +//│ let x, scrut, y, tmp; +//│ scrut = true; +//│ if (scrut) { +//│ tmp = globalThis.AA(globalThis.A); +//│ } else { +//│ tmp = globalThis.BB(globalThis.B); +//│ } +//│ x = tmp; +//│ y = x; +//│ if (y instanceof globalThis.AA.class) { +//│ return globalThis.f(x.x); +//│ } else { +//│ if (y instanceof globalThis.BB.class) { +//│ return globalThis.f(x.x); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ this.test() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ function f(a) { +//│ return a; +//│ } +//│ function test() { +//│ let x, scrut, y, tmp, tmp1, tmp2; +//│ scrut = true; +//│ if (scrut) { +//│ tmp1 = 1; +//│ tmp = globalThis.AA(tmp1); +//│ } else { +//│ tmp2 = 2; +//│ tmp = globalThis.BB(tmp2); +//│ } +//│ x = tmp; +//│ y = x; +//│ if (y instanceof globalThis.AA.class) { +//│ return globalThis.f(x.x); +//│ } else { +//│ if (y instanceof globalThis.BB.class) { +//│ return globalThis.f(x.x); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ this.test() +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 + + :sjs :deforest fun test() = @@ -285,12 +364,14 @@ if s is //│ } //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let scrut, scrut1, tmp; +//│ let scrut, scrut1, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut) { -//│ tmp = this.AA(this.A); +//│ tmp1 = this.A; +//│ tmp = this.AA(tmp1); //│ } else { -//│ tmp = this.BB(this.B); +//│ tmp2 = this.B; +//│ tmp = this.BB(tmp2); //│ } //│ this.s = tmp; //│ scrut1 = this.s; @@ -386,3 +467,106 @@ map(enumFromTo(1, 4)) //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } + + + +// FIXME: +:sjs +:deforest +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + (if ls is + Nil then f => Nil + Cons(h, t) then f => Cons(f(h), map(f, t)) + )(f) +map(x => x + 4, enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, tmp1, tmp2; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ return globalThis.Cons(a, tmp2); +//│ } else { +//│ return globalThis.Nil; +//│ } +//│ } +//│ function map(f, ls) { +//│ let param0, param1, h, t, tmp1; +//│ if (ls instanceof globalThis.Nil.class) { +//│ tmp1 = (f1) => { +//│ return globalThis.Nil; +//│ }; +//│ } else { +//│ if (ls instanceof globalThis.Cons.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp1 = (f1) => { +//│ let tmp2, tmp3; +//│ tmp2 = f1(h) ?? null; +//│ tmp3 = globalThis.map(f1, t); +//│ return globalThis.Cons(tmp2, tmp3); +//│ }; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ return tmp1(f) ?? null; +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map((x) => { +//│ return x + 4; +//│ }, tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let tmp; +//│ function enumFromTo(a, b) { +//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4; +//│ scrut = a < b; +//│ if (scrut) { +//│ tmp1 = a + 1; +//│ tmp2 = globalThis.enumFromTo(tmp1, b); +//│ tmp3 = a; +//│ tmp4 = tmp2; +//│ param0 = tmp3; +//│ param1 = tmp4; +//│ h = param0; +//│ t = param1; +//│ return (f) => { +//│ let tmp5, tmp6; +//│ tmp5 = f(h) ?? null; +//│ tmp6 = globalThis.map(f, t); +//│ return globalThis.Cons(tmp5, tmp6); +//│ }; +//│ } else { +//│ return (f) => { +//│ return globalThis.Nil; +//│ }; +//│ } +//│ } +//│ function map(f, ls) { +//│ return ls; +//│ return $tmp_not_in_scope_(f) ?? null; +//│ } +//│ tmp = this.enumFromTo(1, 4); +//│ this.map((x) => { +//│ return x + 4; +//│ }, tmp) +//│ = [Function (anonymous)] +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } + + + +// :sjs +// :deforest +// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +// fun sum(ls) = if ls is +// Nil then 0 +// Cons(h, t) then h + sum(t) +// sum(enumFromTo(1,10)) + From 3783c315803d6c486686a2132a1269fd6dcef0aa Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:43:34 +0800 Subject: [PATCH 023/303] wip: fix after merge --- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../hkmc2/codegen/BlockTransformer.scala | 2 +- .../scala/hkmc2/codegen/Deforestation.scala | 10 ++--- .../scala/hkmc2/codegen/HandlerLowering.scala | 4 +- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 44 ++++++++++--------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 2c08902f3b..f0e4e1f711 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -316,7 +316,7 @@ extension (k: Block => Block) def continue(l: Local): Block = k.rest(Continue(l)) def define(defn: Defn) = k.chain(Define(defn, _)) def end = k.rest(End()) - def ifthen(scrut: Path, cse: Case, trm: Block): Block => Block = k.chain(Match(scrut, cse -> trm :: Nil, N, _)) + def ifthen(scrut: Path, cse: Case, trm: Block): Block => Block = k.chain(Match(scrut.asInstanceOf[Value.Ref], cse -> trm :: Nil, N, _)) def label(label: Local, body: Block) = k.chain(Label(label, body, _)) def ret(r: Result) = k.rest(Return(r, false)) def staticif(b: Boolean, f: (Block => Block) => (Block => Block)) = if b then k.transform(f) else k diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 72c501efc9..8b9538cd19 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -41,7 +41,7 @@ class BlockTransformer(subst: SymbolSubst): if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) - then b else Match(scrut2, arms2, dflt2, rst2) + then b else Match(scrut2.asInstanceOf[Value.Ref], arms2, dflt2, rst2) case Label(lbl, bod, rst) => val lbl2 = applyLocal(lbl) val bod2 = applyBlock(bod) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f9ee1b3327..8dbcb551d8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -10,7 +10,7 @@ import scala.collection.mutable type StratVar type StratVarId = Uid[StratVar] -type ClsOrModSymbol = ClassSymbol | ModuleSymbol +type ClsOrModSymbol = ClassLikeSymbol sealed abstract class Strat @@ -91,7 +91,7 @@ extension (b: Block) case Begin(sub, rest) => ??? case TryBlock(sub, finallyDo, rest) => ??? case AssignField(_, _, _, _) => ??? - case HandleBlock(lhs, res, cls, handlers, body, rest) => ??? + case _: HandleBlock => ??? case HandleBlockReturn(res) => ??? case End(msg) => ??? @@ -220,7 +220,7 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(rest) case Define(defn, rest) => defn match - case FunDefn(sym, params, body) => + case FunDefn(_, sym, params, body) => val funSymStratVar = freshVar(sym.nme) symToStrat += sym -> funSymStratVar._1 val param = params.head match @@ -229,7 +229,7 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(funStrat, funSymStratVar._2) funSymStratVar._1 case ValDefn(owner, k, sym, rhs) => NoProd // TODO: - case ClsLikeDefn(sym, k, parentSym, methods, privateFields, publicFields, preCtor, ctor) => NoProd + case _: ClsLikeDefn => NoProd processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd @@ -504,7 +504,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Begin(sub, rest) => Begin(rewriteBlock(sub), rewriteBlock(rest)) case d@Define(defn, rest) => defn match - case FunDefn(sym, params, body) => Define(FunDefn(sym, params, rewriteBlock(body)), rewriteBlock(rest)) + case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, rewriteBlock(body)), rewriteBlock(rest)) case _ => d case End(msg) => End(msg) case Throw(exc) => rewriteResult(exc)(Throw.apply) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 94d2bffb44..c779f2c404 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -541,7 +541,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): // match block representing the function body val mainMatchCases = parts.toList.map(b => (Case.Lit(Tree.IntLit(b.id)), transformPart(b.blk))) val mainMatchBlk = Match( - pcSymbol.asPath, + pcSymbol.asPath.asInstanceOf[Value.Ref], mainMatchCases, N, End() @@ -564,7 +564,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): lbl else Match( - pcSymbol.asPath, + pcSymbol.asPath.asInstanceOf[Value.Ref], assignedResumedCases, N, lbl diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index e1fcf5ff5a..5f1262dd11 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -56,27 +56,27 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: try super.run() finally if hostCreated then host.terminate() - def mkQuery(prefix: Str, jsStr: Str)(using Raise) = + def mkQuery(prefix: Str, preStr: Str, jsStr: Str)(using Raise) = import hkmc2.Message.MessageContext val queryStr = jsStr.replaceAll("\n", " ") - val (reply, stderr) = host.query(queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) + val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) reply match case ReplHost.Result(content, stdout) => - if silent.isUnset then - stdout match - case None | Some("") => - case Some(str) => - str.splitSane('\n').foreach: line => - output(s"> ${line}") - content match - case "undefined" => - case "null" => - case _ => - expect.get match - case S(expected) if content != expected => raise: - ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, - source = Diagnostic.Source.Runtime) - case _ => output(s"$prefix= ${content}") + stdout match + case None | Some("") => + case Some(str) => + str.splitSane('\n').foreach: line => + output(s"> ${line}") + expect.get match + case S(expected) if content != expected && prefix == "" => raise: + ErrorReport(msg"Expected: ${expected}, got: ${content}" -> N :: Nil, + source = Diagnostic.Source.Runtime) + case _ => + content match + case "undefined" => + case "null" => + case _ => + if silent.isUnset then output(s"$prefix= ${content}") case ReplHost.Empty => case ReplHost.Unexecuted(message) => ??? case ReplHost.Error(isSyntaxError, message, otherOutputs) => @@ -134,13 +134,15 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(deforestRes.showAsTree) output("\n") val nestedScp = baseScp.nest - val je = nestedScp.givenIn: - jsb.program(deforestRes, N, wd) + val (pre, je) = nestedScp.givenIn: + jsb.worksheet(deforestRes) output("==== JS (deforested): ====") val jsStr = je.stripBreaks.mkString(100) + val preStr = pre.stripBreaks.mkString(100) + output(preStr) output(jsStr) - mkQuery("", jsStr) + mkQuery("", preStr, jsStr) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet && !showingJSYieldedCompileError then @@ -198,6 +200,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val je = nestedScp.givenIn: jsb.block(le) val jsStr = je.stripBreaks.mkString(100) - mkQuery(s"$nme ", jsStr) + mkQuery(s"$nme ", "", jsStr) From 94014883a413e0417010d8052418c70e880f7445 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 23 Jan 2025 03:02:31 +0800 Subject: [PATCH 024/303] fix after merge --- .../scala/hkmc2/codegen/Deforestation.scala | 96 ++-- .../src/test/mlscript/deforest/simple.mls | 465 +++++++++--------- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 15 +- 3 files changed, 292 insertions(+), 284 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8dbcb551d8..cb41e24f0f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -33,8 +33,10 @@ object StratVarState: val c = s.asConsStrat p -> c + +type CtorExpr = Call | Select | Value.Ref // enum ProdStrat: -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: Call | Select) extends ProdStrat +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat @@ -311,7 +313,10 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(pStrat, FieldSel(nme, tpeVar._2)(sel)) tpeVar._1 - case Value.Ref(l) => getStratOfSym(l) + case v@Value.Ref(l) => l.asMod match + case None => getStratOfSym(l) + case Some(m) => Ctor(m, Map.empty)(v) + case Value.This(sym) => ??? case Value.Lit(lit) => NoProd case Value.Lam(ParamList(_, params, N), body) => @@ -322,8 +327,8 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])].withDefaultValue(Map.empty -> Nil) - val dtorSources = mutable.Map.empty[Value.Ref | Select, Ls[Call | Select]].withDefaultValue(Nil) + val ctorDests = mutable.Map.empty[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])].withDefaultValue(Map.empty -> Nil) + val dtorSources = mutable.Map.empty[Value.Ref | Select, Ls[CtorExpr]].withDefaultValue(Nil) def resolveConstraints: Unit = @@ -413,10 +418,10 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[Call | Select, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])] - type DtorToCtor = Map[Value.Ref | Select, Ls[Call | Select]] + type CtorToDtor = Map[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])] + type DtorToCtor = Map[Value.Ref | Select, Ls[CtorExpr]] - def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[Call | Select]): CtorToDtor -> DtorToCtor = + def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = if rm.isEmpty then ctorDests -> dtorSources else @@ -448,8 +453,8 @@ class Deforest(using TL, Raise, Elaborator.State): - lazy val filteredCtorDests: Map[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = - val res = mutable.Map.empty[Call | Select, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] + lazy val filteredCtorDests: Map[CtorExpr, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = + val res = mutable.Map.empty[CtorExpr, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] resolveClashes._1.foreach { case (ctor, dests) => val (dtors, sels) = dests val filteredDtor = { @@ -518,41 +523,42 @@ class Deforest(using TL, Raise, Elaborator.State): def rewriteResult(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => + def handleCtorCall(c: ClassSymbol) = + // assert(ctorDests(call).size == 1, s"$call has more than one destination") + filteredCtorDests.get(call) match + case None => + val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? + val tmpSym = TempSymbol(N) + v -> tmpSym + } + newArgSyms.foldRight( + k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) + ){ case (arg, sym) -> rest => + rewriteResult(arg)(r => Assign(sym, r, rest)) } + case Some(Select(p, nme)) => ??? + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + tl.log(call.toString() + " ----> " + body) + + val newArgs = args.map(_ => TempSymbol(N)) + // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => + // rewriteResult(a.value): r => + // Assign(tmp, r, rest) + // } + val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap + + args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => + rewriteResult(a.value): r => + Assign(tmp, r, rest) + } f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => k(call) // TODO: args need to be rewritten - case Some(c) => - // assert(ctorDests(call).size == 1, s"$call has more than one destination") - filteredCtorDests.get(call) match - case None => - val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? - val tmpSym = TempSymbol(N) - v -> tmpSym - } - newArgSyms.foldRight( - k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) - ){ case (arg, sym) -> rest => - rewriteResult(arg)(r => Assign(sym, r, rest)) } - case Some(Select(p, nme)) => ??? - case Some(scrut -> (arms, sels)) => - val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - tl.log(call.toString() + " ----> " + body) - - val newArgs = args.map(_ => TempSymbol(N)) - // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => - // rewriteResult(a.value): r => - // Assign(tmp, r, rest) - // } - val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - - args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => - rewriteResult(a.value): r => - Assign(tmp, r, rest) - } + case Some(c) => handleCtorCall(c) case Value.Ref(l) => l.asCls match case None => k(call) // TODO: args need to be rewritten - case Some(c) => ??? // TODO: + case Some(c) => handleCtorCall(c) case Value.Lam(params, body) => k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) case Instantiate(cls, args) => k(r) @@ -562,7 +568,7 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(s) match case None => k(s) - case Some(Select(p, nme)) => ??? // TODO: + case Some(Select(p, nme)) => ??? // TODO: a select consumes an object case Some(scrut -> (arms, sels)) => val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) @@ -570,7 +576,17 @@ class Deforest(using TL, Raise, Elaborator.State): body._2.mapRes(k) - case Value.Ref(l) => k(Value.Ref(l)) + case r@Value.Ref(l) => l.asMod match + case None => k(r) + case Some(mod) => + filteredCtorDests.get(r) match + case None => + k(r) + case Some(Select(p, nme)) => ??? // TODO: a select consumes an object + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get + tl.log(mod.toString + " ----> " + body) + body._2.mapRes(k) case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) case Value.Lam(params, body) => k(Value.Lam(params, rewriteBlock(body))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 3e363138b8..19766419e4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1,5 +1,6 @@ :js +:deforest object A object B class AA(x) @@ -14,40 +15,42 @@ fun test() = B then 2 test() //│ JS (unsanitized): -//│ function test() { +//│ let test1; +//│ test1 = function test() { //│ let x, scrut, tmp; //│ scrut = true; -//│ if (scrut) { -//│ tmp = globalThis.A; +//│ if (scrut === true) { +//│ tmp = A1; //│ } else { -//│ tmp = globalThis.B; +//│ tmp = B1; //│ } //│ x = tmp; -//│ if (x instanceof globalThis.A.class) { +//│ if (x instanceof A1.class) { //│ return 1; //│ } else { -//│ if (x instanceof globalThis.B.class) { +//│ if (x instanceof B1.class) { //│ return 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ this.test() +//│ }; +//│ test1() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ function test() { +//│ let test1; +//│ test1 = function test() { //│ let x, scrut, tmp; //│ scrut = true; -//│ if (scrut) { +//│ if (scrut === true) { //│ tmp = 1; //│ } else { //│ tmp = 2; //│ } //│ x = tmp; //│ return x; -//│ } -//│ this.test() +//│ }; +//│ test1() //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -62,21 +65,22 @@ fun test() = BB(x) then x test() //│ JS (unsanitized): -//│ function test() { +//│ let test4; +//│ test4 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp; //│ scrut = true; -//│ if (scrut) { -//│ tmp = globalThis.AA(globalThis.A); +//│ if (scrut === true) { +//│ tmp = AA1(A1); //│ } else { -//│ tmp = globalThis.BB(globalThis.B); +//│ tmp = BB1(B1); //│ } //│ x = tmp; -//│ if (x instanceof globalThis.AA.class) { +//│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; //│ return x2; //│ } else { -//│ if (x instanceof globalThis.BB.class) { +//│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; //│ return x1; @@ -84,28 +88,29 @@ test() //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ this.test() +//│ }; +//│ test4() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ function test() { +//│ let test4; +//│ test4 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; //│ scrut = true; -//│ if (scrut) { -//│ tmp1 = globalThis.A; +//│ if (scrut === true) { +//│ tmp1 = A1; //│ param01 = tmp1; //│ x2 = param01; //│ tmp = x2; //│ } else { -//│ tmp2 = globalThis.B; +//│ tmp2 = B1; //│ param0 = tmp2; //│ x1 = param0; //│ tmp = x1; //│ } //│ x = tmp; //│ return x; -//│ } -//│ this.test() +//│ }; +//│ test4() //│ = A { class: [class A] } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A { class: [class A] } @@ -123,64 +128,66 @@ fun test() = BB(x) then f(x) test() //│ JS (unsanitized): -//│ function f(a) { -//│ if (a instanceof globalThis.A.class) { +//│ let test7, f1; +//│ f1 = function f(a) { +//│ if (a instanceof A1.class) { //│ return 1; //│ } else { -//│ if (a instanceof globalThis.B.class) { +//│ if (a instanceof B1.class) { //│ return 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ function test() { +//│ }; +//│ test7 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp; //│ scrut = true; -//│ if (scrut) { -//│ tmp = globalThis.AA(globalThis.A); +//│ if (scrut === true) { +//│ tmp = AA1(A1); //│ } else { -//│ tmp = globalThis.BB(globalThis.B); +//│ tmp = BB1(B1); //│ } //│ x = tmp; -//│ if (x instanceof globalThis.AA.class) { +//│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; -//│ return globalThis.f(x2); +//│ return f1(x2); //│ } else { -//│ if (x instanceof globalThis.BB.class) { +//│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; -//│ return globalThis.f(x1); +//│ return f1(x1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ this.test() +//│ }; +//│ test7() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ function f(a) { +//│ let test7, f1; +//│ f1 = function f(a) { //│ return a; -//│ } -//│ function test() { +//│ }; +//│ test7 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; //│ scrut = true; -//│ if (scrut) { +//│ if (scrut === true) { //│ tmp1 = 1; //│ param01 = tmp1; //│ x2 = param01; -//│ tmp = globalThis.f(x2); +//│ tmp = f1(x2); //│ } else { //│ tmp2 = 2; //│ param0 = tmp2; //│ x1 = param0; -//│ tmp = globalThis.f(x1); +//│ tmp = f1(x1); //│ } //│ x = tmp; //│ return x; -//│ } -//│ this.test() +//│ }; +//│ test7() //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -200,66 +207,68 @@ fun test() = BB then f(x.x) test() //│ JS (unsanitized): -//│ function f(a) { -//│ if (a instanceof globalThis.A.class) { +//│ let test10, f4; +//│ f4 = function f(a) { +//│ if (a instanceof A1.class) { //│ return 1; //│ } else { -//│ if (a instanceof globalThis.B.class) { +//│ if (a instanceof B1.class) { //│ return 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ function test() { +//│ }; +//│ test10 = function test() { //│ let x, scrut, y, tmp; //│ scrut = true; -//│ if (scrut) { -//│ tmp = globalThis.AA(globalThis.A); +//│ if (scrut === true) { +//│ tmp = AA1(A1); //│ } else { -//│ tmp = globalThis.BB(globalThis.B); +//│ tmp = BB1(B1); //│ } //│ x = tmp; //│ y = x; -//│ if (y instanceof globalThis.AA.class) { -//│ return globalThis.f(x.x); +//│ if (y instanceof AA1.class) { +//│ return f4(x.x); //│ } else { -//│ if (y instanceof globalThis.BB.class) { -//│ return globalThis.f(x.x); +//│ if (y instanceof BB1.class) { +//│ return f4(x.x); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ this.test() +//│ }; +//│ test10() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ function f(a) { +//│ let test10, f4; +//│ f4 = function f(a) { //│ return a; -//│ } -//│ function test() { +//│ }; +//│ test10 = function test() { //│ let x, scrut, y, tmp, tmp1, tmp2; //│ scrut = true; -//│ if (scrut) { +//│ if (scrut === true) { //│ tmp1 = 1; -//│ tmp = globalThis.AA(tmp1); +//│ tmp = AA1(tmp1); //│ } else { //│ tmp2 = 2; -//│ tmp = globalThis.BB(tmp2); +//│ tmp = BB1(tmp2); //│ } //│ x = tmp; //│ y = x; -//│ if (y instanceof globalThis.AA.class) { -//│ return globalThis.f(x.x); +//│ if (y instanceof AA1.class) { +//│ return f4(x.x); //│ } else { -//│ if (y instanceof globalThis.BB.class) { -//│ return globalThis.f(x.x); +//│ if (y instanceof BB1.class) { +//│ return f4(x.x); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ this.test() +//│ }; +//│ test10() //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -275,25 +284,26 @@ fun test() = c(g(true)) test() //│ JS (unsanitized): -//│ function test() { -//│ let tmp; -//│ function g(x) { +//│ let test13; +//│ test13 = function test() { +//│ let c, g, tmp; +//│ g = function g(x) { //│ let scrut; //│ scrut = true; -//│ if (scrut) { -//│ return globalThis.AA(11); +//│ if (scrut === true) { +//│ return AA1(11); //│ } else { -//│ return globalThis.BB(22); +//│ return BB1(22); //│ } -//│ } -//│ function c(x) { +//│ }; +//│ c = function c(x) { //│ let param0, x1, param01, x2; -//│ if (x instanceof globalThis.AA.class) { +//│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; //│ return x2; //│ } else { -//│ if (x instanceof globalThis.BB.class) { +//│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; //│ return x1; @@ -301,19 +311,20 @@ test() //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } +//│ }; //│ tmp = g(true); //│ return c(tmp); -//│ } -//│ this.test() +//│ }; +//│ test13() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ function test() { -//│ let tmp; -//│ function g(x) { +//│ let test13; +//│ test13 = function test() { +//│ let c, g, tmp; +//│ g = function g(x) { //│ let scrut, param0, x1, param01, x2, tmp1, tmp2; //│ scrut = true; -//│ if (scrut) { +//│ if (scrut === true) { //│ tmp1 = 11; //│ param01 = tmp1; //│ x2 = param01; @@ -324,14 +335,14 @@ test() //│ x1 = param0; //│ return x1; //│ } -//│ } -//│ function c(x) { +//│ }; +//│ c = function c(x) { //│ return x; -//│ } +//│ }; //│ tmp = g(true); //│ return c(tmp); -//│ } -//│ this.test() +//│ }; +//│ test13() //│ = 11 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 @@ -344,46 +355,36 @@ if s is AA then s.x BB then s.x //│ JS (unsanitized): -//│ let scrut, scrut1, tmp; +//│ let s, scrut, tmp; //│ scrut = true; -//│ if (scrut) { -//│ tmp = this.AA(this.A); +//│ if (scrut === true) { +//│ tmp = AA1(A1); //│ } else { -//│ tmp = this.BB(this.B); +//│ tmp = BB1(B1); //│ } -//│ this.s = tmp; -//│ scrut1 = this.s; -//│ if (scrut1 instanceof this.AA.class) { -//│ this.s.x +//│ s = tmp; +//│ if (s instanceof AA1.class) { +//│ s.x //│ } else { -//│ if (scrut1 instanceof this.BB.class) { -//│ this.s.x +//│ if (s instanceof BB1.class) { +//│ s.x //│ } else { //│ throw new this.Error("match error"); //│ } //│ } //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let scrut, scrut1, tmp, tmp1, tmp2; +//│ let s, scrut, tmp, tmp1, tmp2; //│ scrut = true; -//│ if (scrut) { -//│ tmp1 = this.A; -//│ tmp = this.AA(tmp1); -//│ } else { -//│ tmp2 = this.B; -//│ tmp = this.BB(tmp2); -//│ } -//│ this.s = tmp; -//│ scrut1 = this.s; -//│ if (scrut1 instanceof this.AA.class) { -//│ this.s.x +//│ if (scrut === true) { +//│ tmp1 = A1; +//│ tmp = tmp1; //│ } else { -//│ if (scrut1 instanceof this.BB.class) { -//│ this.s.x -//│ } else { -//│ throw new this.Error("match error"); -//│ } +//│ tmp2 = B1; +//│ tmp = tmp2; //│ } +//│ s = tmp; +//│ s //│ = A { class: [class A] } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A { class: [class A] } @@ -393,7 +394,7 @@ if s is - +:deforest object Nil class Cons(h, t) @@ -405,168 +406,150 @@ fun map(ls) = if ls is Cons(h, t) then Cons(h + 4, map(t)) map(enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ return globalThis.Cons(a, tmp2); +//│ let enumFromTo1, map1, tmp4; +//│ enumFromTo1 = function enumFromTo(a, b) { +//│ let scrut1, tmp5, tmp6; +//│ scrut1 = a < b; +//│ if (scrut1 === true) { +//│ tmp5 = a + 1; +//│ tmp6 = enumFromTo1(tmp5, b); +//│ return Cons1(a, tmp6); //│ } else { -//│ return globalThis.Nil; +//│ return Nil1; //│ } -//│ } -//│ function map(ls) { -//│ let param0, param1, h, t, tmp1, tmp2; -//│ if (ls instanceof globalThis.Nil.class) { -//│ return globalThis.Nil; +//│ }; +//│ map1 = function map(ls) { +//│ let param0, param1, h, t, tmp5, tmp6; +//│ if (ls instanceof Nil1.class) { +//│ return Nil1; //│ } else { -//│ if (ls instanceof globalThis.Cons.class) { +//│ if (ls instanceof Cons1.class) { //│ param0 = ls.h; //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp1 = h + 4; -//│ tmp2 = globalThis.map(t); -//│ return globalThis.Cons(tmp1, tmp2); +//│ tmp5 = h + 4; +//│ tmp6 = map1(t); +//│ return Cons1(tmp5, tmp6); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map(tmp) +//│ }; +//│ tmp4 = enumFromTo1(1, 4); +//│ map1(tmp4) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ tmp5 = a; -//│ tmp6 = tmp2; -//│ param0 = tmp5; -//│ param1 = tmp6; +//│ let enumFromTo1, map1, tmp4; +//│ enumFromTo1 = function enumFromTo(a, b) { +//│ let scrut1, param0, param1, h, t, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; +//│ scrut1 = a < b; +//│ if (scrut1 === true) { +//│ tmp5 = a + 1; +//│ tmp6 = enumFromTo1(tmp5, b); +//│ tmp9 = a; +//│ tmp10 = tmp6; +//│ param0 = tmp9; +//│ param1 = tmp10; //│ h = param0; //│ t = param1; -//│ tmp3 = h + 4; -//│ tmp4 = globalThis.map(t); -//│ return globalThis.Cons(tmp3, tmp4); +//│ tmp7 = h + 4; +//│ tmp8 = map1(t); +//│ return Cons1(tmp7, tmp8); //│ } else { -//│ return globalThis.Nil; +//│ return Nil1; //│ } -//│ } -//│ function map(ls) { +//│ }; +//│ map1 = function map(ls) { //│ return ls; -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map(tmp) +//│ }; +//│ tmp4 = enumFromTo1(1, 4); +//│ map1(tmp4) //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } -// FIXME: +// // FIXME: +// :sjs +// :deforest +// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +// fun map(f, ls) = +// (if ls is +// Nil then f => Nil +// Cons(h, t) then f => Cons(f(h), map(f, t)) +// )(f) +// map(x => x + 4, enumFromTo(1, 4)) + + + :sjs :deforest fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(f, ls) = - (if ls is - Nil then f => Nil - Cons(h, t) then f => Cons(f(h), map(f, t)) - )(f) -map(x => x + 4, enumFromTo(1, 4)) +fun sum(ls) = if ls is + Nil then 0 + Cons(h, t) then h + sum(t) +sum(enumFromTo(1,10)) //│ JS (unsanitized): -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ return globalThis.Cons(a, tmp2); +//│ let enumFromTo4, sum1, tmp6; +//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let scrut1, tmp7, tmp8; +//│ scrut1 = a < b; +//│ if (scrut1 === true) { +//│ tmp7 = a + 1; +//│ tmp8 = enumFromTo4(tmp7, b); +//│ return Cons1(a, tmp8); //│ } else { -//│ return globalThis.Nil; +//│ return Nil1; //│ } -//│ } -//│ function map(f, ls) { -//│ let param0, param1, h, t, tmp1; -//│ if (ls instanceof globalThis.Nil.class) { -//│ tmp1 = (f1) => { -//│ return globalThis.Nil; -//│ }; +//│ }; +//│ sum1 = function sum(ls) { +//│ let param0, param1, h, t, tmp7; +//│ if (ls instanceof Nil1.class) { +//│ return 0; //│ } else { -//│ if (ls instanceof globalThis.Cons.class) { +//│ if (ls instanceof Cons1.class) { //│ param0 = ls.h; //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp1 = (f1) => { -//│ let tmp2, tmp3; -//│ tmp2 = f1(h) ?? null; -//│ tmp3 = globalThis.map(f1, t); -//│ return globalThis.Cons(tmp2, tmp3); -//│ }; +//│ tmp7 = sum1(t); +//│ return h + tmp7; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ return tmp1(f) ?? null; -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map((x) => { -//│ return x + 4; -//│ }, tmp) +//│ }; +//│ tmp6 = enumFromTo4(1, 10); +//│ sum1(tmp6) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let tmp; -//│ function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4; -//│ scrut = a < b; -//│ if (scrut) { -//│ tmp1 = a + 1; -//│ tmp2 = globalThis.enumFromTo(tmp1, b); -//│ tmp3 = a; -//│ tmp4 = tmp2; -//│ param0 = tmp3; -//│ param1 = tmp4; +//│ let enumFromTo4, sum1, tmp6; +//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let scrut1, param0, param1, h, t, tmp7, tmp8, tmp9, tmp10, tmp11; +//│ scrut1 = a < b; +//│ if (scrut1 === true) { +//│ tmp7 = a + 1; +//│ tmp8 = enumFromTo4(tmp7, b); +//│ tmp10 = a; +//│ tmp11 = tmp8; +//│ param0 = tmp10; +//│ param1 = tmp11; //│ h = param0; //│ t = param1; -//│ return (f) => { -//│ let tmp5, tmp6; -//│ tmp5 = f(h) ?? null; -//│ tmp6 = globalThis.map(f, t); -//│ return globalThis.Cons(tmp5, tmp6); -//│ }; +//│ tmp9 = sum1(t); +//│ return h + tmp9; //│ } else { -//│ return (f) => { -//│ return globalThis.Nil; -//│ }; +//│ return 0; //│ } -//│ } -//│ function map(f, ls) { +//│ }; +//│ sum1 = function sum(ls) { //│ return ls; -//│ return $tmp_not_in_scope_(f) ?? null; -//│ } -//│ tmp = this.enumFromTo(1, 4); -//│ this.map((x) => { -//│ return x + 4; -//│ }, tmp) -//│ = [Function (anonymous)] +//│ }; +//│ tmp6 = enumFromTo4(1, 10); +//│ sum1(tmp6) +//│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } - - - -// :sjs -// :deforest -// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -// fun sum(ls) = if ls is -// Nil then 0 -// Cons(h, t) then h + sum(t) -// sum(enumFromTo(1,10)) +//│ = 45 diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 5f1262dd11..2e2c927836 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -51,12 +51,18 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val h = ReplHost(rootPath) h + lazy val hostDeforest = + hostCreated = true + given TL = replTL + val h = ReplHost(rootPath) + h + private var hostCreated = false override def run(): Unit = try super.run() finally if hostCreated then host.terminate() - def mkQuery(prefix: Str, preStr: Str, jsStr: Str)(using Raise) = + def mkQuery(prefix: Str, preStr: Str, jsStr: Str)(using host: ReplHost = host, r: Raise) = import hkmc2.Message.MessageContext val queryStr = jsStr.replaceAll("\n", " ") val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) @@ -133,7 +139,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output("\n==== deforested tree ====") output(deforestRes.showAsTree) output("\n") - val nestedScp = baseScp.nest + val nestedScp = baseScp val (pre, je) = nestedScp.givenIn: jsb.worksheet(deforestRes) output("==== JS (deforested): ====") @@ -142,7 +148,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val preStr = pre.stripBreaks.mkString(100) output(preStr) output(jsStr) - mkQuery("", preStr, jsStr) + mkQuery("", preStr, jsStr)(using hostDeforest) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet && !showingJSYieldedCompileError then @@ -182,6 +188,9 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: mkQuery("", preStr, jsStr) + if deforestFlag.isSet && showJS.isUnset then // TODO: refine this logic... + mkQuery("", preStr, jsStr)(using hostDeforest) + if traceJS.isSet then host.execute("globalThis.Predef.TraceLogger.enabled = false") From 00656249e7d7199e0555055d31c81074e1e231f1 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 23 Jan 2025 03:39:11 +0800 Subject: [PATCH 025/303] wip: start to use blocktransformer --- .../scala/hkmc2/codegen/Deforestation.scala | 203 +++++++++--------- 1 file changed, 103 insertions(+), 100 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cb41e24f0f..d5835f6e41 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -4,7 +4,7 @@ package codegen import semantics.* import semantics.Elaborator.State import syntax.{Literal, Tree} -import utils.{TL, tl} +import utils.{TL, tl, SymbolSubst} import mlscript.utils.*, shorthands.* import scala.collection.mutable @@ -490,106 +490,109 @@ class Deforest(using TL, Raise, Elaborator.State): def rewrite(p: Program) = Program( p.imports, - rewriteBlock(p.main) + DeforestTransformer.applyBlock(p.main) ) - def rewriteBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut) then - // TODO: - rest match - case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? - case _ => rest - else - Match(scrut, arms.map{ (cse, blk) => (cse, rewriteBlock(blk)) }, dflt.map(rewriteBlock), rewriteBlock(rest)) - case Return(res, implct) => - rewriteResult(res)(r => Return(r, implct)) - case Assign(lhs, rhs, rest) => - rewriteResult(rhs)(r => Assign(lhs, r, rewriteBlock(rest))) - case Begin(sub, rest) => Begin(rewriteBlock(sub), rewriteBlock(rest)) - case d@Define(defn, rest) => - defn match - case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, rewriteBlock(body)), rewriteBlock(rest)) - case _ => d - case End(msg) => End(msg) - case Throw(exc) => rewriteResult(exc)(Throw.apply) + object DeforestTransformer extends BlockTransformer(new SymbolSubst()): - case AssignField(lhs, nme, rhs, rest) => ??? - case Label(label, body, rest) => ??? - case Break(label) => Break(label) - case Continue(label) => ??? - case TryBlock(sub, finallyDo, rest) => ??? - - - def rewriteResult(r: Result)(k: Result => Block): Block = r match - case call@Call(f, args) => - def handleCtorCall(c: ClassSymbol) = - // assert(ctorDests(call).size == 1, s"$call has more than one destination") - filteredCtorDests.get(call) match - case None => - val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? - val tmpSym = TempSymbol(N) - v -> tmpSym - } - newArgSyms.foldRight( - k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) - ){ case (arg, sym) -> rest => - rewriteResult(arg)(r => Assign(sym, r, rest)) } - case Some(Select(p, nme)) => ??? - case Some(scrut -> (arms, sels)) => - val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - tl.log(call.toString() + " ----> " + body) - - val newArgs = args.map(_ => TempSymbol(N)) - // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => - // rewriteResult(a.value): r => - // Assign(tmp, r, rest) - // } - val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - - args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => - rewriteResult(a.value): r => - Assign(tmp, r, rest) - } - f match - case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case None => - k(call) // TODO: args need to be rewritten - case Some(c) => handleCtorCall(c) - case Value.Ref(l) => l.asCls match - case None => k(call) // TODO: args need to be rewritten - case Some(c) => handleCtorCall(c) - case Value.Lam(params, body) => - k(Call(Value.Lam(params, rewriteBlock(body)), args)(call.isMlsFun)) - case Instantiate(cls, args) => k(r) - case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match - case None => k(s) - case Some(mod) => - filteredCtorDests.get(s) match - case None => - k(s) - case Some(Select(p, nme)) => ??? // TODO: a select consumes an object - case Some(scrut -> (arms, sels)) => - val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - tl.log(mod.toString + " ----> " + body) - - body._2.mapRes(k) - + override def applyBlock(b: Block): Block = b match + case mat@Match(scrut, arms, dflt, rest) => + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut) then + // TODO: + rest match + case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? + case _ => rest + else + Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) + case Return(res, implct) => + applyResult2(res)(r => Return(r, implct)) + case Assign(lhs, rhs, rest) => + applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) + case Begin(sub, rest) => Begin(applyBlock(sub), applyBlock(rest)) + case d@Define(defn, rest) => + defn match + case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) + case _ => d + case End(msg) => End(msg) + case Throw(exc) => applyResult2(exc)(Throw.apply) + + case _ => super.applyBlock(b) + // case AssignField(lhs, nme, rhs, rest) => ??? + // case Label(label, body, rest) => ??? + // case Break(label) => Break(label) + // case Continue(label) => ??? + // case TryBlock(sub, finallyDo, rest) => ??? + + + override def applyResult2(r: Result)(k: Result => Block): Block = r match + case call@Call(f, args) => + def handleCtorCall(c: ClassSymbol) = + // assert(ctorDests(call).size == 1, s"$call has more than one destination") + filteredCtorDests.get(call) match + case None => + val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? + val tmpSym = TempSymbol(N) + v -> tmpSym + } + newArgSyms.foldRight( + k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) + ){ case (arg, sym) -> rest => + applyResult2(arg)(r => Assign(sym, r, rest)) } + case Some(Select(p, nme)) => ??? + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + tl.log(call.toString() + " ----> " + body) + + val newArgs = args.map(_ => TempSymbol(N)) + // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => + // applyResult2(a.value): r => + // Assign(tmp, r, rest) + // } + val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap + + args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => + applyResult2(a.value): r => + Assign(tmp, r, rest) + } + f match + case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match + case None => + k(call) // TODO: args need to be rewritten + case Some(c) => handleCtorCall(c) + case Value.Ref(l) => l.asCls match + case None => k(call) // TODO: args need to be rewritten + case Some(c) => handleCtorCall(c) + case Value.Lam(params, body) => + k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun)) + case Instantiate(cls, args) => k(r) + case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match + case None => k(s) + case Some(mod) => + filteredCtorDests.get(s) match + case None => + k(s) + case Some(Select(p, nme)) => ??? // TODO: a select consumes an object + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get + tl.log(mod.toString + " ----> " + body) + + body._2.mapRes(k) + + + case r@Value.Ref(l) => l.asMod match + case None => k(r) + case Some(mod) => + filteredCtorDests.get(r) match + case None => + k(r) + case Some(Select(p, nme)) => ??? // TODO: a select consumes an object + case Some(scrut -> (arms, sels)) => + val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get + tl.log(mod.toString + " ----> " + body) + body._2.mapRes(k) + case Value.This(sym) => k(Value.This(sym)) + case Value.Lit(lit) => k(Value.Lit(lit)) + case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) + case Value.Arr(elems) => k(Value.Arr(elems)) - case r@Value.Ref(l) => l.asMod match - case None => k(r) - case Some(mod) => - filteredCtorDests.get(r) match - case None => - k(r) - case Some(Select(p, nme)) => ??? // TODO: a select consumes an object - case Some(scrut -> (arms, sels)) => - val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - tl.log(mod.toString + " ----> " + body) - body._2.mapRes(k) - case Value.This(sym) => k(Value.This(sym)) - case Value.Lit(lit) => k(Value.Lit(lit)) - case Value.Lam(params, body) => k(Value.Lam(params, rewriteBlock(body))) - case Value.Arr(elems) => k(Value.Arr(elems)) - - \ No newline at end of file + \ No newline at end of file From dd3e57fc284f364403aa6e52e9e6a811f3fec7c2 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:05:52 +0800 Subject: [PATCH 026/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 15 +- .../src/test/mlscript/deforest/simple.mls | 362 ++++++++++++------ 2 files changed, 250 insertions(+), 127 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index d5835f6e41..7df1e455bf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -231,7 +231,18 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(funStrat, funSymStratVar._2) funSymStratVar._1 case ValDefn(owner, k, sym, rhs) => NoProd // TODO: - case _: ClsLikeDefn => NoProd + case c: ClsLikeDefn if c.sym.asMod.isDefined => + c.methods.foreach{ case FunDefn(_, sym, params, body) => + val funSymStratVar = freshVar(sym.nme) + symToStrat += sym -> funSymStratVar._1 + val param = params.head match + case ParamList(flags, params, restParam) => params + val funStrat = constrFun(param, body) // TODO: handle mutiple param list + constrain(funStrat, funSymStratVar._2) + funSymStratVar._1 + } + processBlock(c.ctor) + case _ => ??? // TODO: processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd @@ -512,7 +523,7 @@ class Deforest(using TL, Raise, Elaborator.State): case d@Define(defn, rest) => defn match case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) - case _ => d + case _ => super.applyBlock(d) case End(msg) => End(msg) case Throw(exc) => applyResult2(exc)(Throw.apply) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 19766419e4..becf11d0fd 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -3,6 +3,7 @@ :deforest object A object B +object C class AA(x) class BB(x) @@ -196,31 +197,54 @@ test() :sjs :deforest -fun f(a) = if a is +fun f1(a) = if a is A then 1 B then 2 + C then 3 +fun f2(a) = if a is + A then 4 + B then 5 + C then 6 fun test() = let x = if true then AA(A) else BB(B) - let y = x - if y is - AA then f(x.x) - BB then f(x.x) + if x is + AA then f1(x.x) + BB then f2(x.x) test() //│ JS (unsanitized): -//│ let test10, f4; -//│ f4 = function f(a) { +//│ let test10, f12, f22; +//│ f12 = function f1(a) { //│ if (a instanceof A1.class) { //│ return 1; //│ } else { //│ if (a instanceof B1.class) { //│ return 2; //│ } else { -//│ throw new globalThis.Error("match error"); +//│ if (a instanceof C1.class) { +//│ return 3; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ }; +//│ f22 = function f2(a) { +//│ if (a instanceof A1.class) { +//│ return 4; +//│ } else { +//│ if (a instanceof B1.class) { +//│ return 5; +//│ } else { +//│ if (a instanceof C1.class) { +//│ return 6; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ } //│ } //│ }; //│ test10 = function test() { -//│ let x, scrut, y, tmp; +//│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { //│ tmp = AA1(A1); @@ -228,12 +252,11 @@ test() //│ tmp = BB1(B1); //│ } //│ x = tmp; -//│ y = x; -//│ if (y instanceof AA1.class) { -//│ return f4(x.x); +//│ if (x instanceof AA1.class) { +//│ return f12(x.x); //│ } else { -//│ if (y instanceof BB1.class) { -//│ return f4(x.x); +//│ if (x instanceof BB1.class) { +//│ return f22(x.x); //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -242,31 +265,25 @@ test() //│ test10() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test10, f4; -//│ f4 = function f(a) { +//│ let test10, f12, f22; +//│ f12 = function f1(a) { +//│ return a; +//│ }; +//│ f22 = function f2(a) { //│ return a; //│ }; //│ test10 = function test() { -//│ let x, scrut, y, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 1; -//│ tmp = AA1(tmp1); +//│ tmp = f12(tmp1); //│ } else { -//│ tmp2 = 2; -//│ tmp = BB1(tmp2); +//│ tmp2 = 5; +//│ tmp = f22(tmp2); //│ } //│ x = tmp; -//│ y = x; -//│ if (y instanceof AA1.class) { -//│ return f4(x.x); -//│ } else { -//│ if (y instanceof BB1.class) { -//│ return f4(x.x); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } +//│ return x; //│ }; //│ test10() //│ = 1 @@ -348,47 +365,6 @@ test() //│ = 11 -:sjs -:deforest -let s = if true then AA(A) else BB(B) -if s is - AA then s.x - BB then s.x -//│ JS (unsanitized): -//│ let s, scrut, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ s = tmp; -//│ if (s instanceof AA1.class) { -//│ s.x -//│ } else { -//│ if (s instanceof BB1.class) { -//│ s.x -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ } -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let s, scrut, tmp, tmp1, tmp2; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp1 = A1; -//│ tmp = tmp1; -//│ } else { -//│ tmp2 = B1; -//│ tmp = tmp2; -//│ } -//│ s = tmp; -//│ s -//│ = A { class: [class A] } -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A { class: [class A] } -//│ s = AA { x: A { class: [class A] } } @@ -406,20 +382,20 @@ fun map(ls) = if ls is Cons(h, t) then Cons(h + 4, map(t)) map(enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo1, map1, tmp4; +//│ let enumFromTo1, map1, tmp; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut1, tmp5, tmp6; -//│ scrut1 = a < b; -//│ if (scrut1 === true) { -//│ tmp5 = a + 1; -//│ tmp6 = enumFromTo1(tmp5, b); -//│ return Cons1(a, tmp6); +//│ let scrut, tmp1, tmp2; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp1 = a + 1; +//│ tmp2 = enumFromTo1(tmp1, b); +//│ return Cons1(a, tmp2); //│ } else { //│ return Nil1; //│ } //│ }; //│ map1 = function map(ls) { -//│ let param0, param1, h, t, tmp5, tmp6; +//│ let param0, param1, h, t, tmp1, tmp2; //│ if (ls instanceof Nil1.class) { //│ return Nil1; //│ } else { @@ -428,34 +404,34 @@ map(enumFromTo(1, 4)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp5 = h + 4; -//│ tmp6 = map1(t); -//│ return Cons1(tmp5, tmp6); +//│ tmp1 = h + 4; +//│ tmp2 = map1(t); +//│ return Cons1(tmp1, tmp2); //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp4 = enumFromTo1(1, 4); -//│ map1(tmp4) +//│ tmp = enumFromTo1(1, 4); +//│ map1(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo1, map1, tmp4; +//│ let enumFromTo1, map1, tmp; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut1, param0, param1, h, t, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; -//│ scrut1 = a < b; -//│ if (scrut1 === true) { -//│ tmp5 = a + 1; -//│ tmp6 = enumFromTo1(tmp5, b); -//│ tmp9 = a; -//│ tmp10 = tmp6; -//│ param0 = tmp9; -//│ param1 = tmp10; +//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp1 = a + 1; +//│ tmp2 = enumFromTo1(tmp1, b); +//│ tmp5 = a; +//│ tmp6 = tmp2; +//│ param0 = tmp5; +//│ param1 = tmp6; //│ h = param0; //│ t = param1; -//│ tmp7 = h + 4; -//│ tmp8 = map1(t); -//│ return Cons1(tmp7, tmp8); +//│ tmp3 = h + 4; +//│ tmp4 = map1(t); +//│ return Cons1(tmp3, tmp4); //│ } else { //│ return Nil1; //│ } @@ -463,8 +439,8 @@ map(enumFromTo(1, 4)) //│ map1 = function map(ls) { //│ return ls; //│ }; -//│ tmp4 = enumFromTo1(1, 4); -//│ map1(tmp4) +//│ tmp = enumFromTo1(1, 4); +//│ map1(tmp) //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } @@ -492,20 +468,20 @@ fun sum(ls) = if ls is Cons(h, t) then h + sum(t) sum(enumFromTo(1,10)) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp6; +//│ let enumFromTo4, sum1, tmp2; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut1, tmp7, tmp8; -//│ scrut1 = a < b; -//│ if (scrut1 === true) { -//│ tmp7 = a + 1; -//│ tmp8 = enumFromTo4(tmp7, b); -//│ return Cons1(a, tmp8); +//│ let scrut, tmp3, tmp4; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp3 = a + 1; +//│ tmp4 = enumFromTo4(tmp3, b); +//│ return Cons1(a, tmp4); //│ } else { //│ return Nil1; //│ } //│ }; //│ sum1 = function sum(ls) { -//│ let param0, param1, h, t, tmp7; +//│ let param0, param1, h, t, tmp3; //│ if (ls instanceof Nil1.class) { //│ return 0; //│ } else { @@ -514,32 +490,32 @@ sum(enumFromTo(1,10)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp7 = sum1(t); -//│ return h + tmp7; +//│ tmp3 = sum1(t); +//│ return h + tmp3; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp6 = enumFromTo4(1, 10); -//│ sum1(tmp6) +//│ tmp2 = enumFromTo4(1, 10); +//│ sum1(tmp2) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp6; +//│ let enumFromTo4, sum1, tmp2; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut1, param0, param1, h, t, tmp7, tmp8, tmp9, tmp10, tmp11; -//│ scrut1 = a < b; -//│ if (scrut1 === true) { -//│ tmp7 = a + 1; -//│ tmp8 = enumFromTo4(tmp7, b); -//│ tmp10 = a; -//│ tmp11 = tmp8; -//│ param0 = tmp10; -//│ param1 = tmp11; +//│ let scrut, param0, param1, h, t, tmp3, tmp4, tmp5, tmp6, tmp7; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp3 = a + 1; +//│ tmp4 = enumFromTo4(tmp3, b); +//│ tmp6 = a; +//│ tmp7 = tmp4; +//│ param0 = tmp6; +//│ param1 = tmp7; //│ h = param0; //│ t = param1; -//│ tmp9 = sum1(t); -//│ return h + tmp9; +//│ tmp5 = sum1(t); +//│ return h + tmp5; //│ } else { //│ return 0; //│ } @@ -547,9 +523,145 @@ sum(enumFromTo(1,10)) //│ sum1 = function sum(ls) { //│ return ls; //│ }; -//│ tmp6 = enumFromTo4(1, 10); -//│ sum1(tmp6) +//│ tmp2 = enumFromTo4(1, 10); +//│ sum1(tmp2) //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 + +:sjs +:deforest +module Test with + fun f1(a) = if a is + A then 1 + B then 2 + C then 3 + fun f2(a) = if a is + A then 4 + B then 5 + C then 6 + let s = if true then AA(A) else BB(B) + if s is + AA then f1(s.x) + BB then f2(s.x) +//│ JS (unsanitized): +//│ let Test1; +//│ const Test$class = class Test { +//│ #s; +//│ constructor() { +//│ let scrut, scrut1, tmp4; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp4 = AA1(A1); +//│ } else { +//│ tmp4 = BB1(B1); +//│ } +//│ this.#s = tmp4; +//│ scrut1 = this.#s; +//│ if (scrut1 instanceof AA1.class) { +//│ this.f1(this.#s.x) +//│ } else { +//│ if (scrut1 instanceof BB1.class) { +//│ this.f2(this.#s.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ f1(a) { +//│ if (a instanceof A1.class) { +//│ return 1; +//│ } else { +//│ if (a instanceof B1.class) { +//│ return 2; +//│ } else { +//│ if (a instanceof C1.class) { +//│ return 3; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ f2(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 4; +//│ } else { +//│ if (a1 instanceof B1.class) { +//│ return 5; +//│ } else { +//│ if (a1 instanceof C1.class) { +//│ return 6; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ toString() { return "Test"; } +//│ }; Test1 = new Test$class; +//│ Test1.class = Test$class; +//│ null +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let Test1; +//│ const Test$class = class Test { +//│ #s; +//│ constructor() { +//│ let scrut, scrut1, tmp4, tmp5, tmp6; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp5 = A1; +//│ tmp4 = AA1(tmp5); +//│ } else { +//│ tmp6 = B1; +//│ tmp4 = BB1(tmp6); +//│ } +//│ this.#s = tmp4; +//│ scrut1 = this.#s; +//│ if (scrut1 instanceof AA1.class) { +//│ this.f1(this.#s.x) +//│ } else { +//│ if (scrut1 instanceof BB1.class) { +//│ this.f2(this.#s.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ f1(a) { +//│ if (a instanceof A1.class) { +//│ return 1; +//│ } else { +//│ if (a instanceof B1.class) { +//│ return 2; +//│ } else { +//│ if (a instanceof C1.class) { +//│ return 3; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ f2(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 4; +//│ } else { +//│ if (a1 instanceof B1.class) { +//│ return 5; +//│ } else { +//│ if (a1 instanceof C1.class) { +//│ return 6; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ toString() { return "Test"; } +//│ }; Test1 = new Test$class; +//│ Test1.class = Test$class; +//│ null +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From de68a142557708d9978df5b68d6925122181221a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:41:07 +0800 Subject: [PATCH 027/303] update after merge --- hkmc2/shared/src/test/mlscript/deforest/simple.mls | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index becf11d0fd..98ea424159 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -530,6 +530,7 @@ sum(enumFromTo(1,10)) //│ = 45 +// TODO: :sjs :deforest module Test with @@ -607,7 +608,7 @@ module Test with //│ ==== JS (deforested): ==== //│ let Test1; //│ const Test$class = class Test { -//│ #s; +//│ #s1; //│ constructor() { //│ let scrut, scrut1, tmp4, tmp5, tmp6; //│ scrut = true; @@ -618,13 +619,13 @@ module Test with //│ tmp6 = B1; //│ tmp4 = BB1(tmp6); //│ } -//│ this.#s = tmp4; -//│ scrut1 = this.#s; +//│ this.#s1 = tmp4; +//│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { -//│ this.f1(this.#s.x) +//│ this.f1(this.#s1.x) //│ } else { //│ if (scrut1 instanceof BB1.class) { -//│ this.f2(this.#s.x) +//│ this.f2(this.#s1.x) //│ } else { //│ throw new globalThis.Error("match error"); //│ } From 11d6bab4a56a284ceec4d31f3b5f30e7b050d0ff Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:01:24 +0800 Subject: [PATCH 028/303] exprid --- .../src/main/scala/hkmc2/codegen/Block.scala | 13 +++ .../scala/hkmc2/codegen/Deforestation.scala | 95 +++++++++++-------- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 9b2e443f8e..65a83ceb8d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -231,6 +231,14 @@ enum Case: case Cls(_, path) => path.freeVars case Tup(_, _) => Set.empty +type ResultId = Uid[Result] +object ResultUidHandler extends Uid.Handler[Result] +object ResultUid extends ResultUidHandler.State: + val uidToResult = collection.mutable.Map.empty[ResultId, Result] + def apply(id: ResultId) = uidToResult(id) + + + sealed abstract class Result: lazy val freeVars: Set[Local] = this match @@ -243,6 +251,11 @@ sealed abstract class Result: case Value.Lam(params, body) => body.freeVars -- params.paramSyms case Value.Arr(elems) => elems.flatMap(_.value.freeVars).toSet + lazy val uid = + val id = ResultUid.nextUid + ResultUid.uidToResult.addOne(id -> this) + id + // type Local = LocalSymbol type Local = Symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7df1e455bf..1b1915303d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -33,8 +33,8 @@ object StratVarState: val c = s.asConsStrat p -> c - -type CtorExpr = Call | Select | Value.Ref + +type CtorExpr = ResultId // enum ProdStrat: case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat @@ -44,11 +44,20 @@ case object NoProd extends ProdStrat // enum ConsStrat: case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: Select) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat + +enum DtorExpr: + case Match(s: Value.Ref) + case Sel(s: ResultId) + +enum CtorFinalDest: + case Match(scrut: Value.Ref, arms: Ls[Case -> Block], selInArms: Ls[ResultId]) + case Sel(s: ResultId) + trait FieldSelTrait: this: FieldSel => val filter = mutable.Map.empty[ProdVar, Ls[ClsOrModSymbol]].withDefaultValue(Nil) @@ -64,10 +73,10 @@ trait StratVarTrait(stratState: StratVarState): lazy val uid = stratState.uid extension (r: Result) - def replaceSelect(using p: Set[Select], args: Map[Tree.Ident, Path]): Result = r match + def replaceSelect(using p: Set[ResultId], args: Map[Tree.Ident, Path]): Result = r match case c@Call(f, args) => Call(f, args.map{case Arg(spread, value) => Arg(spread, value.replaceSelect.asInstanceOf[Path])})(c.isMlsFun) case sel@Select(path, nme) => - if p.contains(sel) then args(nme) else sel + if p.contains(sel.uid) then args(nme) else sel case _ => r // case Value.Ref(l) => r // case Instantiate(cls, args) => ??? @@ -97,7 +106,7 @@ extension (b: Block) case HandleBlockReturn(res) => ??? case End(msg) => ??? - def replaceSelect(using p: Set[Select], args: Map[Tree.Ident, Path]): Block = b match + def replaceSelect(using p: Set[ResultId], args: Map[Tree.Ident, Path]): Block = b match case Assign(lhs, rhs, rest) => Assign(lhs, rhs.replaceSelect, rest.replaceSelect) case Return(res, implct) => Return(res.replaceSelect, implct) case Match(scrut, arms, dflt, rest) => ??? @@ -271,7 +280,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)(s)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid)) val appRes = freshVar() constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -282,12 +291,12 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap)(c) + Ctor(s, clsFields.zip(argsTpe).toMap)(c.uid) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap)(c) + Ctor(s, clsFields.zip(argsTpe).toMap)(c.uid) case _ => // then it is a function val appRes = freshVar("call_" + l.nme + "_res") constrain(getStratOfSym(l), ConsFun(argsTpe, appRes._2)) @@ -308,25 +317,25 @@ class Deforest(using TL, Raise, Elaborator.State): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asMod.isDefined => - Ctor(s.asMod.get, Map.empty)(sel) + Ctor(s.asMod.get, Map.empty)(sel.uid) case _ => val pStrat = processResult(p) inArm match case Some(armP -> clsSym) if armP === pStrat => // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) val tpeVar = freshVar() - val selStrat = FieldSel(nme, tpeVar._2)(sel) + val selStrat = FieldSel(nme, tpeVar._2)(sel.uid) selStrat.updateFilter(armP, clsSym :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)(sel)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) tpeVar._1 case v@Value.Ref(l) => l.asMod match case None => getStratOfSym(l) - case Some(m) => Ctor(m, Map.empty)(v) + case Some(m) => Ctor(m, Map.empty)(v.uid) case Value.This(sym) => ??? case Value.Lit(lit) => NoProd @@ -338,8 +347,8 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])].withDefaultValue(Map.empty -> Nil) - val dtorSources = mutable.Map.empty[Value.Ref | Select, Ls[CtorExpr]].withDefaultValue(Nil) + val ctorDests = mutable.Map.empty[ResultId, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[ResultId])].withDefaultValue(Map.empty -> Nil) + val dtorSources = mutable.Map.empty[DtorExpr, Ls[ResultId]].withDefaultValue(Nil) def resolveConstraints: Unit = @@ -364,7 +373,7 @@ class Deforest(using TL, Raise, Elaborator.State): ) case None => Some(Map(scrut -> arms) -> Nil) } - dtorSources += scrut -> (ctorStrat.expr :: dtorSources(scrut)) + dtorSources += DtorExpr.Match(scrut) -> (ctorStrat.expr :: dtorSources(DtorExpr.Match(scrut))) // TODO: keep track of this ctor to dtor case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => // if clsSym.isDefined then @@ -374,7 +383,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(d -> s) => Some(d -> (selDtor.expr :: s)) case None => Some(Map.empty -> (selDtor.expr :: Nil)) } - dtorSources.updateWith(selDtor.expr){ + dtorSources.updateWith(DtorExpr.Sel(selDtor.expr)){ case None => Some(ctorStrat.expr :: Nil) case Some(value) => Some(ctorStrat.expr :: value) } @@ -429,17 +438,19 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[Select])] - type DtorToCtor = Map[Value.Ref | Select, Ls[CtorExpr]] + type CtorToDtor = Map[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[ResultId])] + type DtorToCtor = Map[DtorExpr, Ls[CtorExpr]] def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = if rm.isEmpty then ctorDests -> dtorSources else val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) - removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[Value.Ref | Select]{ case (mat -> sels) => mat.keySet ++ sels }.toSet) + removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case (mat -> sels) => + mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s)) + }.toSet) - def removeDtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[Value.Ref | Select]): CtorToDtor -> DtorToCtor = + def removeDtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[DtorExpr]): CtorToDtor -> DtorToCtor = if rm.isEmpty then ctorDests -> dtorSources else @@ -457,19 +468,21 @@ class Deforest(using TL, Raise, Elaborator.State): (dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { val Value.Ref(scrut) = dtors.head._1 - sels.forall { case Select(Value.Ref(l), nme) => l == scrut; case _ => false } + sels.forall { s => ResultUid(s) match + case Select(Value.Ref(l), nme) => l == scrut + case _ => false } }) }.keySet ) - lazy val filteredCtorDests: Map[CtorExpr, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] = - val res = mutable.Map.empty[CtorExpr, (Value.Ref -> (Ls[Case -> Block] -> Ls[Select])) | Select] + lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = + val res = mutable.Map.empty[CtorExpr, CtorFinalDest] resolveClashes._1.foreach { case (ctor, dests) => val (dtors, sels) = dests val filteredDtor = { - if dtors.size == 0 && sels.size == 1 then Some(sels.head) + if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head)) else if dtors.size == 0 && sels.size > 1 then throw Error("more than one consumer") None @@ -478,8 +491,11 @@ class Deforest(using TL, Raise, Elaborator.State): None else if dtors.size == 1 then val Value.Ref(scrut) = dtors.head._1 - if sels.forall{ case Select(Value.Ref(l), nme) => l == scrut; case _ => false } then - Some(dtors.head._1 -> (dtors.head._2 -> sels)) + if sels.forall{ s => ResultUid(s) match + case Select(Value.Ref(l), nme) => l == scrut + case _ => false + } then + Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels)) else throw Error("more than one consumer") None @@ -490,12 +506,12 @@ class Deforest(using TL, Raise, Elaborator.State): res.toMap lazy val rewritingSel = filteredCtorDests.values.flatMap { - case Select(p, nme) => None // TODO: - case scrut -> (_ -> sels) => sels + case CtorFinalDest.Match(_, _, sels) => sels + case _ => Nil // TODO: } lazy val filteredDtors = filteredCtorDests.values.collect { - case (scrut -> _) => scrut + case CtorFinalDest.Match(scrut, _, _) => scrut }.toSet def rewrite(p: Program) = @@ -539,7 +555,7 @@ class Deforest(using TL, Raise, Elaborator.State): case call@Call(f, args) => def handleCtorCall(c: ClassSymbol) = // assert(ctorDests(call).size == 1, s"$call has more than one destination") - filteredCtorDests.get(call) match + filteredCtorDests.get(call.uid) match case None => val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? val tmpSym = TempSymbol(N) @@ -549,8 +565,7 @@ class Deforest(using TL, Raise, Elaborator.State): k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) ){ case (arg, sym) -> rest => applyResult2(arg)(r => Assign(sym, r, rest)) } - case Some(Select(p, nme)) => ??? - case Some(scrut -> (arms, sels)) => + case Some(CtorFinalDest.Match(scrut, arms, sels)) => val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 tl.log(call.toString() + " ----> " + body) @@ -565,6 +580,7 @@ class Deforest(using TL, Raise, Elaborator.State): applyResult2(a.value): r => Assign(tmp, r, rest) } + case Some(_) => ??? f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => @@ -579,28 +595,27 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match case None => k(s) case Some(mod) => - filteredCtorDests.get(s) match + filteredCtorDests.get(s.uid) match case None => k(s) - case Some(Select(p, nme)) => ??? // TODO: a select consumes an object - case Some(scrut -> (arms, sels)) => + case Some(CtorFinalDest.Match(scrut, arms, sels)) => val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) - + case Some(_) => ??? // TODO: a select consumes an object case r@Value.Ref(l) => l.asMod match case None => k(r) case Some(mod) => - filteredCtorDests.get(r) match + filteredCtorDests.get(r.uid) match case None => k(r) - case Some(Select(p, nme)) => ??? // TODO: a select consumes an object - case Some(scrut -> (arms, sels)) => + case Some(CtorFinalDest.Match(scrut, arms, sels)) => val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) + case Some(_) => ??? // TODO: a select consumes an object case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) From cf4c3169ae7b41b5111c01aef01ec3619e4befe3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:10:17 +0800 Subject: [PATCH 029/303] wip cleanup --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1b1915303d..53dc7c5dd6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -87,7 +87,6 @@ extension (r: Result) extension (b: Block) - // TODO: similar to Block.mapTail? def mapRes(f: Result => Block): Block = b match case Return(res, implct) => f(res) case Assign(lhs, rhs, rest: End) => f(rhs) @@ -374,7 +373,6 @@ class Deforest(using TL, Raise, Elaborator.State): case None => Some(Map(scrut -> arms) -> Nil) } dtorSources += DtorExpr.Match(scrut) -> (ctorStrat.expr :: dtorSources(DtorExpr.Match(scrut))) - // TODO: keep track of this ctor to dtor case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) From 2792e52dc7f4081f4245ad7f130da169713a0cab Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:02:57 +0800 Subject: [PATCH 030/303] wip handle call args --- .../scala/hkmc2/codegen/Deforestation.scala | 35 +++-- .../src/test/mlscript/deforest/simple.mls | 136 ++++++++++-------- 2 files changed, 104 insertions(+), 67 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 53dc7c5dd6..f7bf6faf09 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -551,18 +551,29 @@ class Deforest(using TL, Raise, Elaborator.State): override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => + def handleNormalCall(args: List[Arg]) = + val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? + val tmpSym = TempSymbol(N) + v -> tmpSym + } + newArgSyms.foldRight( + k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) + ){ case (arg, sym) -> rest => + applyResult2(arg)(r => Assign(sym, r, rest)) } + def handleCtorCall(c: ClassSymbol) = // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call.uid) match case None => - val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? - val tmpSym = TempSymbol(N) - v -> tmpSym - } - newArgSyms.foldRight( - k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) - ){ case (arg, sym) -> rest => - applyResult2(arg)(r => Assign(sym, r, rest)) } + handleNormalCall(args) + // val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? + // val tmpSym = TempSymbol(N) + // v -> tmpSym + // } + // newArgSyms.foldRight( + // k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) + // ){ case (arg, sym) -> rest => + // applyResult2(arg)(r => Assign(sym, r, rest)) } case Some(CtorFinalDest.Match(scrut, arms, sels)) => val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 tl.log(call.toString() + " ----> " + body) @@ -579,13 +590,17 @@ class Deforest(using TL, Raise, Elaborator.State): Assign(tmp, r, rest) } case Some(_) => ??? + f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => - k(call) // TODO: args need to be rewritten + handleNormalCall(args) + // k(call) // TODO: args need to be rewritten case Some(c) => handleCtorCall(c) case Value.Ref(l) => l.asCls match - case None => k(call) // TODO: args need to be rewritten + case None => + handleNormalCall(args) + // k(call) // TODO: args need to be rewritten case Some(c) => handleCtorCall(c) case Value.Lam(params, body) => k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 98ea424159..f519717890 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -337,18 +337,18 @@ test() //│ ==== JS (deforested): ==== //│ let test13; //│ test13 = function test() { -//│ let c, g, tmp; +//│ let c, g, tmp, tmp1, tmp2; //│ g = function g(x) { -//│ let scrut, param0, x1, param01, x2, tmp1, tmp2; +//│ let scrut, param0, x1, param01, x2, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = 11; -//│ param01 = tmp1; +//│ tmp3 = 11; +//│ param01 = tmp3; //│ x2 = param01; //│ return x2; //│ } else { -//│ tmp2 = 22; -//│ param0 = tmp2; +//│ tmp4 = 22; +//│ param0 = tmp4; //│ x1 = param0; //│ return x1; //│ } @@ -356,8 +356,10 @@ test() //│ c = function c(x) { //│ return x; //│ }; -//│ tmp = g(true); -//│ return c(tmp); +//│ tmp1 = true; +//│ tmp = g(tmp1); +//│ tmp2 = tmp; +//│ return c(tmp2); //│ }; //│ test13() //│ = 11 @@ -416,22 +418,28 @@ map(enumFromTo(1, 4)) //│ map1(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo1, map1, tmp; +//│ let enumFromTo1, map1, tmp, tmp1, tmp2, tmp3; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; -//│ scrut = a < b; +//│ let scrut, param0, param1, h, t, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ tmp8 = a; +//│ tmp9 = b; +//│ scrut = tmp8 < tmp9; //│ if (scrut === true) { -//│ tmp1 = a + 1; -//│ tmp2 = enumFromTo1(tmp1, b); -//│ tmp5 = a; -//│ tmp6 = tmp2; -//│ param0 = tmp5; -//│ param1 = tmp6; +//│ tmp10 = a; +//│ tmp11 = 1; +//│ tmp4 = tmp10 + tmp11; +//│ tmp12 = tmp4; +//│ tmp13 = b; +//│ tmp5 = enumFromTo1(tmp12, tmp13); +//│ tmp14 = a; +//│ tmp15 = tmp5; +//│ param0 = tmp14; +//│ param1 = tmp15; //│ h = param0; //│ t = param1; -//│ tmp3 = h + 4; -//│ tmp4 = map1(t); -//│ return Cons1(tmp3, tmp4); +//│ tmp6 = h + 4; +//│ tmp7 = map1(t); +//│ return Cons1(tmp6, tmp7); //│ } else { //│ return Nil1; //│ } @@ -439,8 +447,11 @@ map(enumFromTo(1, 4)) //│ map1 = function map(ls) { //│ return ls; //│ }; -//│ tmp = enumFromTo1(1, 4); -//│ map1(tmp) +//│ tmp1 = 1; +//│ tmp2 = 4; +//│ tmp = enumFromTo1(tmp1, tmp2); +//│ tmp3 = tmp; +//│ map1(tmp3) //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } @@ -468,20 +479,20 @@ fun sum(ls) = if ls is Cons(h, t) then h + sum(t) sum(enumFromTo(1,10)) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp2; +//│ let enumFromTo4, sum1, tmp5; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp3, tmp4; +//│ let scrut, tmp6, tmp7; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp3 = a + 1; -//│ tmp4 = enumFromTo4(tmp3, b); -//│ return Cons1(a, tmp4); +//│ tmp6 = a + 1; +//│ tmp7 = enumFromTo4(tmp6, b); +//│ return Cons1(a, tmp7); //│ } else { //│ return Nil1; //│ } //│ }; //│ sum1 = function sum(ls) { -//│ let param0, param1, h, t, tmp3; +//│ let param0, param1, h, t, tmp6; //│ if (ls instanceof Nil1.class) { //│ return 0; //│ } else { @@ -490,32 +501,38 @@ sum(enumFromTo(1,10)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp3 = sum1(t); -//│ return h + tmp3; +//│ tmp6 = sum1(t); +//│ return h + tmp6; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp2 = enumFromTo4(1, 10); -//│ sum1(tmp2) +//│ tmp5 = enumFromTo4(1, 10); +//│ sum1(tmp5) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp2; +//│ let enumFromTo4, sum1, tmp5, tmp6, tmp7, tmp8; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp3, tmp4, tmp5, tmp6, tmp7; -//│ scrut = a < b; +//│ let scrut, param0, param1, h, t, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19; +//│ tmp12 = a; +//│ tmp13 = b; +//│ scrut = tmp12 < tmp13; //│ if (scrut === true) { -//│ tmp3 = a + 1; -//│ tmp4 = enumFromTo4(tmp3, b); -//│ tmp6 = a; -//│ tmp7 = tmp4; -//│ param0 = tmp6; -//│ param1 = tmp7; +//│ tmp14 = a; +//│ tmp15 = 1; +//│ tmp9 = tmp14 + tmp15; +//│ tmp16 = tmp9; +//│ tmp17 = b; +//│ tmp10 = enumFromTo4(tmp16, tmp17); +//│ tmp18 = a; +//│ tmp19 = tmp10; +//│ param0 = tmp18; +//│ param1 = tmp19; //│ h = param0; //│ t = param1; -//│ tmp5 = sum1(t); -//│ return h + tmp5; +//│ tmp11 = sum1(t); +//│ return h + tmp11; //│ } else { //│ return 0; //│ } @@ -523,8 +540,11 @@ sum(enumFromTo(1,10)) //│ sum1 = function sum(ls) { //│ return ls; //│ }; -//│ tmp2 = enumFromTo4(1, 10); -//│ sum1(tmp2) +//│ tmp6 = 1; +//│ tmp7 = 10; +//│ tmp5 = enumFromTo4(tmp6, tmp7); +//│ tmp8 = tmp5; +//│ sum1(tmp8) //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 @@ -551,14 +571,14 @@ module Test with //│ const Test$class = class Test { //│ #s; //│ constructor() { -//│ let scrut, scrut1, tmp4; +//│ let scrut, scrut1, tmp10; //│ scrut = true; //│ if (scrut === true) { -//│ tmp4 = AA1(A1); +//│ tmp10 = AA1(A1); //│ } else { -//│ tmp4 = BB1(B1); +//│ tmp10 = BB1(B1); //│ } -//│ this.#s = tmp4; +//│ this.#s = tmp10; //│ scrut1 = this.#s; //│ if (scrut1 instanceof AA1.class) { //│ this.f1(this.#s.x) @@ -610,22 +630,24 @@ module Test with //│ const Test$class = class Test { //│ #s1; //│ constructor() { -//│ let scrut, scrut1, tmp4, tmp5, tmp6; +//│ let scrut, scrut1, tmp10, tmp11, tmp12, tmp13, tmp14; //│ scrut = true; //│ if (scrut === true) { -//│ tmp5 = A1; -//│ tmp4 = AA1(tmp5); +//│ tmp11 = A1; +//│ tmp10 = AA1(tmp11); //│ } else { -//│ tmp6 = B1; -//│ tmp4 = BB1(tmp6); +//│ tmp12 = B1; +//│ tmp10 = BB1(tmp12); //│ } -//│ this.#s1 = tmp4; +//│ this.#s1 = tmp10; //│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { -//│ this.f1(this.#s1.x) +//│ tmp13 = this.#s1.x; +//│ this.f1(tmp13) //│ } else { //│ if (scrut1 instanceof BB1.class) { -//│ this.f2(this.#s1.x) +//│ tmp14 = this.#s1.x; +//│ this.f2(tmp14) //│ } else { //│ throw new globalThis.Error("match error"); //│ } From 4c4ce7136114be21e5779f1f50f198f7fc8f58a6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:18:18 +0800 Subject: [PATCH 031/303] cleanup --- .../scala/hkmc2/codegen/Deforestation.scala | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f7bf6faf09..a2514bebe9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -559,30 +559,20 @@ class Deforest(using TL, Raise, Elaborator.State): newArgSyms.foldRight( k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) ){ case (arg, sym) -> rest => - applyResult2(arg)(r => Assign(sym, r, rest)) } + applyResult2(arg)(r => Assign(sym, r, rest)) // TODO: avoid new tmpvars when args are not rewritten... + } def handleCtorCall(c: ClassSymbol) = // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call.uid) match case None => handleNormalCall(args) - // val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? - // val tmpSym = TempSymbol(N) - // v -> tmpSym - // } - // newArgSyms.foldRight( - // k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun)) - // ){ case (arg, sym) -> rest => - // applyResult2(arg)(r => Assign(sym, r, rest)) } case Some(CtorFinalDest.Match(scrut, arms, sels)) => val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 tl.log(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) - // args.zip(newArgs).foldRight[Block](body.replaceAssignments(newArgs.map(a => Value.Ref(a))).mapRes(k)){ case ((a, tmp), rest) => - // applyResult2(a.value): r => - // Assign(tmp, r, rest) - // } + val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => @@ -595,12 +585,10 @@ class Deforest(using TL, Raise, Elaborator.State): case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match case None => handleNormalCall(args) - // k(call) // TODO: args need to be rewritten case Some(c) => handleCtorCall(c) case Value.Ref(l) => l.asCls match case None => handleNormalCall(args) - // k(call) // TODO: args need to be rewritten case Some(c) => handleCtorCall(c) case Value.Lam(params, body) => k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun)) From f3808e7db4dc940a435225dcc80adde352be6ce5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:59:24 +0800 Subject: [PATCH 032/303] fuse selection as consumer --- .../scala/hkmc2/codegen/Deforestation.scala | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a2514bebe9..e05402093e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -503,10 +503,10 @@ class Deforest(using TL, Raise, Elaborator.State): } res.toMap - lazy val rewritingSel = filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, sels) => sels - case _ => Nil // TODO: - } + lazy val rewritingSelConsumer = filteredCtorDests.values.flatMap { + case CtorFinalDest.Match(_, _, sels) => None + case CtorFinalDest.Sel(s) => Some(s) + }.toSet lazy val filteredDtors = filteredCtorDests.values.collect { case CtorFinalDest.Match(scrut, _, _) => scrut @@ -575,11 +575,14 @@ class Deforest(using TL, Raise, Elaborator.State): val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - args.zip(newArgs).foldRight[Block](body.replaceSelect(using rewritingSel.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => + args.zip(newArgs).foldRight[Block](body.replaceSelect(using sels.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) } - case Some(_) => ??? + case Some(CtorFinalDest.Sel(s)) => + val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } + val idx = getClsFields(c).indexWhere(s => s.id === selFieldName) + k(args(idx).value) f match case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match @@ -594,7 +597,11 @@ class Deforest(using TL, Raise, Elaborator.State): k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun)) case Instantiate(cls, args) => k(r) case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match - case None => k(s) + case None => + if rewritingSelConsumer.contains(s.uid) then + k(p) + else + k(s) case Some(mod) => filteredCtorDests.get(s.uid) match case None => @@ -604,7 +611,7 @@ class Deforest(using TL, Raise, Elaborator.State): tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) - case Some(_) => ??? // TODO: a select consumes an object + case Some(_) => ??? // TODO: a selection on a module consumes it case r@Value.Ref(l) => l.asMod match case None => k(r) @@ -616,7 +623,7 @@ class Deforest(using TL, Raise, Elaborator.State): val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) - case Some(_) => ??? // TODO: a select consumes an object + case Some(_) => ??? // TODO: a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) From 163df5d535a7302291364a91ae9a1a837ad950d0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:30:46 +0800 Subject: [PATCH 033/303] fix: recurse into rewritten body --- .../scala/hkmc2/codegen/Deforestation.scala | 2 +- .../src/test/mlscript/deforest/simple.mls | 44 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e05402093e..3286765f25 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -575,7 +575,7 @@ class Deforest(using TL, Raise, Elaborator.State): val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - args.zip(newArgs).foldRight[Block](body.replaceSelect(using sels.toSet, idsToArgs).mapRes(k)){ case ((a, tmp), rest) => + args.zip(newArgs).foldRight[Block](applyBlock(body.replaceSelect(using sels.toSet, idsToArgs)).mapRes(k)){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) } diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index f519717890..7d9b1cda15 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -172,18 +172,20 @@ test() //│ return a; //│ }; //│ test7 = function test() { -//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; +//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 1; //│ param01 = tmp1; //│ x2 = param01; -//│ tmp = f1(x2); +//│ tmp2 = x2; +//│ tmp = f1(tmp2); //│ } else { -//│ tmp2 = 2; -//│ param0 = tmp2; +//│ tmp3 = 2; +//│ param0 = tmp3; //│ x1 = param0; -//│ tmp = f1(x1); +//│ tmp4 = x1; +//│ tmp = f1(tmp4); //│ } //│ x = tmp; //│ return x; @@ -273,14 +275,16 @@ test() //│ return a; //│ }; //│ test10 = function test() { -//│ let x, scrut, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, tmp1, tmp2, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 1; -//│ tmp = f12(tmp1); +//│ tmp2 = tmp1; +//│ tmp = f12(tmp2); //│ } else { -//│ tmp2 = 5; -//│ tmp = f22(tmp2); +//│ tmp3 = 5; +//│ tmp4 = tmp3; +//│ tmp = f22(tmp4); //│ } //│ x = tmp; //│ return x; @@ -420,7 +424,7 @@ map(enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo1, map1, tmp, tmp1, tmp2, tmp3; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ let scrut, param0, param1, h, t, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; //│ tmp8 = a; //│ tmp9 = b; //│ scrut = tmp8 < tmp9; @@ -437,9 +441,14 @@ map(enumFromTo(1, 4)) //│ param1 = tmp15; //│ h = param0; //│ t = param1; -//│ tmp6 = h + 4; -//│ tmp7 = map1(t); -//│ return Cons1(tmp6, tmp7); +//│ tmp16 = h; +//│ tmp17 = 4; +//│ tmp6 = tmp16 + tmp17; +//│ tmp18 = t; +//│ tmp7 = map1(tmp18); +//│ tmp19 = tmp6; +//│ tmp20 = tmp7; +//│ return Cons1(tmp19, tmp20); //│ } else { //│ return Nil1; //│ } @@ -514,7 +523,7 @@ sum(enumFromTo(1,10)) //│ ==== JS (deforested): ==== //│ let enumFromTo4, sum1, tmp5, tmp6, tmp7, tmp8; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19; +//│ let scrut, param0, param1, h, t, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22; //│ tmp12 = a; //│ tmp13 = b; //│ scrut = tmp12 < tmp13; @@ -531,8 +540,11 @@ sum(enumFromTo(1,10)) //│ param1 = tmp19; //│ h = param0; //│ t = param1; -//│ tmp11 = sum1(t); -//│ return h + tmp11; +//│ tmp20 = t; +//│ tmp11 = sum1(tmp20); +//│ tmp21 = h; +//│ tmp22 = tmp11; +//│ return tmp21 + tmp22; //│ } else { //│ return 0; //│ } From dd3acf8df7ad0735ad983ff34e53bf44030e9c19 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:24:06 +0800 Subject: [PATCH 034/303] wip: fix merge --- .../scala/hkmc2/codegen/Deforestation.scala | 10 +- .../src/test/mlscript/deforest/simple.mls | 304 +++++++++--------- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 15 +- 3 files changed, 164 insertions(+), 165 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0826305753..b4d4897881 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -315,8 +315,8 @@ class Deforest(using TL, Raise, Elaborator.State): freshVar()._1 case sel@Select(p, nme) => sel.symbol match - case Some(s) if s.asMod.isDefined => - Ctor(s.asMod.get, Map.empty)(sel.uid) + case Some(s) if s.asObj.isDefined => + Ctor(s.asObj.get, Map.empty)(sel.uid) case _ => val pStrat = processResult(p) inArm match @@ -332,7 +332,7 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) tpeVar._1 - case v@Value.Ref(l) => l.asMod match + case v@Value.Ref(l) => l.asObj match case None => getStratOfSym(l) case Some(m) => Ctor(m, Map.empty)(v.uid) @@ -596,7 +596,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Value.Lam(params, body) => k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun, call.mayRaiseEffects)) case Instantiate(cls, args) => k(r) - case s@Select(p, nme) => s.symbol.flatMap(f => f.asMod) match + case s@Select(p, nme) => s.symbol.flatMap(f => f.asObj) match case None => if rewritingSelConsumer.contains(s.uid) then k(p) @@ -613,7 +613,7 @@ class Deforest(using TL, Raise, Elaborator.State): body._2.mapRes(k) case Some(_) => ??? // TODO: a selection on a module consumes it - case r@Value.Ref(l) => l.asMod match + case r@Value.Ref(l) => l.asObj match case None => k(r) case Some(mod) => filteredCtorDests.get(r.uid) match diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 7d9b1cda15..a5e4c9dc5e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -16,8 +16,8 @@ fun test() = B then 2 test() //│ JS (unsanitized): -//│ let test1; -//│ test1 = function test() { +//│ let test; +//│ test = function test() { //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { @@ -27,20 +27,20 @@ test() //│ } //│ x = tmp; //│ if (x instanceof A1.class) { -//│ return 1; +//│ return 1 //│ } else { //│ if (x instanceof B1.class) { -//│ return 2; +//│ return 2 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ test1() +//│ test() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1; -//│ test1 = function test() { +//│ let test; +//│ test = function test() { //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { @@ -49,10 +49,9 @@ test() //│ tmp = 2; //│ } //│ x = tmp; -//│ return x; +//│ return x //│ }; -//│ test1() -//│ = 1 +//│ test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -66,8 +65,8 @@ fun test() = BB(x) then x test() //│ JS (unsanitized): -//│ let test4; -//│ test4 = function test() { +//│ let test1; +//│ test1 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp; //│ scrut = true; //│ if (scrut === true) { @@ -79,22 +78,22 @@ test() //│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; -//│ return x2; +//│ return x2 //│ } else { //│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ test4() +//│ test1() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test4; -//│ test4 = function test() { +//│ let test1; +//│ test1 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { @@ -109,12 +108,11 @@ test() //│ tmp = x1; //│ } //│ x = tmp; -//│ return x; +//│ return x //│ }; -//│ test4() -//│ = A { class: [class A] } +//│ test1() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A { class: [class A] } +//│ = A :sjs @@ -129,19 +127,19 @@ fun test() = BB(x) then f(x) test() //│ JS (unsanitized): -//│ let test7, f1; -//│ f1 = function f(a) { +//│ let test2, f; +//│ f = function f(a) { //│ if (a instanceof A1.class) { -//│ return 1; +//│ return 1 //│ } else { //│ if (a instanceof B1.class) { -//│ return 2; +//│ return 2 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ test7 = function test() { +//│ test2 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp; //│ scrut = true; //│ if (scrut === true) { @@ -153,25 +151,25 @@ test() //│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; -//│ return f1(x2); +//│ return f(x2) //│ } else { //│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; -//│ return f1(x1); +//│ return f(x1) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ test7() +//│ test2() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test7, f1; -//│ f1 = function f(a) { -//│ return a; +//│ let test2, f; +//│ f = function f(a) { +//│ return a //│ }; -//│ test7 = function test() { +//│ test2 = function test() { //│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { @@ -179,24 +177,22 @@ test() //│ param01 = tmp1; //│ x2 = param01; //│ tmp2 = x2; -//│ tmp = f1(tmp2); +//│ tmp = f(tmp2); //│ } else { //│ tmp3 = 2; //│ param0 = tmp3; //│ x1 = param0; //│ tmp4 = x1; -//│ tmp = f1(tmp4); +//│ tmp = f(tmp4); //│ } //│ x = tmp; -//│ return x; +//│ return x //│ }; -//│ test7() -//│ = 1 +//│ test2() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 - :sjs :deforest fun f1(a) = if a is @@ -214,38 +210,38 @@ fun test() = BB then f2(x.x) test() //│ JS (unsanitized): -//│ let test10, f12, f22; -//│ f12 = function f1(a) { +//│ let test3, f1, f2; +//│ f1 = function f1(a) { //│ if (a instanceof A1.class) { -//│ return 1; +//│ return 1 //│ } else { //│ if (a instanceof B1.class) { -//│ return 2; +//│ return 2 //│ } else { //│ if (a instanceof C1.class) { -//│ return 3; +//│ return 3 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ }; -//│ f22 = function f2(a) { +//│ f2 = function f2(a) { //│ if (a instanceof A1.class) { -//│ return 4; +//│ return 4 //│ } else { //│ if (a instanceof B1.class) { -//│ return 5; +//│ return 5 //│ } else { //│ if (a instanceof C1.class) { -//│ return 6; +//│ return 6 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ }; -//│ test10 = function test() { +//│ test3 = function test() { //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { @@ -255,42 +251,41 @@ test() //│ } //│ x = tmp; //│ if (x instanceof AA1.class) { -//│ return f12(x.x); +//│ return f1(x.x) //│ } else { //│ if (x instanceof BB1.class) { -//│ return f22(x.x); +//│ return f2(x.x) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ test10() +//│ test3() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test10, f12, f22; -//│ f12 = function f1(a) { -//│ return a; +//│ let test3, f1, f2; +//│ f1 = function f1(a) { +//│ return a //│ }; -//│ f22 = function f2(a) { -//│ return a; +//│ f2 = function f2(a) { +//│ return a //│ }; -//│ test10 = function test() { +//│ test3 = function test() { //│ let x, scrut, tmp, tmp1, tmp2, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 1; //│ tmp2 = tmp1; -//│ tmp = f12(tmp2); +//│ tmp = f1(tmp2); //│ } else { //│ tmp3 = 5; //│ tmp4 = tmp3; -//│ tmp = f22(tmp4); +//│ tmp = f2(tmp4); //│ } //│ x = tmp; -//│ return x; +//│ return x //│ }; -//│ test10() -//│ = 1 +//│ test3() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -305,16 +300,16 @@ fun test() = c(g(true)) test() //│ JS (unsanitized): -//│ let test13; -//│ test13 = function test() { +//│ let test4; +//│ test4 = function test() { //│ let c, g, tmp; //│ g = function g(x) { //│ let scrut; //│ scrut = true; //│ if (scrut === true) { -//│ return AA1(11); +//│ return AA1(11) //│ } else { -//│ return BB1(22); +//│ return BB1(22) //│ } //│ }; //│ c = function c(x) { @@ -322,25 +317,25 @@ test() //│ if (x instanceof AA1.class) { //│ param01 = x.x; //│ x2 = param01; -//│ return x2; +//│ return x2 //│ } else { //│ if (x instanceof BB1.class) { //│ param0 = x.x; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; //│ tmp = g(true); -//│ return c(tmp); +//│ return c(tmp) //│ }; -//│ test13() +//│ test4() //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test13; -//│ test13 = function test() { +//│ let test4; +//│ test4 = function test() { //│ let c, g, tmp, tmp1, tmp2; //│ g = function g(x) { //│ let scrut, param0, x1, param01, x2, tmp3, tmp4; @@ -349,24 +344,23 @@ test() //│ tmp3 = 11; //│ param01 = tmp3; //│ x2 = param01; -//│ return x2; +//│ return x2 //│ } else { //│ tmp4 = 22; //│ param0 = tmp4; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ } //│ }; //│ c = function c(x) { -//│ return x; +//│ return x //│ }; //│ tmp1 = true; //│ tmp = g(tmp1); //│ tmp2 = tmp; -//│ return c(tmp2); +//│ return c(tmp2) //│ }; -//│ test13() -//│ = 11 +//│ test4() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 @@ -388,22 +382,22 @@ fun map(ls) = if ls is Cons(h, t) then Cons(h + 4, map(t)) map(enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo1, map1, tmp; -//│ enumFromTo1 = function enumFromTo(a, b) { +//│ let enumFromTo, map, tmp; +//│ enumFromTo = function enumFromTo(a, b) { //│ let scrut, tmp1, tmp2; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp1 = a + 1; -//│ tmp2 = enumFromTo1(tmp1, b); -//│ return Cons1(a, tmp2); +//│ tmp2 = enumFromTo(tmp1, b); +//│ return Cons1(a, tmp2) //│ } else { -//│ return Nil1; +//│ return Nil1 //│ } //│ }; -//│ map1 = function map(ls) { +//│ map = function map(ls) { //│ let param0, param1, h, t, tmp1, tmp2; //│ if (ls instanceof Nil1.class) { -//│ return Nil1; +//│ return Nil1 //│ } else { //│ if (ls instanceof Cons1.class) { //│ param0 = ls.h; @@ -411,19 +405,19 @@ map(enumFromTo(1, 4)) //│ h = param0; //│ t = param1; //│ tmp1 = h + 4; -//│ tmp2 = map1(t); -//│ return Cons1(tmp1, tmp2); +//│ tmp2 = map(t); +//│ return Cons1(tmp1, tmp2) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp = enumFromTo1(1, 4); -//│ map1(tmp) +//│ tmp = enumFromTo(1, 4); +//│ map(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo1, map1, tmp, tmp1, tmp2, tmp3; -//│ enumFromTo1 = function enumFromTo(a, b) { +//│ let enumFromTo, map, tmp, tmp1, tmp2, tmp3; +//│ enumFromTo = function enumFromTo(a, b) { //│ let scrut, param0, param1, h, t, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; //│ tmp8 = a; //│ tmp9 = b; @@ -434,7 +428,7 @@ map(enumFromTo(1, 4)) //│ tmp4 = tmp10 + tmp11; //│ tmp12 = tmp4; //│ tmp13 = b; -//│ tmp5 = enumFromTo1(tmp12, tmp13); +//│ tmp5 = enumFromTo(tmp12, tmp13); //│ tmp14 = a; //│ tmp15 = tmp5; //│ param0 = tmp14; @@ -445,25 +439,24 @@ map(enumFromTo(1, 4)) //│ tmp17 = 4; //│ tmp6 = tmp16 + tmp17; //│ tmp18 = t; -//│ tmp7 = map1(tmp18); +//│ tmp7 = map(tmp18); //│ tmp19 = tmp6; //│ tmp20 = tmp7; -//│ return Cons1(tmp19, tmp20); +//│ return Cons1(tmp19, tmp20) //│ } else { -//│ return Nil1; +//│ return Nil1 //│ } //│ }; -//│ map1 = function map(ls) { -//│ return ls; +//│ map = function map(ls) { +//│ return ls //│ }; //│ tmp1 = 1; //│ tmp2 = 4; -//│ tmp = enumFromTo1(tmp1, tmp2); +//│ tmp = enumFromTo(tmp1, tmp2); //│ tmp3 = tmp; -//│ map1(tmp3) -//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } +//│ map(tmp3) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons { h: 5, t: Cons { h: 6, t: Cons { h: 7, t: [Nil] } } } +//│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -488,41 +481,41 @@ fun sum(ls) = if ls is Cons(h, t) then h + sum(t) sum(enumFromTo(1,10)) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp5; -//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let enumFromTo1, sum, tmp5; +//│ enumFromTo1 = function enumFromTo(a, b) { //│ let scrut, tmp6, tmp7; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp6 = a + 1; -//│ tmp7 = enumFromTo4(tmp6, b); -//│ return Cons1(a, tmp7); +//│ tmp7 = enumFromTo1(tmp6, b); +//│ return Cons1(a, tmp7) //│ } else { -//│ return Nil1; +//│ return Nil1 //│ } //│ }; -//│ sum1 = function sum(ls) { +//│ sum = function sum(ls) { //│ let param0, param1, h, t, tmp6; //│ if (ls instanceof Nil1.class) { -//│ return 0; +//│ return 0 //│ } else { //│ if (ls instanceof Cons1.class) { //│ param0 = ls.h; //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp6 = sum1(t); -//│ return h + tmp6; +//│ tmp6 = sum(t); +//│ return h + tmp6 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp5 = enumFromTo4(1, 10); -//│ sum1(tmp5) +//│ tmp5 = enumFromTo1(1, 10); +//│ sum(tmp5) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp5, tmp6, tmp7, tmp8; -//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let enumFromTo1, sum, tmp5, tmp6, tmp7, tmp8; +//│ enumFromTo1 = function enumFromTo(a, b) { //│ let scrut, param0, param1, h, t, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22; //│ tmp12 = a; //│ tmp13 = b; @@ -533,7 +526,7 @@ sum(enumFromTo(1,10)) //│ tmp9 = tmp14 + tmp15; //│ tmp16 = tmp9; //│ tmp17 = b; -//│ tmp10 = enumFromTo4(tmp16, tmp17); +//│ tmp10 = enumFromTo1(tmp16, tmp17); //│ tmp18 = a; //│ tmp19 = tmp10; //│ param0 = tmp18; @@ -541,23 +534,22 @@ sum(enumFromTo(1,10)) //│ h = param0; //│ t = param1; //│ tmp20 = t; -//│ tmp11 = sum1(tmp20); +//│ tmp11 = sum(tmp20); //│ tmp21 = h; //│ tmp22 = tmp11; -//│ return tmp21 + tmp22; +//│ return tmp21 + tmp22 //│ } else { -//│ return 0; +//│ return 0 //│ } //│ }; -//│ sum1 = function sum(ls) { -//│ return ls; +//│ sum = function sum(ls) { +//│ return ls //│ }; //│ tmp6 = 1; //│ tmp7 = 10; -//│ tmp5 = enumFromTo4(tmp6, tmp7); +//│ tmp5 = enumFromTo1(tmp6, tmp7); //│ tmp8 = tmp5; -//│ sum1(tmp8) -//│ = 45 +//│ sum(tmp8) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 @@ -580,9 +572,9 @@ module Test with BB then f2(s.x) //│ JS (unsanitized): //│ let Test1; -//│ const Test$class = class Test { -//│ #s; -//│ constructor() { +//│ Test1 = class Test { +//│ static #s; +//│ static { //│ let scrut, scrut1, tmp10; //│ scrut = true; //│ if (scrut === true) { @@ -593,55 +585,53 @@ module Test with //│ this.#s = tmp10; //│ scrut1 = this.#s; //│ if (scrut1 instanceof AA1.class) { -//│ this.f1(this.#s.x) +//│ Test.f1(this.#s.x) //│ } else { //│ if (scrut1 instanceof BB1.class) { -//│ this.f2(this.#s.x) +//│ Test.f2(this.#s.x) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } -//│ f1(a) { +//│ static f1(a) { //│ if (a instanceof A1.class) { -//│ return 1; +//│ return 1 //│ } else { //│ if (a instanceof B1.class) { -//│ return 2; +//│ return 2 //│ } else { //│ if (a instanceof C1.class) { -//│ return 3; +//│ return 3 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ } -//│ f2(a1) { +//│ static f2(a1) { //│ if (a1 instanceof A1.class) { -//│ return 4; +//│ return 4 //│ } else { //│ if (a1 instanceof B1.class) { -//│ return 5; +//│ return 5 //│ } else { //│ if (a1 instanceof C1.class) { -//│ return 6; +//│ return 6 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ } -//│ toString() { return "Test"; } -//│ }; Test1 = new Test$class; -//│ Test1.class = Test$class; -//│ null +//│ static toString() { return "Test"; } +//│ }; //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let Test1; -//│ const Test$class = class Test { -//│ #s1; -//│ constructor() { +//│ Test1 = class Test { +//│ static #s1; +//│ static { //│ let scrut, scrut1, tmp10, tmp11, tmp12, tmp13, tmp14; //│ scrut = true; //│ if (scrut === true) { @@ -655,48 +645,46 @@ module Test with //│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { //│ tmp13 = this.#s1.x; -//│ this.f1(tmp13) +//│ Test.f1(tmp13) //│ } else { //│ if (scrut1 instanceof BB1.class) { //│ tmp14 = this.#s1.x; -//│ this.f2(tmp14) +//│ Test.f2(tmp14) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } -//│ f1(a) { +//│ static f1(a) { //│ if (a instanceof A1.class) { -//│ return 1; +//│ return 1 //│ } else { //│ if (a instanceof B1.class) { -//│ return 2; +//│ return 2 //│ } else { //│ if (a instanceof C1.class) { -//│ return 3; +//│ return 3 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ } -//│ f2(a1) { +//│ static f2(a1) { //│ if (a1 instanceof A1.class) { -//│ return 4; +//│ return 4 //│ } else { //│ if (a1 instanceof B1.class) { -//│ return 5; +//│ return 5 //│ } else { //│ if (a1 instanceof C1.class) { -//│ return 6; +//│ return 6 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ } //│ } -//│ toString() { return "Test"; } -//│ }; Test1 = new Test$class; -//│ Test1.class = Test$class; -//│ null +//│ static toString() { return "Test"; } +//│ }; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 399e912422..1bd8327490 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -157,7 +157,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val preStr = pre.stripBreaks.mkString(100) output(preStr) output(jsStr) - mkQuery("", preStr, jsStr)(using hostDeforest) + mkQuery("", preStr, jsStr)(using hostDeforest): stdout => + stdout.splitSane('\n').init + .foreach: line => + output(s"> ${line}") output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet then @@ -216,11 +219,19 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: // * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first host.execute(s"$resNme = undefined") + mkQuery("", preStr, jsStr): stdout => + stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) + .foreach: line => + output(s"> ${line}") + if deforestFlag.isSet && showJS.isUnset then // TODO: refine this logic... - mkQuery("", preStr, jsStr): stdout => + mkQuery("", preStr, jsStr)(using hostDeforest): stdout => stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) .foreach: line => output(s"> ${line}") + + + if traceJS.isSet then host.execute("globalThis.Predef.TraceLogger.enabled = false") From fec747d8c21df228824ea94dccff450e39e9b074 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:58:24 +0800 Subject: [PATCH 035/303] wip: fix merge --- .../src/test/mlscript/deforest/simple.mls | 30 ++++++--- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 66 ++++++++++++++++--- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index a5e4c9dc5e..87f759b4fe 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -51,11 +51,12 @@ test() //│ x = tmp; //│ return x //│ }; -//│ test() +//│ block$res2 = test(); +//│ undefined +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 - :sjs :deforest fun test() = @@ -110,7 +111,9 @@ test() //│ x = tmp; //│ return x //│ }; -//│ test1() +//│ block$res4 = test1(); +//│ undefined +//│ = A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A @@ -188,7 +191,9 @@ test() //│ x = tmp; //│ return x //│ }; -//│ test2() +//│ block$res6 = test2(); +//│ undefined +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -285,7 +290,9 @@ test() //│ x = tmp; //│ return x //│ }; -//│ test3() +//│ block$res8 = test3(); +//│ undefined +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -360,7 +367,9 @@ test() //│ tmp2 = tmp; //│ return c(tmp2) //│ }; -//│ test4() +//│ block$res10 = test4(); +//│ undefined +//│ = 11 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 @@ -454,7 +463,9 @@ map(enumFromTo(1, 4)) //│ tmp2 = 4; //│ tmp = enumFromTo(tmp1, tmp2); //│ tmp3 = tmp; -//│ map(tmp3) +//│ block$res13 = map(tmp3); +//│ undefined +//│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -549,7 +560,9 @@ sum(enumFromTo(1,10)) //│ tmp7 = 10; //│ tmp5 = enumFromTo1(tmp6, tmp7); //│ tmp8 = tmp5; -//│ sum(tmp8) +//│ block$res15 = sum(tmp8); +//│ undefined +//│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 @@ -687,4 +700,5 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; +//│ block$res17 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 1bd8327490..4afadd9788 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -148,19 +148,72 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output("\n==== deforested tree ====") output(deforestRes.showAsTree) output("\n") - val nestedScp = baseScp - val (pre, je) = nestedScp.givenIn: - jsb.worksheet(deforestRes) + + val resSym = new TempSymbol(S(blk), "block$res") + + val resNme = baseScp.allocateName(resSym) + + val le0 = deforestRes.copy(main = deforestRes.main.mapTail: + case e: End => + Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) + case Return(res, implct) => + assert(implct) + Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) + case _: HandleBlockReturn => ??? + case tl: (Throw | Break | Continue) => tl + ) + + val (pre, je) = baseScp.givenIn: + jsb.worksheet(le0) output("==== JS (deforested): ====") val jsStr = je.stripBreaks.mkString(100) val preStr = pre.stripBreaks.mkString(100) output(preStr) output(jsStr) + + + host.execute(s"$resNme = undefined") mkQuery("", preStr, jsStr)(using hostDeforest): stdout => stdout.splitSane('\n').init .foreach: line => output(s"> ${line}") + + if silent.isUnset then + import Elaborator.Ctx.* + def definedValues = curCtx.env.iterator.flatMap: + case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => + e.symbol match + case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) + case S(ts: BlockMemberSymbol) + if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) + case S(vs: VarSymbol) => S((nme, vs, N)) + case _ => N + case _ => N + val valuesToPrint = ("", resSym, expect.get) +: definedValues.toSeq.sortBy(_._1) + valuesToPrint.foreach: (nme, sym, expect) => + val le = + import codegen.* + Return( + Call( + Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), + Arg(false, Value.Ref(sym)) :: Nil)(true, false), + implct = true) + val je = baseScp.givenIn: + jsb.block(le, endSemi = false) + val jsStr = je.stripBreaks.mkString(100) + mkQuery("", "", jsStr)(using hostDeforest): out => + val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) + expect match + case S(expected) if result =/= expected => raise: + ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, + source = Diagnostic.Source.Runtime) + case _ => () + result match + case "undefined" => + case "()" => + case _ => + output(s"${if nme.isEmpty then "" else s"$nme "}= ${result.indentNewLines("| ")}") output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet then @@ -224,11 +277,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: .foreach: line => output(s"> ${line}") - if deforestFlag.isSet && showJS.isUnset then // TODO: refine this logic... - mkQuery("", preStr, jsStr)(using hostDeforest): stdout => - stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) - .foreach: line => - output(s"> ${line}") + if deforestFlag.isUnset || showJS.isUnset then + mkQuery("", preStr, jsStr)(using hostDeforest)(_ => ()) From 8a6ad5e29bc6f6b0ba8978fd5b70ba7d240ded50 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:06:34 +0800 Subject: [PATCH 036/303] wip: fix merge --- .../src/test/scala/hkmc2/JSBackendDiffMaker.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 4afadd9788..88f3d9e365 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -61,14 +61,18 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: h lazy val hostDeforest = - hostCreated = true + hostDeforestCreated = true given TL = replTL val h = ReplHost(rootPath) h private var hostCreated = false + private var hostDeforestCreated = false + override def run(): Unit = - try super.run() finally if hostCreated then host.terminate() + try super.run() finally + if hostCreated then host.terminate() + if hostDeforestCreated then hostDeforest.terminate() private val DEFAULT_STACK_LIMT = 500 @@ -173,7 +177,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(jsStr) - host.execute(s"$resNme = undefined") + hostDeforest.execute(s"$resNme = undefined") mkQuery("", preStr, jsStr)(using hostDeforest): stdout => stdout.splitSane('\n').init .foreach: line => @@ -277,7 +281,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: .foreach: line => output(s"> ${line}") - if deforestFlag.isUnset || showJS.isUnset then + if (deforestFlag.isUnset || showJS.isUnset) then mkQuery("", preStr, jsStr)(using hostDeforest)(_ => ()) From f1f9ee19f79364bde3d1d2063ad2e00c5561d586 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:42:10 +0800 Subject: [PATCH 037/303] fix after merge --- hkmc2/shared/src/test/mlscript/deforest/simple.mls | 14 +++----------- .../src/test/scala/hkmc2/JSBackendDiffMaker.scala | 6 +++++- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 87f759b4fe..5a8c71de2f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1,6 +1,7 @@ :js - :deforest + + object A object B object C @@ -8,7 +9,6 @@ class AA(x) class BB(x) :sjs -:deforest fun test() = let x = if true then A else B if x is @@ -58,7 +58,6 @@ test() //│ = 1 :sjs -:deforest fun test() = let x = if true then AA(A) else BB(B) if x is @@ -119,7 +118,6 @@ test() :sjs -:deforest fun f(a) = if a is A then 1 B then 2 @@ -199,7 +197,6 @@ test() :sjs -:deforest fun f1(a) = if a is A then 1 B then 2 @@ -298,7 +295,6 @@ test() :sjs -:deforest fun test() = fun g(x) = if true then AA(11) else BB(22) fun c(x) = if x is @@ -379,12 +375,11 @@ test() -:deforest + object Nil class Cons(h, t) :sjs -:deforest fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(ls) = if ls is Nil then Nil @@ -473,7 +468,6 @@ map(enumFromTo(1, 4)) // // FIXME: // :sjs -// :deforest // fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil // fun map(f, ls) = // (if ls is @@ -485,7 +479,6 @@ map(enumFromTo(1, 4)) :sjs -:deforest fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun sum(ls) = if ls is Nil then 0 @@ -569,7 +562,6 @@ sum(enumFromTo(1,10)) // TODO: :sjs -:deforest module Test with fun f1(a) = if a is A then 1 diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 88f3d9e365..8245f430dd 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -64,6 +64,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: hostDeforestCreated = true given TL = replTL val h = ReplHost(rootPath) + h.execute(s"const $runtimeNme = (await import(\"${runtimeFile}\")).default;") match + case ReplHost.Result(msg) => + if msg.startsWith("Uncaught") then output(s"Failed to load runtime: $msg") + case r => output(s"Failed to load runtime: $r") h private var hostCreated = false @@ -281,7 +285,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: .foreach: line => output(s"> ${line}") - if (deforestFlag.isUnset || showJS.isUnset) then + if deforestFlag.isSet && showJS.isUnset then mkQuery("", preStr, jsStr)(using hostDeforest)(_ => ()) From 2d4e428d72db2274a4f5f61881adb2c6f0a0c08d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 13:52:56 +0800 Subject: [PATCH 038/303] fix merging of mkQuery function --- .../src/test/scala/hkmc2/JSBackendDiffMaker.scala | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 8245f430dd..653abfd765 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -81,8 +81,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: private val DEFAULT_STACK_LIMT = 500 - def mkQuery(prefix: Str, preStr: Str, jsStr: Str)(using host: ReplHost = host, r: Raise)(k: Str => Unit) = - import hkmc2.Message.MessageContext + def mkQuery(preStr: Str, jsStr: Str)(using host: ReplHost = host, r: Raise)(k: Str => Unit) = val queryStr = jsStr.replaceAll("\n", " ") val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) reply match @@ -93,7 +92,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if otherOutputs.nonEmpty then otherOutputs.splitSane('\n').foreach: line => output(s"> ${line}") - if (isSyntaxError) then // If there is a syntax error in the generated code, // it should be a code generation error. @@ -182,7 +180,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: hostDeforest.execute(s"$resNme = undefined") - mkQuery("", preStr, jsStr)(using hostDeforest): stdout => + mkQuery(preStr, jsStr)(using hostDeforest): stdout => stdout.splitSane('\n').init .foreach: line => output(s"> ${line}") @@ -210,7 +208,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val je = baseScp.givenIn: jsb.block(le, endSemi = false) val jsStr = je.stripBreaks.mkString(100) - mkQuery("", "", jsStr)(using hostDeforest): out => + mkQuery("", jsStr)(using hostDeforest): out => val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) expect match case S(expected) if result =/= expected => raise: @@ -280,13 +278,13 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: // * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first host.execute(s"$resNme = undefined") - mkQuery("", preStr, jsStr): stdout => + mkQuery(preStr, jsStr): stdout => stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) .foreach: line => output(s"> ${line}") if deforestFlag.isSet && showJS.isUnset then - mkQuery("", preStr, jsStr)(using hostDeforest)(_ => ()) + mkQuery(preStr, jsStr)(using hostDeforest)(_ => ()) @@ -316,7 +314,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val je = nestedScp.givenIn: jsb.block(le, endSemi = false) val jsStr = je.stripBreaks.mkString(100) - mkQuery("", "", jsStr): out => + mkQuery("", jsStr): out => val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) expect match case S(expected) if result =/= expected => raise: From 0b370884779c2d78e46130121e072732a99b338d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:55:22 +0800 Subject: [PATCH 039/303] merge match arms --- .../scala/hkmc2/codegen/Deforestation.scala | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b4d4897881..5cbcbe2b4a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -84,7 +84,15 @@ extension (r: Result) // case Value.Lit(lit) => ??? // case Value.Lam(params, body) => ??? // case Value.Arr(elems) => ??? - + +extension (m: Match) + def mergeArms: Match = + val Match(s, arms, dflt, rest) = m + dflt.map(_.mergeMatchArms).fold(m){ + case m@Match(s2, arms2, dflt2, _: End) if s2 === s => + Match(s, arms ::: arms2, dflt2, rest.mergeMatchArms) + case d => Match(s, arms, S(d), rest) + } extension (b: Block) def mapRes(f: Result => Block): Block = b match @@ -145,12 +153,17 @@ extension (b: Block) // case HandleBlock(lhs, res, cls, handlers, body, rest) => // case HandleBlockReturn(res) => - + def mergeMatchArms: Block = + object MergeMatchArmTransformer extends BlockTransformer(new SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case m: Match => m.mergeArms + case _ => super.applyBlock(b) + MergeMatchArmTransformer.applyBlock(b) class Deforest(using TL, Raise, Elaborator.State): def apply(p: Program) = - processBlock(p.main) + processBlock(p.main.mergeMatchArms) resolveConstraints tl.log("upper:") From f02642764c542dfdfe6c6a938e3469728bd5266e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:39:02 +0800 Subject: [PATCH 040/303] match use uid --- .../scala/hkmc2/codegen/Deforestation.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5cbcbe2b4a..13f57c788b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -43,7 +43,7 @@ case object NoProd extends ProdStrat // enum ConsStrat: -case class Dtor(scrut: Value.Ref, arms: Ls[Case -> Block]) extends ConsStrat +case class Dtor(scrut: ResultId, arms: Ls[Case -> Block]) extends ConsStrat case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) @@ -51,11 +51,11 @@ case object NoCons extends ConsStrat enum DtorExpr: - case Match(s: Value.Ref) + case Match(s: ResultId) case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: Value.Ref, arms: Ls[Case -> Block], selInArms: Ls[ResultId]) + case Match(scrut: ResultId, arms: Ls[Case -> Block], selInArms: Ls[ResultId]) case Sel(s: ResultId) trait FieldSelTrait: @@ -212,7 +212,7 @@ class Deforest(using TL, Raise, Elaborator.State): val scrutStrat = processResult(scrut) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => - constrain(scrutStrat, Dtor(scrut, arms)) + constrain(scrutStrat, Dtor(scrut.uid, arms)) // TODO: fix this "asInstanceOf"? processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) } @@ -359,7 +359,7 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[ResultId, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[ResultId])].withDefaultValue(Map.empty -> Nil) + val ctorDests = mutable.Map.empty[ResultId, (Map[ResultId, Ls[Case -> Block]] -> Ls[ResultId])].withDefaultValue(Map.empty -> Nil) val dtorSources = mutable.Map.empty[DtorExpr, Ls[ResultId]].withDefaultValue(Nil) @@ -449,7 +449,7 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[CtorExpr, (Map[Value.Ref, Ls[Case -> Block]] -> Ls[ResultId])] + type CtorToDtor = Map[CtorExpr, (Map[ResultId, Ls[Case -> Block]] -> Ls[ResultId])] type DtorToCtor = Map[DtorExpr, Ls[CtorExpr]] def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = @@ -478,7 +478,7 @@ class Deforest(using TL, Raise, Elaborator.State): val (dtors, sels) = dests (dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { - val Value.Ref(scrut) = dtors.head._1 + val Value.Ref(scrut) = ResultUid(dtors.head._1) sels.forall { s => ResultUid(s) match case Select(Value.Ref(l), nme) => l == scrut case _ => false } @@ -501,7 +501,7 @@ class Deforest(using TL, Raise, Elaborator.State): throw Error("more than one consumer") None else if dtors.size == 1 then - val Value.Ref(scrut) = dtors.head._1 + val Value.Ref(scrut) = ResultUid(dtors.head._1) if sels.forall{ s => ResultUid(s) match case Select(Value.Ref(l), nme) => l == scrut case _ => false @@ -535,7 +535,7 @@ class Deforest(using TL, Raise, Elaborator.State): override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut) then + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then // TODO: rest match case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? From c16cb0a01004c74af4c5908fd257b548c2b857dd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:45:24 +0800 Subject: [PATCH 041/303] add test --- .../src/test/mlscript/deforest/simple.mls | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 5a8c71de2f..2164c4554f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -559,6 +559,63 @@ sum(enumFromTo(1,10)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 +:sjs +fun test() = + let x = B + if x is + A then 1 + B then 3 + if x is + B then 2 +test() +//│ JS (unsanitized): +//│ let test5; +//│ test5 = function test() { +//│ let x, tmp10; +//│ x = B1; +//│ if (x instanceof A1.class) { +//│ tmp10 = 1; +//│ } else { +//│ if (x instanceof B1.class) { +//│ tmp10 = 3; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ if (x instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test5() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test5; +//│ test5 = function test() { +//│ let x, tmp10; +//│ x = B1; +//│ if (x instanceof A1.class) { +//│ tmp10 = 1; +//│ } else { +//│ if (x instanceof B1.class) { +//│ tmp10 = 3; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ if (x instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ block$res17 = test5(); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + // TODO: :sjs @@ -692,5 +749,5 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; -//│ block$res17 = undefined; +//│ block$res19 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 943a329e18f7f7fdc8a40bb3aa210ee7e26b07d6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Feb 2025 18:07:58 +0800 Subject: [PATCH 042/303] better dtor strat defn --- .../main/scala/hkmc2/codegen/Deforestation.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 13f57c788b..b3fa6109e9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -43,7 +43,7 @@ case object NoProd extends ProdStrat // enum ConsStrat: -case class Dtor(scrut: ResultId, arms: Ls[Case -> Block]) extends ConsStrat +case class Dtor(scrut: ResultId)(val arms: Ls[Case -> Block]) extends ConsStrat case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) @@ -212,7 +212,7 @@ class Deforest(using TL, Raise, Elaborator.State): val scrutStrat = processResult(scrut) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => - constrain(scrutStrat, Dtor(scrut.uid, arms)) + constrain(scrutStrat, Dtor(scrut.uid)(arms)) // TODO: fix this "asInstanceOf"? processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) } @@ -374,16 +374,16 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (ctorStrat@Ctor(ctor, args), Dtor(scrut, arms)) => + case (ctorStrat@Ctor(ctor, args), dtorStrat@Dtor(scrut)) => ctorDests.updateWith(ctorStrat.expr){ case Some(d -> s) => Some( (d.updatedWith(scrut){ - case None => Some(arms) - case Some(v) => Some(arms ::: v) + case None => Some(dtorStrat.arms) + case Some(v) => Some(dtorStrat.arms ::: v) }) -> s ) - case None => Some(Map(scrut -> arms) -> Nil) + case None => Some(Map(scrut -> dtorStrat.arms) -> Nil) } dtorSources += DtorExpr.Match(scrut) -> (ctorStrat.expr :: dtorSources(DtorExpr.Match(scrut))) case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => @@ -432,13 +432,13 @@ class Deforest(using TL, Raise, Elaborator.State): } // upperBounds(uid).foreach(c => handle(prod -> c)) case (Ctor(ctor, args), NoCons) => () - case (ProdFun(l, r), Dtor(cls, _)) => ??? + case (ProdFun(l, r), Dtor(cls)) => ??? case (ProdFun(l, r), FieldSel(field, consVar)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => lc.zip(lp).foreach(handle) handle(rp, rc) case (ProdFun(l, r), NoCons) => () - case (NoProd, Dtor(cls, _)) => () + case (NoProd, Dtor(cls)) => () case (NoProd, FieldSel(field, consVar)) => () case (NoProd, ConsFun(l, r)) => () case (NoProd, NoCons) => () From f6a920aec957190b992cda02a46986352dfca9ea Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:27:01 +0800 Subject: [PATCH 043/303] better arm merging --- .../scala/hkmc2/codegen/Deforestation.scala | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b3fa6109e9..7addf310fe 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -88,11 +88,20 @@ extension (r: Result) extension (m: Match) def mergeArms: Match = val Match(s, arms, dflt, rest) = m - dflt.map(_.mergeMatchArms).fold(m){ + dflt.map(_.mergeMatchArms).fold(m): case m@Match(s2, arms2, dflt2, _: End) if s2 === s => - Match(s, arms ::: arms2, dflt2, rest.mergeMatchArms) - case d => Match(s, arms, S(d), rest) - } + Match( + s, + (arms ::: arms2).map((cse, b) => (cse, b.mergeMatchArms)), + dflt2, + rest.mergeMatchArms + ) + case d => Match( + s, + arms.map((cse, b) => (cse, b.mergeMatchArms)), + S(d), + rest.mergeMatchArms + ) extension (b: Block) def mapRes(f: Result => Block): Block = b match From d3d2a3b078b6e3d12a79565501952db3056bd99f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Feb 2025 12:06:42 +0800 Subject: [PATCH 044/303] noprod for unknown blockmembersymbols --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7addf310fe..0ee6de7bda 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -204,7 +204,7 @@ class Deforest(using TL, Raise, Elaborator.State): s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd - case _: BlockMemberSymbol => symToStrat(s) // For `fun` and `let` only, not classes or modules? + case _: BlockMemberSymbol => symToStrat.getOrElse(s, {tl.log(""); NoProd}) // For `fun` and `let` only, not classes or modules? case _: LocalSymbol => symToStrat(s) case _: FlowSymbol => symToStrat(s) From fa815211783fc47ef2c62c82fee674a5f0dc0aa5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:21:32 +0800 Subject: [PATCH 045/303] wip: keep track of the whole match block --- .../scala/hkmc2/codegen/Deforestation.scala | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0ee6de7bda..717fcbff97 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -43,7 +43,9 @@ case object NoProd extends ProdStrat // enum ConsStrat: -case class Dtor(scrut: ResultId)(val arms: Ls[Case -> Block]) extends ConsStrat +case class Dtor(scrut: ResultId)(val expr: Match) extends ConsStrat: + assert(scrut === expr.scrut.uid) + case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) @@ -204,7 +206,7 @@ class Deforest(using TL, Raise, Elaborator.State): s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd - case _: BlockMemberSymbol => symToStrat.getOrElse(s, {tl.log(""); NoProd}) // For `fun` and `let` only, not classes or modules? + case _: BlockMemberSymbol => symToStrat.getOrElse(s, {tl.log(s"${s.nme} no strat"); NoProd}) // For `fun` and `let` only, not classes or modules? case _: LocalSymbol => symToStrat(s) case _: FlowSymbol => symToStrat(s) @@ -217,11 +219,11 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def processBlock(b: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N): ProdStrat = b match - case Match(scrut, arms, dflt, rest) => + case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => - constrain(scrutStrat, Dtor(scrut.uid)(arms)) + constrain(scrutStrat, Dtor(scrut.uid)(m)) // TODO: fix this "asInstanceOf"? processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) } @@ -368,8 +370,29 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - val ctorDests = mutable.Map.empty[ResultId, (Map[ResultId, Ls[Case -> Block]] -> Ls[ResultId])].withDefaultValue(Map.empty -> Nil) - val dtorSources = mutable.Map.empty[DtorExpr, Ls[ResultId]].withDefaultValue(Nil) + case class CtorDest(matches: Map[ResultId, Match], sels: Ls[ResultId]) + + object ctorDests: + val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil)) + def update(ctor: CtorExpr, m: Match) = ctorDests.updateWith(ctor): + case Some(CtorDest(matches, sels)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels)) + case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil)) + def update(ctor: CtorExpr, s: ResultId) = ctorDests.updateWith(ctor): + case Some(CtorDest(matches, sels)) => Some(CtorDest(matches, s :: sels)) + case None => Some(CtorDest(Map.empty, s :: Nil)) + def get(ctor: CtorExpr) = ctorDests.get(ctor) + + object dtorSources: + val dtorSources = mutable.Map.empty[DtorExpr, Ls[ResultId]].withDefaultValue(Nil) + private def getDtorExprOfResultId(i: ResultId) = ResultUid(i) match + case s: Select => DtorExpr.Sel(i) + case r: Value.Ref => DtorExpr.Match(i) + case _ => ??? // unreachable + def update(dtor: ResultId, ctor: ResultId) = + val dtorExpr = getDtorExprOfResultId(dtor) + dtorSources += dtorExpr -> (ctor :: dtorSources(dtorExpr)) + def get(dtor: ResultId) = dtorSources.get(getDtorExprOfResultId(dtor)) + def resolveConstraints: Unit = @@ -384,29 +407,14 @@ class Deforest(using TL, Raise, Elaborator.State): (prod, cons) match case (ctorStrat@Ctor(ctor, args), dtorStrat@Dtor(scrut)) => - ctorDests.updateWith(ctorStrat.expr){ - case Some(d -> s) => - Some( - (d.updatedWith(scrut){ - case None => Some(dtorStrat.arms) - case Some(v) => Some(dtorStrat.arms ::: v) - }) -> s - ) - case None => Some(Map(scrut -> dtorStrat.arms) -> Nil) - } - dtorSources += DtorExpr.Match(scrut) -> (ctorStrat.expr :: dtorSources(DtorExpr.Match(scrut))) + ctorDests.update(ctorStrat.expr, dtorStrat.expr) + dtorSources.update(scrut, ctorStrat.expr) case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else - ctorDests.updateWith(ctorStrat.expr){ - case Some(d -> s) => Some(d -> (selDtor.expr :: s)) - case None => Some(Map.empty -> (selDtor.expr :: Nil)) - } - dtorSources.updateWith(DtorExpr.Sel(selDtor.expr)){ - case None => Some(ctorStrat.expr :: Nil) - case Some(value) => Some(ctorStrat.expr :: value) - } + ctorDests.update(ctorStrat.expr, selDtor.expr) + dtorSources.update(selDtor.expr, ctorStrat.expr) args.find(a => a._1.id == field).map(p => // rewritingSel.add(sel) handle(p._2 -> consVar) @@ -458,7 +466,7 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[CtorExpr, (Map[ResultId, Ls[Case -> Block]] -> Ls[ResultId])] + type CtorToDtor = Map[CtorExpr, CtorDest] type DtorToCtor = Map[DtorExpr, Ls[CtorExpr]] def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = @@ -466,7 +474,7 @@ class Deforest(using TL, Raise, Elaborator.State): ctorDests -> dtorSources else val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) - removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case (mat -> sels) => + removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s)) }.toSet) @@ -477,14 +485,13 @@ class Deforest(using TL, Raise, Elaborator.State): val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) removeCtor(ctorDests, newDtorSources, toDelete.values.flatten.toSet) - val ctorToDtor = ctorDests.toMap - val dtorToCtor = dtorSources.toMap + val ctorToDtor = ctorDests.ctorDests.toMap + val dtorToCtor = dtorSources.dtorSources.toMap removeCtor( ctorToDtor, dtorToCtor, - ctorToDtor.filterNot { case (_, dests) => - val (dtors, sels) = dests + ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels) => (dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { val Value.Ref(scrut) = ResultUid(dtors.head._1) @@ -499,8 +506,7 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = val res = mutable.Map.empty[CtorExpr, CtorFinalDest] - resolveClashes._1.foreach { case (ctor, dests) => - val (dtors, sels) = dests + resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head)) else if dtors.size == 0 && sels.size > 1 then @@ -515,7 +521,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Select(Value.Ref(l), nme) => l == scrut case _ => false } then - Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels)) + Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2.arms, sels)) else throw Error("more than one consumer") None From e7b4562318425cc6764a32f2cc210ae84b864cc9 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:10:02 +0800 Subject: [PATCH 046/303] keep track of the whole match block --- .../main/scala/hkmc2/codegen/Deforestation.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 717fcbff97..97fa5ea01c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -57,7 +57,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, arms: Ls[Case -> Block], selInArms: Ls[ResultId]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId]) case Sel(s: ResultId) trait FieldSelTrait: @@ -521,7 +521,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Select(Value.Ref(l), nme) => l == scrut case _ => false } then - Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2.arms, sels)) + Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels)) else throw Error("more than one consumer") None @@ -595,8 +595,8 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(call.uid) match case None => handleNormalCall(args) - case Some(CtorFinalDest.Match(scrut, arms, sels)) => - val body = arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 + case Some(CtorFinalDest.Match(scrut, expr, sels)) => + val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 tl.log(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) @@ -634,8 +634,8 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(s.uid) match case None => k(s) - case Some(CtorFinalDest.Match(scrut, arms, sels)) => - val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get + case Some(CtorFinalDest.Match(scrut, expr, sels)) => + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) @@ -647,8 +647,8 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(r.uid) match case None => k(r) - case Some(CtorFinalDest.Match(scrut, arms, sels)) => - val body = arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get + case Some(CtorFinalDest.Match(scrut, expr, sels)) => + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get tl.log(mod.toString + " ----> " + body) body._2.mapRes(k) case Some(_) => ??? // TODO: a selection on a module consumes it From ec12a65cc99f7d7e5b1d30346c4d8972bfe06257 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:24:28 +0800 Subject: [PATCH 047/303] process rest before defn for Define blocks --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 97fa5ea01c..f3a8f82f0b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -207,7 +207,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd case _: BlockMemberSymbol => symToStrat.getOrElse(s, {tl.log(s"${s.nme} no strat"); NoProd}) // For `fun` and `let` only, not classes or modules? - case _: LocalSymbol => symToStrat(s) + case _: LocalSymbol => symToStrat.getOrElse(s, NoProd) case _: FlowSymbol => symToStrat(s) def getClsFields(s: ClassSymbol) = s.tree.clsParams @@ -253,6 +253,7 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(sub) processBlock(rest) case Define(defn, rest) => + processBlock(rest) defn match case FunDefn(_, sym, params, body) => val funSymStratVar = freshVar(sym.nme) @@ -275,7 +276,7 @@ class Deforest(using TL, Raise, Elaborator.State): } processBlock(c.ctor) case _ => ??? // TODO: - processBlock(rest) + case End(msg) => NoProd case Throw(exc) => NoProd case AssignField(lhs, nme, rhs, rest) => ??? From d67f6670781df5edc0354c5e77d7e406af9cb86b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:06:26 +0800 Subject: [PATCH 048/303] wip: allocate freshvar for all symbols before --- .../scala/hkmc2/codegen/Deforestation.scala | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f3a8f82f0b..62f2535759 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -173,7 +173,16 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): + object StratVarUidHandler extends Uid.Handler[StratVar]() + given Uid.Handler[StratVar]#State = StratVarUidHandler.State() + import StratVarState.freshVar + def apply(p: Program) = + // allocate type vars for defined symbols in the blocks + symToStrat.init(p.main) + // p.main.definedVars.foreach: v => + // symToStrat += v -> freshVar(v.nme)._1 + processBlock(p.main.mergeMatchArms) resolveConstraints @@ -199,22 +208,48 @@ class Deforest(using TL, Raise, Elaborator.State): var constraints: Ls[ProdStrat -> ConsStrat] = Nil - val symToStrat = mutable.Map.empty[Symbol, ProdVar] - // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols - // TODO: ctor as a function? - def getStratOfSym(s: Symbol) = - s match - case _: BuiltinSymbol => NoProd - case _: TopLevelSymbol => NoProd - case _: BlockMemberSymbol => symToStrat.getOrElse(s, {tl.log(s"${s.nme} no strat"); NoProd}) // For `fun` and `let` only, not classes or modules? - case _: LocalSymbol => symToStrat.getOrElse(s, NoProd) - case _: FlowSymbol => symToStrat(s) + object symToStrat: + val store = mutable.Map.empty[Symbol, ProdVar] + + def init(p: Block) = + if store.isEmpty then + object AllVarsSymbolSubst extends SymbolSubst: + override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapFlowSym(s: FlowSymbol): FlowSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapTempSym(s: TempSymbol): TempSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapVarSym(s: VarSymbol): VarSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapInstSym(s: InstSymbol): InstSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapTermSym(s: TermSymbol): TermSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapClsSym(s: ClassSymbol): ClassSymbol = + store += s -> freshVar(s.nme)._1; s + override def mapModuleSym(s: ModuleSymbol): ModuleSymbol = + store += s -> freshVar(s.nme)._1; s + object FreshVarForAllVars extends BlockTransformer(AllVarsSymbolSubst) + FreshVarForAllVars.applyBlock(p) + + // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols + // TODO: ctor as a function? + def getStratOfSym(s: Symbol) = + s match + case _: BuiltinSymbol => NoProd + case _: TopLevelSymbol => NoProd + case _: BlockMemberSymbol => store.getOrElse(s, {tl.log(s"${s.nme} no strat"); NoProd}) // For `fun` and `let` only, not classes or modules? + case _: LocalSymbol => store.getOrElse(s, NoProd) + case _: FlowSymbol => store(s) + def get(s: Symbol) = store.get(s) + def +=(e: Symbol -> ProdVar) = store += e + def addAll(es: Iterable[Symbol -> ProdVar]) = es.foreach(store += _) + def apply(s: Symbol) = store(s) def getClsFields(s: ClassSymbol) = s.tree.clsParams - object StratVarUidHandler extends Uid.Handler[StratVar]() - given Uid.Handler[StratVar]#State = StratVarUidHandler.State() - import StratVarState.freshVar + def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c @@ -242,10 +277,10 @@ class Deforest(using TL, Raise, Elaborator.State): case Return(res, implct) => processResult(res) case Assign(lhs, rhs, rest) => symToStrat.get(lhs) match - case None => - val lhsTpeVar = freshVar(lhs.nme) - constrain(processResult(rhs), lhsTpeVar._2) - symToStrat += lhs -> lhsTpeVar._1 + // case None => + // val lhsTpeVar = freshVar(lhs.nme) + // constrain(processResult(rhs), lhsTpeVar._2) + // symToStrat += lhs -> lhsTpeVar._1 case Some(v) => constrain(processResult(rhs), v.asConsStrat) processBlock(rest) @@ -256,13 +291,14 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(rest) defn match case FunDefn(_, sym, params, body) => - val funSymStratVar = freshVar(sym.nme) - symToStrat += sym -> funSymStratVar._1 + // val funSymStratVar = freshVar(sym.nme) + // symToStrat += sym -> funSymStratVar._1 + val funSymStratVar = symToStrat(sym) val param = params.head match case ParamList(flags, params, restParam) => params val funStrat = constrFun(param, body) // TODO: handle mutiple param list - constrain(funStrat, funSymStratVar._2) - funSymStratVar._1 + constrain(funStrat, funSymStratVar.asConsStrat) + funSymStratVar case ValDefn(owner, k, sym, rhs) => NoProd // TODO: case c: ClsLikeDefn if c.sym.asMod.isDefined => c.methods.foreach{ case FunDefn(_, sym, params, body) => @@ -311,7 +347,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(None) => val funSym = s.symbol.get val appRes = freshVar("call_" + funSym.nme + "_res") - constrain(getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) @@ -323,7 +359,7 @@ class Deforest(using TL, Raise, Elaborator.State): Ctor(s, clsFields.zip(argsTpe).toMap)(c.uid) case _ => // then it is a function val appRes = freshVar("call_" + l.nme + "_res") - constrain(getStratOfSym(l), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) @@ -358,7 +394,7 @@ class Deforest(using TL, Raise, Elaborator.State): tpeVar._1 case v@Value.Ref(l) => l.asObj match - case None => getStratOfSym(l) + case None => symToStrat.getStratOfSym(l) case Some(m) => Ctor(m, Map.empty)(v.uid) case Value.This(sym) => ??? From 2ab41d7d5083f68bb1d72a148d58cb875c8466a7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Feb 2025 17:16:59 +0800 Subject: [PATCH 049/303] no need to process rest before defn for Define blocks --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 62f2535759..dcc04ae3c3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -288,7 +288,6 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(sub) processBlock(rest) case Define(defn, rest) => - processBlock(rest) defn match case FunDefn(_, sym, params, body) => // val funSymStratVar = freshVar(sym.nme) @@ -312,7 +311,7 @@ class Deforest(using TL, Raise, Elaborator.State): } processBlock(c.ctor) case _ => ??? // TODO: - + processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd case AssignField(lhs, nme, rhs, rest) => ??? @@ -323,11 +322,11 @@ class Deforest(using TL, Raise, Elaborator.State): def constrFun(params: Ls[Param], body: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N) = val paramSyms = params.map{ case Param(_, sym, _) => sym } - val paramStrats = paramSyms.map{ sym => freshVar(sym.name) } - symToStrat.addAll(paramSyms.zip(paramStrats.map(_._1))) + val paramStrats = paramSyms.map{ sym => symToStrat(sym) } + symToStrat.addAll(paramSyms.zip(paramStrats)) val res = freshVar() constrain(processBlock(body), res._2) - ProdFun(paramStrats.map(_._2), res._1) + ProdFun(paramStrats.map(s => s.asConsStrat), res._1) def processResult(r: Result)(using inArm: Option[ProdVar -> ClsOrModSymbol]): ProdStrat = r match case c@Call(f, args) => From 77762b960676d4a8db512c24c17884ac36aea369 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:01:24 +0800 Subject: [PATCH 050/303] wip: ad hoc tail transformation for match blocks in rewriting --- .../scala/hkmc2/codegen/Deforestation.scala | 126 +++++++++++++++--- 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index dcc04ae3c3..54cfbfae82 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -170,6 +170,89 @@ extension (b: Block) case m: Match => m.mergeArms case _ => super.applyBlock(b) MergeMatchArmTransformer.applyBlock(b) + + def flattened = b.flatten(identity) + + private def flatten(k: End => Block): Block = b match + case Match(scrut, arms, dflt, rest) => + val newRest = rest.flatten(k) + val newArms = arms.mapConserve: arm => + val newBody = arm._2.flattened + if newBody is arm._2 then arm else (arm._1, newBody) + val newDflt = dflt.map(_.flattened) + if (newRest is rest) && (newArms is arms) && (dflt is newDflt) + then b + else Match(scrut, newArms, newDflt, newRest) + + case Label(label, body, rest) => + val newBody = body.flattened + val newRest = rest.flatten(k) + if (newBody is body) && (newRest is rest) + then b + else Label(label, newBody, newRest) + + case Begin(sub, rest) => + sub.flatten(_ => rest.flatten(k)) + + case TryBlock(sub, finallyDo, rest) => + val newSub = sub.flattened + val newFinallyDo = finallyDo.flattened + val newRest = rest.flatten(k) + if (newSub is sub) && (newFinallyDo is finallyDo) && (newRest is rest) + then b + else TryBlock(newSub, newFinallyDo, newRest) + + case Assign(lhs, rhs, rest) => + val newRest = rest.flatten(k) + if newRest is rest + then b + else Assign(lhs, rhs, newRest) + + case a@AssignField(lhs, nme, rhs, rest) => + val newRest = rest.flatten(k) + if newRest is rest + then b + else AssignField(lhs, nme, rhs, newRest)(a.symbol) + + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => + val newRest = rest.flatten(k) + if newRest is rest + then b + else AssignDynField(lhs, fld, arrayIdx, rhs, newRest) + + case Define(defn, rest) => + val newDefn = defn match + case d: FunDefn => + val newBody = d.body.flattened + if newBody is d.body + then d + else d.copy(body = newBody) + case v: ValDefn => v + case c: ClsLikeDefn => + val newPreCtor = c.preCtor.flattened + val newCtor = c.ctor.flattened + if (newPreCtor is c.preCtor) && (newCtor is c.ctor) + then c + else c.copy(preCtor = newPreCtor, ctor = newCtor) + + val newRest = rest.flatten(k) + if (newDefn is defn) && (newRest is rest) + then b + else Define(newDefn, newRest) + + case HandleBlock(lhs, res, par, args, cls, handlers, body, rest) => + val newHandlers = handlers.mapConserve: h => + val newBody = h.body.flattened + if newBody is h.body then h else h.copy(body = newBody) + val newBody = body.flattened + val newRest = rest.flatten(k) + if (newHandlers is handlers) && (newBody is body) && (newRest is rest) + then b + else HandleBlock(lhs, res, par, args, cls, newHandlers, newBody, newRest) + + case e: End => k(e) + case t: BlockTail => b + class Deforest(using TL, Raise, Elaborator.State): @@ -178,12 +261,15 @@ class Deforest(using TL, Raise, Elaborator.State): import StratVarState.freshVar def apply(p: Program) = + val flattenP = p.main.flattened + val mergedArms = flattenP.mergeMatchArms + // allocate type vars for defined symbols in the blocks symToStrat.init(p.main) // p.main.definedVars.foreach: v => // symToStrat += v -> freshVar(v.nme)._1 - processBlock(p.main.mergeMatchArms) + processBlock(mergedArms) resolveConstraints tl.log("upper:") @@ -202,7 +288,10 @@ class Deforest(using TL, Raise, Elaborator.State): tl.log("dtor -> ctor") resolveClashes._2.foreach(l => tl.log("\t" + l)) - rewrite(p) + Program( + p.imports, + rewrite(mergedArms) + ) @@ -576,28 +665,22 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Match(scrut, _, _) => scrut }.toSet - def rewrite(p: Program) = - Program( - p.imports, - DeforestTransformer.applyBlock(p.main) - ) + def rewrite(p: Block) = + DeforestTransformer.applyBlock(p) object DeforestTransformer extends BlockTransformer(new SymbolSubst()): override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then - // TODO: - rest match - case End(msg) => Return(scrut, mat.hasImplctRet) // TODO: true or false? - case _ => rest + + Return(Call(scrut, Nil)(false, false), rest.hasImplctRet) // TODO: free var application else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) case Return(res, implct) => applyResult2(res)(r => Return(r, implct)) case Assign(lhs, rhs, rest) => applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) - case Begin(sub, rest) => Begin(applyBlock(sub), applyBlock(rest)) case d@Define(defn, rest) => defn match case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) @@ -612,6 +695,7 @@ class Deforest(using TL, Raise, Elaborator.State): // case Continue(label) => ??? // case TryBlock(sub, finallyDo, rest) => ??? + def makeLambda(body: Block) = Value.Lam(ParamList(ParamListFlags.empty, Nil, N), body) override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => @@ -633,13 +717,16 @@ class Deforest(using TL, Raise, Elaborator.State): handleNormalCall(args) case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - tl.log(call.toString() + " ----> " + body) + // tl.log(call.toString() + " ----> " + body) + val bodyAndRest = Begin(body, expr.rest) // TODO: need return, and make it a lambda? val newArgs = args.map(_ => TempSymbol(N)) val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - args.zip(newArgs).foldRight[Block](applyBlock(body.replaceSelect(using sels.toSet, idsToArgs)).mapRes(k)){ case ((a, tmp), rest) => + args.zip(newArgs).foldRight[Block](k(makeLambda( + applyBlock(bodyAndRest.replaceSelect(using sels.toSet, idsToArgs)) + ))){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) } @@ -672,9 +759,9 @@ class Deforest(using TL, Raise, Elaborator.State): k(s) case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - tl.log(mod.toString + " ----> " + body) - - body._2.mapRes(k) + // tl.log(mod.toString + " ----> " + body) + val bodyAndRest = Begin(body._2, expr.rest) + k(makeLambda(bodyAndRest)) case Some(_) => ??? // TODO: a selection on a module consumes it case r@Value.Ref(l) => l.asObj match @@ -685,8 +772,9 @@ class Deforest(using TL, Raise, Elaborator.State): k(r) case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - tl.log(mod.toString + " ----> " + body) - body._2.mapRes(k) + // tl.log(mod.toString + " ----> " + body) + val bodyAndRest = Begin(body._2, expr.rest) + k(makeLambda(bodyAndRest)) case Some(_) => ??? // TODO: a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) From 3430239ba30026cdb45ec44a488b700a53ff4e8e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:31:20 +0800 Subject: [PATCH 051/303] do not print all definedvalues for deforested programs --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 653abfd765..93c40f2a36 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -187,16 +187,16 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if silent.isUnset then import Elaborator.Ctx.* - def definedValues = curCtx.env.iterator.flatMap: - case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => - e.symbol match - case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) - case S(ts: BlockMemberSymbol) - if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) - case S(vs: VarSymbol) => S((nme, vs, N)) - case _ => N - case _ => N - val valuesToPrint = ("", resSym, expect.get) +: definedValues.toSeq.sortBy(_._1) + // def definedValues = curCtx.env.iterator.flatMap: + // case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => + // e.symbol match + // case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) + // case S(ts: BlockMemberSymbol) + // if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) + // case S(vs: VarSymbol) => S((nme, vs, N)) + // case _ => N + // case _ => N + val valuesToPrint = List(("", resSym, expect.get)) valuesToPrint.foreach: (nme, sym, expect) => val le = import codegen.* From 294d10db727e5db204b217b51b3784abd272a344 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Feb 2025 16:52:06 +0800 Subject: [PATCH 052/303] remove useless functions; improve explicit return when rewriting --- .../scala/hkmc2/codegen/Deforestation.scala | 59 ++++++------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 54cfbfae82..919b4705f3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -106,24 +106,6 @@ extension (m: Match) ) extension (b: Block) - def mapRes(f: Result => Block): Block = b match - case Return(res, implct) => f(res) - case Assign(lhs, rhs, rest: End) => f(rhs) - case Assign(lhs, rhs, rest) => Assign(lhs, rhs, rest.mapRes(f)) - case Define(defn, rest) => Define(defn, rest.mapRes(f)) - // case Match(scrut, arms, dflt, e: End) => Match(scrut, arms) - // case Match(scrut, arms, dflt, rest) => Match(scrut, arms, dflt, rest.mapRes(f)) - case Throw(exc) => ??? - case Label(label, body, rest) => ??? - case Break(label) => ??? - case Continue(label) => ??? - case Begin(sub, rest) => ??? - case TryBlock(sub, finallyDo, rest) => ??? - case AssignField(_, _, _, _) => ??? - case _: HandleBlock => ??? - case HandleBlockReturn(res) => ??? - case End(msg) => ??? - def replaceSelect(using p: Set[ResultId], args: Map[Tree.Ident, Path]): Block = b match case Assign(lhs, rhs, rest) => Assign(lhs, rhs.replaceSelect, rest.replaceSelect) case Return(res, implct) => Return(res.replaceSelect, implct) @@ -141,28 +123,18 @@ extension (b: Block) // case HandleBlockReturn(res) => ??? // case End(msg) => ??? + def hasExplicitRet: Boolean = + object HasExplicitRetTransformer extends BlockTransformer(new SymbolSubst()): + var flag = false + override def applyBlock(b: Block): Block = b match + case Return(_, imp) => flag = !imp; b + case Define(defn, rest) => applyBlock(rest) + case _ => super.applyBlock(b) + override def applyResult(r: Result): Result = r + + HasExplicitRetTransformer.applyBlock(b) + HasExplicitRetTransformer.flag - def replaceAssignments(args: List[Path]): Block = args match - case head :: tail => b match - case Assign(lhs, rhs, rest) => Assign(lhs, head, rest.replaceAssignments(tail)) - case Nil => b - - def hasImplctRet: Boolean = b match - case Match(scrut, arms, dflt, rest) => arms.map(a => a._2).appendedAll(dflt).exists(b => b.hasImplctRet) - case Return(res, implct) => implct - case Assign(lhs, rhs, rest) => rest.hasImplctRet - case Define(defn, rest) => rest.hasImplctRet - case End(msg) => false - case _ => false - // case Throw(exc) => - // case Label(label, body, rest) => - // case Break(label) => - // case Continue(label) => - // case Begin(sub, rest) => - // case TryBlock(sub, finallyDo, rest) => - // case AssignField(symbol) => - // case HandleBlock(lhs, res, cls, handlers, body, rest) => - // case HandleBlockReturn(res) => def mergeMatchArms: Block = object MergeMatchArmTransformer extends BlockTransformer(new SymbolSubst()): @@ -674,7 +646,7 @@ class Deforest(using TL, Raise, Elaborator.State): case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then - Return(Call(scrut, Nil)(false, false), rest.hasImplctRet) // TODO: free var application + Return(Call(scrut, Nil)(false, false), !rest.hasExplicitRet) // TODO: free var application else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) case Return(res, implct) => @@ -695,7 +667,12 @@ class Deforest(using TL, Raise, Elaborator.State): // case Continue(label) => ??? // case TryBlock(sub, finallyDo, rest) => ??? - def makeLambda(body: Block) = Value.Lam(ParamList(ParamListFlags.empty, Nil, N), body) + def makeLambda(body: Block) = Value.Lam( + ParamList(ParamListFlags.empty, Nil, N), + body.mapTail: + case Return(res, implct) => Return(res, false) + case t => t + ) override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => From 881b85933ba96505c340d0f5d20398f502028105 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Feb 2025 19:59:56 +0800 Subject: [PATCH 053/303] better replace select --- .../scala/hkmc2/codegen/Deforestation.scala | 43 +++++-------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 919b4705f3..1cbf222317 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -74,19 +74,6 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid -extension (r: Result) - def replaceSelect(using p: Set[ResultId], args: Map[Tree.Ident, Path]): Result = r match - case c@Call(f, args) => Call(f, args.map{case Arg(spread, value) => Arg(spread, value.replaceSelect.asInstanceOf[Path])})(c.isMlsFun, c.mayRaiseEffects) - case sel@Select(path, nme) => - if p.contains(sel.uid) then args(nme) else sel - case _ => r - // case Value.Ref(l) => r - // case Instantiate(cls, args) => ??? - // case Value.This(sym) => ??? - // case Value.Lit(lit) => ??? - // case Value.Lam(params, body) => ??? - // case Value.Arr(elems) => ??? - extension (m: Match) def mergeArms: Match = val Match(s, arms, dflt, rest) = m @@ -106,22 +93,12 @@ extension (m: Match) ) extension (b: Block) - def replaceSelect(using p: Set[ResultId], args: Map[Tree.Ident, Path]): Block = b match - case Assign(lhs, rhs, rest) => Assign(lhs, rhs.replaceSelect, rest.replaceSelect) - case Return(res, implct) => Return(res.replaceSelect, implct) - case Match(scrut, arms, dflt, rest) => ??? - case _ => b - // case Throw(exc) => ??? - // case Label(label, body, rest) => ??? - // case Break(label) => ??? - // case Continue(label) => ??? - // case Begin(sub, rest) => ??? - // case TryBlock(sub, finallyDo, rest) => ??? - // case AssignField(_, _, _, _) => ??? - // case Define(defn, rest) => ??? - // case HandleBlock(lhs, res, cls, handlers, body, rest) => ??? - // case HandleBlockReturn(res) => ??? - // case End(msg) => ??? + def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Path]): Block = + object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): + override def applyPath(p: Path): Path = p match + case s@Select(_, nme) if ss(s.uid) => args(nme) + case _ => p + ReplaceSelectTransformer.applyBlock(b) def hasExplicitRet: Boolean = object HasExplicitRetTransformer extends BlockTransformer(new SymbolSubst()): @@ -645,8 +622,8 @@ class Deforest(using TL, Raise, Elaborator.State): override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then - - Return(Call(scrut, Nil)(false, false), !rest.hasExplicitRet) // TODO: free var application + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) + Return(Call(scrut, Nil)(false, false), !needExplicitRet) // TODO: free var application else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) case Return(res, implct) => @@ -695,14 +672,14 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 // tl.log(call.toString() + " ----> " + body) - val bodyAndRest = Begin(body, expr.rest) // TODO: need return, and make it a lambda? val newArgs = args.map(_ => TempSymbol(N)) val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap + val bodyAndRest = Begin(body.replaceSelect(using sels.toSet, idsToArgs), expr.rest) args.zip(newArgs).foldRight[Block](k(makeLambda( - applyBlock(bodyAndRest.replaceSelect(using sels.toSet, idsToArgs)) + applyBlock(bodyAndRest) ))){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) From ae2601e3f5c990e3f05071f3890c0a633c38f154 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 14 Feb 2025 20:10:30 +0800 Subject: [PATCH 054/303] update tests --- .../src/test/mlscript/deforest/simple.mls | 388 +++++++----------- .../src/test/mlscript/deforest/todos.mls | 146 +++++++ 2 files changed, 293 insertions(+), 241 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/todos.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 2164c4554f..f8c111779c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -44,12 +44,16 @@ test() //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { -//│ tmp = 1; +//│ tmp = () => { +//│ return 1; +//│ }; //│ } else { -//│ tmp = 2; +//│ tmp = () => { +//│ return 2; +//│ }; //│ } //│ x = tmp; -//│ return x +//│ return runtime.safeCall(x()) //│ }; //│ block$res2 = test(); //│ undefined @@ -94,21 +98,27 @@ test() //│ ==== JS (deforested): ==== //│ let test1; //│ test1 = function test() { -//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = A1; -//│ param01 = tmp1; -//│ x2 = param01; -//│ tmp = x2; +//│ tmp = () => { +//│ let param0, x1; +//│ param0 = tmp1; +//│ x1 = param0; +//│ return x1; +//│ }; //│ } else { //│ tmp2 = B1; -//│ param0 = tmp2; -//│ x1 = param0; -//│ tmp = x1; +//│ tmp = () => { +//│ let param0, x1; +//│ param0 = tmp2; +//│ x1 = param0; +//│ return x1; +//│ }; //│ } //│ x = tmp; -//│ return x +//│ return runtime.safeCall(x()) //│ }; //│ block$res4 = test1(); //│ undefined @@ -168,26 +178,36 @@ test() //│ ==== JS (deforested): ==== //│ let test2, f; //│ f = function f(a) { -//│ return a +//│ return runtime.safeCall(a()) //│ }; //│ test2 = function test() { -//│ let x, scrut, param0, x1, param01, x2, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = 1; -//│ param01 = tmp1; -//│ x2 = param01; -//│ tmp2 = x2; -//│ tmp = f(tmp2); +//│ tmp1 = () => { +//│ return 1; +//│ }; +//│ tmp = () => { +//│ let param0, x1, tmp3; +//│ param0 = tmp1; +//│ x1 = param0; +//│ tmp3 = x1; +//│ return f(tmp3); +//│ }; //│ } else { -//│ tmp3 = 2; -//│ param0 = tmp3; -//│ x1 = param0; -//│ tmp4 = x1; -//│ tmp = f(tmp4); +//│ tmp2 = () => { +//│ return 2; +//│ }; +//│ tmp = () => { +//│ let param0, x1, tmp3; +//│ param0 = tmp2; +//│ x1 = param0; +//│ tmp3 = x1; +//│ return f(tmp3); +//│ }; //│ } //│ x = tmp; -//│ return x +//│ return runtime.safeCall(x()) //│ }; //│ block$res6 = test2(); //│ undefined @@ -196,6 +216,7 @@ test() //│ = 1 +// `x.x` is successfully fused :sjs fun f1(a) = if a is A then 1 @@ -267,25 +288,35 @@ test() //│ ==== JS (deforested): ==== //│ let test3, f1, f2; //│ f1 = function f1(a) { -//│ return a +//│ return runtime.safeCall(a()) //│ }; //│ f2 = function f2(a) { -//│ return a +//│ return runtime.safeCall(a()) //│ }; //│ test3 = function test() { -//│ let x, scrut, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = 1; -//│ tmp2 = tmp1; -//│ tmp = f1(tmp2); +//│ tmp1 = () => { +//│ return 1; +//│ }; +//│ tmp = () => { +//│ let tmp3; +//│ tmp3 = tmp1; +//│ return f1(tmp3); +//│ }; //│ } else { -//│ tmp3 = 5; -//│ tmp4 = tmp3; -//│ tmp = f2(tmp4); +//│ tmp2 = () => { +//│ return 5; +//│ }; +//│ tmp = () => { +//│ let tmp3; +//│ tmp3 = tmp2; +//│ return f2(tmp3); +//│ }; //│ } //│ x = tmp; -//│ return x +//│ return runtime.safeCall(x()) //│ }; //│ block$res8 = test3(); //│ undefined @@ -341,22 +372,28 @@ test() //│ test4 = function test() { //│ let c, g, tmp, tmp1, tmp2; //│ g = function g(x) { -//│ let scrut, param0, x1, param01, x2, tmp3, tmp4; +//│ let scrut, tmp3, tmp4; //│ scrut = true; //│ if (scrut === true) { //│ tmp3 = 11; -//│ param01 = tmp3; -//│ x2 = param01; -//│ return x2 +//│ return () => { +//│ let param0, x1; +//│ param0 = tmp3; +//│ x1 = param0; +//│ return x1; +//│ } //│ } else { //│ tmp4 = 22; -//│ param0 = tmp4; -//│ x1 = param0; -//│ return x1 +//│ return () => { +//│ let param0, x1; +//│ param0 = tmp4; +//│ x1 = param0; +//│ return x1; +//│ } //│ } //│ }; //│ c = function c(x) { -//│ return x +//│ return runtime.safeCall(x()) //│ }; //│ tmp1 = true; //│ tmp = g(tmp1); @@ -422,37 +459,42 @@ map(enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo, map, tmp, tmp1, tmp2, tmp3; //│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; -//│ tmp8 = a; -//│ tmp9 = b; -//│ scrut = tmp8 < tmp9; +//│ let scrut, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; +//│ tmp6 = a; +//│ tmp7 = b; +//│ scrut = tmp6 < tmp7; //│ if (scrut === true) { -//│ tmp10 = a; -//│ tmp11 = 1; -//│ tmp4 = tmp10 + tmp11; -//│ tmp12 = tmp4; -//│ tmp13 = b; -//│ tmp5 = enumFromTo(tmp12, tmp13); -//│ tmp14 = a; -//│ tmp15 = tmp5; -//│ param0 = tmp14; -//│ param1 = tmp15; -//│ h = param0; -//│ t = param1; -//│ tmp16 = h; -//│ tmp17 = 4; -//│ tmp6 = tmp16 + tmp17; -//│ tmp18 = t; -//│ tmp7 = map(tmp18); -//│ tmp19 = tmp6; -//│ tmp20 = tmp7; -//│ return Cons1(tmp19, tmp20) +//│ tmp8 = a; +//│ tmp9 = 1; +//│ tmp4 = tmp8 + tmp9; +//│ tmp10 = tmp4; +//│ tmp11 = b; +//│ tmp5 = enumFromTo(tmp10, tmp11); +//│ tmp12 = a; +//│ tmp13 = tmp5; +//│ return () => { +//│ let param0, param1, h, t, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ param0 = tmp12; +//│ param1 = tmp13; +//│ h = param0; +//│ t = param1; +//│ tmp16 = h; +//│ tmp17 = 4; +//│ tmp14 = tmp16 + tmp17; +//│ tmp18 = t; +//│ tmp15 = map(tmp18); +//│ tmp19 = tmp14; +//│ tmp20 = tmp15; +//│ return Cons1(tmp19, tmp20); +//│ } //│ } else { -//│ return Nil1 +//│ return () => { +//│ return Nil1; +//│ } //│ } //│ }; //│ map = function map(ls) { -//│ return ls +//│ return runtime.safeCall(ls()) //│ }; //│ tmp1 = 1; //│ tmp2 = 4; @@ -466,17 +508,6 @@ map(enumFromTo(1, 4)) -// // FIXME: -// :sjs -// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -// fun map(f, ls) = -// (if ls is -// Nil then f => Nil -// Cons(h, t) then f => Cons(f(h), map(f, t)) -// )(f) -// map(x => x + 4, enumFromTo(1, 4)) - - :sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil @@ -520,34 +551,39 @@ sum(enumFromTo(1,10)) //│ ==== JS (deforested): ==== //│ let enumFromTo1, sum, tmp5, tmp6, tmp7, tmp8; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, param0, param1, h, t, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22; -//│ tmp12 = a; -//│ tmp13 = b; -//│ scrut = tmp12 < tmp13; +//│ let scrut, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18; +//│ tmp11 = a; +//│ tmp12 = b; +//│ scrut = tmp11 < tmp12; //│ if (scrut === true) { -//│ tmp14 = a; -//│ tmp15 = 1; -//│ tmp9 = tmp14 + tmp15; -//│ tmp16 = tmp9; -//│ tmp17 = b; -//│ tmp10 = enumFromTo1(tmp16, tmp17); -//│ tmp18 = a; -//│ tmp19 = tmp10; -//│ param0 = tmp18; -//│ param1 = tmp19; -//│ h = param0; -//│ t = param1; -//│ tmp20 = t; -//│ tmp11 = sum(tmp20); -//│ tmp21 = h; -//│ tmp22 = tmp11; -//│ return tmp21 + tmp22 +//│ tmp13 = a; +//│ tmp14 = 1; +//│ tmp9 = tmp13 + tmp14; +//│ tmp15 = tmp9; +//│ tmp16 = b; +//│ tmp10 = enumFromTo1(tmp15, tmp16); +//│ tmp17 = a; +//│ tmp18 = tmp10; +//│ return () => { +//│ let param0, param1, h, t, tmp19, tmp20, tmp21, tmp22; +//│ param0 = tmp17; +//│ param1 = tmp18; +//│ h = param0; +//│ t = param1; +//│ tmp20 = t; +//│ tmp19 = sum(tmp20); +//│ tmp21 = h; +//│ tmp22 = tmp19; +//│ return tmp21 + tmp22; +//│ } //│ } else { -//│ return 0 +//│ return () => { +//│ return 0; +//│ } //│ } //│ }; //│ sum = function sum(ls) { -//│ return ls +//│ return runtime.safeCall(ls()) //│ }; //│ tmp6 = 1; //│ tmp7 = 10; @@ -559,6 +595,8 @@ sum(enumFromTo(1,10)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 + +// multiple match, no fusion :sjs fun test() = let x = B @@ -597,12 +635,10 @@ test() //│ x = B1; //│ if (x instanceof A1.class) { //│ tmp10 = 1; +//│ } else if (x instanceof B1.class) { +//│ tmp10 = 3; //│ } else { -//│ if (x instanceof B1.class) { -//│ tmp10 = 3; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ if (x instanceof B1.class) { //│ return 2 @@ -617,137 +653,7 @@ test() //│ = 2 -// TODO: -:sjs -module Test with - fun f1(a) = if a is - A then 1 - B then 2 - C then 3 - fun f2(a) = if a is - A then 4 - B then 5 - C then 6 - let s = if true then AA(A) else BB(B) - if s is - AA then f1(s.x) - BB then f2(s.x) -//│ JS (unsanitized): -//│ let Test1; -//│ Test1 = class Test { -//│ static #s; -//│ static { -//│ let scrut, scrut1, tmp10; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp10 = AA1(A1); -//│ } else { -//│ tmp10 = BB1(B1); -//│ } -//│ this.#s = tmp10; -//│ scrut1 = this.#s; -//│ if (scrut1 instanceof AA1.class) { -//│ Test.f1(this.#s.x) -//│ } else { -//│ if (scrut1 instanceof BB1.class) { -//│ Test.f2(this.#s.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ static f1(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else { -//│ if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ } -//│ static f2(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 4 -//│ } else { -//│ if (a1 instanceof B1.class) { -//│ return 5 -//│ } else { -//│ if (a1 instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let Test1; -//│ Test1 = class Test { -//│ static #s1; -//│ static { -//│ let scrut, scrut1, tmp10, tmp11, tmp12, tmp13, tmp14; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp11 = A1; -//│ tmp10 = AA1(tmp11); -//│ } else { -//│ tmp12 = B1; -//│ tmp10 = BB1(tmp12); -//│ } -//│ this.#s1 = tmp10; -//│ scrut1 = this.#s1; -//│ if (scrut1 instanceof AA1.class) { -//│ tmp13 = this.#s1.x; -//│ Test.f1(tmp13) -//│ } else { -//│ if (scrut1 instanceof BB1.class) { -//│ tmp14 = this.#s1.x; -//│ Test.f2(tmp14) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ static f1(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else { -//│ if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ } -//│ static f2(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 4 -//│ } else { -//│ if (a1 instanceof B1.class) { -//│ return 5 -//│ } else { -//│ if (a1 instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ } -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ block$res19 = undefined; -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls new file mode 100644 index 0000000000..9f68f61391 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -0,0 +1,146 @@ +:js +:deforest + +object A +object B +object C +class AA(x) +class BB(x) + + +// TODO: how to keep track of the correct s.x +:sjs +module Test with + fun f1(a) = if a is + A then 1 + B then 2 + C then 3 + fun f2(a) = if a is + A then 4 + B then 5 + C then 6 + let s = if true then AA(A) else BB(B) + if s is + AA then f1(s.x) + BB then f2(s.x) +//│ JS (unsanitized): +//│ let Test1; +//│ Test1 = class Test { +//│ static #s; +//│ static { +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = AA1(A1); +//│ } else { +//│ tmp = BB1(B1); +//│ } +//│ this.#s = tmp; +//│ scrut1 = this.#s; +//│ if (scrut1 instanceof AA1.class) { +//│ Test.f1(this.#s.x) +//│ } else { +//│ if (scrut1 instanceof BB1.class) { +//│ Test.f2(this.#s.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ static f1(a) { +//│ if (a instanceof A1.class) { +//│ return 1 +//│ } else { +//│ if (a instanceof B1.class) { +//│ return 2 +//│ } else { +//│ if (a instanceof C1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ static f2(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 4 +//│ } else { +//│ if (a1 instanceof B1.class) { +//│ return 5 +//│ } else { +//│ if (a1 instanceof C1.class) { +//│ return 6 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ } +//│ } +//│ static toString() { return "Test"; } +//│ }; +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let Test1; +//│ Test1 = class Test { +//│ static #s1; +//│ static { +//│ let scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp1 = A1; +//│ tmp = AA1(tmp1); +//│ } else { +//│ tmp2 = B1; +//│ tmp = BB1(tmp2); +//│ } +//│ this.#s1 = tmp; +//│ scrut1 = this.#s1; +//│ if (scrut1 instanceof AA1.class) { +//│ tmp3 = this.#s1.x; +//│ Test.f1(tmp3) +//│ } else if (scrut1 instanceof BB1.class) { +//│ tmp4 = this.#s1.x; +//│ Test.f2(tmp4) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static f1(a) { +//│ if (a instanceof A1.class) { +//│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else if (a instanceof C1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static f2(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 4 +//│ } else if (a1 instanceof B1.class) { +//│ return 5 +//│ } else if (a1 instanceof C1.class) { +//│ return 6 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static toString() { return "Test"; } +//│ }; +//│ block$res2 = undefined; +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +// // FIXME: free var +// :sjs +// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +// fun map(f, ls) = +// (if ls is +// Nil then f => Nil +// Cons(h, t) then f => Cons(f(h), map(f, t)) +// )(f) +// map(x => x + 4, enumFromTo(1, 4)) From 35704a2b68eaa8874fbdb608df817a6b9cebe303 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 14 Feb 2025 22:36:04 +0800 Subject: [PATCH 055/303] forgot to recurse into rest of matches; wip: extract rests of matches into functions --- .../scala/hkmc2/codegen/Deforestation.scala | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1cbf222317..0d6f912790 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -651,6 +651,31 @@ class Deforest(using TL, Raise, Elaborator.State): case t => t ) + object matchRest: + val store = mutable.Map.empty[ResultId, FunDefn] + + def getOrElse(s: ResultId, restRewritten: Block): Opt[Symbol] = + store.get(s) match + case Some(s) => Some(s.sym) + case None if restRewritten.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => None + case _ => // now need to build a new function and update the store + val sym = BlockMemberSymbol(s"match_${s}_rest", Nil) + val freeVarsAndTheirNewSyms = restRewritten.freeVars.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + + object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): + override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) + + // val newVars = freeVars. + val newFunDef = FunDefn( + N, + sym, + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, + ReplaceLocalSymTransformer.applyBlock(restRewritten) + ) + store += s -> newFunDef + Some(sym) + + override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => def handleNormalCall(args: List[Arg]) = @@ -676,7 +701,7 @@ class Deforest(using TL, Raise, Elaborator.State): val newArgs = args.map(_ => TempSymbol(N)) val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - val bodyAndRest = Begin(body.replaceSelect(using sels.toSet, idsToArgs), expr.rest) + val bodyAndRest = Begin(body.replaceSelect(using sels.toSet, idsToArgs), applyBlock(expr.rest)) args.zip(newArgs).foldRight[Block](k(makeLambda( applyBlock(bodyAndRest) @@ -714,7 +739,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - val bodyAndRest = Begin(body._2, expr.rest) + val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) k(makeLambda(bodyAndRest)) case Some(_) => ??? // TODO: a selection on a module consumes it @@ -727,7 +752,9 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - val bodyAndRest = Begin(body._2, expr.rest) + + // val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) + val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) k(makeLambda(bodyAndRest)) case Some(_) => ??? // TODO: a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) From 970957830e764103370b97669e3fb6a58fd7c034 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 15 Feb 2025 16:06:56 +0800 Subject: [PATCH 056/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 24 +++--- .../src/test/mlscript/deforest/simple.mls | 86 ++++++++++++++----- .../src/test/mlscript/deforest/todos.mls | 72 ++++++++++++---- 3 files changed, 135 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0d6f912790..cbf79444aa 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -646,11 +646,15 @@ class Deforest(using TL, Raise, Elaborator.State): def makeLambda(body: Block) = Value.Lam( ParamList(ParamListFlags.empty, Nil, N), - body.mapTail: + body.flattened.mapTail: case Return(res, implct) => Return(res, false) case t => t ) + def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value]) = + val lambdaBody = Begin(applyBlock(body).replaceSelect(using sel, selMap), applyBlock(rest)) + makeLambda(lambdaBody) + object matchRest: val store = mutable.Map.empty[ResultId, FunDefn] @@ -701,11 +705,10 @@ class Deforest(using TL, Raise, Elaborator.State): val newArgs = args.map(_ => TempSymbol(N)) val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap - val bodyAndRest = Begin(body.replaceSelect(using sels.toSet, idsToArgs), applyBlock(expr.rest)) - args.zip(newArgs).foldRight[Block](k(makeLambda( - applyBlock(bodyAndRest) - ))){ case ((a, tmp), rest) => + val bodyAndRestInLam = setupBodyAndRest(body, expr.rest, scrut, sels.toSet, idsToArgs) + + args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) } @@ -739,8 +742,8 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) - k(makeLambda(bodyAndRest)) + val bodyAndRestInLam = setupBodyAndRest(body._2, expr.rest, scrut, Set.empty, Map.empty) + k(bodyAndRestInLam) case Some(_) => ??? // TODO: a selection on a module consumes it case r@Value.Ref(l) => l.asObj match @@ -753,13 +756,12 @@ class Deforest(using TL, Raise, Elaborator.State): val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - // val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) - val bodyAndRest = Begin(body._2, applyBlock(expr.rest)) - k(makeLambda(bodyAndRest)) + val bodyAndRestInLam = setupBodyAndRest(body._2, expr.rest, scrut, Set.empty, Map.empty) + k(bodyAndRestInLam) case Some(_) => ??? // TODO: a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) case Value.Arr(elems) => k(Value.Arr(elems)) - \ No newline at end of file + diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index f8c111779c..c4215dcf57 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -45,11 +45,11 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp = () => { -//│ return 1; +//│ return 1 //│ }; //│ } else { //│ tmp = () => { -//│ return 2; +//│ return 2 //│ }; //│ } //│ x = tmp; @@ -106,7 +106,7 @@ test() //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ }; //│ } else { //│ tmp2 = B1; @@ -114,7 +114,7 @@ test() //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ }; //│ } //│ x = tmp; @@ -185,25 +185,25 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = () => { -//│ return 1; +//│ return 1 //│ }; //│ tmp = () => { //│ let param0, x1, tmp3; //│ param0 = tmp1; //│ x1 = param0; //│ tmp3 = x1; -//│ return f(tmp3); +//│ return f(tmp3) //│ }; //│ } else { //│ tmp2 = () => { -//│ return 2; +//│ return 2 //│ }; //│ tmp = () => { //│ let param0, x1, tmp3; //│ param0 = tmp2; //│ x1 = param0; //│ tmp3 = x1; -//│ return f(tmp3); +//│ return f(tmp3) //│ }; //│ } //│ x = tmp; @@ -298,21 +298,21 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = () => { -//│ return 1; +//│ return 1 //│ }; //│ tmp = () => { //│ let tmp3; //│ tmp3 = tmp1; -//│ return f1(tmp3); +//│ return f1(tmp3) //│ }; //│ } else { //│ tmp2 = () => { -//│ return 5; +//│ return 5 //│ }; //│ tmp = () => { //│ let tmp3; //│ tmp3 = tmp2; -//│ return f2(tmp3); +//│ return f2(tmp3) //│ }; //│ } //│ x = tmp; @@ -380,7 +380,7 @@ test() //│ let param0, x1; //│ param0 = tmp3; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ } //│ } else { //│ tmp4 = 22; @@ -388,7 +388,7 @@ test() //│ let param0, x1; //│ param0 = tmp4; //│ x1 = param0; -//│ return x1; +//│ return x1 //│ } //│ } //│ }; @@ -485,11 +485,11 @@ map(enumFromTo(1, 4)) //│ tmp15 = map(tmp18); //│ tmp19 = tmp14; //│ tmp20 = tmp15; -//│ return Cons1(tmp19, tmp20); +//│ return Cons1(tmp19, tmp20) //│ } //│ } else { //│ return () => { -//│ return Nil1; +//│ return Nil1 //│ } //│ } //│ }; @@ -574,11 +574,11 @@ sum(enumFromTo(1,10)) //│ tmp19 = sum(tmp20); //│ tmp21 = h; //│ tmp22 = tmp19; -//│ return tmp21 + tmp22; +//│ return tmp21 + tmp22 //│ } //│ } else { //│ return () => { -//│ return 0; +//│ return 0 //│ } //│ } //│ }; @@ -654,6 +654,50 @@ test() - - - +:sjs +fun test() = + let x = A + let y = B + if x is + A then 1 + if y is + B then 2 +test() +//│ JS (unsanitized): +//│ let test6; +//│ test6 = function test() { +//│ let x, y, tmp10; +//│ x = A1; +//│ y = B1; +//│ if (x instanceof A1.class) { +//│ tmp10 = 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ if (y instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test6() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test6; +//│ test6 = function test() { +//│ let x, y; +//│ x = () => { +//│ let tmp10; +//│ tmp10 = 1; +//│ return runtime.safeCall(y()) +//│ }; +//│ y = () => { +//│ return 2 +//│ }; +//│ return runtime.safeCall(x()) +//│ }; +//│ block$res19 = test6(); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 9f68f61391..14234872f9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -7,6 +7,48 @@ object C class AA(x) class BB(x) +// FIXME: still lacks a return in the rewritten lambda for `y` +:sjs +let x = A +let y = B +if x is + A then 1 +if y is + B then 2 +//│ JS (unsanitized): +//│ let x, y, tmp; +//│ x = A1; +//│ y = B1; +//│ if (x instanceof A1.class) { +//│ tmp = 1; +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ if (y instanceof B1.class) { +//│ 2 +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let x, y; +//│ x = () => { +//│ let tmp; +//│ tmp = 1; +//│ return runtime.safeCall(y()) +//│ }; +//│ y = () => { +//│ return 2 +//│ }; +//│ block$res2 = runtime.safeCall(x()); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ x = A +//│ y = B + + // TODO: how to keep track of the correct s.x :sjs @@ -28,14 +70,14 @@ module Test with //│ Test1 = class Test { //│ static #s; //│ static { -//│ let scrut, scrut1, tmp; +//│ let scrut, scrut1, tmp1; //│ scrut = true; //│ if (scrut === true) { -//│ tmp = AA1(A1); +//│ tmp1 = AA1(A1); //│ } else { -//│ tmp = BB1(B1); +//│ tmp1 = BB1(B1); //│ } -//│ this.#s = tmp; +//│ this.#s = tmp1; //│ scrut1 = this.#s; //│ if (scrut1 instanceof AA1.class) { //│ Test.f1(this.#s.x) @@ -85,23 +127,23 @@ module Test with //│ Test1 = class Test { //│ static #s1; //│ static { -//│ let scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let scrut, scrut1, tmp1, tmp2, tmp3, tmp4, tmp5; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = A1; -//│ tmp = AA1(tmp1); +//│ tmp2 = A1; +//│ tmp1 = AA1(tmp2); //│ } else { -//│ tmp2 = B1; -//│ tmp = BB1(tmp2); +//│ tmp3 = B1; +//│ tmp1 = BB1(tmp3); //│ } -//│ this.#s1 = tmp; +//│ this.#s1 = tmp1; //│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { -//│ tmp3 = this.#s1.x; -//│ Test.f1(tmp3) -//│ } else if (scrut1 instanceof BB1.class) { //│ tmp4 = this.#s1.x; -//│ Test.f2(tmp4) +//│ Test.f1(tmp4) +//│ } else if (scrut1 instanceof BB1.class) { +//│ tmp5 = this.#s1.x; +//│ Test.f2(tmp5) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -130,7 +172,7 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; -//│ block$res2 = undefined; +//│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From d4358d77a02e843fd52880954ec2391407daa880 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:24:19 +0800 Subject: [PATCH 057/303] FIXME: blocktransformer: subst vs applyLocal in applyValue? --- .../shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index e4e027acca..301b425188 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -107,7 +107,7 @@ class BlockTransformer(subst: SymbolSubst): def applyValue(v: Value): Value = v match case Value.Ref(l) => - val l2 = l.subst + val l2 = applyLocal(l) if (l2 is l) then v else Value.Ref(l2) case Value.This(sym) => val sym2 = sym.subst From fc630a34d0c934cfbca8547a3bdd0c5b6dfa1104 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:40:32 +0800 Subject: [PATCH 058/303] extract the rest of consumer matches as functions --- .../scala/hkmc2/codegen/Deforestation.scala | 37 +++++++++-- .../src/test/mlscript/deforest/simple.mls | 66 +++++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cbf79444aa..22ae1abed8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -93,6 +93,8 @@ extension (m: Match) ) extension (b: Block) + def sortedFvs = (b.freeVars -- b.definedVars).toList.sortBy(_.uid) + def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Path]): Block = object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): override def applyPath(p: Path): Path = p match @@ -615,7 +617,9 @@ class Deforest(using TL, Raise, Elaborator.State): }.toSet def rewrite(p: Block) = - DeforestTransformer.applyBlock(p) + val rest = DeforestTransformer.applyBlock(p) + val newDefs = DeforestTransformer.matchRest.getAllFunDefs + newDefs(rest) object DeforestTransformer extends BlockTransformer(new SymbolSubst()): @@ -652,8 +656,24 @@ class Deforest(using TL, Raise, Elaborator.State): ) def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value]) = - val lambdaBody = Begin(applyBlock(body).replaceSelect(using sel, selMap), applyBlock(rest)) - makeLambda(lambdaBody) + val rewrittenRest = applyBlock(rest) + val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) + restFunOrRestBlock match + case None => + val lambdaBody = Begin(applyBlock(body).replaceSelect(using sel, selMap), rewrittenRest) + makeLambda(lambdaBody) + case Some(f) => + val lambdaBody = Begin( + applyBlock(body).replaceSelect(using sel, selMap), + Return( + Call( + Value.Ref(f), + rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + false + ) + ) + makeLambda(lambdaBody) + object matchRest: val store = mutable.Map.empty[ResultId, FunDefn] @@ -663,13 +683,14 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(s) => Some(s.sym) case None if restRewritten.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => None case _ => // now need to build a new function and update the store - val sym = BlockMemberSymbol(s"match_${s}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.freeVars.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme + val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) + val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): + // FIXME: depends on my hacky change (d4358d7) to blocktransformer to work... override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) - // val newVars = freeVars. val newFunDef = FunDefn( N, sym, @@ -679,6 +700,10 @@ class Deforest(using TL, Raise, Elaborator.State): store += s -> newFunDef Some(sym) + def getAllFunDefs: Block => Block = + store.values.foldRight(identity: Block => Block): (defn, k) => + r => Define(defn, k(r)) + override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index c4215dcf57..a858fa2f49 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -701,3 +701,69 @@ test() //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 + + +:sjs +fun c(a) = + val x = if a is + A then 1 + B then 2 + print(x) + x +c(A) + c(B) +//│ JS (unsanitized): +//│ let c, tmp10, tmp11; +//│ c = function c(a) { +//│ let x, tmp12, tmp13; +//│ if (a instanceof A1.class) { +//│ tmp12 = 1; +//│ } else { +//│ if (a instanceof B1.class) { +//│ tmp12 = 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ x = tmp12; +//│ tmp13 = Predef.print(x); +//│ return x +//│ }; +//│ tmp10 = c(A1); +//│ tmp11 = c(B1); +//│ tmp10 + tmp11 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, match_a_rest; +//│ match_a_rest = function match_a_rest(tmp16) { +//│ let x, tmp17, tmp18; +//│ x = tmp16; +//│ tmp18 = x; +//│ tmp17 = Predef.print(tmp18); +//│ return x +//│ }; +//│ c = function c(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ tmp12 = () => { +//│ let tmp16; +//│ tmp16 = 1; +//│ return match_a_rest(tmp16) +//│ }; +//│ tmp10 = c(tmp12); +//│ tmp13 = () => { +//│ let tmp16; +//│ tmp16 = 2; +//│ return match_a_rest(tmp16) +//│ }; +//│ tmp11 = c(tmp13); +//│ tmp14 = tmp10; +//│ tmp15 = tmp11; +//│ block$res21 = tmp14 + tmp15; +//│ undefined +//│ > 1 +//│ > 2 +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ > 1 +//│ > 2 +//│ = 3 From 7823ec583f24037f647fdcc6d4dd499b45566965 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 18 Feb 2025 01:32:32 +0800 Subject: [PATCH 059/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 22ae1abed8..c21840d995 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -93,6 +93,12 @@ extension (m: Match) ) extension (b: Block) + def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = + object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): + // FIXME: depends on my hacky change (d4358d7) to blocktransformer to work... + override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) + ReplaceLocalSymTransformer.applyBlock(b) + def sortedFvs = (b.freeVars -- b.definedVars).toList.sortBy(_.uid) def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Path]): Block = @@ -648,22 +654,25 @@ class Deforest(using TL, Raise, Elaborator.State): // case Continue(label) => ??? // case TryBlock(sub, finallyDo, rest) => ??? - def makeLambda(body: Block) = Value.Lam( - ParamList(ParamListFlags.empty, Nil, N), - body.flattened.mapTail: - case Return(res, implct) => Return(res, false) - case t => t - ) + def makeLambda(body: Block) = + val bodyFlattened = body.flattened // otherwise mapTail to make all return explicit may not work + val freeVarsAndTheirNewSyms = bodyFlattened.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + Value.Lam( + ParamList(ParamListFlags.empty, Nil, N), + bodyFlattened.mapTail: + case Return(res, implct) => Return(res, false) + case t => t + ) def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value]) = val rewrittenRest = applyBlock(rest) val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) - restFunOrRestBlock match + val lambdaBody = restFunOrRestBlock match case None => - val lambdaBody = Begin(applyBlock(body).replaceSelect(using sel, selMap), rewrittenRest) - makeLambda(lambdaBody) + Begin(applyBlock(body).replaceSelect(using sel, selMap), rewrittenRest) case Some(f) => - val lambdaBody = Begin( + Begin( applyBlock(body).replaceSelect(using sel, selMap), Return( Call( @@ -672,7 +681,7 @@ class Deforest(using TL, Raise, Elaborator.State): false ) ) - makeLambda(lambdaBody) + makeLambda(lambdaBody) object matchRest: @@ -687,15 +696,11 @@ class Deforest(using TL, Raise, Elaborator.State): val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): - // FIXME: depends on my hacky change (d4358d7) to blocktransformer to work... - override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) - val newFunDef = FunDefn( N, sym, ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, - ReplaceLocalSymTransformer.applyBlock(restRewritten) + restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) ) store += s -> newFunDef Some(sym) From f2908446230cec42e0cb77e8ce6fb26e410f32bd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 18 Feb 2025 22:49:01 +0800 Subject: [PATCH 060/303] wip: handle free vars... --- .../scala/hkmc2/codegen/Deforestation.scala | 40 +++-- .../src/test/mlscript/deforest/simple.mls | 108 ++++++++++++- .../src/test/mlscript/deforest/todos.mls | 144 ++++++++++++++++-- 3 files changed, 262 insertions(+), 30 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c21840d995..8458151007 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -99,9 +99,10 @@ extension (b: Block) override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs = (b.freeVars -- b.definedVars).toList.sortBy(_.uid) + // TODO: freeVars does not include any vars in function caller positions... + def sortedFvs = (b.freeVars -- b.definedVars).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) - def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Path]): Block = + def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): override def applyPath(p: Path): Path = p match case s@Select(_, nme) if ss(s.uid) => args(nme) @@ -633,7 +634,15 @@ class Deforest(using TL, Raise, Elaborator.State): case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - Return(Call(scrut, Nil)(false, false), !needExplicitRet) // TODO: free var application + val freeVars = + (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs) + .filterNot( + v => v == scrut.l || + (arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)))(v) // TODO: shouldn't intersect with dflt since it's just throw Error? + ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) + .sortBy(_.uid) + .map(v => Arg(false, Value.Ref(v))) + Return(Call(scrut, freeVars)(false, false), !needExplicitRet) // TODO: free var application else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) case Return(res, implct) => @@ -654,26 +663,31 @@ class Deforest(using TL, Raise, Elaborator.State): // case Continue(label) => ??? // case TryBlock(sub, finallyDo, rest) => ??? - def makeLambda(body: Block) = + def makeLambda(body: Block, freeVarsAndTheirNewSyms: Map[Symbol, VarSymbol]) = val bodyFlattened = body.flattened // otherwise mapTail to make all return explicit may not work - val freeVarsAndTheirNewSyms = bodyFlattened.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) Value.Lam( - ParamList(ParamListFlags.empty, Nil, N), - bodyFlattened.mapTail: + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + newBody.mapTail: case Return(res, implct) => Return(res, false) case t => t ) - def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value]) = - val rewrittenRest = applyBlock(rest) + def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = + val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) + val rewrittenRest = applyBlock(rest) // TODO: avoid rewriting it more than once + val freeVarsAndTheirNewSyms = + (rewrittenBody.sortedFvs ::: rest.sortedFvs) // TODO: why it's rest.sortedFvs instead of rewrittenRest?? + .map(s => s -> VarSymbol(Tree.Ident(s.nme))) + .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) + .toMap val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) val lambdaBody = restFunOrRestBlock match case None => - Begin(applyBlock(body).replaceSelect(using sel, selMap), rewrittenRest) + Begin(rewrittenBody.replaceSelect(using sel, selMap), rewrittenRest) case Some(f) => Begin( - applyBlock(body).replaceSelect(using sel, selMap), + rewrittenBody.replaceSelect(using sel, selMap), Return( Call( Value.Ref(f), @@ -681,7 +695,7 @@ class Deforest(using TL, Raise, Elaborator.State): false ) ) - makeLambda(lambdaBody) + makeLambda(lambdaBody, freeVarsAndTheirNewSyms) object matchRest: @@ -734,7 +748,7 @@ class Deforest(using TL, Raise, Elaborator.State): val newArgs = args.map(_ => TempSymbol(N)) - val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s))).toMap + val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap val bodyAndRestInLam = setupBodyAndRest(body, expr.rest, scrut, sels.toSet, idsToArgs) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index a858fa2f49..aa8b789469 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -686,15 +686,15 @@ test() //│ let test6; //│ test6 = function test() { //│ let x, y; -//│ x = () => { +//│ x = (y1) => { //│ let tmp10; //│ tmp10 = 1; -//│ return runtime.safeCall(y()) +//│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(y)) //│ }; //│ block$res19 = test6(); //│ undefined @@ -767,3 +767,105 @@ c(A) + c(B) //│ > 1 //│ > 2 //│ = 3 + + + +// simple free var example +:sjs +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + if ls is + Nil then Nil + Cons(h, t) then Cons(f(h), map(f, t)) +map(x => x + 4, enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let enumFromTo2, map1, tmp18; +//│ enumFromTo2 = function enumFromTo(a, b) { +//│ let scrut, tmp19, tmp20; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp19 = a + 1; +//│ tmp20 = enumFromTo2(tmp19, b); +//│ return Cons1(a, tmp20) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ map1 = function map(f3, ls) { +//│ let param0, param1, h, t, tmp19, tmp20; +//│ if (ls instanceof Nil1.class) { +//│ return Nil1 +//│ } else { +//│ if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp19 = runtime.safeCall(f3(h)); +//│ tmp20 = map1(f3, t); +//│ return Cons1(tmp19, tmp20) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ }; +//│ tmp18 = enumFromTo2(1, 4); +//│ map1((x) => { +//│ return x + 4 +//│ }, tmp18) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let enumFromTo2, map1, tmp18, tmp19, tmp20, tmp21, tmp22; +//│ enumFromTo2 = function enumFromTo(a, b) { +//│ let scrut, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32; +//│ tmp25 = a; +//│ tmp26 = b; +//│ scrut = tmp25 < tmp26; +//│ if (scrut === true) { +//│ tmp27 = a; +//│ tmp28 = 1; +//│ tmp23 = tmp27 + tmp28; +//│ tmp29 = tmp23; +//│ tmp30 = b; +//│ tmp24 = enumFromTo2(tmp29, tmp30); +//│ tmp31 = a; +//│ tmp32 = tmp24; +//│ return (f3) => { +//│ let param0, param1, h, t, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39; +//│ param0 = tmp31; +//│ param1 = tmp32; +//│ h = param0; +//│ t = param1; +//│ tmp35 = h; +//│ tmp33 = runtime.safeCall(f3(tmp35)); +//│ tmp36 = f3; +//│ tmp37 = t; +//│ tmp34 = map1(tmp36, tmp37); +//│ tmp38 = tmp33; +//│ tmp39 = tmp34; +//│ return Cons1(tmp38, tmp39) +//│ } +//│ } else { +//│ return () => { +//│ return Nil1 +//│ } +//│ } +//│ }; +//│ map1 = function map(f3, ls) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ tmp19 = 1; +//│ tmp20 = 4; +//│ tmp18 = enumFromTo2(tmp19, tmp20); +//│ tmp21 = (x) => { +//│ let tmp23, tmp24; +//│ tmp23 = x; +//│ tmp24 = 4; +//│ return tmp23 + tmp24 +//│ }; +//│ tmp22 = tmp18; +//│ block$res23 = map1(tmp21, tmp22); +//│ undefined +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 14234872f9..4b65e839f3 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -7,7 +7,10 @@ object C class AA(x) class BB(x) -// FIXME: still lacks a return in the rewritten lambda for `y` +object Nil +class Cons(h, t) + +// FIXME: why it's different when all in a function? :sjs let x = A let y = B @@ -32,15 +35,15 @@ if y is //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let x, y; -//│ x = () => { +//│ x = (y1) => { //│ let tmp; //│ tmp = 1; -//│ return runtime.safeCall(y()) +//│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ block$res2 = runtime.safeCall(x()); +//│ block$res3 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -172,17 +175,130 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; -//│ block$res4 = undefined; +//│ block$res5 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// // FIXME: free var -// :sjs -// fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -// fun map(f, ls) = -// (if ls is -// Nil then f => Nil -// Cons(h, t) then f => Cons(f(h), map(f, t)) -// )(f) -// map(x => x + 4, enumFromTo(1, 4)) + + + +// FIXME: still not well-scoped... +:fixme +:sjs +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + (if ls is + Nil then f => Nil + Cons(h, t) then f => Cons(f(h), map(f, t)) + )(f) +map(x => x + 4, enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let enumFromTo, map, tmp1; +//│ enumFromTo = function enumFromTo(a, b) { +//│ let scrut, tmp2, tmp3; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp2 = a + 1; +//│ tmp3 = enumFromTo(tmp2, b); +//│ return Cons1(a, tmp3) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ map = function map(f, ls) { +//│ let param0, param1, h, t, tmp2; +//│ if (ls instanceof Nil1.class) { +//│ tmp2 = (f1) => { +//│ return Nil1 +//│ }; +//│ } else { +//│ if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp2 = (f1) => { +//│ let tmp3, tmp4; +//│ tmp3 = runtime.safeCall(f1(h)); +//│ tmp4 = map(f1, t); +//│ return Cons1(tmp3, tmp4) +//│ }; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ return runtime.safeCall(tmp2(f)) +//│ }; +//│ tmp1 = enumFromTo(1, 4); +//│ map((x1) => { +//│ return x1 + 4 +//│ }, tmp1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let enumFromTo, map, tmp1, match_ls_rest, tmp2, tmp3, tmp4, tmp5; +//│ match_ls_rest = function match_ls_rest(f) { +//│ let tmp6; +//│ tmp6 = f; +//│ return runtime.safeCall(tmp_not_in_scope(tmp6)) +//│ }; +//│ enumFromTo = function enumFromTo(a, b) { +//│ let scrut, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ tmp8 = a; +//│ tmp9 = b; +//│ scrut = tmp8 < tmp9; +//│ if (scrut === true) { +//│ tmp10 = a; +//│ tmp11 = 1; +//│ tmp6 = tmp10 + tmp11; +//│ tmp12 = tmp6; +//│ tmp13 = b; +//│ tmp7 = enumFromTo(tmp12, tmp13); +//│ tmp14 = a; +//│ tmp15 = tmp7; +//│ return (tmp16, f, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22) => { +//│ let param0, param1, h, t, tmp23; +//│ param0 = tmp14; +//│ param1 = tmp15; +//│ h = param0; +//│ t = param1; +//│ tmp23 = (f1) => { +//│ tmp18 = h; +//│ tmp16 = runtime.safeCall(f1(tmp18)); +//│ tmp20 = f1; +//│ tmp22 = t; +//│ tmp21 = map(tmp20, tmp22); +//│ tmp19 = tmp16; +//│ tmp17 = tmp21; +//│ return Cons1(tmp19, tmp17) +//│ }; +//│ return match_ls_rest(f) +//│ } +//│ } else { +//│ return (f) => { +//│ let tmp16; +//│ tmp16 = (f1) => { +//│ return Nil1 +//│ }; +//│ return match_ls_rest(f) +//│ } +//│ } +//│ }; +//│ map = function map(f, ls) { +//│ return runtime.safeCall(ls(f, tmp_not_in_scope, tmp_not_in_scope)) +//│ }; +//│ tmp2 = 1; +//│ tmp3 = 4; +//│ tmp1 = enumFromTo(tmp2, tmp3); +//│ tmp4 = (x1) => { +//│ let tmp6, tmp7; +//│ tmp6 = x1; +//│ tmp7 = 4; +//│ return tmp6 + tmp7 +//│ }; +//│ tmp5 = tmp1; +//│ block$res7 = map(tmp4, tmp5); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) From 7e5160c9971b7b0d39548cf940d093b17d16d6a3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 00:38:26 +0800 Subject: [PATCH 061/303] wip --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8458151007..c4240e9071 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -110,7 +110,7 @@ extension (b: Block) ReplaceSelectTransformer.applyBlock(b) def hasExplicitRet: Boolean = - object HasExplicitRetTransformer extends BlockTransformer(new SymbolSubst()): + object HasExplicitRetTransformer extends BlockTransformerShallow(new SymbolSubst()): var flag = false override def applyBlock(b: Block): Block = b match case Return(_, imp) => flag = !imp; b @@ -638,7 +638,7 @@ class Deforest(using TL, Raise, Elaborator.State): (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs) .filterNot( v => v == scrut.l || - (arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)))(v) // TODO: shouldn't intersect with dflt since it's just throw Error? + (arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)))(v) // TODO: shouldn't intersect with dflt since it just throws Error? ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) .sortBy(_.uid) .map(v => Arg(false, Value.Ref(v))) From 8b5453ba216ca4d766817d76bdf563e09d98cc5e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:01:35 +0800 Subject: [PATCH 062/303] CHECK LATER: change the definition of freeVars --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index b71fa997b2..861b434ca8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -254,7 +254,7 @@ enum Case: lazy val freeVars: Set[Local] = this match case Lit(_) => Set.empty - case Cls(_, path) => path.freeVars + case Cls(_, path) => Set.empty //path.freeVars case Tup(_, _) => Set.empty type ResultId = Uid[Result] @@ -277,13 +277,13 @@ sealed abstract class Result: case _ => Nil lazy val freeVars: Set[Local] = this match - case Call(fun, args) => args.flatMap(_.value.freeVars).toSet + case Call(fun, args) => fun.freeVars ++ args.flatMap(_.value.freeVars).toSet case Instantiate(cls, args) => args.flatMap(_.freeVars).toSet case Select(qual, name) => qual.freeVars case Value.Ref(l) => Set(l) case Value.This(sym) => Set.empty case Value.Lit(lit) => Set.empty - case Value.Lam(params, body) => body.freeVars -- params.paramSyms + case Value.Lam(params, body) => body.freeVars -- body.definedVars -- params.paramSyms case Value.Arr(elems) => elems.flatMap(_.value.freeVars).toSet lazy val uid = From fd4dd8bc62f648ae0361f6b6f8b877bd8e72818b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 14:48:26 +0800 Subject: [PATCH 063/303] wip: fix the handling of free vars --- .../scala/hkmc2/codegen/Deforestation.scala | 66 +++++++--- .../src/test/mlscript/deforest/simple.mls | 121 +++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 123 ------------------ 3 files changed, 172 insertions(+), 138 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c4240e9071..e01adc9af7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -100,7 +100,7 @@ extension (b: Block) ReplaceLocalSymTransformer.applyBlock(b) // TODO: freeVars does not include any vars in function caller positions... - def sortedFvs = (b.freeVars -- b.definedVars).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) + def sortedFvs(using alwaysDefined: Set[Symbol]) = (b.freeVars -- b.definedVars -- alwaysDefined).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): @@ -222,10 +222,10 @@ class Deforest(using TL, Raise, Elaborator.State): val flattenP = p.main.flattened val mergedArms = flattenP.mergeMatchArms + globallyDefinedVars.init(mergedArms) + // allocate type vars for defined symbols in the blocks - symToStrat.init(p.main) - // p.main.definedVars.foreach: v => - // symToStrat += v -> freshVar(v.nme)._1 + symToStrat.init(mergedArms) processBlock(mergedArms) resolveConstraints @@ -250,8 +250,25 @@ class Deforest(using TL, Raise, Elaborator.State): p.imports, rewrite(mergedArms) ) - + // these are never considered as free vars (because of their symbol type) + // currently cannot distinguish toplevel `let` and function params (both are varsymbol) + // so toplevel `let`s are not considered globally defined vars for now... + object globallyDefinedVars: + val store = mutable.Set.empty[Symbol] + + def apply(s: Symbol) = store.contains(s) + + def init(b: Block) = + object Subst extends SymbolSubst: + // only consider block member symbols as globally defined + override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = + store += s; s + override def mapBuiltInSym(s: BuiltinSymbol): BuiltinSymbol = + store += s; s + + object FreshVarForAllVars extends BlockTransformer(Subst) + FreshVarForAllVars.applyBlock(b) var constraints: Ls[ProdStrat -> ConsStrat] = Nil @@ -262,20 +279,24 @@ class Deforest(using TL, Raise, Elaborator.State): if store.isEmpty then object AllVarsSymbolSubst extends SymbolSubst: override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = + println("blkmem: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapFlowSym(s: FlowSymbol): FlowSymbol = store += s -> freshVar(s.nme)._1; s override def mapTempSym(s: TempSymbol): TempSymbol = store += s -> freshVar(s.nme)._1; s override def mapVarSym(s: VarSymbol): VarSymbol = + println("var: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapInstSym(s: InstSymbol): InstSymbol = store += s -> freshVar(s.nme)._1; s override def mapTermSym(s: TermSymbol): TermSymbol = store += s -> freshVar(s.nme)._1; s override def mapClsSym(s: ClassSymbol): ClassSymbol = + println("cls: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapModuleSym(s: ModuleSymbol): ModuleSymbol = + println("mod: " + s.nme) store += s -> freshVar(s.nme)._1; s object FreshVarForAllVars extends BlockTransformer(AllVarsSymbolSubst) FreshVarForAllVars.applyBlock(p) @@ -624,24 +645,37 @@ class Deforest(using TL, Raise, Elaborator.State): }.toSet def rewrite(p: Block) = - val rest = DeforestTransformer.applyBlock(p) - val newDefs = DeforestTransformer.matchRest.getAllFunDefs + val deforestTransformer = DeforestTransformer(using globallyDefinedVars.store.toSet) + val rest = deforestTransformer.applyBlock(p) + val newDefs = deforestTransformer.matchRest.getAllFunDefs newDefs(rest) - object DeforestTransformer extends BlockTransformer(new SymbolSubst()): + class DeforestTransformer(using Set[Symbol]) extends BlockTransformer(new SymbolSubst()): override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) val freeVars = - (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs) - .filterNot( - v => v == scrut.l || - (arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)))(v) // TODO: shouldn't intersect with dflt since it just throws Error? - ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) - .sortBy(_.uid) - .map(v => Arg(false, Value.Ref(v))) + // println(rest.sortedFvs) + println(arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)).map(_.uid)) + val res = (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs).distinct + println(arms.flatMap(_._2.sortedFvs)) + println(dflt.fold(Nil)(_.sortedFvs)) + println(rest.sortedFvs.map(_.uid)) + val res2 = + res + .filterNot( + v => + val isScrut = v == scrut.l + val armsDefinedIntersection = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) + // println(armsDefinedIntersection.size) + isScrut || armsDefinedIntersection.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? + ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) + .sortBy(_.uid) + .map(v => Arg(false, Value.Ref(v))) + println(res2.map(a => a.value.asInstanceOf[Value.Ref].l.uid)) + res2 Return(Call(scrut, freeVars)(false, false), !needExplicitRet) // TODO: free var application else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) @@ -678,9 +712,11 @@ class Deforest(using TL, Raise, Elaborator.State): val rewrittenRest = applyBlock(rest) // TODO: avoid rewriting it more than once val freeVarsAndTheirNewSyms = (rewrittenBody.sortedFvs ::: rest.sortedFvs) // TODO: why it's rest.sortedFvs instead of rewrittenRest?? + .distinct .map(s => s -> VarSymbol(Tree.Ident(s.nme))) .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) .toMap + println(s">>> ${freeVarsAndTheirNewSyms.size}") val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) val lambdaBody = restFunOrRestBlock match case None => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index aa8b789469..fc4c9231d0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -869,3 +869,124 @@ map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) + + + +:sjs +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + (if ls is + Nil then f => Nil + Cons(h, t) then f => Cons(f(h), map(f, t)) + )(f) +map(x => x + 4, enumFromTo(1, 4)) +//│ JS (unsanitized): +//│ let enumFromTo3, map2, tmp24; +//│ enumFromTo3 = function enumFromTo(a, b) { +//│ let scrut, tmp25, tmp26; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp25 = a + 1; +//│ tmp26 = enumFromTo3(tmp25, b); +//│ return Cons1(a, tmp26) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ map2 = function map(f3, ls) { +//│ let param0, param1, h, t, tmp25; +//│ if (ls instanceof Nil1.class) { +//│ tmp25 = (f4) => { +//│ return Nil1 +//│ }; +//│ } else { +//│ if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp25 = (f4) => { +//│ let tmp26, tmp27; +//│ tmp26 = runtime.safeCall(f4(h)); +//│ tmp27 = map2(f4, t); +//│ return Cons1(tmp26, tmp27) +//│ }; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ return runtime.safeCall(tmp25(f3)) +//│ }; +//│ tmp24 = enumFromTo3(1, 4); +//│ map2((x) => { +//│ return x + 4 +//│ }, tmp24) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let enumFromTo3, map2, tmp24, match_ls_rest, tmp25, tmp26, tmp27, tmp28; +//│ match_ls_rest = function match_ls_rest(f3, tmp29) { +//│ let tmp30; +//│ tmp30 = f3; +//│ return runtime.safeCall(tmp29(tmp30)) +//│ }; +//│ enumFromTo3 = function enumFromTo(a, b) { +//│ let scrut, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38; +//│ tmp31 = a; +//│ tmp32 = b; +//│ scrut = tmp31 < tmp32; +//│ if (scrut === true) { +//│ tmp33 = a; +//│ tmp34 = 1; +//│ tmp29 = tmp33 + tmp34; +//│ tmp35 = tmp29; +//│ tmp36 = b; +//│ tmp30 = enumFromTo3(tmp35, tmp36); +//│ tmp37 = a; +//│ tmp38 = tmp30; +//│ return (f3) => { +//│ let param0, param1, h, t, tmp39; +//│ param0 = tmp37; +//│ param1 = tmp38; +//│ h = param0; +//│ t = param1; +//│ tmp39 = (f4) => { +//│ let tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46; +//│ tmp42 = h; +//│ tmp40 = runtime.safeCall(f4(tmp42)); +//│ tmp43 = f4; +//│ tmp44 = t; +//│ tmp41 = map2(tmp43, tmp44); +//│ tmp45 = tmp40; +//│ tmp46 = tmp41; +//│ return Cons1(tmp45, tmp46) +//│ }; +//│ return match_ls_rest(f3, tmp39) +//│ } +//│ } else { +//│ return (f3) => { +//│ let tmp39; +//│ tmp39 = (f4) => { +//│ return Nil1 +//│ }; +//│ return match_ls_rest(f3, tmp39) +//│ } +//│ } +//│ }; +//│ map2 = function map(f3, ls) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ tmp25 = 1; +//│ tmp26 = 4; +//│ tmp24 = enumFromTo3(tmp25, tmp26); +//│ tmp27 = (x) => { +//│ let tmp29, tmp30; +//│ tmp29 = x; +//│ tmp30 = 4; +//│ return tmp29 + tmp30 +//│ }; +//│ tmp28 = tmp24; +//│ block$res25 = map2(tmp27, tmp28); +//│ undefined +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 4b65e839f3..751cbc95ea 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -179,126 +179,3 @@ module Test with //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - - -// FIXME: still not well-scoped... -:fixme -:sjs -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(f, ls) = - (if ls is - Nil then f => Nil - Cons(h, t) then f => Cons(f(h), map(f, t)) - )(f) -map(x => x + 4, enumFromTo(1, 4)) -//│ JS (unsanitized): -//│ let enumFromTo, map, tmp1; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp2, tmp3; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp2 = a + 1; -//│ tmp3 = enumFromTo(tmp2, b); -//│ return Cons1(a, tmp3) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ map = function map(f, ls) { -//│ let param0, param1, h, t, tmp2; -//│ if (ls instanceof Nil1.class) { -//│ tmp2 = (f1) => { -//│ return Nil1 -//│ }; -//│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp2 = (f1) => { -//│ let tmp3, tmp4; -//│ tmp3 = runtime.safeCall(f1(h)); -//│ tmp4 = map(f1, t); -//│ return Cons1(tmp3, tmp4) -//│ }; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ return runtime.safeCall(tmp2(f)) -//│ }; -//│ tmp1 = enumFromTo(1, 4); -//│ map((x1) => { -//│ return x1 + 4 -//│ }, tmp1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo, map, tmp1, match_ls_rest, tmp2, tmp3, tmp4, tmp5; -//│ match_ls_rest = function match_ls_rest(f) { -//│ let tmp6; -//│ tmp6 = f; -//│ return runtime.safeCall(tmp_not_in_scope(tmp6)) -//│ }; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; -//│ tmp8 = a; -//│ tmp9 = b; -//│ scrut = tmp8 < tmp9; -//│ if (scrut === true) { -//│ tmp10 = a; -//│ tmp11 = 1; -//│ tmp6 = tmp10 + tmp11; -//│ tmp12 = tmp6; -//│ tmp13 = b; -//│ tmp7 = enumFromTo(tmp12, tmp13); -//│ tmp14 = a; -//│ tmp15 = tmp7; -//│ return (tmp16, f, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22) => { -//│ let param0, param1, h, t, tmp23; -//│ param0 = tmp14; -//│ param1 = tmp15; -//│ h = param0; -//│ t = param1; -//│ tmp23 = (f1) => { -//│ tmp18 = h; -//│ tmp16 = runtime.safeCall(f1(tmp18)); -//│ tmp20 = f1; -//│ tmp22 = t; -//│ tmp21 = map(tmp20, tmp22); -//│ tmp19 = tmp16; -//│ tmp17 = tmp21; -//│ return Cons1(tmp19, tmp17) -//│ }; -//│ return match_ls_rest(f) -//│ } -//│ } else { -//│ return (f) => { -//│ let tmp16; -//│ tmp16 = (f1) => { -//│ return Nil1 -//│ }; -//│ return match_ls_rest(f) -//│ } -//│ } -//│ }; -//│ map = function map(f, ls) { -//│ return runtime.safeCall(ls(f, tmp_not_in_scope, tmp_not_in_scope)) -//│ }; -//│ tmp2 = 1; -//│ tmp3 = 4; -//│ tmp1 = enumFromTo(tmp2, tmp3); -//│ tmp4 = (x1) => { -//│ let tmp6, tmp7; -//│ tmp6 = x1; -//│ tmp7 = 4; -//│ return tmp6 + tmp7 -//│ }; -//│ tmp5 = tmp1; -//│ block$res7 = map(tmp4, tmp5); -//│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) From 88f3dd834f4f0c73752c845a5d8a2f54cc4764d5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:31:27 +0800 Subject: [PATCH 064/303] cleanup for free vars handling --- .../scala/hkmc2/codegen/Deforestation.scala | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e01adc9af7..c70320d05e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -99,7 +99,6 @@ extension (b: Block) override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) ReplaceLocalSymTransformer.applyBlock(b) - // TODO: freeVars does not include any vars in function caller positions... def sortedFvs(using alwaysDefined: Set[Symbol]) = (b.freeVars -- b.definedVars -- alwaysDefined).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = @@ -279,24 +278,20 @@ class Deforest(using TL, Raise, Elaborator.State): if store.isEmpty then object AllVarsSymbolSubst extends SymbolSubst: override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = - println("blkmem: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapFlowSym(s: FlowSymbol): FlowSymbol = store += s -> freshVar(s.nme)._1; s override def mapTempSym(s: TempSymbol): TempSymbol = store += s -> freshVar(s.nme)._1; s override def mapVarSym(s: VarSymbol): VarSymbol = - println("var: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapInstSym(s: InstSymbol): InstSymbol = store += s -> freshVar(s.nme)._1; s override def mapTermSym(s: TermSymbol): TermSymbol = store += s -> freshVar(s.nme)._1; s override def mapClsSym(s: ClassSymbol): ClassSymbol = - println("cls: " + s.nme) store += s -> freshVar(s.nme)._1; s override def mapModuleSym(s: ModuleSymbol): ModuleSymbol = - println("mod: " + s.nme) store += s -> freshVar(s.nme)._1; s object FreshVarForAllVars extends BlockTransformer(AllVarsSymbolSubst) FreshVarForAllVars.applyBlock(p) @@ -650,33 +645,23 @@ class Deforest(using TL, Raise, Elaborator.State): val newDefs = deforestTransformer.matchRest.getAllFunDefs newDefs(rest) - class DeforestTransformer(using Set[Symbol]) extends BlockTransformer(new SymbolSubst()): + class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) val freeVars = - // println(rest.sortedFvs) - println(arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)).map(_.uid)) + val definedInAllArms = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) val res = (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs).distinct - println(arms.flatMap(_._2.sortedFvs)) - println(dflt.fold(Nil)(_.sortedFvs)) - println(rest.sortedFvs.map(_.uid)) - val res2 = - res - .filterNot( + res.filterNot( v => val isScrut = v == scrut.l - val armsDefinedIntersection = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) - // println(armsDefinedIntersection.size) - isScrut || armsDefinedIntersection.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? + isScrut || definedInAllArms.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) .sortBy(_.uid) .map(v => Arg(false, Value.Ref(v))) - println(res2.map(a => a.value.asInstanceOf[Value.Ref].l.uid)) - res2 - Return(Call(scrut, freeVars)(false, false), !needExplicitRet) // TODO: free var application + Return(Call(scrut, freeVars)(false, false), !needExplicitRet) else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) case Return(res, implct) => @@ -711,12 +696,11 @@ class Deforest(using TL, Raise, Elaborator.State): val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) val rewrittenRest = applyBlock(rest) // TODO: avoid rewriting it more than once val freeVarsAndTheirNewSyms = - (rewrittenBody.sortedFvs ::: rest.sortedFvs) // TODO: why it's rest.sortedFvs instead of rewrittenRest?? + (rewrittenBody.sortedFvs ::: rewrittenRest.sortedFvs) .distinct .map(s => s -> VarSymbol(Tree.Ident(s.nme))) .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) .toMap - println(s">>> ${freeVarsAndTheirNewSyms.size}") val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) val lambdaBody = restFunOrRestBlock match case None => From 6e089d7c7dad00982885921cc26d7c8d3401f752 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 17:02:32 +0800 Subject: [PATCH 065/303] update tests --- .../src/test/mlscript/deforest/simple.mls | 94 ++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 96 ++++++++++++++++++- 2 files changed, 188 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index fc4c9231d0..8baba9ee90 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -7,6 +7,8 @@ object B object C class AA(x) class BB(x) +object None +class Some(x) :sjs fun test() = @@ -990,3 +992,95 @@ map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) + + + +:sjs +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun sum(ls, a) = if ls is + Nil then a + Cons(h, t) then sum(t, h + a) +sum(enumFromTo(1, 10), 0) +//│ JS (unsanitized): +//│ let enumFromTo4, sum1, tmp30; +//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let scrut, tmp31, tmp32; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp31 = a + 1; +//│ tmp32 = enumFromTo4(tmp31, b); +//│ return Cons1(a, tmp32) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ sum1 = function sum(ls, a) { +//│ let param0, param1, h, t, tmp31; +//│ if (ls instanceof Nil1.class) { +//│ return a +//│ } else { +//│ if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp31 = h + a; +//│ return sum1(t, tmp31) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ }; +//│ tmp30 = enumFromTo4(1, 10); +//│ sum1(tmp30, 0) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let enumFromTo4, sum1, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ enumFromTo4 = function enumFromTo(a, b) { +//│ let scrut, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44; +//│ tmp37 = a; +//│ tmp38 = b; +//│ scrut = tmp37 < tmp38; +//│ if (scrut === true) { +//│ tmp39 = a; +//│ tmp40 = 1; +//│ tmp35 = tmp39 + tmp40; +//│ tmp41 = tmp35; +//│ tmp42 = b; +//│ tmp36 = enumFromTo4(tmp41, tmp42); +//│ tmp43 = a; +//│ tmp44 = tmp36; +//│ return (a1) => { +//│ let param0, param1, h, t, tmp45, tmp46, tmp47, tmp48, tmp49; +//│ param0 = tmp43; +//│ param1 = tmp44; +//│ h = param0; +//│ t = param1; +//│ tmp46 = h; +//│ tmp47 = a1; +//│ tmp45 = tmp46 + tmp47; +//│ tmp48 = t; +//│ tmp49 = tmp45; +//│ return sum1(tmp48, tmp49) +//│ } +//│ } else { +//│ return (a1) => { +//│ return a1 +//│ } +//│ } +//│ }; +//│ sum1 = function sum(ls, a) { +//│ return runtime.safeCall(ls(a)) +//│ }; +//│ tmp31 = 1; +//│ tmp32 = 10; +//│ tmp30 = enumFromTo4(tmp31, tmp32); +//│ tmp33 = tmp30; +//│ tmp34 = 0; +//│ block$res27 = sum1(tmp33, tmp34); +//│ undefined +//│ = 45 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 45 + + diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 751cbc95ea..76a8981647 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -10,6 +10,9 @@ class BB(x) object Nil class Cons(h, t) +object None +class Some(x) + // FIXME: why it's different when all in a function? :sjs let x = A @@ -43,7 +46,7 @@ if y is //│ y = () => { //│ return 2 //│ }; -//│ block$res3 = runtime.safeCall(x(y)); +//│ block$res4 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -175,7 +178,96 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; -//│ block$res5 = undefined; +//│ block$res6 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +// FIXME: the `Cons` branch does use `a`, but should also take that argument +:sjs +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun last(ls, a) = if ls is + Nil then a + Cons(h, t) then last(t, Some(h)) +last(enumFromTo(1, 10), None) +//│ JS (unsanitized): +//│ let last, enumFromTo, tmp1; +//│ enumFromTo = function enumFromTo(a, b) { +//│ let scrut, tmp2, tmp3; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp2 = a + 1; +//│ tmp3 = enumFromTo(tmp2, b); +//│ return Cons1(a, tmp3) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ last = function last(ls, a) { +//│ let param0, param1, h, t, tmp2; +//│ if (ls instanceof Nil1.class) { +//│ return a +//│ } else { +//│ if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp2 = Some1(h); +//│ return last(t, tmp2) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ }; +//│ tmp1 = enumFromTo(1, 10); +//│ last(tmp1, None1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let last, enumFromTo, tmp1, tmp2, tmp3, tmp4, tmp5; +//│ enumFromTo = function enumFromTo(a, b) { +//│ let scrut, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ tmp8 = a; +//│ tmp9 = b; +//│ scrut = tmp8 < tmp9; +//│ if (scrut === true) { +//│ tmp10 = a; +//│ tmp11 = 1; +//│ tmp6 = tmp10 + tmp11; +//│ tmp12 = tmp6; +//│ tmp13 = b; +//│ tmp7 = enumFromTo(tmp12, tmp13); +//│ tmp14 = a; +//│ tmp15 = tmp7; +//│ return () => { +//│ let param0, param1, h, t, tmp16, tmp17, tmp18, tmp19; +//│ param0 = tmp14; +//│ param1 = tmp15; +//│ h = param0; +//│ t = param1; +//│ tmp17 = h; +//│ tmp16 = Some1(tmp17); +//│ tmp18 = t; +//│ tmp19 = tmp16; +//│ return last(tmp18, tmp19) +//│ } +//│ } else { +//│ return (a1) => { +//│ return a1 +//│ } +//│ } +//│ }; +//│ last = function last(ls, a) { +//│ return runtime.safeCall(ls(a)) +//│ }; +//│ tmp2 = 1; +//│ tmp3 = 10; +//│ tmp1 = enumFromTo(tmp2, tmp3); +//│ tmp4 = tmp1; +//│ tmp5 = None1; +//│ block$res8 = last(tmp4, tmp5); +//│ undefined +//│ = Some(9) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Some(9) + From 3ed8c7ae062d1b8907457c33c14321f6fa0889e1 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 17:20:10 +0800 Subject: [PATCH 066/303] ctor expr ids needed for prodstrats --- .../scala/hkmc2/codegen/Deforestation.scala | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c70320d05e..7a25375063 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -36,7 +36,7 @@ object StratVarState: type CtorExpr = ResultId // enum ProdStrat: -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat])(val expr: CtorExpr) extends ProdStrat +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat @@ -413,12 +413,12 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap)(c.uid) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap)(c.uid) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid) case _ => // then it is a function val appRes = freshVar("call_" + l.nme + "_res") constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) @@ -439,7 +439,7 @@ class Deforest(using TL, Raise, Elaborator.State): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => - Ctor(s.asObj.get, Map.empty)(sel.uid) + Ctor(s.asObj.get, Map.empty, sel.uid) case _ => val pStrat = processResult(p) inArm match @@ -457,7 +457,7 @@ class Deforest(using TL, Raise, Elaborator.State): case v@Value.Ref(l) => l.asObj match case None => symToStrat.getStratOfSym(l) - case Some(m) => Ctor(m, Map.empty)(v.uid) + case Some(m) => Ctor(m, Map.empty, v.uid) case Value.This(sym) => ??? case Value.Lit(lit) => NoProd @@ -505,20 +505,20 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (ctorStrat@Ctor(ctor, args), dtorStrat@Dtor(scrut)) => - ctorDests.update(ctorStrat.expr, dtorStrat.expr) - dtorSources.update(scrut, ctorStrat.expr) - case (ctorStrat@Ctor(ctor, args), selDtor@FieldSel(field, consVar)) => + case (Ctor(ctor, args, expr), dtorStrat@Dtor(scrut)) => + ctorDests.update(expr, dtorStrat.expr) + dtorSources.update(scrut, expr) + case (Ctor(ctor, args, expr), selDtor@FieldSel(field, consVar)) => // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else - ctorDests.update(ctorStrat.expr, selDtor.expr) - dtorSources.update(selDtor.expr, ctorStrat.expr) + ctorDests.update(expr, selDtor.expr) + dtorSources.update(selDtor.expr, expr) args.find(a => a._1.id == field).map(p => // rewritingSel.add(sel) handle(p._2 -> consVar) ) - case (Ctor(ctor, args), ConsFun(l, r)) => ??? + case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? case (p: ProdVar, _) => upperBounds += p.uid -> (cons :: upperBounds(p.uid)) @@ -527,7 +527,7 @@ class Deforest(using TL, Raise, Elaborator.State): case (l: ProdVar, sel@FieldSel(field, consVar)) => sel.updateFilter(l, sel.filter(p)) handle(l -> cons) - case (Ctor(ctor, args), sel@FieldSel(field, consVar)) => + case (Ctor(ctor, args, _), sel@FieldSel(field, consVar)) => if sel.filter.get(p).forall(_.contains(ctor)) then handle(l -> cons) else @@ -538,7 +538,7 @@ class Deforest(using TL, Raise, Elaborator.State): lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) upperBounds(c.uid).foreach { u => (prod, u) match - case (Ctor(ctor, args), sel@FieldSel(field, consVar)) => + case (Ctor(ctor, args, _), sel@FieldSel(field, consVar)) => if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then handle(prod -> u) else @@ -547,7 +547,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => handle(prod -> u) } // upperBounds(uid).foreach(c => handle(prod -> c)) - case (Ctor(ctor, args), NoCons) => () + case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls)) => ??? case (ProdFun(l, r), FieldSel(field, consVar)) => ??? case (ProdFun(lp, rp), ConsFun(lc, rc)) => From d30693766e1dafa5b8ef3a559e58e69ef3ac0a3d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:04:23 +0800 Subject: [PATCH 067/303] minor --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7a25375063..8906757624 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -43,8 +43,11 @@ case object NoProd extends ProdStrat // enum ConsStrat: -case class Dtor(scrut: ResultId)(val expr: Match) extends ConsStrat: +case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends ConsStrat: assert(scrut === expr.scrut.uid) + d.matchScrutToMatchBlock.updateWith(scrut): + case None => Some(expr) + case Some(exist) => ??? // should only update once case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat @@ -271,6 +274,7 @@ class Deforest(using TL, Raise, Elaborator.State): var constraints: Ls[ProdStrat -> ConsStrat] = Nil + val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] object symToStrat: val store = mutable.Map.empty[Symbol, ProdVar] @@ -319,9 +323,9 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) + constrain(scrutStrat, Dtor(scrut.uid)(m)(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => - constrain(scrutStrat, Dtor(scrut.uid)(m)) // TODO: fix this "asInstanceOf"? processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) } From bb12dfc78d33f2d7fc340a0a762cbd98627f4005 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Thu, 20 Feb 2025 10:53:57 +0800 Subject: [PATCH 068/303] Changes from meeting --- .../src/test/mlscript/backlog/ToTriage.mls | 7 + .../src/test/mlscript/deforest/imperative.mls | 216 ++++++++++++++++++ .../src/test/mlscript/deforest/simple.mls | 26 +-- 3 files changed, 236 insertions(+), 13 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/imperative.mls diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index 3ac7fe664b..bcbd6565b2 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -316,3 +316,10 @@ Int // ——— ——— ——— +if x > 0 + then A + then B +//│ /!!!\ Uncaught error: scala.MatchError: InfixApp(App(Ident(>),Tup(List(Ident(x), IntLit(0)))),keyword 'then',Ident(A)) (of class hkmc2.syntax.Tree$InfixApp) + +// ——— ——— ——— + diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls new file mode 100644 index 0000000000..7b7a9511e9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -0,0 +1,216 @@ +:js +:deforest + + +object A +object B +// object C +// class AA(aa) +// class BB(bb) +// object None +// class Some(value) + +// * Not fused: two `x is A` consumers +:sjs +let x = if true then A else B +fun foo(x) = + if x is A do print(123) + if x is + A then 1 + B then 2 +//│ JS (unsanitized): +//│ let foo, x, scrut, tmp; +//│ foo = function foo(x1) { +//│ let tmp1; +//│ if (x1 instanceof A1.class) { +//│ tmp1 = Predef.print(123); +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ if (x1 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ if (x1 instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ }; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = A1; +//│ } else { +//│ tmp = B1; +//│ } +//│ x = tmp; +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let foo, x, scrut, tmp; +//│ foo = function foo(x1) { +//│ let tmp1, tmp2; +//│ if (x1 instanceof A1.class) { +//│ tmp2 = 123; +//│ tmp1 = Predef.print(tmp2); +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ if (x1 instanceof A1.class) { +//│ return 1 +//│ } else if (x1 instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = A1; +//│ } else { +//│ tmp = B1; +//│ } +//│ x = tmp; +//│ block$res2 = undefined; +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ x = A + +// * We could make it work. But it's a special case that's probably not very important +:sjs +let x = if true then A else B +fun foo(x) = + if x is A do print(123) + if x is B do print(456) +//│ JS (unsanitized): +//│ let foo1, x1, scrut1, tmp2; +//│ foo1 = function foo(x2) { +//│ let tmp3; +//│ if (x2 instanceof A1.class) { +//│ tmp3 = Predef.print(123); +//│ } else { +//│ tmp3 = runtime.Unit; +//│ } +//│ if (x2 instanceof B1.class) { +//│ return Predef.print(456) +//│ } else { +//│ return runtime.Unit +//│ } +//│ }; +//│ scrut1 = true; +//│ if (scrut1 === true) { +//│ tmp2 = A1; +//│ } else { +//│ tmp2 = B1; +//│ } +//│ x1 = tmp2; +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let foo1, x1, scrut1, tmp2; +//│ foo1 = function foo(x2) { +//│ let tmp3, tmp4, tmp5; +//│ if (x2 instanceof A1.class) { +//│ tmp4 = 123; +//│ tmp3 = Predef.print(tmp4); +//│ } else { +//│ tmp3 = runtime.Unit; +//│ } +//│ if (x2 instanceof B1.class) { +//│ tmp5 = 456; +//│ return Predef.print(tmp5) +//│ } else { +//│ return runtime.Unit +//│ } +//│ }; +//│ scrut1 = true; +//│ if (scrut1 === true) { +//│ tmp2 = A1; +//│ } else { +//│ tmp2 = B1; +//│ } +//│ x1 = tmp2; +//│ block$res4 = undefined; +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ x = A + +:sjs +fun foo(k, x) = + if x === 0 do k(A) + k(if x > 0 + then A + else B) +fun bar(v) = if v is + A then 1 + B then 2 +foo(bar, 123) +//│ JS (unsanitized): +//│ let bar, foo2; +//│ foo2 = function foo(k, x2) { +//│ let scrut2, scrut3, tmp4, tmp5; +//│ scrut2 = x2 === 0; +//│ if (scrut2 === true) { +//│ tmp4 = runtime.safeCall(k(A1)); +//│ } else { +//│ tmp4 = runtime.Unit; +//│ } +//│ scrut3 = x2 > 0; +//│ if (scrut3 === true) { +//│ tmp5 = A1; +//│ } else { +//│ tmp5 = B1; +//│ } +//│ return runtime.safeCall(k(tmp5)) +//│ }; +//│ bar = function bar(v) { +//│ if (v instanceof A1.class) { +//│ return 1 +//│ } else { +//│ if (v instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ }; +//│ foo2(bar, 123) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let bar, foo2, tmp4, tmp5; +//│ foo2 = function foo(k, x2) { +//│ let scrut2, scrut3, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; +//│ tmp8 = x2; +//│ tmp9 = 0; +//│ scrut2 = tmp8 === tmp9; +//│ if (scrut2 === true) { +//│ tmp10 = () => { +//│ return 1 +//│ }; +//│ tmp6 = runtime.safeCall(k(tmp10)); +//│ } else { +//│ tmp6 = runtime.Unit; +//│ } +//│ tmp11 = x2; +//│ tmp12 = 0; +//│ scrut3 = tmp11 > tmp12; +//│ if (scrut3 === true) { +//│ tmp7 = () => { +//│ return 1 +//│ }; +//│ } else { +//│ tmp7 = () => { +//│ return 2 +//│ }; +//│ } +//│ tmp13 = tmp7; +//│ return runtime.safeCall(k(tmp13)) +//│ }; +//│ bar = function bar(v) { +//│ return runtime.safeCall(v()) +//│ }; +//│ tmp4 = bar; +//│ tmp5 = 123; +//│ block$res6 = foo2(tmp4, tmp5); +//│ undefined +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 + + diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 8baba9ee90..aadee0a37e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -5,10 +5,10 @@ object A object B object C -class AA(x) -class BB(x) +class AA(aa) +class BB(bb) object None -class Some(x) +class Some(value) :sjs fun test() = @@ -82,12 +82,12 @@ test() //│ } //│ x = tmp; //│ if (x instanceof AA1.class) { -//│ param01 = x.x; +//│ param01 = x.aa; //│ x2 = param01; //│ return x2 //│ } else { //│ if (x instanceof BB1.class) { -//│ param0 = x.x; +//│ param0 = x.bb; //│ x1 = param0; //│ return x1 //│ } else { @@ -162,12 +162,12 @@ test() //│ } //│ x = tmp; //│ if (x instanceof AA1.class) { -//│ param01 = x.x; +//│ param01 = x.aa; //│ x2 = param01; //│ return f(x2) //│ } else { //│ if (x instanceof BB1.class) { -//│ param0 = x.x; +//│ param0 = x.bb; //│ x1 = param0; //│ return f(x1) //│ } else { @@ -231,8 +231,8 @@ fun f2(a) = if a is fun test() = let x = if true then AA(A) else BB(B) if x is - AA then f1(x.x) - BB then f2(x.x) + AA then f1(x.aa) + BB then f2(x.bb) test() //│ JS (unsanitized): //│ let test3, f1, f2; @@ -276,10 +276,10 @@ test() //│ } //│ x = tmp; //│ if (x instanceof AA1.class) { -//│ return f1(x.x) +//│ return f1(x.aa) //│ } else { //│ if (x instanceof BB1.class) { -//│ return f2(x.x) +//│ return f2(x.bb) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -351,12 +351,12 @@ test() //│ c = function c(x) { //│ let param0, x1, param01, x2; //│ if (x instanceof AA1.class) { -//│ param01 = x.x; +//│ param01 = x.aa; //│ x2 = param01; //│ return x2 //│ } else { //│ if (x instanceof BB1.class) { -//│ param0 = x.x; +//│ param0 = x.bb; //│ x1 = param0; //│ return x1 //│ } else { From 85f0381049525d03da5e804b8b562e6dfc5d61a1 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 20 Feb 2025 17:01:24 +0800 Subject: [PATCH 069/303] found more fixmes --- .../src/test/mlscript/deforest/todos.mls | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 76a8981647..de589306f0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -271,3 +271,97 @@ last(enumFromTo(1, 10), None) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Some(9) +:fixme +:sjs +fun c(x) = if x is + AA then + if A is + A then x.x +c(AA(2)) +//│ JS (unsanitized): +//│ let c, tmp7; +//│ c = function c(x1) { +//│ let scrut; +//│ if (x1 instanceof AA1.class) { +//│ scrut = A1; +//│ if (scrut instanceof A1.class) { +//│ return x1.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp7 = AA1(2); +//│ c(tmp7) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c, tmp7, tmp8, tmp9; +//│ c = function c(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ tmp8 = 2; +//│ tmp7 = (x1) => { +//│ let scrut; +//│ scrut = (x2) => { +//│ return x2.x +//│ }; +//│ return runtime.safeCall(scrut(x1)) +//│ }; +//│ tmp9 = tmp7; +//│ block$res10 = c(tmp9); +//│ undefined +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + +:fixme +:expect 5 +:sjs +fun c(x) = + let t = x.x + let n = if x is + AA then 2 + n + t +c(AA(3)) +//│ JS (unsanitized): +//│ let c1, tmp11; +//│ c1 = function c(x1) { +//│ let t, n, tmp12; +//│ t = x1.x; +//│ if (x1 instanceof AA1.class) { +//│ tmp12 = 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp12; +//│ return n + t +//│ }; +//│ tmp11 = AA1(3); +//│ c1(tmp11) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c1, tmp11, tmp12, tmp13; +//│ c1 = function c(x1) { +//│ let t; +//│ t = x1.x; +//│ return runtime.safeCall(x1(t)) +//│ }; +//│ tmp12 = 3; +//│ tmp11 = (t) => { +//│ let n, tmp14, tmp15, tmp16; +//│ tmp14 = 2; +//│ n = tmp14; +//│ tmp15 = n; +//│ tmp16 = t; +//│ return tmp15 + tmp16 +//│ }; +//│ tmp13 = tmp11; +//│ block$res12 = c1(tmp13); +//│ undefined +//│ ═══[RUNTIME ERROR] Expected: '5', got: 'NaN' +//│ = NaN +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 From 0c0805d4f751d7f7c0fb0d4064663e363e9ec87d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:23:24 +0800 Subject: [PATCH 070/303] avoid replacing selects twice for rewrittenBody --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8906757624..05aba0ee95 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -708,10 +708,10 @@ class Deforest(using TL, Raise, Elaborator.State): val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) val lambdaBody = restFunOrRestBlock match case None => - Begin(rewrittenBody.replaceSelect(using sel, selMap), rewrittenRest) + Begin(rewrittenBody, rewrittenRest) case Some(f) => Begin( - rewrittenBody.replaceSelect(using sel, selMap), + rewrittenBody, Return( Call( Value.Ref(f), From 1495b255f04f123626ee358683c0d45e69e3d026 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:54:26 +0800 Subject: [PATCH 071/303] minor fix on free vars --- .../scala/hkmc2/codegen/Deforestation.scala | 45 ++++++++++++------- .../src/test/mlscript/deforest/simple.mls | 2 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 05aba0ee95..fe5282327a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -643,6 +643,18 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Match(scrut, _, _) => scrut }.toSet + lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = + resolveClashes._2.keys.flatMap{ + case DtorExpr.Match(s) => + val Match(scrut, arms, dflt, rest) = matchScrutToMatchBlock(s) + val fvsInallBodiesAndRest = (rest :: arms.map(_._2).appendedAll(dflt)).flatMap(b => b.sortedFvs(using globallyDefinedVars.store.toSet)) + // NOTE: doesn't intersect with defined vars in dflt because it may be only `throw error` + val definedInAllArms = arms.map(_._2.definedVars).reduce((a, b) => a.intersect(b)) + Some(s -> fvsInallBodiesAndRest.filterNot(a => (a.uid == scrut.l.uid) || definedInAllArms.contains(a)).distinct.sortBy(_.uid)) + case DtorExpr.Sel(s) => None + }.toMap + + def rewrite(p: Block) = val deforestTransformer = DeforestTransformer(using globallyDefinedVars.store.toSet) val rest = deforestTransformer.applyBlock(p) @@ -655,16 +667,16 @@ class Deforest(using TL, Raise, Elaborator.State): case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = - val definedInAllArms = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) - val res = (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs).distinct - res.filterNot( - v => - val isScrut = v == scrut.l - isScrut || definedInAllArms.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? - ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) - .sortBy(_.uid) - .map(v => Arg(false, Value.Ref(v))) + val freeVars = scopeExtrusionInfo(scrut.uid).map(v => Arg(false, Value.Ref(v))) + // val definedInAllArms = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) + // val res = (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs).distinct + // res.filterNot( + // v => + // val isScrut = v == scrut.l + // isScrut || definedInAllArms.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? + // ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) + // .sortBy(_.uid) + // .map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) @@ -699,12 +711,13 @@ class Deforest(using TL, Raise, Elaborator.State): def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) val rewrittenRest = applyBlock(rest) // TODO: avoid rewriting it more than once - val freeVarsAndTheirNewSyms = - (rewrittenBody.sortedFvs ::: rewrittenRest.sortedFvs) - .distinct - .map(s => s -> VarSymbol(Tree.Ident(s.nme))) - .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) - .toMap + // should first rewrite, then replace symbol, otherwise the exprids will change? + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + // (rewrittenBody.sortedFvs ::: rewrittenRest.sortedFvs) + // .distinct + // .map(s => s -> VarSymbol(Tree.Ident(s.nme))) + // .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) + // .toMap val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) val lambdaBody = restFunOrRestBlock match case None => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 8baba9ee90..3ca4eb7b94 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -848,7 +848,7 @@ map(x => x + 4, enumFromTo(1, 4)) //│ return Cons1(tmp38, tmp39) //│ } //│ } else { -//│ return () => { +//│ return (f3) => { //│ return Nil1 //│ } //│ } From fc675285d17013d39eb1496fb85f1f08f50f44fe Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 21 Feb 2025 20:13:27 +0800 Subject: [PATCH 072/303] wip: a select can be in multiple match arms (nested) --- .../scala/hkmc2/codegen/Deforestation.scala | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index fe5282327a..8d02986daf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -320,14 +320,14 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c - def processBlock(b: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N): ProdStrat = b match + def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol]): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) constrain(scrutStrat, Dtor(scrut.uid)(m)(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => // TODO: fix this "asInstanceOf"? - processBlock(body)(using S(scrutStrat.asInstanceOf[ProdVar] -> s)) + processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s)) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -387,7 +387,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Continue(label) => ??? case TryBlock(sub, finallyDo, rest) => ??? - def constrFun(params: Ls[Param], body: Block)(using inArm: Option[ProdVar -> ClsOrModSymbol] = N) = + def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol]) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => symToStrat(sym) } symToStrat.addAll(paramSyms.zip(paramStrats)) @@ -395,7 +395,7 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) - def processResult(r: Result)(using inArm: Option[ProdVar -> ClsOrModSymbol]): ProdStrat = r match + def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol]): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => processResult(value) @@ -446,18 +446,29 @@ class Deforest(using TL, Raise, Elaborator.State): Ctor(s.asObj.get, Map.empty, sel.uid) case _ => val pStrat = processResult(p) - inArm match - case Some(armP -> clsSym) if armP === pStrat => - // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) + pStrat match + case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => val tpeVar = freshVar() val selStrat = FieldSel(nme, tpeVar._2)(sel.uid) - selStrat.updateFilter(armP, clsSym :: Nil) + selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) tpeVar._1 + + // if inArm.contains(pStrat) then + // // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) + // val tpeVar = freshVar() + // val selStrat = FieldSel(nme, tpeVar._2)(sel.uid) + // selStrat.updateFilter(pStrat, inArm(pStrat) :: Nil) + // constrain(pStrat, selStrat) + // tpeVar._1 + // else + // val tpeVar = freshVar() + // constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) + // tpeVar._1 case v@Value.Ref(l) => l.asObj match case None => symToStrat.getStratOfSym(l) @@ -620,7 +631,7 @@ class Deforest(using TL, Raise, Elaborator.State): None else if dtors.size == 1 then val Value.Ref(scrut) = ResultUid(dtors.head._1) - if sels.forall{ s => ResultUid(s) match + if sels.forall{ s => ResultUid(s) match // FIXME: this will include selects that are not in the arms case Select(Value.Ref(l), nme) => l == scrut case _ => false } then From b11d65412df50b4193b7a83a6e8acf0f1bce7e20 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:05:34 +0800 Subject: [PATCH 073/303] also keep track of which matching stmts a selection belongs to --- .../scala/hkmc2/codegen/Deforestation.scala | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8d02986daf..bb5a734c55 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -49,7 +49,7 @@ case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends Con case None => Some(expr) case Some(exist) => ??? // should only update once -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Set[ResultId]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat @@ -320,14 +320,17 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c - def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol]): ProdStrat = b match + def processBlock(b: Block)(using + inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], + matching: Set[ResultId] = Set.empty[ResultId] + ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) constrain(scrutStrat, Dtor(scrut.uid)(m)(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => // TODO: fix this "asInstanceOf"? - processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s)) + processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + scrut.uid) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -387,7 +390,10 @@ class Deforest(using TL, Raise, Elaborator.State): case Continue(label) => ??? case TryBlock(sub, finallyDo, rest) => ??? - def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol]) = + def constrFun(params: Ls[Param], body: Block)(using + inArm: Map[ProdVar, ClsOrModSymbol], + matching: Set[ResultId] + ) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => symToStrat(sym) } symToStrat.addAll(paramSyms.zip(paramStrats)) @@ -395,7 +401,10 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) - def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol]): ProdStrat = r match + def processResult(r: Result)(using + inArm: Map[ProdVar, ClsOrModSymbol], + matching: Set[ResultId] + ): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => processResult(value) @@ -406,7 +415,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) val appRes = freshVar() constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -449,13 +458,13 @@ class Deforest(using TL, Raise, Elaborator.State): pStrat match case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => val tpeVar = freshVar() - val selStrat = FieldSel(nme, tpeVar._2)(sel.uid) + val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => val tpeVar = freshVar() - constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 // if inArm.contains(pStrat) then From 4e221bc52b31e17d537d1cea10aff76b9ac7a8d5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 25 Feb 2025 14:10:26 +0800 Subject: [PATCH 074/303] correct selections in match stmts --- .../scala/hkmc2/codegen/Deforestation.scala | 24 +++---- .../src/test/mlscript/deforest/simple.mls | 49 +++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 63 +++---------------- 3 files changed, 69 insertions(+), 67 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index bb5a734c55..bd8c20577e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -493,14 +493,14 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - case class CtorDest(matches: Map[ResultId, Match], sels: Ls[ResultId]) + case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel]) object ctorDests: val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil)) def update(ctor: CtorExpr, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels)) case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil)) - def update(ctor: CtorExpr, s: ResultId) = ctorDests.updateWith(ctor): + def update(ctor: CtorExpr, s: FieldSel) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels)) => Some(CtorDest(matches, s :: sels)) case None => Some(CtorDest(Map.empty, s :: Nil)) def get(ctor: CtorExpr) = ctorDests.get(ctor) @@ -536,7 +536,7 @@ class Deforest(using TL, Raise, Elaborator.State): // if clsSym.isDefined then // args.get(clsSym.get).map(p => handle(p -> consVar)) // else - ctorDests.update(expr, selDtor.expr) + ctorDests.update(expr, selDtor) dtorSources.update(selDtor.expr, expr) args.find(a => a._1.id == field).map(p => // rewritingSel.add(sel) @@ -598,7 +598,7 @@ class Deforest(using TL, Raise, Elaborator.State): else val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels) => - mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s)) + mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) }.toSet) def removeDtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[DtorExpr]): CtorToDtor -> DtorToCtor = @@ -617,9 +617,9 @@ class Deforest(using TL, Raise, Elaborator.State): ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels) => (dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { - val Value.Ref(scrut) = ResultUid(dtors.head._1) - sels.forall { s => ResultUid(s) match - case Select(Value.Ref(l), nme) => l == scrut + val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + sels.forall { s => ResultUid(s.expr) match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee case _ => false } }) }.keySet @@ -631,7 +631,7 @@ class Deforest(using TL, Raise, Elaborator.State): val res = mutable.Map.empty[CtorExpr, CtorFinalDest] resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels)) => val filteredDtor = { - if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head)) + if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head.expr)) else if dtors.size == 0 && sels.size > 1 then throw Error("more than one consumer") None @@ -639,12 +639,12 @@ class Deforest(using TL, Raise, Elaborator.State): throw Error("more than one consumer") None else if dtors.size == 1 then - val Value.Ref(scrut) = ResultUid(dtors.head._1) - if sels.forall{ s => ResultUid(s) match // FIXME: this will include selects that are not in the arms - case Select(Value.Ref(l), nme) => l == scrut + val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + if sels.forall{ s => ResultUid(s.expr) match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) case _ => false } then - Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels)) + Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels.map(_.expr))) else throw Error("more than one consumer") None diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 3ca4eb7b94..e50c47f89b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1084,3 +1084,52 @@ sum(enumFromTo(1, 10), 0) //│ = 45 + + +:expect 5 +:sjs +fun c(x) = + let t = x.x + let n = if x is + AA then 2 + n + t +c(AA(3)) +//│ JS (unsanitized): +//│ let c1, tmp36; +//│ c1 = function c(x) { +//│ let t, n, tmp37; +//│ t = x.x; +//│ if (x instanceof AA1.class) { +//│ tmp37 = 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp37; +//│ return n + t +//│ }; +//│ tmp36 = AA1(3); +//│ c1(tmp36) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c1, tmp36, tmp37, tmp38; +//│ c1 = function c(x) { +//│ let t, n, tmp39, tmp40, tmp41; +//│ t = x.x; +//│ if (x instanceof AA1.class) { +//│ tmp39 = 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp39; +//│ tmp40 = n; +//│ tmp41 = t; +//│ return tmp40 + tmp41 +//│ }; +//│ tmp37 = 3; +//│ tmp36 = AA1(tmp37); +//│ tmp38 = tmp36; +//│ block$res29 = c1(tmp38); +//│ undefined +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index de589306f0..37a25b3dff 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -239,7 +239,7 @@ last(enumFromTo(1, 10), None) //│ tmp7 = enumFromTo(tmp12, tmp13); //│ tmp14 = a; //│ tmp15 = tmp7; -//│ return () => { +//│ return (a1) => { //│ let param0, param1, h, t, tmp16, tmp17, tmp18, tmp19; //│ param0 = tmp14; //│ param1 = tmp15; @@ -271,6 +271,8 @@ last(enumFromTo(1, 10), None) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Some(9) + +// if we replace `x.x` first, the nested pat mat will not be fused... :fixme :sjs fun c(x) = if x is @@ -302,66 +304,17 @@ c(AA(2)) //│ return runtime.safeCall(x1()) //│ }; //│ tmp8 = 2; -//│ tmp7 = (x1) => { +//│ tmp7 = () => { //│ let scrut; -//│ scrut = (x2) => { -//│ return x2.x +//│ scrut = (x1) => { +//│ return x1.x //│ }; -//│ return runtime.safeCall(scrut(x1)) +//│ return runtime.safeCall(scrut(x_not_in_scope)) //│ }; //│ tmp9 = tmp7; //│ block$res10 = c(tmp9); //│ undefined -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 - -:fixme -:expect 5 -:sjs -fun c(x) = - let t = x.x - let n = if x is - AA then 2 - n + t -c(AA(3)) -//│ JS (unsanitized): -//│ let c1, tmp11; -//│ c1 = function c(x1) { -//│ let t, n, tmp12; -//│ t = x1.x; -//│ if (x1 instanceof AA1.class) { -//│ tmp12 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp12; -//│ return n + t -//│ }; -//│ tmp11 = AA1(3); -//│ c1(tmp11) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c1, tmp11, tmp12, tmp13; -//│ c1 = function c(x1) { -//│ let t; -//│ t = x1.x; -//│ return runtime.safeCall(x1(t)) -//│ }; -//│ tmp12 = 3; -//│ tmp11 = (t) => { -//│ let n, tmp14, tmp15, tmp16; -//│ tmp14 = 2; -//│ n = tmp14; -//│ tmp15 = n; -//│ tmp16 = t; -//│ return tmp15 + tmp16 -//│ }; -//│ tmp13 = tmp11; -//│ block$res12 = c1(tmp13); -//│ undefined -//│ ═══[RUNTIME ERROR] Expected: '5', got: 'NaN' -//│ = NaN -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 5 From ab886a1c546f1f5e9c476e6494568750cdd436a8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:01:24 +0800 Subject: [PATCH 075/303] fix the filtering of strategies --- .../scala/hkmc2/codegen/Deforestation.scala | 72 ++++++++++++++++++- .../src/test/mlscript/deforest/todos.mls | 57 +++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index bd8c20577e..700d5854fe 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -588,6 +588,24 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== + def handleCtors[A](c: ResultId, k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A): Opt[A] = + ResultUid(c) match + case Call(fun, args) => fun match + case s: Select if s.symbol.flatMap(_.asCls).isDefined => + Some(k(c, s, s.symbol.get.asCls.get, args)) + case v: Value.Ref if v.l.asCls.isDefined => + Some(k(c, v, v.l.asCls.get, args)) + case _ => None + case s: Select if s.symbol.flatMap(_.asObj).isDefined => + Some(k(c, s, s.symbol.get.asObj.get, Nil)) + case v: Value.Ref if v.l.asObj.isDefined => + Some(k(c, v, v.l.asObj.get, Nil)) + case _ => None + + def getClsSymOfUid(c: CtorExpr): ClsOrModSymbol = + handleCtors(c, (_, _, s, _) => s).get + + lazy val resolveClashes = type CtorToDtor = Map[CtorExpr, CtorDest] type DtorToCtor = Map[DtorExpr, Ls[CtorExpr]] @@ -611,7 +629,7 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorToDtor = ctorDests.ctorDests.toMap val dtorToCtor = dtorSources.dtorSources.toMap - removeCtor( + val removeClashes = removeCtor( ctorToDtor, dtorToCtor, ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels) => @@ -625,6 +643,58 @@ class Deforest(using TL, Raise, Elaborator.State): }.keySet ) + val removeCycle = { + def getCtorInArm(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = + val ctorSym = getClsSymOfUid(ctor) + val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.get._2 + + object GetCtorsTransformer extends BlockTransformer(new SymbolSubst()): + val ctors = mutable.Set.empty[ResultId] + override def applyResult(r: Result): Result = + handleCtors( + r.uid, + (id, f, clsOrMod, args) => + ctors += id + args.foreach { case Arg(_, value) => applyResult(value) } + ) match + case Some(_) => r + case None => r match + case Call(_, args) => + args.foreach { case Arg(_, value) => applyResult(value) }; r + case Instantiate(cls, args) => + args.foreach(applyResult); r + case _ => r + + GetCtorsTransformer.applyBlock(arm) + GetCtorsTransformer.ctors.toSet + + def findCycle(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = + val cache = mutable.Set(ctor) + def go(ctorAndMatches: Set[CtorExpr -> Match]): Set[CtorExpr] = + val newCtorsAndNewMatches = + ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => + removeClashes._1.get(c).flatMap: + case CtorDest(matches, sels) => matches.values.headOption.map(m => c -> m) + val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) + if newCtorsAndNewMatches.isEmpty then + Set.empty + else if cycled.nonEmpty then + cycled.map(_._1) + else + go(newCtorsAndNewMatches) + go(Set(ctor -> dtor)) + + val toRmCtor = removeClashes._1.flatMap: + case (c, CtorDest(matches, sels)) => + assert(matches.size <= 1) + matches.values.flatMap(m => findCycle(c, m)) + + removeCtor(removeClashes._1, removeClashes._2, toRmCtor.toSet) + } + + val finalRes = removeCycle + finalRes + lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 37a25b3dff..448e1f2f5d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -318,3 +318,60 @@ c(AA(2)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 + + +:sjs +fun f(x) = if x is + Some then if x.x > 1 then f(Some(x.x - 1)) else 0 +f(Some(2)) +//│ JS (unsanitized): +//│ let f, tmp11; +//│ f = function f(x1) { +//│ let scrut, tmp12, tmp13; +//│ if (x1 instanceof Some1.class) { +//│ scrut = x1.x > 1; +//│ if (scrut === true) { +//│ tmp12 = x1.x - 1; +//│ tmp13 = Some1(tmp12); +//│ return f(tmp13) +//│ } else { +//│ return 0 +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp11 = Some1(2); +//│ f(tmp11) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f, tmp11, tmp12, tmp13; +//│ f = function f(x1) { +//│ let scrut, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; +//│ if (x1 instanceof Some1.class) { +//│ tmp16 = x1.x; +//│ tmp17 = 1; +//│ scrut = tmp16 > tmp17; +//│ if (scrut === true) { +//│ tmp18 = x1.x; +//│ tmp19 = 1; +//│ tmp14 = tmp18 - tmp19; +//│ tmp20 = tmp14; +//│ tmp15 = Some1(tmp20); +//│ tmp21 = tmp15; +//│ return f(tmp21) +//│ } else { +//│ return 0 +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp12 = 2; +//│ tmp11 = Some1(tmp12); +//│ tmp13 = tmp11; +//│ block$res12 = f(tmp13); +//│ undefined +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 From 935b5ff2082d8721637e012ef5640ffe12521c47 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 26 Feb 2025 20:10:14 +0800 Subject: [PATCH 076/303] does not generate tmp vars for function arguments --- .../scala/hkmc2/codegen/Deforestation.scala | 16 +- .../src/test/mlscript/deforest/simple.mls | 518 ++++++++---------- .../src/test/mlscript/deforest/todos.mls | 108 ++-- 3 files changed, 277 insertions(+), 365 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 700d5854fe..f1971218ff 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -854,16 +854,12 @@ class Deforest(using TL, Raise, Elaborator.State): override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => def handleNormalCall(args: List[Arg]) = - val newArgSyms = args.map{ case Arg(false, v) => // TODO: spread..? - val tmpSym = TempSymbol(N) - v -> tmpSym - } - newArgSyms.foldRight( - k(Call(f, newArgSyms.map{ case _ -> s => Arg(false, Value.Ref(s)) })(call.isMlsFun, call.mayRaiseEffects)) - ){ case (arg, sym) -> rest => - applyResult2(arg)(r => Assign(sym, r, rest)) // TODO: avoid new tmpvars when args are not rewritten... - } - + var newArgs: Ls[Arg] = Nil + val ks = args.map: + case Arg(spread, value) => applyResult2(value): r => + newArgs = Arg(spread, r.asInstanceOf[Path]) :: newArgs + End() + ks.foldRight(k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects))) { case (blk, r) => Begin(blk, r) }.flatten(identity) def handleCtorCall(c: ClassSymbol) = // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call.uid) match diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index e50c47f89b..0a36a483c4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -190,22 +190,20 @@ test() //│ return 1 //│ }; //│ tmp = () => { -//│ let param0, x1, tmp3; +//│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; -//│ tmp3 = x1; -//│ return f(tmp3) +//│ return f(x1) //│ }; //│ } else { //│ tmp2 = () => { //│ return 2 //│ }; //│ tmp = () => { -//│ let param0, x1, tmp3; +//│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; -//│ tmp3 = x1; -//│ return f(tmp3) +//│ return f(x1) //│ }; //│ } //│ x = tmp; @@ -303,18 +301,14 @@ test() //│ return 1 //│ }; //│ tmp = () => { -//│ let tmp3; -//│ tmp3 = tmp1; -//│ return f1(tmp3) +//│ return f1(tmp1) //│ }; //│ } else { //│ tmp2 = () => { //│ return 5 //│ }; //│ tmp = () => { -//│ let tmp3; -//│ tmp3 = tmp2; -//│ return f2(tmp3) +//│ return f2(tmp2) //│ }; //│ } //│ x = tmp; @@ -372,23 +366,23 @@ test() //│ ==== JS (deforested): ==== //│ let test4; //│ test4 = function test() { -//│ let c, g, tmp, tmp1, tmp2; +//│ let c, g, tmp; //│ g = function g(x) { -//│ let scrut, tmp3, tmp4; +//│ let scrut, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp3 = 11; +//│ tmp1 = 11; //│ return () => { //│ let param0, x1; -//│ param0 = tmp3; +//│ param0 = tmp1; //│ x1 = param0; //│ return x1 //│ } //│ } else { -//│ tmp4 = 22; +//│ tmp2 = 22; //│ return () => { //│ let param0, x1; -//│ param0 = tmp4; +//│ param0 = tmp2; //│ x1 = param0; //│ return x1 //│ } @@ -397,10 +391,8 @@ test() //│ c = function c(x) { //│ return runtime.safeCall(x()) //│ }; -//│ tmp1 = true; -//│ tmp = g(tmp1); -//│ tmp2 = tmp; -//│ return c(tmp2) +//│ tmp = g(true); +//│ return c(tmp) //│ }; //│ block$res10 = test4(); //│ undefined @@ -459,35 +451,24 @@ map(enumFromTo(1, 4)) //│ map(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo, map, tmp, tmp1, tmp2, tmp3; +//│ let enumFromTo, map, tmp; //│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; -//│ tmp6 = a; -//│ tmp7 = b; -//│ scrut = tmp6 < tmp7; +//│ let scrut, tmp1, tmp2, tmp3, tmp4; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp8 = a; -//│ tmp9 = 1; -//│ tmp4 = tmp8 + tmp9; -//│ tmp10 = tmp4; -//│ tmp11 = b; -//│ tmp5 = enumFromTo(tmp10, tmp11); -//│ tmp12 = a; -//│ tmp13 = tmp5; +//│ tmp1 = a + 1; +//│ tmp2 = enumFromTo(tmp1, b); +//│ tmp3 = a; +//│ tmp4 = tmp2; //│ return () => { -//│ let param0, param1, h, t, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; -//│ param0 = tmp12; -//│ param1 = tmp13; +//│ let param0, param1, h, t, tmp5, tmp6; +//│ param0 = tmp3; +//│ param1 = tmp4; //│ h = param0; //│ t = param1; -//│ tmp16 = h; -//│ tmp17 = 4; -//│ tmp14 = tmp16 + tmp17; -//│ tmp18 = t; -//│ tmp15 = map(tmp18); -//│ tmp19 = tmp14; -//│ tmp20 = tmp15; -//│ return Cons1(tmp19, tmp20) +//│ tmp5 = h + 4; +//│ tmp6 = map(t); +//│ return Cons1(tmp5, tmp6) //│ } //│ } else { //│ return () => { @@ -498,11 +479,8 @@ map(enumFromTo(1, 4)) //│ map = function map(ls) { //│ return runtime.safeCall(ls()) //│ }; -//│ tmp1 = 1; -//│ tmp2 = 4; -//│ tmp = enumFromTo(tmp1, tmp2); -//│ tmp3 = tmp; -//│ block$res13 = map(tmp3); +//│ tmp = enumFromTo(1, 4); +//│ block$res13 = map(tmp); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -518,20 +496,20 @@ fun sum(ls) = if ls is Cons(h, t) then h + sum(t) sum(enumFromTo(1,10)) //│ JS (unsanitized): -//│ let enumFromTo1, sum, tmp5; +//│ let enumFromTo1, sum, tmp2; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp6, tmp7; +//│ let scrut, tmp3, tmp4; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp6 = a + 1; -//│ tmp7 = enumFromTo1(tmp6, b); -//│ return Cons1(a, tmp7) +//│ tmp3 = a + 1; +//│ tmp4 = enumFromTo1(tmp3, b); +//│ return Cons1(a, tmp4) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum = function sum(ls) { -//│ let param0, param1, h, t, tmp6; +//│ let param0, param1, h, t, tmp3; //│ if (ls instanceof Nil1.class) { //│ return 0 //│ } else { @@ -540,43 +518,34 @@ sum(enumFromTo(1,10)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp6 = sum(t); -//│ return h + tmp6 +//│ tmp3 = sum(t); +//│ return h + tmp3 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp5 = enumFromTo1(1, 10); -//│ sum(tmp5) +//│ tmp2 = enumFromTo1(1, 10); +//│ sum(tmp2) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo1, sum, tmp5, tmp6, tmp7, tmp8; +//│ let enumFromTo1, sum, tmp2; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18; -//│ tmp11 = a; -//│ tmp12 = b; -//│ scrut = tmp11 < tmp12; +//│ let scrut, tmp3, tmp4, tmp5, tmp6; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp13 = a; -//│ tmp14 = 1; -//│ tmp9 = tmp13 + tmp14; -//│ tmp15 = tmp9; -//│ tmp16 = b; -//│ tmp10 = enumFromTo1(tmp15, tmp16); -//│ tmp17 = a; -//│ tmp18 = tmp10; +//│ tmp3 = a + 1; +//│ tmp4 = enumFromTo1(tmp3, b); +//│ tmp5 = a; +//│ tmp6 = tmp4; //│ return () => { -//│ let param0, param1, h, t, tmp19, tmp20, tmp21, tmp22; -//│ param0 = tmp17; -//│ param1 = tmp18; +//│ let param0, param1, h, t, tmp7; +//│ param0 = tmp5; +//│ param1 = tmp6; //│ h = param0; //│ t = param1; -//│ tmp20 = t; -//│ tmp19 = sum(tmp20); -//│ tmp21 = h; -//│ tmp22 = tmp19; -//│ return tmp21 + tmp22 +//│ tmp7 = sum(t); +//│ return h + tmp7 //│ } //│ } else { //│ return () => { @@ -587,11 +556,8 @@ sum(enumFromTo(1,10)) //│ sum = function sum(ls) { //│ return runtime.safeCall(ls()) //│ }; -//│ tmp6 = 1; -//│ tmp7 = 10; -//│ tmp5 = enumFromTo1(tmp6, tmp7); -//│ tmp8 = tmp5; -//│ block$res15 = sum(tmp8); +//│ tmp2 = enumFromTo1(1, 10); +//│ block$res15 = sum(tmp2); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -611,13 +577,13 @@ test() //│ JS (unsanitized): //│ let test5; //│ test5 = function test() { -//│ let x, tmp10; +//│ let x, tmp4; //│ x = B1; //│ if (x instanceof A1.class) { -//│ tmp10 = 1; +//│ tmp4 = 1; //│ } else { //│ if (x instanceof B1.class) { -//│ tmp10 = 3; +//│ tmp4 = 3; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -633,12 +599,12 @@ test() //│ ==== JS (deforested): ==== //│ let test5; //│ test5 = function test() { -//│ let x, tmp10; +//│ let x, tmp4; //│ x = B1; //│ if (x instanceof A1.class) { -//│ tmp10 = 1; +//│ tmp4 = 1; //│ } else if (x instanceof B1.class) { -//│ tmp10 = 3; +//│ tmp4 = 3; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -668,11 +634,11 @@ test() //│ JS (unsanitized): //│ let test6; //│ test6 = function test() { -//│ let x, y, tmp10; +//│ let x, y, tmp4; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp10 = 1; +//│ tmp4 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -689,8 +655,8 @@ test() //│ test6 = function test() { //│ let x, y; //│ x = (y1) => { -//│ let tmp10; -//│ tmp10 = 1; +//│ let tmp4; +//│ tmp4 = 1; //│ return runtime.safeCall(y1()) //│ }; //│ y = () => { @@ -714,53 +680,48 @@ fun c(a) = x c(A) + c(B) //│ JS (unsanitized): -//│ let c, tmp10, tmp11; +//│ let c, tmp4, tmp5; //│ c = function c(a) { -//│ let x, tmp12, tmp13; +//│ let x, tmp6, tmp7; //│ if (a instanceof A1.class) { -//│ tmp12 = 1; +//│ tmp6 = 1; //│ } else { //│ if (a instanceof B1.class) { -//│ tmp12 = 2; +//│ tmp6 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ x = tmp12; -//│ tmp13 = Predef.print(x); +//│ x = tmp6; +//│ tmp7 = Predef.print(x); //│ return x //│ }; -//│ tmp10 = c(A1); -//│ tmp11 = c(B1); -//│ tmp10 + tmp11 +//│ tmp4 = c(A1); +//│ tmp5 = c(B1); +//│ tmp4 + tmp5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, match_a_rest; -//│ match_a_rest = function match_a_rest(tmp16) { -//│ let x, tmp17, tmp18; -//│ x = tmp16; -//│ tmp18 = x; -//│ tmp17 = Predef.print(tmp18); +//│ let c, tmp4, tmp5, match_a_rest; +//│ match_a_rest = function match_a_rest(tmp6) { +//│ let x, tmp7; +//│ x = tmp6; +//│ tmp7 = Predef.print(x); //│ return x //│ }; //│ c = function c(a) { //│ return runtime.safeCall(a()) //│ }; -//│ tmp12 = () => { -//│ let tmp16; -//│ tmp16 = 1; -//│ return match_a_rest(tmp16) -//│ }; -//│ tmp10 = c(tmp12); -//│ tmp13 = () => { -//│ let tmp16; -//│ tmp16 = 2; -//│ return match_a_rest(tmp16) -//│ }; -//│ tmp11 = c(tmp13); -//│ tmp14 = tmp10; -//│ tmp15 = tmp11; -//│ block$res21 = tmp14 + tmp15; +//│ tmp4 = c(() => { +//│ let tmp6; +//│ tmp6 = 1; +//│ return match_a_rest(tmp6) +//│ }); +//│ tmp5 = c(() => { +//│ let tmp6; +//│ tmp6 = 2; +//│ return match_a_rest(tmp6) +//│ }); +//│ block$res21 = tmp4 + tmp5; //│ undefined //│ > 1 //│ > 2 @@ -781,20 +742,20 @@ fun map(f, ls) = Cons(h, t) then Cons(f(h), map(f, t)) map(x => x + 4, enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo2, map1, tmp18; +//│ let enumFromTo2, map1, tmp8; //│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp19, tmp20; +//│ let scrut, tmp9, tmp10; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp19 = a + 1; -//│ tmp20 = enumFromTo2(tmp19, b); -//│ return Cons1(a, tmp20) +//│ tmp9 = a + 1; +//│ tmp10 = enumFromTo2(tmp9, b); +//│ return Cons1(a, tmp10) //│ } else { //│ return Nil1 //│ } //│ }; //│ map1 = function map(f3, ls) { -//│ let param0, param1, h, t, tmp19, tmp20; +//│ let param0, param1, h, t, tmp9, tmp10; //│ if (ls instanceof Nil1.class) { //│ return Nil1 //│ } else { @@ -803,49 +764,38 @@ map(x => x + 4, enumFromTo(1, 4)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp19 = runtime.safeCall(f3(h)); -//│ tmp20 = map1(f3, t); -//│ return Cons1(tmp19, tmp20) +//│ tmp9 = runtime.safeCall(f3(h)); +//│ tmp10 = map1(f3, t); +//│ return Cons1(tmp9, tmp10) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp18 = enumFromTo2(1, 4); +//│ tmp8 = enumFromTo2(1, 4); //│ map1((x) => { //│ return x + 4 -//│ }, tmp18) +//│ }, tmp8) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo2, map1, tmp18, tmp19, tmp20, tmp21, tmp22; +//│ let enumFromTo2, map1, tmp8; //│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32; -//│ tmp25 = a; -//│ tmp26 = b; -//│ scrut = tmp25 < tmp26; +//│ let scrut, tmp9, tmp10, tmp11, tmp12; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp27 = a; -//│ tmp28 = 1; -//│ tmp23 = tmp27 + tmp28; -//│ tmp29 = tmp23; -//│ tmp30 = b; -//│ tmp24 = enumFromTo2(tmp29, tmp30); -//│ tmp31 = a; -//│ tmp32 = tmp24; +//│ tmp9 = a + 1; +//│ tmp10 = enumFromTo2(tmp9, b); +//│ tmp11 = a; +//│ tmp12 = tmp10; //│ return (f3) => { -//│ let param0, param1, h, t, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39; -//│ param0 = tmp31; -//│ param1 = tmp32; +//│ let param0, param1, h, t, tmp13, tmp14; +//│ param0 = tmp11; +//│ param1 = tmp12; //│ h = param0; //│ t = param1; -//│ tmp35 = h; -//│ tmp33 = runtime.safeCall(f3(tmp35)); -//│ tmp36 = f3; -//│ tmp37 = t; -//│ tmp34 = map1(tmp36, tmp37); -//│ tmp38 = tmp33; -//│ tmp39 = tmp34; -//│ return Cons1(tmp38, tmp39) +//│ tmp13 = runtime.safeCall(f3(h)); +//│ tmp14 = map1(f3, t); +//│ return Cons1(tmp13, tmp14) //│ } //│ } else { //│ return (f3) => { @@ -856,17 +806,10 @@ map(x => x + 4, enumFromTo(1, 4)) //│ map1 = function map(f3, ls) { //│ return runtime.safeCall(ls(f3)) //│ }; -//│ tmp19 = 1; -//│ tmp20 = 4; -//│ tmp18 = enumFromTo2(tmp19, tmp20); -//│ tmp21 = (x) => { -//│ let tmp23, tmp24; -//│ tmp23 = x; -//│ tmp24 = 4; -//│ return tmp23 + tmp24 -//│ }; -//│ tmp22 = tmp18; -//│ block$res23 = map1(tmp21, tmp22); +//│ tmp8 = enumFromTo2(1, 4); +//│ block$res23 = map1((x) => { +//│ return x + 4 +//│ }, tmp8); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -883,22 +826,22 @@ fun map(f, ls) = )(f) map(x => x + 4, enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo3, map2, tmp24; +//│ let enumFromTo3, map2, tmp10; //│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp25, tmp26; +//│ let scrut, tmp11, tmp12; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp25 = a + 1; -//│ tmp26 = enumFromTo3(tmp25, b); -//│ return Cons1(a, tmp26) +//│ tmp11 = a + 1; +//│ tmp12 = enumFromTo3(tmp11, b); +//│ return Cons1(a, tmp12) //│ } else { //│ return Nil1 //│ } //│ }; //│ map2 = function map(f3, ls) { -//│ let param0, param1, h, t, tmp25; +//│ let param0, param1, h, t, tmp11; //│ if (ls instanceof Nil1.class) { -//│ tmp25 = (f4) => { +//│ tmp11 = (f4) => { //│ return Nil1 //│ }; //│ } else { @@ -907,87 +850,67 @@ map(x => x + 4, enumFromTo(1, 4)) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp25 = (f4) => { -//│ let tmp26, tmp27; -//│ tmp26 = runtime.safeCall(f4(h)); -//│ tmp27 = map2(f4, t); -//│ return Cons1(tmp26, tmp27) +//│ tmp11 = (f4) => { +//│ let tmp12, tmp13; +//│ tmp12 = runtime.safeCall(f4(h)); +//│ tmp13 = map2(f4, t); +//│ return Cons1(tmp12, tmp13) //│ }; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ return runtime.safeCall(tmp25(f3)) +//│ return runtime.safeCall(tmp11(f3)) //│ }; -//│ tmp24 = enumFromTo3(1, 4); +//│ tmp10 = enumFromTo3(1, 4); //│ map2((x) => { //│ return x + 4 -//│ }, tmp24) +//│ }, tmp10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo3, map2, tmp24, match_ls_rest, tmp25, tmp26, tmp27, tmp28; -//│ match_ls_rest = function match_ls_rest(f3, tmp29) { -//│ let tmp30; -//│ tmp30 = f3; -//│ return runtime.safeCall(tmp29(tmp30)) +//│ let enumFromTo3, map2, tmp10, match_ls_rest; +//│ match_ls_rest = function match_ls_rest(f3, tmp11) { +//│ return runtime.safeCall(tmp11(f3)) //│ }; //│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38; -//│ tmp31 = a; -//│ tmp32 = b; -//│ scrut = tmp31 < tmp32; +//│ let scrut, tmp11, tmp12, tmp13, tmp14; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp33 = a; -//│ tmp34 = 1; -//│ tmp29 = tmp33 + tmp34; -//│ tmp35 = tmp29; -//│ tmp36 = b; -//│ tmp30 = enumFromTo3(tmp35, tmp36); -//│ tmp37 = a; -//│ tmp38 = tmp30; +//│ tmp11 = a + 1; +//│ tmp12 = enumFromTo3(tmp11, b); +//│ tmp13 = a; +//│ tmp14 = tmp12; //│ return (f3) => { -//│ let param0, param1, h, t, tmp39; -//│ param0 = tmp37; -//│ param1 = tmp38; +//│ let param0, param1, h, t, tmp15; +//│ param0 = tmp13; +//│ param1 = tmp14; //│ h = param0; //│ t = param1; -//│ tmp39 = (f4) => { -//│ let tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46; -//│ tmp42 = h; -//│ tmp40 = runtime.safeCall(f4(tmp42)); -//│ tmp43 = f4; -//│ tmp44 = t; -//│ tmp41 = map2(tmp43, tmp44); -//│ tmp45 = tmp40; -//│ tmp46 = tmp41; -//│ return Cons1(tmp45, tmp46) +//│ tmp15 = (f4) => { +//│ let tmp16, tmp17; +//│ tmp16 = runtime.safeCall(f4(h)); +//│ tmp17 = map2(f4, t); +//│ return Cons1(tmp16, tmp17) //│ }; -//│ return match_ls_rest(f3, tmp39) +//│ return match_ls_rest(f3, tmp15) //│ } //│ } else { //│ return (f3) => { -//│ let tmp39; -//│ tmp39 = (f4) => { +//│ let tmp15; +//│ tmp15 = (f4) => { //│ return Nil1 //│ }; -//│ return match_ls_rest(f3, tmp39) +//│ return match_ls_rest(f3, tmp15) //│ } //│ } //│ }; //│ map2 = function map(f3, ls) { //│ return runtime.safeCall(ls(f3)) //│ }; -//│ tmp25 = 1; -//│ tmp26 = 4; -//│ tmp24 = enumFromTo3(tmp25, tmp26); -//│ tmp27 = (x) => { -//│ let tmp29, tmp30; -//│ tmp29 = x; -//│ tmp30 = 4; -//│ return tmp29 + tmp30 -//│ }; -//│ tmp28 = tmp24; -//│ block$res25 = map2(tmp27, tmp28); +//│ tmp10 = enumFromTo3(1, 4); +//│ block$res25 = map2((x) => { +//│ return x + 4 +//│ }, tmp10); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1002,20 +925,20 @@ fun sum(ls, a) = if ls is Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp30; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp31, tmp32; +//│ let scrut, tmp13, tmp14; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp31 = a + 1; -//│ tmp32 = enumFromTo4(tmp31, b); -//│ return Cons1(a, tmp32) +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ return Cons1(a, tmp14) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp31; +//│ let param0, param1, h, t, tmp13; //│ if (ls instanceof Nil1.class) { //│ return a //│ } else { @@ -1024,44 +947,34 @@ sum(enumFromTo(1, 10), 0) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp31 = h + a; -//│ return sum1(t, tmp31) +//│ tmp13 = h + a; +//│ return sum1(t, tmp13) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; -//│ tmp30 = enumFromTo4(1, 10); -//│ sum1(tmp30, 0) +//│ tmp12 = enumFromTo4(1, 10); +//│ sum1(tmp12, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44; -//│ tmp37 = a; -//│ tmp38 = b; -//│ scrut = tmp37 < tmp38; +//│ let scrut, tmp13, tmp14, tmp15, tmp16; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp39 = a; -//│ tmp40 = 1; -//│ tmp35 = tmp39 + tmp40; -//│ tmp41 = tmp35; -//│ tmp42 = b; -//│ tmp36 = enumFromTo4(tmp41, tmp42); -//│ tmp43 = a; -//│ tmp44 = tmp36; +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ tmp15 = a; +//│ tmp16 = tmp14; //│ return (a1) => { -//│ let param0, param1, h, t, tmp45, tmp46, tmp47, tmp48, tmp49; -//│ param0 = tmp43; -//│ param1 = tmp44; +//│ let param0, param1, h, t, tmp17; +//│ param0 = tmp15; +//│ param1 = tmp16; //│ h = param0; //│ t = param1; -//│ tmp46 = h; -//│ tmp47 = a1; -//│ tmp45 = tmp46 + tmp47; -//│ tmp48 = t; -//│ tmp49 = tmp45; -//│ return sum1(tmp48, tmp49) +//│ tmp17 = h + a1; +//│ return sum1(t, tmp17) //│ } //│ } else { //│ return (a1) => { @@ -1072,12 +985,8 @@ sum(enumFromTo(1, 10), 0) //│ sum1 = function sum(ls, a) { //│ return runtime.safeCall(ls(a)) //│ }; -//│ tmp31 = 1; -//│ tmp32 = 10; -//│ tmp30 = enumFromTo4(tmp31, tmp32); -//│ tmp33 = tmp30; -//│ tmp34 = 0; -//│ block$res27 = sum1(tmp33, tmp34); +//│ tmp12 = enumFromTo4(1, 10); +//│ block$res27 = sum1(tmp12, 0); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1095,41 +1004,74 @@ fun c(x) = n + t c(AA(3)) //│ JS (unsanitized): -//│ let c1, tmp36; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp37; +//│ let t, n, tmp15; //│ t = x.x; //│ if (x instanceof AA1.class) { -//│ tmp37 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp37; +//│ n = tmp15; //│ return n + t //│ }; -//│ tmp36 = AA1(3); -//│ c1(tmp36) +//│ tmp14 = AA1(3); +//│ c1(tmp14) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp36, tmp37, tmp38; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp39, tmp40, tmp41; +//│ let t, n, tmp15; //│ t = x.x; //│ if (x instanceof AA1.class) { -//│ tmp39 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp39; -//│ tmp40 = n; -//│ tmp41 = t; -//│ return tmp40 + tmp41 +//│ n = tmp15; +//│ return n + t //│ }; -//│ tmp37 = 3; -//│ tmp36 = AA1(tmp37); -//│ tmp38 = tmp36; -//│ block$res29 = c1(tmp38); +//│ tmp14 = AA1(3); +//│ block$res29 = c1(tmp14); //│ undefined //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 + + + +:sjs +fun f(a, b) = if a is + A then if b is + B then 3 +f(A, B) +//│ JS (unsanitized): +//│ let f3; +//│ f3 = function f(a, b) { +//│ if (a instanceof A1.class) { +//│ if (b instanceof B1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f3(A1, B1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f3; +//│ f3 = function f(a, b) { +//│ return runtime.safeCall(a(b)) +//│ }; +//│ block$res31 = f3((b) => { +//│ return runtime.safeCall(b()) +//│ }, () => { +//│ return 3 +//│ }); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 448e1f2f5d..22a3a6bcda 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -133,23 +133,19 @@ module Test with //│ Test1 = class Test { //│ static #s1; //│ static { -//│ let scrut, scrut1, tmp1, tmp2, tmp3, tmp4, tmp5; +//│ let scrut, scrut1, tmp1; //│ scrut = true; //│ if (scrut === true) { -//│ tmp2 = A1; -//│ tmp1 = AA1(tmp2); +//│ tmp1 = AA1(A1); //│ } else { -//│ tmp3 = B1; -//│ tmp1 = BB1(tmp3); +//│ tmp1 = BB1(B1); //│ } //│ this.#s1 = tmp1; //│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { -//│ tmp4 = this.#s1.x; -//│ Test.f1(tmp4) +//│ Test.f1(this.#s1.x) //│ } else if (scrut1 instanceof BB1.class) { -//│ tmp5 = this.#s1.x; -//│ Test.f2(tmp5) +//│ Test.f2(this.#s1.x) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -224,32 +220,23 @@ last(enumFromTo(1, 10), None) //│ last(tmp1, None1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let last, enumFromTo, tmp1, tmp2, tmp3, tmp4, tmp5; +//│ let last, enumFromTo, tmp1; //│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; -//│ tmp8 = a; -//│ tmp9 = b; -//│ scrut = tmp8 < tmp9; +//│ let scrut, tmp2, tmp3, tmp4, tmp5; +//│ scrut = a < b; //│ if (scrut === true) { -//│ tmp10 = a; -//│ tmp11 = 1; -//│ tmp6 = tmp10 + tmp11; -//│ tmp12 = tmp6; -//│ tmp13 = b; -//│ tmp7 = enumFromTo(tmp12, tmp13); -//│ tmp14 = a; -//│ tmp15 = tmp7; +//│ tmp2 = a + 1; +//│ tmp3 = enumFromTo(tmp2, b); +//│ tmp4 = a; +//│ tmp5 = tmp3; //│ return (a1) => { -//│ let param0, param1, h, t, tmp16, tmp17, tmp18, tmp19; -//│ param0 = tmp14; -//│ param1 = tmp15; +//│ let param0, param1, h, t, tmp6; +//│ param0 = tmp4; +//│ param1 = tmp5; //│ h = param0; //│ t = param1; -//│ tmp17 = h; -//│ tmp16 = Some1(tmp17); -//│ tmp18 = t; -//│ tmp19 = tmp16; -//│ return last(tmp18, tmp19) +//│ tmp6 = Some1(h); +//│ return last(t, tmp6) //│ } //│ } else { //│ return (a1) => { @@ -260,12 +247,8 @@ last(enumFromTo(1, 10), None) //│ last = function last(ls, a) { //│ return runtime.safeCall(ls(a)) //│ }; -//│ tmp2 = 1; -//│ tmp3 = 10; -//│ tmp1 = enumFromTo(tmp2, tmp3); -//│ tmp4 = tmp1; -//│ tmp5 = None1; -//│ block$res8 = last(tmp4, tmp5); +//│ tmp1 = enumFromTo(1, 10); +//│ block$res8 = last(tmp1, None1); //│ undefined //│ = Some(9) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -281,7 +264,7 @@ fun c(x) = if x is A then x.x c(AA(2)) //│ JS (unsanitized): -//│ let c, tmp7; +//│ let c, tmp3; //│ c = function c(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -295,24 +278,23 @@ c(AA(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp7 = AA1(2); -//│ c(tmp7) +//│ tmp3 = AA1(2); +//│ c(tmp3) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp7, tmp8, tmp9; +//│ let c, tmp3, tmp4; //│ c = function c(x1) { //│ return runtime.safeCall(x1()) //│ }; -//│ tmp8 = 2; -//│ tmp7 = () => { +//│ tmp4 = 2; +//│ tmp3 = () => { //│ let scrut; //│ scrut = (x1) => { //│ return x1.x //│ }; //│ return runtime.safeCall(scrut(x_not_in_scope)) //│ }; -//│ tmp9 = tmp7; -//│ block$res10 = c(tmp9); +//│ block$res10 = c(tmp3); //│ undefined //│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -325,15 +307,15 @@ fun f(x) = if x is Some then if x.x > 1 then f(Some(x.x - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f, tmp11; +//│ let f, tmp6; //│ f = function f(x1) { -//│ let scrut, tmp12, tmp13; +//│ let scrut, tmp7, tmp8; //│ if (x1 instanceof Some1.class) { //│ scrut = x1.x > 1; //│ if (scrut === true) { -//│ tmp12 = x1.x - 1; -//│ tmp13 = Some1(tmp12); -//│ return f(tmp13) +//│ tmp7 = x1.x - 1; +//│ tmp8 = Some1(tmp7); +//│ return f(tmp8) //│ } else { //│ return 0 //│ } @@ -341,25 +323,19 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp11 = Some1(2); -//│ f(tmp11) +//│ tmp6 = Some1(2); +//│ f(tmp6) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, tmp11, tmp12, tmp13; +//│ let f, tmp6; //│ f = function f(x1) { -//│ let scrut, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; +//│ let scrut, tmp7, tmp8; //│ if (x1 instanceof Some1.class) { -//│ tmp16 = x1.x; -//│ tmp17 = 1; -//│ scrut = tmp16 > tmp17; +//│ scrut = x1.x > 1; //│ if (scrut === true) { -//│ tmp18 = x1.x; -//│ tmp19 = 1; -//│ tmp14 = tmp18 - tmp19; -//│ tmp20 = tmp14; -//│ tmp15 = Some1(tmp20); -//│ tmp21 = tmp15; -//│ return f(tmp21) +//│ tmp7 = x1.x - 1; +//│ tmp8 = Some1(tmp7); +//│ return f(tmp8) //│ } else { //│ return 0 //│ } @@ -367,10 +343,8 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp12 = 2; -//│ tmp11 = Some1(tmp12); -//│ tmp13 = tmp11; -//│ block$res12 = f(tmp13); +//│ tmp6 = Some1(2); +//│ block$res12 = f(tmp6); //│ undefined //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 1b5901b9909d5bc10b41009f5ad0b05ba18ade42 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Feb 2025 13:04:24 +0800 Subject: [PATCH 077/303] improve previous commit on avoiding tmp vars for function args --- .../scala/hkmc2/codegen/Deforestation.scala | 10 +++- .../src/test/mlscript/deforest/simple.mls | 49 +++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 48 ------------------ 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f1971218ff..297c8d0dee 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -855,11 +855,17 @@ class Deforest(using TL, Raise, Elaborator.State): case call@Call(f, args) => def handleNormalCall(args: List[Arg]) = var newArgs: Ls[Arg] = Nil - val ks = args.map: + args.foreach: case Arg(spread, value) => applyResult2(value): r => + // since the arguments must be paths, + // and calls with parameters are not paths, + // so paths will always be rewritten to paths, + // and there won't be more blocks added by `applyResult2(value)` + // so just use a dummy `End` here, to use `applyResult2` as `applyResult` newArgs = Arg(spread, r.asInstanceOf[Path]) :: newArgs End() - ks.foldRight(k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects))) { case (blk, r) => Begin(blk, r) }.flatten(identity) + k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects)) + def handleCtorCall(c: ClassSymbol) = // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call.uid) match diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 0a36a483c4..4e8abf48eb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1075,3 +1075,52 @@ f(A, B) //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 + + +:sjs +fun f(x) = if x is + Some then if x.x > 1 then f(Some(x.x - 1)) else 0 +f(Some(2)) +//│ JS (unsanitized): +//│ let f4, tmp16; +//│ f4 = function f(x) { +//│ let scrut, tmp17, tmp18; +//│ if (x instanceof Some1.class) { +//│ scrut = x.x > 1; +//│ if (scrut === true) { +//│ tmp17 = x.x - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) +//│ } else { +//│ return 0 +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp16 = Some1(2); +//│ f4(tmp16) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f4, tmp16; +//│ f4 = function f(x) { +//│ let scrut, tmp17, tmp18; +//│ if (x instanceof Some1.class) { +//│ scrut = x.x > 1; +//│ if (scrut === true) { +//│ tmp17 = x.x - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) +//│ } else { +//│ return 0 +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp16 = Some1(2); +//│ block$res33 = f4(tmp16); +//│ undefined +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 22a3a6bcda..c6b35eaa33 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -301,51 +301,3 @@ c(AA(2)) //│ = 2 - -:sjs -fun f(x) = if x is - Some then if x.x > 1 then f(Some(x.x - 1)) else 0 -f(Some(2)) -//│ JS (unsanitized): -//│ let f, tmp6; -//│ f = function f(x1) { -//│ let scrut, tmp7, tmp8; -//│ if (x1 instanceof Some1.class) { -//│ scrut = x1.x > 1; -//│ if (scrut === true) { -//│ tmp7 = x1.x - 1; -//│ tmp8 = Some1(tmp7); -//│ return f(tmp8) -//│ } else { -//│ return 0 -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp6 = Some1(2); -//│ f(tmp6) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f, tmp6; -//│ f = function f(x1) { -//│ let scrut, tmp7, tmp8; -//│ if (x1 instanceof Some1.class) { -//│ scrut = x1.x > 1; -//│ if (scrut === true) { -//│ tmp7 = x1.x - 1; -//│ tmp8 = Some1(tmp7); -//│ return f(tmp8) -//│ } else { -//│ return 0 -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp6 = Some1(2); -//│ block$res12 = f(tmp6); -//│ undefined -//│ = 0 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 0 From 58c4cefb2815dc6fc0bca8483e075d93d9c973ed Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:23:10 +0800 Subject: [PATCH 078/303] selections in nested matching arms are not considered --- .../scala/hkmc2/codegen/Deforestation.scala | 10 ++++---- .../src/test/mlscript/deforest/todos.mls | 25 ++++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 297c8d0dee..ab0dd8d399 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -49,7 +49,7 @@ case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends Con case None => Some(expr) case Some(exist) => ??? // should only update once -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Set[ResultId]) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Opt[ResultId]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat @@ -322,7 +322,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] = Set.empty[ResultId] + matching: Opt[ResultId] = None ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) @@ -330,7 +330,7 @@ class Deforest(using TL, Raise, Elaborator.State): val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => // TODO: fix this "asInstanceOf"? - processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + scrut.uid) + processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), Some(scrut.uid)) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -392,7 +392,7 @@ class Deforest(using TL, Raise, Elaborator.State): def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] + matching: Opt[ResultId] ) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => symToStrat(sym) } @@ -403,7 +403,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] + matching: Opt[ResultId] ): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index c6b35eaa33..144a2a8154 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -255,8 +255,8 @@ last(enumFromTo(1, 10), None) //│ = Some(9) -// if we replace `x.x` first, the nested pat mat will not be fused... -:fixme +// NOTE: if we replace `x.x` first, the nested pat mat will not be fused... +// currently this is fine because `x.x` is not considered to be inside the matching arm... :sjs fun c(x) = if x is AA then @@ -282,21 +282,22 @@ c(AA(2)) //│ c(tmp3) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp3, tmp4; +//│ let c, tmp3; //│ c = function c(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ tmp4 = 2; -//│ tmp3 = () => { //│ let scrut; -//│ scrut = (x1) => { -//│ return x1.x -//│ }; -//│ return runtime.safeCall(scrut(x_not_in_scope)) +//│ if (x1 instanceof AA1.class) { +//│ scrut = (x2) => { +//│ return x2.x +//│ }; +//│ return runtime.safeCall(scrut(x1)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ }; +//│ tmp3 = AA1(2); //│ block$res10 = c(tmp3); //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 From 210d96a28fff86d399714d8f3e3a2a4facd6a3ca Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Feb 2025 17:01:20 +0800 Subject: [PATCH 079/303] update tests --- .../src/test/mlscript/deforest/simple.mls | 92 ++++++++++ .../src/test/mlscript/deforest/todos.mls | 163 +++--------------- 2 files changed, 115 insertions(+), 140 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 4e8abf48eb..e15b1ffda4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1124,3 +1124,95 @@ f(Some(2)) //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 + + + +:sjs +let x = A +let y = B +if x is + A then 1 +if y is + B then 2 +//│ JS (unsanitized): +//│ let x, y, tmp18; +//│ x = A1; +//│ y = B1; +//│ if (x instanceof A1.class) { +//│ tmp18 = 1; +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ if (y instanceof B1.class) { +//│ 2 +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let x, y; +//│ x = (y1) => { +//│ let tmp18; +//│ tmp18 = 1; +//│ return runtime.safeCall(y1()) +//│ }; +//│ y = () => { +//│ return 2 +//│ }; +//│ block$res35 = runtime.safeCall(x(y)); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ x = A +//│ y = B + + + +:sjs +fun test() = + let x = A + let y = B + if x is + A then 1 + if y is + B then 2 +test() +//│ JS (unsanitized): +//│ let test7; +//│ test7 = function test() { +//│ let x1, y1, tmp19; +//│ x1 = A1; +//│ y1 = B1; +//│ if (x1 instanceof A1.class) { +//│ tmp19 = 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ if (y1 instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test7() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test7; +//│ test7 = function test() { +//│ let x1, y1; +//│ x1 = (y2) => { +//│ let tmp19; +//│ tmp19 = 1; +//│ return runtime.safeCall(y2()) +//│ }; +//│ y1 = () => { +//│ return 2 +//│ }; +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ block$res37 = test7(); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 144a2a8154..b3d0aa7400 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -13,47 +13,6 @@ class Cons(h, t) object None class Some(x) -// FIXME: why it's different when all in a function? -:sjs -let x = A -let y = B -if x is - A then 1 -if y is - B then 2 -//│ JS (unsanitized): -//│ let x, y, tmp; -//│ x = A1; -//│ y = B1; -//│ if (x instanceof A1.class) { -//│ tmp = 1; -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ if (y instanceof B1.class) { -//│ 2 -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let x, y; -//│ x = (y1) => { -//│ let tmp; -//│ tmp = 1; -//│ return runtime.safeCall(y1()) -//│ }; -//│ y = () => { -//│ return 2 -//│ }; -//│ block$res4 = runtime.safeCall(x(y)); -//│ undefined -//│ = 2 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 -//│ x = A -//│ y = B - // TODO: how to keep track of the correct s.x @@ -76,14 +35,14 @@ module Test with //│ Test1 = class Test { //│ static #s; //│ static { -//│ let scrut, scrut1, tmp1; +//│ let scrut, scrut1, tmp; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = AA1(A1); +//│ tmp = AA1(A1); //│ } else { -//│ tmp1 = BB1(B1); +//│ tmp = BB1(B1); //│ } -//│ this.#s = tmp1; +//│ this.#s = tmp; //│ scrut1 = this.#s; //│ if (scrut1 instanceof AA1.class) { //│ Test.f1(this.#s.x) @@ -133,14 +92,14 @@ module Test with //│ Test1 = class Test { //│ static #s1; //│ static { -//│ let scrut, scrut1, tmp1; +//│ let scrut, scrut1, tmp; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = AA1(A1); +//│ tmp = AA1(A1); //│ } else { -//│ tmp1 = BB1(B1); +//│ tmp = BB1(B1); //│ } -//│ this.#s1 = tmp1; +//│ this.#s1 = tmp; //│ scrut1 = this.#s1; //│ if (scrut1 instanceof AA1.class) { //│ Test.f1(this.#s1.x) @@ -174,87 +133,11 @@ module Test with //│ } //│ static toString() { return "Test"; } //│ }; -//│ block$res6 = undefined; +//│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// FIXME: the `Cons` branch does use `a`, but should also take that argument -:sjs -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun last(ls, a) = if ls is - Nil then a - Cons(h, t) then last(t, Some(h)) -last(enumFromTo(1, 10), None) -//│ JS (unsanitized): -//│ let last, enumFromTo, tmp1; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp2, tmp3; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp2 = a + 1; -//│ tmp3 = enumFromTo(tmp2, b); -//│ return Cons1(a, tmp3) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ last = function last(ls, a) { -//│ let param0, param1, h, t, tmp2; -//│ if (ls instanceof Nil1.class) { -//│ return a -//│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp2 = Some1(h); -//│ return last(t, tmp2) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ }; -//│ tmp1 = enumFromTo(1, 10); -//│ last(tmp1, None1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let last, enumFromTo, tmp1; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp2, tmp3, tmp4, tmp5; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp2 = a + 1; -//│ tmp3 = enumFromTo(tmp2, b); -//│ tmp4 = a; -//│ tmp5 = tmp3; -//│ return (a1) => { -//│ let param0, param1, h, t, tmp6; -//│ param0 = tmp4; -//│ param1 = tmp5; -//│ h = param0; -//│ t = param1; -//│ tmp6 = Some1(h); -//│ return last(t, tmp6) -//│ } -//│ } else { -//│ return (a1) => { -//│ return a1 -//│ } -//│ } -//│ }; -//│ last = function last(ls, a) { -//│ return runtime.safeCall(ls(a)) -//│ }; -//│ tmp1 = enumFromTo(1, 10); -//│ block$res8 = last(tmp1, None1); -//│ undefined -//│ = Some(9) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Some(9) - - // NOTE: if we replace `x.x` first, the nested pat mat will not be fused... // currently this is fine because `x.x` is not considered to be inside the matching arm... :sjs @@ -264,13 +147,13 @@ fun c(x) = if x is A then x.x c(AA(2)) //│ JS (unsanitized): -//│ let c, tmp3; -//│ c = function c(x1) { +//│ let c, tmp; +//│ c = function c(x) { //│ let scrut; -//│ if (x1 instanceof AA1.class) { +//│ if (x instanceof AA1.class) { //│ scrut = A1; //│ if (scrut instanceof A1.class) { -//│ return x1.x +//│ return x.x //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -278,24 +161,24 @@ c(AA(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp3 = AA1(2); -//│ c(tmp3) +//│ tmp = AA1(2); +//│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp3; -//│ c = function c(x1) { +//│ let c, tmp; +//│ c = function c(x) { //│ let scrut; -//│ if (x1 instanceof AA1.class) { -//│ scrut = (x2) => { -//│ return x2.x +//│ if (x instanceof AA1.class) { +//│ scrut = (x1) => { +//│ return x1.x //│ }; -//│ return runtime.safeCall(scrut(x1)) +//│ return runtime.safeCall(scrut(x)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp3 = AA1(2); -//│ block$res10 = c(tmp3); +//│ tmp = AA1(2); +//│ block$res6 = c(tmp); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 414e23ffe7ed00a13ee2a981e6fa939798b467c7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:58:21 +0800 Subject: [PATCH 080/303] better rewritten of rest --- .../scala/hkmc2/codegen/Deforestation.scala | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ab0dd8d399..5da6501406 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -800,19 +800,13 @@ class Deforest(using TL, Raise, Elaborator.State): def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) - val rewrittenRest = applyBlock(rest) // TODO: avoid rewriting it more than once - // should first rewrite, then replace symbol, otherwise the exprids will change? + // should first rewrite, then replace symbol, otherwise the exprids will change val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - // (rewrittenBody.sortedFvs ::: rewrittenRest.sortedFvs) - // .distinct - // .map(s => s -> VarSymbol(Tree.Ident(s.nme))) - // .filterNot(x => selMap.valuesIterator.map(v => v.l).contains(x._1) || rewrittenBody.definedVars(x._1)) - // .toMap - val restFunOrRestBlock = matchRest.getOrElse(scrut, rewrittenRest) + val restFunOrRestBlock = matchRest.getOrElse(scrut, rest) val lambdaBody = restFunOrRestBlock match - case None => + case N -> rewrittenRest => Begin(rewrittenBody, rewrittenRest) - case Some(f) => + case Some(f) -> rewrittenRest => Begin( rewrittenBody, Return( @@ -822,17 +816,22 @@ class Deforest(using TL, Raise, Elaborator.State): false ) ) - makeLambda(lambdaBody, freeVarsAndTheirNewSyms) + makeLambda(lambdaBody, freeVarsAndTheirNewSyms) object matchRest: - val store = mutable.Map.empty[ResultId, FunDefn] + val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] - def getOrElse(s: ResultId, restRewritten: Block): Opt[Symbol] = + // returns the symbol for the rest function (if any), and the rewritten rest block + def getOrElse(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = store.get(s) match - case Some(s) => Some(s.sym) - case None if restRewritten.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => None + case Some(f, b) => f.map(_.sym) -> b + case None if restBeforeRewriting.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => + val res = N -> applyBlock(restBeforeRewriting) + store += s -> res + res case _ => // now need to build a new function and update the store + val restRewritten = applyBlock(restBeforeRewriting) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap @@ -843,13 +842,15 @@ class Deforest(using TL, Raise, Elaborator.State): ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) ) - store += s -> newFunDef - Some(sym) + store += s -> (Some(newFunDef) -> restRewritten) + Some(sym) -> restRewritten def getAllFunDefs: Block => Block = - store.values.foldRight(identity: Block => Block): (defn, k) => - r => Define(defn, k(r)) - + store.values.foldRight(identity: Block => Block): + case (defn -> _, k) => + r => defn match + case None => k(r) + case Some(defn) => Define(defn, k(r)) override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => From 6ed7e7651c38dd71cb2c846e76b58542857f812c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:01:20 +0800 Subject: [PATCH 081/303] minor --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5da6501406..62a0e9b66c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -802,7 +802,7 @@ class Deforest(using TL, Raise, Elaborator.State): val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) // should first rewrite, then replace symbol, otherwise the exprids will change val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - val restFunOrRestBlock = matchRest.getOrElse(scrut, rest) + val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) val lambdaBody = restFunOrRestBlock match case N -> rewrittenRest => Begin(rewrittenBody, rewrittenRest) @@ -823,7 +823,7 @@ class Deforest(using TL, Raise, Elaborator.State): val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] // returns the symbol for the rest function (if any), and the rewritten rest block - def getOrElse(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = + def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = store.get(s) match case Some(f, b) => f.map(_.sym) -> b case None if restBeforeRewriting.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => From 479bcd1295496c44fae46fb6913b126d7715f184 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 28 Feb 2025 23:24:25 +0800 Subject: [PATCH 082/303] cleanup --- .../scala/hkmc2/codegen/Deforestation.scala | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 62a0e9b66c..c32923f5e3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -758,15 +758,6 @@ class Deforest(using TL, Raise, Elaborator.State): if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) val freeVars = scopeExtrusionInfo(scrut.uid).map(v => Arg(false, Value.Ref(v))) - // val definedInAllArms = arms.map(_._2.definedVars).fold(arms.head._2.definedVars)((a, b) => a.intersect(b)) - // val res = (arms.flatMap(_._2.sortedFvs) ::: dflt.fold(Nil)(_.sortedFvs) ::: rest.sortedFvs).distinct - // res.filterNot( - // v => - // val isScrut = v == scrut.l - // isScrut || definedInAllArms.contains(v) // TODO: shouldn't intersect with dflt since it just throws Error? - // ) // not scrut (which will be selected on, or those defined in arms or dflt, but later refered to in the rest) - // .sortBy(_.uid) - // .map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) @@ -780,23 +771,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => super.applyBlock(d) case End(msg) => End(msg) case Throw(exc) => applyResult2(exc)(Throw.apply) - case _ => super.applyBlock(b) - // case AssignField(lhs, nme, rhs, rest) => ??? - // case Label(label, body, rest) => ??? - // case Break(label) => Break(label) - // case Continue(label) => ??? - // case TryBlock(sub, finallyDo, rest) => ??? - - def makeLambda(body: Block, freeVarsAndTheirNewSyms: Map[Symbol, VarSymbol]) = - val bodyFlattened = body.flattened // otherwise mapTail to make all return explicit may not work - val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), - newBody.mapTail: - case Return(res, implct) => Return(res, false) - case t => t - ) def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) @@ -816,7 +791,14 @@ class Deforest(using TL, Raise, Elaborator.State): false ) ) - makeLambda(lambdaBody, freeVarsAndTheirNewSyms) + val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work + val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + newBody.mapTail: + case Return(res, implct) => Return(res, false) + case t => t + ) object matchRest: From 210fa1ddb64e5954c3dc5c42708b03ac57e79625 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 1 Mar 2025 12:30:07 +0800 Subject: [PATCH 083/303] wip: functions for match arms --- .../scala/hkmc2/codegen/Deforestation.scala | 105 +++++++++++++----- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c32923f5e3..dff8e5ef8c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -773,32 +773,79 @@ class Deforest(using TL, Raise, Elaborator.State): case Throw(exc) => applyResult2(exc)(Throw.apply) case _ => super.applyBlock(b) - def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = - val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) - // should first rewrite, then replace symbol, otherwise the exprids will change - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) - val lambdaBody = restFunOrRestBlock match - case N -> rewrittenRest => - Begin(rewrittenBody, rewrittenRest) - case Some(f) -> rewrittenRest => - Begin( - rewrittenBody, - Return( - Call( - Value.Ref(f), - rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - false - ) - ) - val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work - val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), - newBody.mapTail: - case Return(res, implct) => Return(res, false) - case t => t - ) + // def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = + // val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) + // // should first rewrite, then replace symbol, otherwise the exprids will change + // val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + // val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) + // val lambdaBody = restFunOrRestBlock match + // case N -> rewrittenRest => + // Begin(rewrittenBody, rewrittenRest) + // case Some(f) -> rewrittenRest => + // Begin( + // rewrittenBody, + // Return( + // Call( + // Value.Ref(f), + // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + // false + // ) + // ) + // val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work + // val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + // Value.Lam( + // ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + // newBody.mapTail: + // case Return(res, implct) => Return(res, false) + // case t => t + // ) + + object matchArms: + val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]] + + // return either a function symbol, which needs to be applied to the class fields + // or a lambda, which needs to have its class fields replaced + def getOrElseUpdate(scrut: ResultId, m: Match, cls: ClsOrModSymbol, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = + assert(scrut === m.scrut.uid) + store.get(scrut).flatMap(_.get(cls)) match + case None => // not registered before, or this branch of this match will only appear once + val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.get._2 // TODO: handle wildcard case + val rest = m.rest + val bodyRewritten1 = applyBlock(body) + + if resolveClashes._2(DtorExpr.Match(scrut)).count(getClsSymOfUid(_) === cls) > 1 && false then + // make a function, and register, and return a call to that function with correct arguments + ??? + else + // make a lambda, and replace the selections + val rewrittenBody = bodyRewritten1.replaceSelect(using sel, selMap) + // should first rewrite, then replace symbol, otherwise the exprids will change + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) + val lambdaBody = restFunOrRestBlock match + case N -> rewrittenRest => + Begin(rewrittenBody, rewrittenRest) + case Some(f) -> rewrittenRest => + Begin( + rewrittenBody, + Return( + Call( + Value.Ref(f), + rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + false + ) + ) + val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work + val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + newBody.mapTail: + case Return(res, implct) => Return(res, false) + case t => t + ) + + case Some(f) => ??? + // call f with correct arguments object matchRest: @@ -862,7 +909,7 @@ class Deforest(using TL, Raise, Elaborator.State): val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap - val bodyAndRestInLam = setupBodyAndRest(body, expr.rest, scrut, sels.toSet, idsToArgs) + val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => applyResult2(a.value): r => @@ -898,7 +945,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(CtorFinalDest.Match(scrut, expr, sels)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - val bodyAndRestInLam = setupBodyAndRest(body._2, expr.rest, scrut, Set.empty, Map.empty) + val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) k(bodyAndRestInLam) case Some(_) => ??? // TODO: a selection on a module consumes it @@ -912,7 +959,7 @@ class Deforest(using TL, Raise, Elaborator.State): val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) - val bodyAndRestInLam = setupBodyAndRest(body._2, expr.rest, scrut, Set.empty, Map.empty) + val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) k(bodyAndRestInLam) case Some(_) => ??? // TODO: a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) From 264983e54391df504eb4a9350ea464f0d1aa31d2 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:24:17 +0800 Subject: [PATCH 084/303] function for match arms --- .../scala/hkmc2/codegen/Deforestation.scala | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index dff8e5ef8c..1ec1bc9ca4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -748,8 +748,9 @@ class Deforest(using TL, Raise, Elaborator.State): def rewrite(p: Block) = val deforestTransformer = DeforestTransformer(using globallyDefinedVars.store.toSet) val rest = deforestTransformer.applyBlock(p) - val newDefs = deforestTransformer.matchRest.getAllFunDefs - newDefs(rest) + val newDefsRest = deforestTransformer.matchRest.getAllFunDefs + val newDefsArms = deforestTransformer.matchArms.getAllFunDefs + newDefsArms(newDefsRest(rest)) class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): @@ -801,7 +802,7 @@ class Deforest(using TL, Raise, Elaborator.State): // ) object matchArms: - val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]] + val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) // return either a function symbol, which needs to be applied to the class fields // or a lambda, which needs to have its class fields replaced @@ -813,9 +814,56 @@ class Deforest(using TL, Raise, Elaborator.State): val rest = m.rest val bodyRewritten1 = applyBlock(body) - if resolveClashes._2(DtorExpr.Match(scrut)).count(getClsSymOfUid(_) === cls) > 1 && false then - // make a function, and register, and return a call to that function with correct arguments - ??? + // println(s"sdfsdf::: ${resolveClashes._2(DtorExpr.Match(scrut))}") + if resolveClashes._2(DtorExpr.Match(scrut)).distinct.count(getClsSymOfUid(_) === cls) > 1 then + // make a function, and register, and return a lambda calling that function with correct arguments + // arguments for lambda: free vars + // arguments for that function: free vars and pattern vars + val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } + val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } + val bodyReplaceSel = bodyRewritten1.replaceSelect(using sel, newSelMaps) + + val freeVarsAndTheirNewSymsInBody = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) + val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSymsInBody.map(s => s._1 -> VarSymbol(s._2.id)) + + val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) + val funBody1 = restFunOrRestBlock match + case N -> rewrittenRest => + Begin(bodyReplaceSel, rewrittenRest) + case Some(f) -> rewrittenRest => + Begin( + bodyReplaceSel, + Return( + Call( + Value.Ref(f), + rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + false + ) + ) + val funBody2 = funBody1.flattened + val funBody3 = funBody2.replaceSymbols(freeVarsAndTheirNewSymsInBody.toMap) + + val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) + val newDef = FunDefn( + N, + funSym, + ParamList( + ParamListFlags.empty, + freeVarsAndTheirNewSymsInBody.map(s => Param(FldFlags.empty, s._2, N)) ::: newSelMapSyms.toList.sortBy(_._1.name).map(v => Param(FldFlags.empty, v._2, N)), + N + ) :: Nil, + funBody3.mapTail: + case Return(res, implct) => Return(res, false) + case t => t + ) + store += (scrut -> (store(scrut) + (cls -> newDef))) + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + Return( + Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.values.map(a => Arg(false, Value.Ref(a))).toList ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + false + ) + ) else // make a lambda, and replace the selections val rewrittenBody = bodyRewritten1.replaceSelect(using sel, selMap) @@ -844,9 +892,20 @@ class Deforest(using TL, Raise, Elaborator.State): case t => t ) - case Some(f) => ??? - // call f with correct arguments + case Some(f) => + // return a lambda that calls f with correct arguments + val freeVarsAndTheirNewSymsInLam = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + Return( + Call(Value.Ref(f.sym), freeVarsAndTheirNewSymsInLam.values.map(a => Arg(false, Value.Ref(a))).toList ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + false + ) + ) + def getAllFunDefs: Block => Block = + store.values.flatMap(v => v.values).foldRight(identity: Block => Block): + case (defn, k) => r => Define(defn, k(r)) object matchRest: val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] From 31376e51e120c3c8eb40af67e4e3f6fbc5d25cbe Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Mar 2025 15:45:55 +0800 Subject: [PATCH 085/303] update tests --- .../src/test/mlscript/deforest/simple.mls | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index e15b1ffda4..94e27dcc31 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -7,6 +7,8 @@ object B object C class AA(x) class BB(x) +class AAA(x, y) +class BBB(x, y) object None class Some(x) @@ -1216,3 +1218,112 @@ test() //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 + + + +:sjs +fun f(x, y) = + let a = if x is + AAA(n, m) then y + n - m + BBB(n, m) then m + 1 - n + a + 3 +f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) +//│ JS (unsanitized): +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; +//│ f5 = function f(x1, y1) { +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; +//│ if (x1 instanceof AAA1.class) { +//│ param01 = x1.x; +//│ param11 = x1.y; +//│ n1 = param01; +//│ m1 = param11; +//│ tmp29 = y1 + n1; +//│ tmp30 = tmp29 - m1; +//│ } else { +//│ if (x1 instanceof BBB1.class) { +//│ param0 = x1.x; +//│ param1 = x1.y; +//│ n = param0; +//│ m = param1; +//│ tmp31 = m + 1; +//│ tmp30 = tmp31 - n; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ a = tmp30; +//│ return a + 3 +//│ }; +//│ tmp19 = AAA1(1, 3); +//│ tmp20 = f5(tmp19, 1); +//│ tmp21 = BBB1(2, 3); +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp24 = AAA1(3, 2); +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp27 = BBB1(4, 6); +//│ tmp28 = f5(tmp27, 0); +//│ tmp26 + tmp28 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; +//│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { +//│ let param0, param1, n, m, tmp37, tmp38; +//│ param0 = field_x; +//│ param1 = field_y; +//│ n = param0; +//│ m = param1; +//│ tmp37 = y1 + n; +//│ tmp38 = tmp37 - m; +//│ return match_x_rest(tmp38) +//│ }; +//│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { +//│ let param0, param1, n, m, tmp37, tmp38; +//│ param0 = field_x; +//│ param1 = field_y; +//│ n = param0; +//│ m = param1; +//│ tmp38 = m + 1; +//│ tmp37 = tmp38 - n; +//│ return match_x_rest(tmp37) +//│ }; +//│ match_x_rest = function match_x_rest(tmp37) { +//│ let a; +//│ a = tmp37; +//│ return a + 3 +//│ }; +//│ f5 = function f(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ tmp29 = 1; +//│ tmp30 = 3; +//│ tmp19 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp29, tmp30) +//│ }; +//│ tmp20 = f5(tmp19, 1); +//│ tmp31 = 2; +//│ tmp32 = 3; +//│ tmp21 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp31, tmp32) +//│ }; +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp33 = 3; +//│ tmp34 = 2; +//│ tmp24 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp33, tmp34) +//│ }; +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp35 = 4; +//│ tmp36 = 6; +//│ tmp27 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp35, tmp36) +//│ }; +//│ tmp28 = f5(tmp27, 0); +//│ block$res39 = tmp26 + tmp28; +//│ undefined +//│ = 21 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 21 From 8e64cd6adfda2ad17756983d212a2c58ab92108a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:36:48 +0800 Subject: [PATCH 086/303] clean up --- .../scala/hkmc2/codegen/Deforestation.scala | 135 ++++++++---------- 1 file changed, 62 insertions(+), 73 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1ec1bc9ca4..0f4ab8c35e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -774,33 +774,6 @@ class Deforest(using TL, Raise, Elaborator.State): case Throw(exc) => applyResult2(exc)(Throw.apply) case _ => super.applyBlock(b) - // def setupBodyAndRest(body: Block, rest: Block, scrut: ResultId, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = - // val rewrittenBody = applyBlock(body).replaceSelect(using sel, selMap) - // // should first rewrite, then replace symbol, otherwise the exprids will change - // val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - // val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) - // val lambdaBody = restFunOrRestBlock match - // case N -> rewrittenRest => - // Begin(rewrittenBody, rewrittenRest) - // case Some(f) -> rewrittenRest => - // Begin( - // rewrittenBody, - // Return( - // Call( - // Value.Ref(f), - // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - // false - // ) - // ) - // val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work - // val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) - // Value.Lam( - // ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), - // newBody.mapTail: - // case Return(res, implct) => Return(res, false) - // case t => t - // ) - object matchArms: val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) @@ -812,36 +785,54 @@ class Deforest(using TL, Raise, Elaborator.State): case None => // not registered before, or this branch of this match will only appear once val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.get._2 // TODO: handle wildcard case val rest = m.rest - val bodyRewritten1 = applyBlock(body) + val bodyInitRewritten = applyBlock(body) + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) match + case N -> rewrittenRest => (bodyBlk: Block) => + Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms).mapTail: + case Return(res, implct) => Return(res, false) + case t => t + case Some(f) -> rewrittenRest => (bodyBlk: Block) => + Begin( + bodyBlk, + Return( + Call( + Value.Ref(f), + rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + false + ) + ).flattened.replaceSymbols(freeVarsAndTheirNewSyms).mapTail: + case Return(res, implct) => Return(res, false) + case t => t + - // println(s"sdfsdf::: ${resolveClashes._2(DtorExpr.Match(scrut))}") if resolveClashes._2(DtorExpr.Match(scrut)).distinct.count(getClsSymOfUid(_) === cls) > 1 then // make a function, and register, and return a lambda calling that function with correct arguments // arguments for lambda: free vars // arguments for that function: free vars and pattern vars val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - val bodyReplaceSel = bodyRewritten1.replaceSelect(using sel, newSelMaps) + val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) - val freeVarsAndTheirNewSymsInBody = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) - val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSymsInBody.map(s => s._1 -> VarSymbol(s._2.id)) + // val freeVarsAndTheirNewSymsInBody = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) + val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) - val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) - val funBody1 = restFunOrRestBlock match - case N -> rewrittenRest => - Begin(bodyReplaceSel, rewrittenRest) - case Some(f) -> rewrittenRest => - Begin( - bodyReplaceSel, - Return( - Call( - Value.Ref(f), - rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - false - ) - ) - val funBody2 = funBody1.flattened - val funBody3 = funBody2.replaceSymbols(freeVarsAndTheirNewSymsInBody.toMap) + // val funBody1 = restFunOrRestBlock match + // case N -> rewrittenRest => + // Begin(bodyReplaceSel, rewrittenRest) + // case Some(f) -> rewrittenRest => + // Begin( + // bodyReplaceSel, + // Return( + // Call( + // Value.Ref(f), + // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + // false + // ) + // ) + // val funBody2 = funBody1.flattened + // val funBody3 = funBody2.replaceSymbols(freeVarsAndTheirNewSymsInBody.toMap) + val funBody = restFunOrRestBlock(bodyReplaceSel) val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) val newDef = FunDefn( @@ -849,12 +840,10 @@ class Deforest(using TL, Raise, Elaborator.State): funSym, ParamList( ParamListFlags.empty, - freeVarsAndTheirNewSymsInBody.map(s => Param(FldFlags.empty, s._2, N)) ::: newSelMapSyms.toList.sortBy(_._1.name).map(v => Param(FldFlags.empty, v._2, N)), + freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList ::: newSelMapSyms.toList.sortBy(_._1.name).map(v => Param(FldFlags.empty, v._2, N)), N ) :: Nil, - funBody3.mapTail: - case Return(res, implct) => Return(res, false) - case t => t + funBody ) store += (scrut -> (store(scrut) + (cls -> newDef))) Value.Lam( @@ -866,30 +855,30 @@ class Deforest(using TL, Raise, Elaborator.State): ) else // make a lambda, and replace the selections - val rewrittenBody = bodyRewritten1.replaceSelect(using sel, selMap) + val rewrittenBody = bodyInitRewritten.replaceSelect(using sel, selMap) // should first rewrite, then replace symbol, otherwise the exprids will change - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) - val lambdaBody = restFunOrRestBlock match - case N -> rewrittenRest => - Begin(rewrittenBody, rewrittenRest) - case Some(f) -> rewrittenRest => - Begin( - rewrittenBody, - Return( - Call( - Value.Ref(f), - rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - false - ) - ) - val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work - val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + // val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + + // val lambdaBody = restFunOrRestBlock match + // case N -> rewrittenRest => + // Begin(rewrittenBody, rewrittenRest) + // case Some(f) -> rewrittenRest => + // Begin( + // rewrittenBody, + // Return( + // Call( + // Value.Ref(f), + // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + // false + // ) + // ) + // val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work + // val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) + val lambdaBody = restFunOrRestBlock(rewrittenBody) + Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), - newBody.mapTail: - case Return(res, implct) => Return(res, false) - case t => t + lambdaBody ) case Some(f) => From d1f99fe3da4055694cb05808bbfbeafc0e06b814 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 3 Mar 2025 18:59:13 +0800 Subject: [PATCH 087/303] fix parameter order and cleanup --- .../scala/hkmc2/codegen/Deforestation.scala | 65 ++++--------------- 1 file changed, 13 insertions(+), 52 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0f4ab8c35e..f66c986f38 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -777,19 +777,19 @@ class Deforest(using TL, Raise, Elaborator.State): object matchArms: val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) - // return either a function symbol, which needs to be applied to the class fields - // or a lambda, which needs to have its class fields replaced + // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms def getOrElseUpdate(scrut: ResultId, m: Match, cls: ClsOrModSymbol, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = assert(scrut === m.scrut.uid) + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) store.get(scrut).flatMap(_.get(cls)) match case None => // not registered before, or this branch of this match will only appear once val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.get._2 // TODO: handle wildcard case val rest = m.rest val bodyInitRewritten = applyBlock(body) - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - val restFunOrRestBlock = matchRest.getOrElseUpdate(scrut, rest) match + + val makeBody = matchRest.getOrElseUpdate(scrut, rest) match case N -> rewrittenRest => (bodyBlk: Block) => - Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms).mapTail: + Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: case Return(res, implct) => Return(res, false) case t => t case Some(f) -> rewrittenRest => (bodyBlk: Block) => @@ -801,10 +801,9 @@ class Deforest(using TL, Raise, Elaborator.State): rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), false ) - ).flattened.replaceSymbols(freeVarsAndTheirNewSyms).mapTail: + ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: case Return(res, implct) => Return(res, false) case t => t - if resolveClashes._2(DtorExpr.Match(scrut)).distinct.count(getClsSymOfUid(_) === cls) > 1 then // make a function, and register, and return a lambda calling that function with correct arguments @@ -813,27 +812,9 @@ class Deforest(using TL, Raise, Elaborator.State): val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) - - // val freeVarsAndTheirNewSymsInBody = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) - // val funBody1 = restFunOrRestBlock match - // case N -> rewrittenRest => - // Begin(bodyReplaceSel, rewrittenRest) - // case Some(f) -> rewrittenRest => - // Begin( - // bodyReplaceSel, - // Return( - // Call( - // Value.Ref(f), - // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - // false - // ) - // ) - // val funBody2 = funBody1.flattened - // val funBody3 = funBody2.replaceSymbols(freeVarsAndTheirNewSymsInBody.toMap) - val funBody = restFunOrRestBlock(bodyReplaceSel) - + val funBody = makeBody(bodyReplaceSel) val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) val newDef = FunDefn( N, @@ -847,35 +828,16 @@ class Deforest(using TL, Raise, Elaborator.State): ) store += (scrut -> (store(scrut) + (cls -> newDef))) Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N)), N), Return( - Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.values.map(a => Arg(false, Value.Ref(a))).toList ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), false ) ) else // make a lambda, and replace the selections - val rewrittenBody = bodyInitRewritten.replaceSelect(using sel, selMap) - // should first rewrite, then replace symbol, otherwise the exprids will change - // val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - - // val lambdaBody = restFunOrRestBlock match - // case N -> rewrittenRest => - // Begin(rewrittenBody, rewrittenRest) - // case Some(f) -> rewrittenRest => - // Begin( - // rewrittenBody, - // Return( - // Call( - // Value.Ref(f), - // rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - // false - // ) - // ) - // val bodyFlattened = lambdaBody.flattened // otherwise mapTail to make all return explicit may not work - // val newBody = bodyFlattened.replaceSymbols(freeVarsAndTheirNewSyms) - val lambdaBody = restFunOrRestBlock(rewrittenBody) - + val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) + val lambdaBody = makeBody(bodyReplaceSel) Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), lambdaBody @@ -883,11 +845,10 @@ class Deforest(using TL, Raise, Elaborator.State): case Some(f) => // return a lambda that calls f with correct arguments - val freeVarsAndTheirNewSymsInLam = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)), N), Return( - Call(Value.Ref(f.sym), freeVarsAndTheirNewSymsInLam.values.map(a => Arg(false, Value.Ref(a))).toList ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), false ) ) From b24865324a2549957e6741eaafa8fde19ec79ad5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:01:25 +0800 Subject: [PATCH 088/303] use set to record dtor sources --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f66c986f38..29e43c9a88 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -506,14 +506,14 @@ class Deforest(using TL, Raise, Elaborator.State): def get(ctor: CtorExpr) = ctorDests.get(ctor) object dtorSources: - val dtorSources = mutable.Map.empty[DtorExpr, Ls[ResultId]].withDefaultValue(Nil) + val dtorSources = mutable.Map.empty[DtorExpr, Set[ResultId]].withDefaultValue(Set.empty) private def getDtorExprOfResultId(i: ResultId) = ResultUid(i) match case s: Select => DtorExpr.Sel(i) case r: Value.Ref => DtorExpr.Match(i) case _ => ??? // unreachable def update(dtor: ResultId, ctor: ResultId) = val dtorExpr = getDtorExprOfResultId(dtor) - dtorSources += dtorExpr -> (ctor :: dtorSources(dtorExpr)) + dtorSources += dtorExpr -> (dtorSources(dtorExpr) + ctor) def get(dtor: ResultId) = dtorSources.get(getDtorExprOfResultId(dtor)) @@ -608,7 +608,7 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val resolveClashes = type CtorToDtor = Map[CtorExpr, CtorDest] - type DtorToCtor = Map[DtorExpr, Ls[CtorExpr]] + type DtorToCtor = Map[DtorExpr, Set[CtorExpr]] def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = if rm.isEmpty then @@ -805,7 +805,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Return(res, implct) => Return(res, false) case t => t - if resolveClashes._2(DtorExpr.Match(scrut)).distinct.count(getClsSymOfUid(_) === cls) > 1 then + if resolveClashes._2(DtorExpr.Match(scrut)).count(getClsSymOfUid(_) === cls) > 1 then // make a function, and register, and return a lambda calling that function with correct arguments // arguments for lambda: free vars // arguments for that function: free vars and pattern vars @@ -864,7 +864,7 @@ class Deforest(using TL, Raise, Elaborator.State): def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = store.get(s) match case Some(f, b) => f.map(_.sym) -> b - case None if restBeforeRewriting.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).length == 1) => + case None if restBeforeRewriting.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).size == 1) => val res = N -> applyBlock(restBeforeRewriting) store += s -> res res From a81ca856795353cde861fcceca13b28ae0482204 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 4 Mar 2025 16:24:36 +0800 Subject: [PATCH 089/303] wip: try to replace select when rewriting --- .../scala/hkmc2/codegen/Deforestation.scala | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 29e43c9a88..fb7737fec5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -754,6 +754,8 @@ class Deforest(using TL, Raise, Elaborator.State): class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): + var replaceSelInfo = Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] + override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then @@ -785,7 +787,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => // not registered before, or this branch of this match will only appear once val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.get._2 // TODO: handle wildcard case val rest = m.rest - val bodyInitRewritten = applyBlock(body) + val makeBody = matchRest.getOrElseUpdate(scrut, rest) match case N -> rewrittenRest => (bodyBlk: Block) => @@ -810,8 +812,14 @@ class Deforest(using TL, Raise, Elaborator.State): // arguments for lambda: free vars // arguments for that function: free vars and pattern vars val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } - val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) + val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } + + replaceSelInfo += scrut -> (sel -> newSelMaps) + val bodyReplaceSel = applyBlock(body) + replaceSelInfo -= scrut + + // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) + val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) val funBody = makeBody(bodyReplaceSel) @@ -836,7 +844,13 @@ class Deforest(using TL, Raise, Elaborator.State): ) else // make a lambda, and replace the selections - val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) + + replaceSelInfo += scrut -> (sel -> selMap) + val bodyReplaceSel = applyBlock(body) + replaceSelInfo -= scrut + + // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) + val lambdaBody = makeBody(bodyReplaceSel) Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), @@ -946,7 +960,9 @@ class Deforest(using TL, Raise, Elaborator.State): if rewritingSelConsumer.contains(s.uid) then k(p) else - k(s) + replaceSelInfo.values.find(v => v._1.contains(s.uid)) match + case None => k(s) + case Some(v) => k(v._2(nme)) case Some(mod) => filteredCtorDests.get(s.uid) match case None => From a187b4f2e7df252448d04c1c21116436b687f4fc Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:50:33 +0800 Subject: [PATCH 090/303] wip: restore changes to keep track of selections in nested matches; update todo tests --- .../scala/hkmc2/codegen/Deforestation.scala | 10 +-- .../src/test/mlscript/deforest/simple.mls | 74 +++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 25 ++++--- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index fb7737fec5..00424e6e1d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -49,7 +49,7 @@ case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends Con case None => Some(expr) case Some(exist) => ??? // should only update once -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Opt[ResultId]) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Set[ResultId]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat @@ -322,7 +322,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: Opt[ResultId] = None + matching: Set[ResultId] = Set.empty ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) @@ -330,7 +330,7 @@ class Deforest(using TL, Raise, Elaborator.State): val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => // TODO: fix this "asInstanceOf"? - processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), Some(scrut.uid)) + processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + scrut.uid) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -392,7 +392,7 @@ class Deforest(using TL, Raise, Elaborator.State): def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Opt[ResultId] + matching: Set[ResultId] ) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => symToStrat(sym) } @@ -403,7 +403,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Opt[ResultId] + matching: Set[ResultId] ): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 94e27dcc31..ee10cd0943 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1327,3 +1327,77 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ = 21 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 21 + + +:sjs +fun c1(x) = if x is + AA then + if A is + A then x.x +fun c2(x) = if x is + AA then x.x +fun p(a) = c1(a) + c2(a) +p(AA(1)) +//│ JS (unsanitized): +//│ let p, c11, c2, tmp47; +//│ c11 = function c1(x1) { +//│ let scrut; +//│ if (x1 instanceof AA1.class) { +//│ scrut = A1; +//│ if (scrut instanceof A1.class) { +//│ return x1.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c2 = function c2(x1) { +//│ if (x1 instanceof AA1.class) { +//│ return x1.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p = function p(a) { +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 +//│ }; +//│ tmp47 = AA1(1); +//│ p(tmp47) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p, c11, c2, tmp47; +//│ c11 = function c1(x1) { +//│ let scrut; +//│ if (x1 instanceof AA1.class) { +//│ scrut = (x2) => { +//│ return x2.x +//│ }; +//│ return runtime.safeCall(scrut(x1)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c2 = function c2(x1) { +//│ if (x1 instanceof AA1.class) { +//│ return x1.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p = function p(a) { +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 +//│ }; +//│ tmp47 = AA1(1); +//│ block$res41 = p(tmp47); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index b3d0aa7400..e3ef9faa3c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -139,7 +139,9 @@ module Test with // NOTE: if we replace `x.x` first, the nested pat mat will not be fused... -// currently this is fine because `x.x` is not considered to be inside the matching arm... +// we can perform the replacing of `x.x` and rewriting at the same time, but +// then there will be a non-existing free var `x` +:fixme :sjs fun c(x) = if x is AA then @@ -165,22 +167,21 @@ c(AA(2)) //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp; +//│ let c, tmp, tmp1; //│ c = function c(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ tmp1 = 2; +//│ tmp = () => { //│ let scrut; -//│ if (x instanceof AA1.class) { -//│ scrut = (x1) => { -//│ return x1.x -//│ }; -//│ return runtime.safeCall(scrut(x)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ scrut = (x) => { +//│ return tmp1 +//│ }; +//│ return runtime.safeCall(scrut(x_not_in_scope)) //│ }; -//│ tmp = AA1(2); //│ block$res6 = c(tmp); //│ undefined -//│ = 2 +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 From 57df8821b0bf82a17cb2f8d735432b0542f12176 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:07:58 +0800 Subject: [PATCH 091/303] wip: update after merge, no need to flatten and merge arms in deforestation --- .../scala/hkmc2/codegen/Deforestation.scala | 121 +--- .../src/test/mlscript/deforest/simple.mls | 665 ++++++++---------- .../src/test/mlscript/deforest/todos.mls | 69 +- 3 files changed, 322 insertions(+), 533 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 00424e6e1d..52a83b8d01 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -77,23 +77,7 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid -extension (m: Match) - def mergeArms: Match = - val Match(s, arms, dflt, rest) = m - dflt.map(_.mergeMatchArms).fold(m): - case m@Match(s2, arms2, dflt2, _: End) if s2 === s => - Match( - s, - (arms ::: arms2).map((cse, b) => (cse, b.mergeMatchArms)), - dflt2, - rest.mergeMatchArms - ) - case d => Match( - s, - arms.map((cse, b) => (cse, b.mergeMatchArms)), - S(d), - rest.mergeMatchArms - ) + extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = @@ -122,97 +106,7 @@ extension (b: Block) HasExplicitRetTransformer.applyBlock(b) HasExplicitRetTransformer.flag - - - def mergeMatchArms: Block = - object MergeMatchArmTransformer extends BlockTransformer(new SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case m: Match => m.mergeArms - case _ => super.applyBlock(b) - MergeMatchArmTransformer.applyBlock(b) - - def flattened = b.flatten(identity) - - private def flatten(k: End => Block): Block = b match - case Match(scrut, arms, dflt, rest) => - val newRest = rest.flatten(k) - val newArms = arms.mapConserve: arm => - val newBody = arm._2.flattened - if newBody is arm._2 then arm else (arm._1, newBody) - val newDflt = dflt.map(_.flattened) - if (newRest is rest) && (newArms is arms) && (dflt is newDflt) - then b - else Match(scrut, newArms, newDflt, newRest) - - case Label(label, body, rest) => - val newBody = body.flattened - val newRest = rest.flatten(k) - if (newBody is body) && (newRest is rest) - then b - else Label(label, newBody, newRest) - - case Begin(sub, rest) => - sub.flatten(_ => rest.flatten(k)) - - case TryBlock(sub, finallyDo, rest) => - val newSub = sub.flattened - val newFinallyDo = finallyDo.flattened - val newRest = rest.flatten(k) - if (newSub is sub) && (newFinallyDo is finallyDo) && (newRest is rest) - then b - else TryBlock(newSub, newFinallyDo, newRest) - - case Assign(lhs, rhs, rest) => - val newRest = rest.flatten(k) - if newRest is rest - then b - else Assign(lhs, rhs, newRest) - - case a@AssignField(lhs, nme, rhs, rest) => - val newRest = rest.flatten(k) - if newRest is rest - then b - else AssignField(lhs, nme, rhs, newRest)(a.symbol) - - case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => - val newRest = rest.flatten(k) - if newRest is rest - then b - else AssignDynField(lhs, fld, arrayIdx, rhs, newRest) - - case Define(defn, rest) => - val newDefn = defn match - case d: FunDefn => - val newBody = d.body.flattened - if newBody is d.body - then d - else d.copy(body = newBody) - case v: ValDefn => v - case c: ClsLikeDefn => - val newPreCtor = c.preCtor.flattened - val newCtor = c.ctor.flattened - if (newPreCtor is c.preCtor) && (newCtor is c.ctor) - then c - else c.copy(preCtor = newPreCtor, ctor = newCtor) - - val newRest = rest.flatten(k) - if (newDefn is defn) && (newRest is rest) - then b - else Define(newDefn, newRest) - - case HandleBlock(lhs, res, par, args, cls, handlers, body, rest) => - val newHandlers = handlers.mapConserve: h => - val newBody = h.body.flattened - if newBody is h.body then h else h.copy(body = newBody) - val newBody = body.flattened - val newRest = rest.flatten(k) - if (newHandlers is handlers) && (newBody is body) && (newRest is rest) - then b - else HandleBlock(lhs, res, par, args, cls, newHandlers, newBody, newRest) - case e: End => k(e) - case t: BlockTail => b - class Deforest(using TL, Raise, Elaborator.State): @@ -221,15 +115,14 @@ class Deforest(using TL, Raise, Elaborator.State): import StratVarState.freshVar def apply(p: Program) = - val flattenP = p.main.flattened - val mergedArms = flattenP.mergeMatchArms - - globallyDefinedVars.init(mergedArms) + val mainBlk = p.main + + globallyDefinedVars.init(mainBlk) // allocate type vars for defined symbols in the blocks - symToStrat.init(mergedArms) + symToStrat.init(mainBlk) - processBlock(mergedArms) + processBlock(mainBlk) resolveConstraints tl.log("upper:") @@ -250,7 +143,7 @@ class Deforest(using TL, Raise, Elaborator.State): Program( p.imports, - rewrite(mergedArms) + rewrite(mainBlk) ) // these are never considered as free vars (because of their symbol type) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index ee10cd0943..6f3d37b025 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -32,12 +32,10 @@ test() //│ x = tmp; //│ if (x instanceof A1.class) { //│ return 1 +//│ } else if (x instanceof B1.class) { +//│ return 2 //│ } else { -//│ if (x instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test() @@ -48,16 +46,16 @@ test() //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ return 1 //│ }; //│ } else { -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ return 2 //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(globalThis)) //│ }; //│ block$res2 = test(); //│ undefined @@ -87,14 +85,12 @@ test() //│ param01 = x.x; //│ x2 = param01; //│ return x2 +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1 //│ } else { -//│ if (x instanceof BB1.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ return x1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test1() @@ -106,7 +102,7 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = A1; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; @@ -114,7 +110,7 @@ test() //│ }; //│ } else { //│ tmp2 = B1; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -122,7 +118,7 @@ test() //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(globalThis)) //│ }; //│ block$res4 = test1(); //│ undefined @@ -146,12 +142,10 @@ test() //│ f = function f(a) { //│ if (a instanceof A1.class) { //│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 //│ } else { -//│ if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test2 = function test() { @@ -167,14 +161,12 @@ test() //│ param01 = x.x; //│ x2 = param01; //│ return f(x2) +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return f(x1) //│ } else { -//│ if (x instanceof BB1.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ return f(x1) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test2() @@ -182,26 +174,26 @@ test() //│ ==== JS (deforested): ==== //│ let test2, f; //│ f = function f(a) { -//│ return runtime.safeCall(a()) +//│ return runtime.safeCall(a(globalThis)) //│ }; //│ test2 = function test() { //│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = () => { +//│ tmp1 = (globalThis) => { //│ return 1 //│ }; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; //│ return f(x1) //│ }; //│ } else { -//│ tmp2 = () => { +//│ tmp2 = (globalThis) => { //│ return 2 //│ }; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -209,7 +201,7 @@ test() //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(globalThis)) //│ }; //│ block$res6 = test2(); //│ undefined @@ -239,31 +231,23 @@ test() //│ f1 = function f1(a) { //│ if (a instanceof A1.class) { //│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else if (a instanceof C1.class) { +//│ return 3 //│ } else { -//│ if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ f2 = function f2(a) { //│ if (a instanceof A1.class) { //│ return 4 +//│ } else if (a instanceof B1.class) { +//│ return 5 +//│ } else if (a instanceof C1.class) { +//│ return 6 //│ } else { -//│ if (a instanceof B1.class) { -//│ return 5 -//│ } else { -//│ if (a instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test3 = function test() { @@ -277,12 +261,10 @@ test() //│ x = tmp; //│ if (x instanceof AA1.class) { //│ return f1(x.x) +//│ } else if (x instanceof BB1.class) { +//│ return f2(x.x) //│ } else { -//│ if (x instanceof BB1.class) { -//│ return f2(x.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ test3() @@ -290,31 +272,31 @@ test() //│ ==== JS (deforested): ==== //│ let test3, f1, f2; //│ f1 = function f1(a) { -//│ return runtime.safeCall(a()) +//│ return runtime.safeCall(a(globalThis)) //│ }; //│ f2 = function f2(a) { -//│ return runtime.safeCall(a()) +//│ return runtime.safeCall(a(globalThis)) //│ }; //│ test3 = function test() { //│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = () => { +//│ tmp1 = (globalThis) => { //│ return 1 //│ }; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ return f1(tmp1) //│ }; //│ } else { -//│ tmp2 = () => { +//│ tmp2 = (globalThis) => { //│ return 5 //│ }; -//│ tmp = () => { +//│ tmp = (globalThis) => { //│ return f2(tmp2) //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(globalThis)) //│ }; //│ block$res8 = test3(); //│ undefined @@ -350,14 +332,12 @@ test() //│ param01 = x.x; //│ x2 = param01; //│ return x2 +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return x1 //│ } else { -//│ if (x instanceof BB1.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ return x1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp = g(true); @@ -374,7 +354,7 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 11; -//│ return () => { +//│ return (globalThis) => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; @@ -382,7 +362,7 @@ test() //│ } //│ } else { //│ tmp2 = 22; -//│ return () => { +//│ return (globalThis) => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -391,7 +371,7 @@ test() //│ } //│ }; //│ c = function c(x) { -//│ return runtime.safeCall(x()) +//│ return runtime.safeCall(x(globalThis)) //│ }; //│ tmp = g(true); //│ return c(tmp) @@ -435,18 +415,16 @@ map(enumFromTo(1, 4)) //│ let param0, param1, h, t, tmp1, tmp2; //│ if (ls instanceof Nil1.class) { //│ return Nil1 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp1 = h + 4; +//│ tmp2 = map(t); +//│ return Cons1(tmp1, tmp2) //│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp1 = h + 4; -//│ tmp2 = map(t); -//│ return Cons1(tmp1, tmp2) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp = enumFromTo(1, 4); @@ -462,7 +440,7 @@ map(enumFromTo(1, 4)) //│ tmp2 = enumFromTo(tmp1, b); //│ tmp3 = a; //│ tmp4 = tmp2; -//│ return () => { +//│ return (globalThis) => { //│ let param0, param1, h, t, tmp5, tmp6; //│ param0 = tmp3; //│ param1 = tmp4; @@ -473,13 +451,13 @@ map(enumFromTo(1, 4)) //│ return Cons1(tmp5, tmp6) //│ } //│ } else { -//│ return () => { +//│ return (globalThis) => { //│ return Nil1 //│ } //│ } //│ }; //│ map = function map(ls) { -//│ return runtime.safeCall(ls()) +//│ return runtime.safeCall(ls(globalThis)) //│ }; //│ tmp = enumFromTo(1, 4); //│ block$res13 = map(tmp); @@ -514,17 +492,15 @@ sum(enumFromTo(1,10)) //│ let param0, param1, h, t, tmp3; //│ if (ls instanceof Nil1.class) { //│ return 0 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp3 = sum(t); +//│ return h + tmp3 //│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp3 = sum(t); -//│ return h + tmp3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp2 = enumFromTo1(1, 10); @@ -540,7 +516,7 @@ sum(enumFromTo(1,10)) //│ tmp4 = enumFromTo1(tmp3, b); //│ tmp5 = a; //│ tmp6 = tmp4; -//│ return () => { +//│ return (globalThis) => { //│ let param0, param1, h, t, tmp7; //│ param0 = tmp5; //│ param1 = tmp6; @@ -550,13 +526,13 @@ sum(enumFromTo(1,10)) //│ return h + tmp7 //│ } //│ } else { -//│ return () => { +//│ return (globalThis) => { //│ return 0 //│ } //│ } //│ }; //│ sum = function sum(ls) { -//│ return runtime.safeCall(ls()) +//│ return runtime.safeCall(ls(globalThis)) //│ }; //│ tmp2 = enumFromTo1(1, 10); //│ block$res15 = sum(tmp2); @@ -583,12 +559,10 @@ test() //│ x = B1; //│ if (x instanceof A1.class) { //│ tmp4 = 1; +//│ } else if (x instanceof B1.class) { +//│ tmp4 = 3; //│ } else { -//│ if (x instanceof B1.class) { -//│ tmp4 = 3; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ if (x instanceof B1.class) { //│ return 2 @@ -656,15 +630,15 @@ test() //│ let test6; //│ test6 = function test() { //│ let x, y; -//│ x = (y1) => { +//│ x = (globalThis, y1) => { //│ let tmp4; //│ tmp4 = 1; -//│ return runtime.safeCall(y1()) +//│ return runtime.safeCall(y1(globalThis)) //│ }; -//│ y = () => { +//│ y = (globalThis) => { //│ return 2 //│ }; -//│ return runtime.safeCall(x(y)) +//│ return runtime.safeCall(x(globalThis, y)) //│ }; //│ block$res19 = test6(); //│ undefined @@ -687,12 +661,10 @@ c(A) + c(B) //│ let x, tmp6, tmp7; //│ if (a instanceof A1.class) { //│ tmp6 = 1; +//│ } else if (a instanceof B1.class) { +//│ tmp6 = 2; //│ } else { -//│ if (a instanceof B1.class) { -//│ tmp6 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ x = tmp6; //│ tmp7 = Predef.print(x); @@ -711,14 +683,14 @@ c(A) + c(B) //│ return x //│ }; //│ c = function c(a) { -//│ return runtime.safeCall(a()) +//│ return runtime.safeCall(a(globalThis)) //│ }; -//│ tmp4 = c(() => { +//│ tmp4 = c((globalThis) => { //│ let tmp6; //│ tmp6 = 1; //│ return match_a_rest(tmp6) //│ }); -//│ tmp5 = c(() => { +//│ tmp5 = c((globalThis) => { //│ let tmp6; //│ tmp6 = 2; //│ return match_a_rest(tmp6) @@ -744,7 +716,7 @@ fun map(f, ls) = Cons(h, t) then Cons(f(h), map(f, t)) map(x => x + 4, enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo2, map1, tmp8; +//│ let enumFromTo2, map1, tmp8, lambda; //│ enumFromTo2 = function enumFromTo(a, b) { //│ let scrut, tmp9, tmp10; //│ scrut = a < b; @@ -760,27 +732,26 @@ map(x => x + 4, enumFromTo(1, 4)) //│ let param0, param1, h, t, tmp9, tmp10; //│ if (ls instanceof Nil1.class) { //│ return Nil1 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp9 = runtime.safeCall(f3(h)); +//│ tmp10 = map1(f3, t); +//│ return Cons1(tmp9, tmp10) //│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp9 = runtime.safeCall(f3(h)); -//│ tmp10 = map1(f3, t); -//│ return Cons1(tmp9, tmp10) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp8 = enumFromTo2(1, 4); -//│ map1((x) => { +//│ lambda = (undefined, function (x) { //│ return x + 4 -//│ }, tmp8) +//│ }); +//│ map1(lambda, tmp8) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo2, map1, tmp8; +//│ let enumFromTo2, map1, tmp8, lambda; //│ enumFromTo2 = function enumFromTo(a, b) { //│ let scrut, tmp9, tmp10, tmp11, tmp12; //│ scrut = a < b; @@ -789,7 +760,7 @@ map(x => x + 4, enumFromTo(1, 4)) //│ tmp10 = enumFromTo2(tmp9, b); //│ tmp11 = a; //│ tmp12 = tmp10; -//│ return (f3) => { +//│ return (globalThis, f3) => { //│ let param0, param1, h, t, tmp13, tmp14; //│ param0 = tmp11; //│ param1 = tmp12; @@ -800,25 +771,26 @@ map(x => x + 4, enumFromTo(1, 4)) //│ return Cons1(tmp13, tmp14) //│ } //│ } else { -//│ return (f3) => { +//│ return (globalThis, f3) => { //│ return Nil1 //│ } //│ } //│ }; //│ map1 = function map(f3, ls) { -//│ return runtime.safeCall(ls(f3)) +//│ return runtime.safeCall(ls(globalThis, f3)) //│ }; //│ tmp8 = enumFromTo2(1, 4); -//│ block$res23 = map1((x) => { +//│ lambda = (undefined, function (x) { //│ return x + 4 -//│ }, tmp8); +//│ }); +//│ block$res23 = map1(lambda, tmp8); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) - +:fixme :sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(f, ls) = @@ -828,7 +800,7 @@ fun map(f, ls) = )(f) map(x => x + 4, enumFromTo(1, 4)) //│ JS (unsanitized): -//│ let enumFromTo3, map2, tmp10; +//│ let enumFromTo3, map2, tmp10, lambda2; //│ enumFromTo3 = function enumFromTo(a, b) { //│ let scrut, tmp11, tmp12; //│ scrut = a < b; @@ -841,82 +813,37 @@ map(x => x + 4, enumFromTo(1, 4)) //│ } //│ }; //│ map2 = function map(f3, ls) { -//│ let param0, param1, h, t, tmp11; +//│ let param0, param1, h, t, tmp11, tmp12, lambda3, lambda4; //│ if (ls instanceof Nil1.class) { -//│ tmp11 = (f4) => { +//│ lambda3 = (undefined, function (f4) { //│ return Nil1 -//│ }; +//│ }); +//│ tmp11 = lambda3; +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ lambda4 = (undefined, function (f4) { +//│ let tmp13, tmp14; +//│ tmp13 = runtime.safeCall(f4(h)); +//│ tmp14 = map2(f4, t); +//│ return Cons1(tmp13, tmp14) +//│ }); +//│ tmp12 = lambda4; +//│ tmp11 = tmp12; //│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp11 = (f4) => { -//│ let tmp12, tmp13; -//│ tmp12 = runtime.safeCall(f4(h)); -//│ tmp13 = map2(f4, t); -//│ return Cons1(tmp12, tmp13) -//│ }; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ return runtime.safeCall(tmp11(f3)) //│ }; //│ tmp10 = enumFromTo3(1, 4); -//│ map2((x) => { +//│ lambda2 = (undefined, function (x) { //│ return x + 4 -//│ }, tmp10) +//│ }); +//│ map2(lambda2, tmp10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo3, map2, tmp10, match_ls_rest; -//│ match_ls_rest = function match_ls_rest(f3, tmp11) { -//│ return runtime.safeCall(tmp11(f3)) -//│ }; -//│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12, tmp13, tmp14; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp11 = a + 1; -//│ tmp12 = enumFromTo3(tmp11, b); -//│ tmp13 = a; -//│ tmp14 = tmp12; -//│ return (f3) => { -//│ let param0, param1, h, t, tmp15; -//│ param0 = tmp13; -//│ param1 = tmp14; -//│ h = param0; -//│ t = param1; -//│ tmp15 = (f4) => { -//│ let tmp16, tmp17; -//│ tmp16 = runtime.safeCall(f4(h)); -//│ tmp17 = map2(f4, t); -//│ return Cons1(tmp16, tmp17) -//│ }; -//│ return match_ls_rest(f3, tmp15) -//│ } -//│ } else { -//│ return (f3) => { -//│ let tmp15; -//│ tmp15 = (f4) => { -//│ return Nil1 -//│ }; -//│ return match_ls_rest(f3, tmp15) -//│ } -//│ } -//│ }; -//│ map2 = function map(f3, ls) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ tmp10 = enumFromTo3(1, 4); -//│ block$res25 = map2((x) => { -//│ return x + 4 -//│ }, tmp10); -//│ undefined -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) @@ -927,68 +854,66 @@ fun sum(ls, a) = if ls is Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp12; +//│ let enumFromTo4, sum1, tmp11; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14; +//│ let scrut, tmp12, tmp13; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ return Cons1(a, tmp14) +//│ tmp12 = a + 1; +//│ tmp13 = enumFromTo4(tmp12, b); +//│ return Cons1(a, tmp13) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp13; +//│ let param0, param1, h, t, tmp12; //│ if (ls instanceof Nil1.class) { //│ return a +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp12 = h + a; +//│ return sum1(t, tmp12) //│ } else { -//│ if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp13 = h + a; -//│ return sum1(t, tmp13) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ sum1(tmp12, 0) +//│ tmp11 = enumFromTo4(1, 10); +//│ sum1(tmp11, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp12; +//│ let enumFromTo4, sum1, tmp11; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14, tmp15, tmp16; +//│ let scrut, tmp12, tmp13, tmp14, tmp15; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ tmp15 = a; -//│ tmp16 = tmp14; -//│ return (a1) => { -//│ let param0, param1, h, t, tmp17; -//│ param0 = tmp15; -//│ param1 = tmp16; +//│ tmp12 = a + 1; +//│ tmp13 = enumFromTo4(tmp12, b); +//│ tmp14 = a; +//│ tmp15 = tmp13; +//│ return (globalThis, a1) => { +//│ let param0, param1, h, t, tmp16; +//│ param0 = tmp14; +//│ param1 = tmp15; //│ h = param0; //│ t = param1; -//│ tmp17 = h + a1; -//│ return sum1(t, tmp17) +//│ tmp16 = h + a1; +//│ return sum1(t, tmp16) //│ } //│ } else { -//│ return (a1) => { +//│ return (globalThis, a1) => { //│ return a1 //│ } //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ return runtime.safeCall(ls(a)) +//│ return runtime.safeCall(ls(globalThis, a)) //│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ block$res27 = sum1(tmp12, 0); +//│ tmp11 = enumFromTo4(1, 10); +//│ block$res26 = sum1(tmp11, 0); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1006,36 +931,36 @@ fun c(x) = n + t c(AA(3)) //│ JS (unsanitized): -//│ let c1, tmp14; +//│ let c1, tmp13; //│ c1 = function c(x) { -//│ let t, n, tmp15; +//│ let t, n, tmp14; //│ t = x.x; //│ if (x instanceof AA1.class) { -//│ tmp15 = 2; +//│ tmp14 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp15; +//│ n = tmp14; //│ return n + t //│ }; -//│ tmp14 = AA1(3); -//│ c1(tmp14) +//│ tmp13 = AA1(3); +//│ c1(tmp13) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp14; +//│ let c1, tmp13; //│ c1 = function c(x) { -//│ let t, n, tmp15; +//│ let t, n, tmp14; //│ t = x.x; //│ if (x instanceof AA1.class) { -//│ tmp15 = 2; +//│ tmp14 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp15; +//│ n = tmp14; //│ return n + t //│ }; -//│ tmp14 = AA1(3); -//│ block$res29 = c1(tmp14); +//│ tmp13 = AA1(3); +//│ block$res28 = c1(tmp13); //│ undefined //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1066,11 +991,11 @@ f(A, B) //│ ==== JS (deforested): ==== //│ let f3; //│ f3 = function f(a, b) { -//│ return runtime.safeCall(a(b)) +//│ return runtime.safeCall(a(globalThis, b)) //│ }; -//│ block$res31 = f3((b) => { -//│ return runtime.safeCall(b()) -//│ }, () => { +//│ block$res30 = f3((globalThis, b) => { +//│ return runtime.safeCall(b(globalThis)) +//│ }, (globalThis) => { //│ return 3 //│ }); //│ undefined @@ -1084,15 +1009,15 @@ fun f(x) = if x is Some then if x.x > 1 then f(Some(x.x - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f4, tmp16; +//│ let f4, tmp15; //│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; +//│ let scrut, tmp16, tmp17; //│ if (x instanceof Some1.class) { //│ scrut = x.x > 1; //│ if (scrut === true) { -//│ tmp17 = x.x - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) +//│ tmp16 = x.x - 1; +//│ tmp17 = Some1(tmp16); +//│ return f4(tmp17) //│ } else { //│ return 0 //│ } @@ -1100,19 +1025,19 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp16 = Some1(2); -//│ f4(tmp16) +//│ tmp15 = Some1(2); +//│ f4(tmp15) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f4, tmp16; +//│ let f4, tmp15; //│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; +//│ let scrut, tmp16, tmp17; //│ if (x instanceof Some1.class) { //│ scrut = x.x > 1; //│ if (scrut === true) { -//│ tmp17 = x.x - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) +//│ tmp16 = x.x - 1; +//│ tmp17 = Some1(tmp16); +//│ return f4(tmp17) //│ } else { //│ return 0 //│ } @@ -1120,8 +1045,8 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp16 = Some1(2); -//│ block$res33 = f4(tmp16); +//│ tmp15 = Some1(2); +//│ block$res32 = f4(tmp15); //│ undefined //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1137,11 +1062,11 @@ if x is if y is B then 2 //│ JS (unsanitized): -//│ let x, y, tmp18; +//│ let x, y, tmp17; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp18 = 1; +//│ tmp17 = 1; //│ } else { //│ throw new this.Error("match error"); //│ } @@ -1153,15 +1078,15 @@ if y is //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let x, y; -//│ x = (y1) => { -//│ let tmp18; -//│ tmp18 = 1; -//│ return runtime.safeCall(y1()) +//│ x = (globalThis, y1) => { +//│ let tmp17; +//│ tmp17 = 1; +//│ return runtime.safeCall(y1(globalThis)) //│ }; -//│ y = () => { +//│ y = (globalThis) => { //│ return 2 //│ }; -//│ block$res35 = runtime.safeCall(x(y)); +//│ block$res34 = runtime.safeCall(x(this, y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1183,11 +1108,11 @@ test() //│ JS (unsanitized): //│ let test7; //│ test7 = function test() { -//│ let x1, y1, tmp19; +//│ let x1, y1, tmp18; //│ x1 = A1; //│ y1 = B1; //│ if (x1 instanceof A1.class) { -//│ tmp19 = 1; +//│ tmp18 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1203,17 +1128,17 @@ test() //│ let test7; //│ test7 = function test() { //│ let x1, y1; -//│ x1 = (y2) => { -//│ let tmp19; -//│ tmp19 = 1; -//│ return runtime.safeCall(y2()) +//│ x1 = (globalThis, y2) => { +//│ let tmp18; +//│ tmp18 = 1; +//│ return runtime.safeCall(y2(globalThis)) //│ }; -//│ y1 = () => { +//│ y1 = (globalThis) => { //│ return 2 //│ }; -//│ return runtime.safeCall(x1(y1)) +//│ return runtime.safeCall(x1(globalThis, y1)) //│ }; -//│ block$res37 = test7(); +//│ block$res36 = test7(); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1229,100 +1154,98 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ JS (unsanitized): -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; +//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; //│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp28, tmp29, tmp30; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param11 = x1.y; //│ n1 = param01; //│ m1 = param11; -//│ tmp29 = y1 + n1; -//│ tmp30 = tmp29 - m1; +//│ tmp28 = y1 + n1; +//│ tmp29 = tmp28 - m1; +//│ } else if (x1 instanceof BBB1.class) { +//│ param0 = x1.x; +//│ param1 = x1.y; +//│ n = param0; +//│ m = param1; +//│ tmp30 = m + 1; +//│ tmp29 = tmp30 - n; //│ } else { -//│ if (x1 instanceof BBB1.class) { -//│ param0 = x1.x; -//│ param1 = x1.y; -//│ n = param0; -//│ m = param1; -//│ tmp31 = m + 1; -//│ tmp30 = tmp31 - n; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp30; +//│ a = tmp29; //│ return a + 3 //│ }; -//│ tmp19 = AAA1(1, 3); -//│ tmp20 = f5(tmp19, 1); -//│ tmp21 = BBB1(2, 3); -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; -//│ tmp24 = AAA1(3, 2); -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; -//│ tmp27 = BBB1(4, 6); -//│ tmp28 = f5(tmp27, 0); -//│ tmp26 + tmp28 +//│ tmp18 = AAA1(1, 3); +//│ tmp19 = f5(tmp18, 1); +//│ tmp20 = BBB1(2, 3); +//│ tmp21 = f5(tmp20, 2); +//│ tmp22 = tmp19 + tmp21; +//│ tmp23 = AAA1(3, 2); +//│ tmp24 = f5(tmp23, 4); +//│ tmp25 = tmp22 + tmp24; +//│ tmp26 = BBB1(4, 6); +//│ tmp27 = f5(tmp26, 0); +//│ tmp25 + tmp27 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; -//│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; +//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, match_x_rest, match_x_branch_AAA, tmp30, tmp31, match_x_branch_BBB, tmp32, tmp33, tmp34, tmp35; +//│ match_x_branch_AAA = function match_x_branch_AAA(globalThis, y1, field_x, field_y) { +//│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp37 = y1 + n; -//│ tmp38 = tmp37 - m; -//│ return match_x_rest(tmp38) +//│ tmp36 = y1 + n; +//│ tmp37 = tmp36 - m; +//│ return match_x_rest(tmp37) //│ }; -//│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; +//│ match_x_branch_BBB = function match_x_branch_BBB(globalThis, y1, field_x, field_y) { +//│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp38 = m + 1; -//│ tmp37 = tmp38 - n; -//│ return match_x_rest(tmp37) +//│ tmp37 = m + 1; +//│ tmp36 = tmp37 - n; +//│ return match_x_rest(tmp36) //│ }; -//│ match_x_rest = function match_x_rest(tmp37) { +//│ match_x_rest = function match_x_rest(tmp36) { //│ let a; -//│ a = tmp37; +//│ a = tmp36; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { -//│ return runtime.safeCall(x1(y1)) +//│ return runtime.safeCall(x1(globalThis, y1)) +//│ }; +//│ tmp28 = 1; +//│ tmp29 = 3; +//│ tmp18 = (globalThis, y1) => { +//│ return match_x_branch_AAA(globalThis, y1, tmp28, tmp29) //│ }; -//│ tmp29 = 1; -//│ tmp30 = 3; -//│ tmp19 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp29, tmp30) +//│ tmp19 = f5(tmp18, 1); +//│ tmp30 = 2; +//│ tmp31 = 3; +//│ tmp20 = (globalThis, y1) => { +//│ return match_x_branch_BBB(globalThis, y1, tmp30, tmp31) //│ }; -//│ tmp20 = f5(tmp19, 1); -//│ tmp31 = 2; +//│ tmp21 = f5(tmp20, 2); +//│ tmp22 = tmp19 + tmp21; //│ tmp32 = 3; -//│ tmp21 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp31, tmp32) -//│ }; -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; -//│ tmp33 = 3; -//│ tmp34 = 2; -//│ tmp24 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp33, tmp34) -//│ }; -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; -//│ tmp35 = 4; -//│ tmp36 = 6; -//│ tmp27 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp35, tmp36) -//│ }; -//│ tmp28 = f5(tmp27, 0); -//│ block$res39 = tmp26 + tmp28; +//│ tmp33 = 2; +//│ tmp23 = (globalThis, y1) => { +//│ return match_x_branch_AAA(globalThis, y1, tmp32, tmp33) +//│ }; +//│ tmp24 = f5(tmp23, 4); +//│ tmp25 = tmp22 + tmp24; +//│ tmp34 = 4; +//│ tmp35 = 6; +//│ tmp26 = (globalThis, y1) => { +//│ return match_x_branch_BBB(globalThis, y1, tmp34, tmp35) +//│ }; +//│ tmp27 = f5(tmp26, 0); +//│ block$res38 = tmp25 + tmp27; //│ undefined //│ = 21 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1339,7 +1262,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp46; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1361,23 +1284,23 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp47, tmp48; +//│ tmp47 = c11(a); +//│ tmp48 = c2(a); +//│ return tmp47 + tmp48 //│ }; -//│ tmp47 = AA1(1); -//│ p(tmp47) +//│ tmp46 = AA1(1); +//│ p(tmp46) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp46; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { -//│ scrut = (x2) => { +//│ scrut = (globalThis, x2) => { //│ return x2.x //│ }; -//│ return runtime.safeCall(scrut(x1)) +//│ return runtime.safeCall(scrut(globalThis, x1)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1390,13 +1313,13 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp47, tmp48; +//│ tmp47 = c11(a); +//│ tmp48 = c2(a); +//│ return tmp47 + tmp48 //│ }; -//│ tmp47 = AA1(1); -//│ block$res41 = p(tmp47); +//│ tmp46 = AA1(1); +//│ block$res40 = p(tmp46); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index e3ef9faa3c..a116f5ca3d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -42,46 +42,36 @@ module Test with //│ } else { //│ tmp = BB1(B1); //│ } -//│ this.#s = tmp; -//│ scrut1 = this.#s; +//│ Test.#s = tmp; +//│ scrut1 = Test.#s; //│ if (scrut1 instanceof AA1.class) { -//│ Test.f1(this.#s.x) +//│ Test.f1(Test.#s.x) +//│ } else if (scrut1 instanceof BB1.class) { +//│ Test.f2(Test.#s.x) //│ } else { -//│ if (scrut1 instanceof BB1.class) { -//│ Test.f2(this.#s.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ } //│ static f1(a) { //│ if (a instanceof A1.class) { //│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else if (a instanceof C1.class) { +//│ return 3 //│ } else { -//│ if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ } //│ static f2(a1) { //│ if (a1 instanceof A1.class) { //│ return 4 +//│ } else if (a1 instanceof B1.class) { +//│ return 5 +//│ } else if (a1 instanceof C1.class) { +//│ return 6 //│ } else { -//│ if (a1 instanceof B1.class) { -//│ return 5 -//│ } else { -//│ if (a1 instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ } //│ static toString() { return "Test"; } @@ -99,12 +89,12 @@ module Test with //│ } else { //│ tmp = BB1(B1); //│ } -//│ this.#s1 = tmp; -//│ scrut1 = this.#s1; +//│ Test.#s1 = tmp; +//│ scrut1 = Test.#s1; //│ if (scrut1 instanceof AA1.class) { -//│ Test.f1(this.#s1.x) +//│ Test.f1(Test.#s1.x) //│ } else if (scrut1 instanceof BB1.class) { -//│ Test.f2(this.#s1.x) +//│ Test.f2(Test.#s1.x) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -166,23 +156,6 @@ c(AA(2)) //│ tmp = AA1(2); //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, tmp, tmp1; -//│ c = function c(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ tmp1 = 2; -//│ tmp = () => { -//│ let scrut; -//│ scrut = (x) => { -//│ return tmp1 -//│ }; -//│ return runtime.safeCall(scrut(x_not_in_scope)) -//│ }; -//│ block$res6 = c(tmp); -//│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: x (class hkmc2.semantics.VarSymbol) From f27b72c0cac7e431d49a5f22e11ce23f7dd3162c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 5 Mar 2025 16:10:24 +0800 Subject: [PATCH 092/303] toplevel symbols are not freevars --- .../scala/hkmc2/codegen/Deforestation.scala | 2 + .../src/test/mlscript/deforest/simple.mls | 132 +++++++++--------- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 52a83b8d01..c1f155e256 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -157,6 +157,8 @@ class Deforest(using TL, Raise, Elaborator.State): def init(b: Block) = object Subst extends SymbolSubst: // only consider block member symbols as globally defined + override def mapTopLevelSym(s: TopLevelSymbol): TopLevelSymbol = + store += s; s override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = store += s; s override def mapBuiltInSym(s: BuiltinSymbol): BuiltinSymbol = diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 6f3d37b025..10414866dd 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -46,16 +46,16 @@ test() //│ let x, scrut, tmp; //│ scrut = true; //│ if (scrut === true) { -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ return 1 //│ }; //│ } else { -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ return 2 //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x(globalThis)) +//│ return runtime.safeCall(x()) //│ }; //│ block$res2 = test(); //│ undefined @@ -102,7 +102,7 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = A1; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; @@ -110,7 +110,7 @@ test() //│ }; //│ } else { //│ tmp2 = B1; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -118,7 +118,7 @@ test() //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x(globalThis)) +//│ return runtime.safeCall(x()) //│ }; //│ block$res4 = test1(); //│ undefined @@ -174,26 +174,26 @@ test() //│ ==== JS (deforested): ==== //│ let test2, f; //│ f = function f(a) { -//│ return runtime.safeCall(a(globalThis)) +//│ return runtime.safeCall(a()) //│ }; //│ test2 = function test() { //│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = (globalThis) => { +//│ tmp1 = () => { //│ return 1 //│ }; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; //│ return f(x1) //│ }; //│ } else { -//│ tmp2 = (globalThis) => { +//│ tmp2 = () => { //│ return 2 //│ }; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -201,7 +201,7 @@ test() //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x(globalThis)) +//│ return runtime.safeCall(x()) //│ }; //│ block$res6 = test2(); //│ undefined @@ -272,31 +272,31 @@ test() //│ ==== JS (deforested): ==== //│ let test3, f1, f2; //│ f1 = function f1(a) { -//│ return runtime.safeCall(a(globalThis)) +//│ return runtime.safeCall(a()) //│ }; //│ f2 = function f2(a) { -//│ return runtime.safeCall(a(globalThis)) +//│ return runtime.safeCall(a()) //│ }; //│ test3 = function test() { //│ let x, scrut, tmp, tmp1, tmp2; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = (globalThis) => { +//│ tmp1 = () => { //│ return 1 //│ }; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ return f1(tmp1) //│ }; //│ } else { -//│ tmp2 = (globalThis) => { +//│ tmp2 = () => { //│ return 5 //│ }; -//│ tmp = (globalThis) => { +//│ tmp = () => { //│ return f2(tmp2) //│ }; //│ } //│ x = tmp; -//│ return runtime.safeCall(x(globalThis)) +//│ return runtime.safeCall(x()) //│ }; //│ block$res8 = test3(); //│ undefined @@ -354,7 +354,7 @@ test() //│ scrut = true; //│ if (scrut === true) { //│ tmp1 = 11; -//│ return (globalThis) => { +//│ return () => { //│ let param0, x1; //│ param0 = tmp1; //│ x1 = param0; @@ -362,7 +362,7 @@ test() //│ } //│ } else { //│ tmp2 = 22; -//│ return (globalThis) => { +//│ return () => { //│ let param0, x1; //│ param0 = tmp2; //│ x1 = param0; @@ -371,7 +371,7 @@ test() //│ } //│ }; //│ c = function c(x) { -//│ return runtime.safeCall(x(globalThis)) +//│ return runtime.safeCall(x()) //│ }; //│ tmp = g(true); //│ return c(tmp) @@ -440,7 +440,7 @@ map(enumFromTo(1, 4)) //│ tmp2 = enumFromTo(tmp1, b); //│ tmp3 = a; //│ tmp4 = tmp2; -//│ return (globalThis) => { +//│ return () => { //│ let param0, param1, h, t, tmp5, tmp6; //│ param0 = tmp3; //│ param1 = tmp4; @@ -451,13 +451,13 @@ map(enumFromTo(1, 4)) //│ return Cons1(tmp5, tmp6) //│ } //│ } else { -//│ return (globalThis) => { +//│ return () => { //│ return Nil1 //│ } //│ } //│ }; //│ map = function map(ls) { -//│ return runtime.safeCall(ls(globalThis)) +//│ return runtime.safeCall(ls()) //│ }; //│ tmp = enumFromTo(1, 4); //│ block$res13 = map(tmp); @@ -516,7 +516,7 @@ sum(enumFromTo(1,10)) //│ tmp4 = enumFromTo1(tmp3, b); //│ tmp5 = a; //│ tmp6 = tmp4; -//│ return (globalThis) => { +//│ return () => { //│ let param0, param1, h, t, tmp7; //│ param0 = tmp5; //│ param1 = tmp6; @@ -526,13 +526,13 @@ sum(enumFromTo(1,10)) //│ return h + tmp7 //│ } //│ } else { -//│ return (globalThis) => { +//│ return () => { //│ return 0 //│ } //│ } //│ }; //│ sum = function sum(ls) { -//│ return runtime.safeCall(ls(globalThis)) +//│ return runtime.safeCall(ls()) //│ }; //│ tmp2 = enumFromTo1(1, 10); //│ block$res15 = sum(tmp2); @@ -630,15 +630,15 @@ test() //│ let test6; //│ test6 = function test() { //│ let x, y; -//│ x = (globalThis, y1) => { +//│ x = (y1) => { //│ let tmp4; //│ tmp4 = 1; -//│ return runtime.safeCall(y1(globalThis)) +//│ return runtime.safeCall(y1()) //│ }; -//│ y = (globalThis) => { +//│ y = () => { //│ return 2 //│ }; -//│ return runtime.safeCall(x(globalThis, y)) +//│ return runtime.safeCall(x(y)) //│ }; //│ block$res19 = test6(); //│ undefined @@ -683,14 +683,14 @@ c(A) + c(B) //│ return x //│ }; //│ c = function c(a) { -//│ return runtime.safeCall(a(globalThis)) +//│ return runtime.safeCall(a()) //│ }; -//│ tmp4 = c((globalThis) => { +//│ tmp4 = c(() => { //│ let tmp6; //│ tmp6 = 1; //│ return match_a_rest(tmp6) //│ }); -//│ tmp5 = c((globalThis) => { +//│ tmp5 = c(() => { //│ let tmp6; //│ tmp6 = 2; //│ return match_a_rest(tmp6) @@ -760,7 +760,7 @@ map(x => x + 4, enumFromTo(1, 4)) //│ tmp10 = enumFromTo2(tmp9, b); //│ tmp11 = a; //│ tmp12 = tmp10; -//│ return (globalThis, f3) => { +//│ return (f3) => { //│ let param0, param1, h, t, tmp13, tmp14; //│ param0 = tmp11; //│ param1 = tmp12; @@ -771,13 +771,13 @@ map(x => x + 4, enumFromTo(1, 4)) //│ return Cons1(tmp13, tmp14) //│ } //│ } else { -//│ return (globalThis, f3) => { +//│ return (f3) => { //│ return Nil1 //│ } //│ } //│ }; //│ map1 = function map(f3, ls) { -//│ return runtime.safeCall(ls(globalThis, f3)) +//│ return runtime.safeCall(ls(f3)) //│ }; //│ tmp8 = enumFromTo2(1, 4); //│ lambda = (undefined, function (x) { @@ -894,7 +894,7 @@ sum(enumFromTo(1, 10), 0) //│ tmp13 = enumFromTo4(tmp12, b); //│ tmp14 = a; //│ tmp15 = tmp13; -//│ return (globalThis, a1) => { +//│ return (a1) => { //│ let param0, param1, h, t, tmp16; //│ param0 = tmp14; //│ param1 = tmp15; @@ -904,13 +904,13 @@ sum(enumFromTo(1, 10), 0) //│ return sum1(t, tmp16) //│ } //│ } else { -//│ return (globalThis, a1) => { +//│ return (a1) => { //│ return a1 //│ } //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ return runtime.safeCall(ls(globalThis, a)) +//│ return runtime.safeCall(ls(a)) //│ }; //│ tmp11 = enumFromTo4(1, 10); //│ block$res26 = sum1(tmp11, 0); @@ -991,11 +991,11 @@ f(A, B) //│ ==== JS (deforested): ==== //│ let f3; //│ f3 = function f(a, b) { -//│ return runtime.safeCall(a(globalThis, b)) +//│ return runtime.safeCall(a(b)) //│ }; -//│ block$res30 = f3((globalThis, b) => { -//│ return runtime.safeCall(b(globalThis)) -//│ }, (globalThis) => { +//│ block$res30 = f3((b) => { +//│ return runtime.safeCall(b()) +//│ }, () => { //│ return 3 //│ }); //│ undefined @@ -1078,15 +1078,15 @@ if y is //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let x, y; -//│ x = (globalThis, y1) => { +//│ x = (y1) => { //│ let tmp17; //│ tmp17 = 1; -//│ return runtime.safeCall(y1(globalThis)) +//│ return runtime.safeCall(y1()) //│ }; -//│ y = (globalThis) => { +//│ y = () => { //│ return 2 //│ }; -//│ block$res34 = runtime.safeCall(x(this, y)); +//│ block$res34 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1128,15 +1128,15 @@ test() //│ let test7; //│ test7 = function test() { //│ let x1, y1; -//│ x1 = (globalThis, y2) => { +//│ x1 = (y2) => { //│ let tmp18; //│ tmp18 = 1; -//│ return runtime.safeCall(y2(globalThis)) +//│ return runtime.safeCall(y2()) //│ }; -//│ y1 = (globalThis) => { +//│ y1 = () => { //│ return 2 //│ }; -//│ return runtime.safeCall(x1(globalThis, y1)) +//│ return runtime.safeCall(x1(y1)) //│ }; //│ block$res36 = test7(); //│ undefined @@ -1191,7 +1191,7 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, match_x_rest, match_x_branch_AAA, tmp30, tmp31, match_x_branch_BBB, tmp32, tmp33, tmp34, tmp35; -//│ match_x_branch_AAA = function match_x_branch_AAA(globalThis, y1, field_x, field_y) { +//│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { //│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; @@ -1201,7 +1201,7 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ tmp37 = tmp36 - m; //│ return match_x_rest(tmp37) //│ }; -//│ match_x_branch_BBB = function match_x_branch_BBB(globalThis, y1, field_x, field_y) { +//│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { //│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; @@ -1217,32 +1217,32 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { -//│ return runtime.safeCall(x1(globalThis, y1)) +//│ return runtime.safeCall(x1(y1)) //│ }; //│ tmp28 = 1; //│ tmp29 = 3; -//│ tmp18 = (globalThis, y1) => { -//│ return match_x_branch_AAA(globalThis, y1, tmp28, tmp29) +//│ tmp18 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp28, tmp29) //│ }; //│ tmp19 = f5(tmp18, 1); //│ tmp30 = 2; //│ tmp31 = 3; -//│ tmp20 = (globalThis, y1) => { -//│ return match_x_branch_BBB(globalThis, y1, tmp30, tmp31) +//│ tmp20 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp30, tmp31) //│ }; //│ tmp21 = f5(tmp20, 2); //│ tmp22 = tmp19 + tmp21; //│ tmp32 = 3; //│ tmp33 = 2; -//│ tmp23 = (globalThis, y1) => { -//│ return match_x_branch_AAA(globalThis, y1, tmp32, tmp33) +//│ tmp23 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp32, tmp33) //│ }; //│ tmp24 = f5(tmp23, 4); //│ tmp25 = tmp22 + tmp24; //│ tmp34 = 4; //│ tmp35 = 6; -//│ tmp26 = (globalThis, y1) => { -//│ return match_x_branch_BBB(globalThis, y1, tmp34, tmp35) +//│ tmp26 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp34, tmp35) //│ }; //│ tmp27 = f5(tmp26, 0); //│ block$res38 = tmp25 + tmp27; @@ -1297,10 +1297,10 @@ p(AA(1)) //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { -//│ scrut = (globalThis, x2) => { +//│ scrut = (x2) => { //│ return x2.x //│ }; -//│ return runtime.safeCall(scrut(globalThis, x1)) +//│ return runtime.safeCall(scrut(x1)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } From f3833d60b2453c7d46368378c3c2bc44107ff064 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:57:34 +0800 Subject: [PATCH 093/303] fix error by updating free var for fun defns --- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../src/test/mlscript/deforest/simple.mls | 311 ++++++++++-------- 2 files changed, 181 insertions(+), 132 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 38b6eaf363..84ce520446 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -310,7 +310,7 @@ sealed abstract class Defn: preCtor :: ctor :: mtds.flatMap(_.subBlocks) lazy val freeVars: Set[Local] = this match - case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym + case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym -- body.definedVars case ValDefn(owner, k, sym, rhs) => rhs.freeVars case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 065f7a421a..8022371905 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -789,8 +789,6 @@ map(x => x + 4, enumFromTo(1, 4)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) - -:fixme :sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(f, ls) = @@ -843,7 +841,58 @@ map(x => x + 4, enumFromTo(1, 4)) //│ }); //│ map2(lambda2, tmp10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; +//│ match_ls_rest = function match_ls_rest(f3, tmp11) { +//│ return runtime.safeCall(tmp11(f3)) +//│ }; +//│ enumFromTo3 = function enumFromTo(a, b) { +//│ let scrut, tmp11, tmp12, tmp13, tmp14; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp11 = a + 1; +//│ tmp12 = enumFromTo3(tmp11, b); +//│ tmp13 = a; +//│ tmp14 = tmp12; +//│ return (f3) => { +//│ let param0, param1, h, t, tmp15, tmp16, lambda3; +//│ param0 = tmp13; +//│ param1 = tmp14; +//│ h = param0; +//│ t = param1; +//│ lambda3 = (undefined, function (f4) { +//│ let tmp17, tmp18; +//│ tmp17 = runtime.safeCall(f4(h)); +//│ tmp18 = map2(f4, t); +//│ return Cons1(tmp17, tmp18) +//│ }); +//│ tmp16 = lambda3; +//│ tmp15 = tmp16; +//│ return match_ls_rest(f3, tmp15) +//│ } +//│ } else { +//│ return (f3) => { +//│ let tmp15, lambda3; +//│ lambda3 = (undefined, function (f4) { +//│ return Nil1 +//│ }); +//│ tmp15 = lambda3; +//│ return match_ls_rest(f3, tmp15) +//│ } +//│ } +//│ }; +//│ map2 = function map(f3, ls) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ tmp10 = enumFromTo3(1, 4); +//│ lambda2 = (undefined, function (x) { +//│ return x + 4 +//│ }); +//│ block$res25 = map2(lambda2, tmp10); +//│ undefined +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -854,20 +903,20 @@ fun sum(ls, a) = if ls is Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp11; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp12, tmp13; +//│ let scrut, tmp13, tmp14; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp12 = a + 1; -//│ tmp13 = enumFromTo4(tmp12, b); -//│ return Cons1(a, tmp13) +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ return Cons1(a, tmp14) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp12; +//│ let param0, param1, h, t, tmp13; //│ if (ls instanceof Nil1.class) { //│ return a //│ } else if (ls instanceof Cons1.class) { @@ -875,33 +924,33 @@ sum(enumFromTo(1, 10), 0) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp12 = h + a; -//│ return sum1(t, tmp12) +//│ tmp13 = h + a; +//│ return sum1(t, tmp13) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp11 = enumFromTo4(1, 10); -//│ sum1(tmp11, 0) +//│ tmp12 = enumFromTo4(1, 10); +//│ sum1(tmp12, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp11; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp12, tmp13, tmp14, tmp15; +//│ let scrut, tmp13, tmp14, tmp15, tmp16; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp12 = a + 1; -//│ tmp13 = enumFromTo4(tmp12, b); -//│ tmp14 = a; -//│ tmp15 = tmp13; +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ tmp15 = a; +//│ tmp16 = tmp14; //│ return (a1) => { -//│ let param0, param1, h, t, tmp16; -//│ param0 = tmp14; -//│ param1 = tmp15; +//│ let param0, param1, h, t, tmp17; +//│ param0 = tmp15; +//│ param1 = tmp16; //│ h = param0; //│ t = param1; -//│ tmp16 = h + a1; -//│ return sum1(t, tmp16) +//│ tmp17 = h + a1; +//│ return sum1(t, tmp17) //│ } //│ } else { //│ return (a1) => { @@ -912,8 +961,8 @@ sum(enumFromTo(1, 10), 0) //│ sum1 = function sum(ls, a) { //│ return runtime.safeCall(ls(a)) //│ }; -//│ tmp11 = enumFromTo4(1, 10); -//│ block$res26 = sum1(tmp11, 0); +//│ tmp12 = enumFromTo4(1, 10); +//│ block$res27 = sum1(tmp12, 0); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -931,36 +980,36 @@ fun c(x) = n + t c(AA(3)) //│ JS (unsanitized): -//│ let c1, tmp13; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp14; +//│ let t, n, tmp15; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp14 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp14; +//│ n = tmp15; //│ return n + t //│ }; -//│ tmp13 = AA1(3); -//│ c1(tmp13) +//│ tmp14 = AA1(3); +//│ c1(tmp14) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp13; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp14; +//│ let t, n, tmp15; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp14 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp14; +//│ n = tmp15; //│ return n + t //│ }; -//│ tmp13 = AA1(3); -//│ block$res28 = c1(tmp13); +//│ tmp14 = AA1(3); +//│ block$res29 = c1(tmp14); //│ undefined //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -993,7 +1042,7 @@ f(A, B) //│ f3 = function f(a, b) { //│ return runtime.safeCall(a(b)) //│ }; -//│ block$res30 = f3((b) => { +//│ block$res31 = f3((b) => { //│ return runtime.safeCall(b()) //│ }, () => { //│ return 3 @@ -1009,15 +1058,15 @@ fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f4, tmp15; +//│ let f4, tmp16; //│ f4 = function f(x) { -//│ let scrut, tmp16, tmp17; +//│ let scrut, tmp17, tmp18; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp16 = x.value - 1; -//│ tmp17 = Some1(tmp16); -//│ return f4(tmp17) +//│ tmp17 = x.value - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) //│ } else { //│ return 0 //│ } @@ -1025,19 +1074,19 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp15 = Some1(2); -//│ f4(tmp15) +//│ tmp16 = Some1(2); +//│ f4(tmp16) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f4, tmp15; +//│ let f4, tmp16; //│ f4 = function f(x) { -//│ let scrut, tmp16, tmp17; +//│ let scrut, tmp17, tmp18; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp16 = x.value - 1; -//│ tmp17 = Some1(tmp16); -//│ return f4(tmp17) +//│ tmp17 = x.value - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) //│ } else { //│ return 0 //│ } @@ -1045,8 +1094,8 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp15 = Some1(2); -//│ block$res32 = f4(tmp15); +//│ tmp16 = Some1(2); +//│ block$res33 = f4(tmp16); //│ undefined //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1062,11 +1111,11 @@ if x is if y is B then 2 //│ JS (unsanitized): -//│ let x, y, tmp17; +//│ let x, y, tmp18; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp17 = 1; +//│ tmp18 = 1; //│ } else { //│ throw new this.Error("match error"); //│ } @@ -1079,14 +1128,14 @@ if y is //│ ==== JS (deforested): ==== //│ let x, y; //│ x = (y1) => { -//│ let tmp17; -//│ tmp17 = 1; +//│ let tmp18; +//│ tmp18 = 1; //│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ block$res34 = runtime.safeCall(x(y)); +//│ block$res35 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1108,11 +1157,11 @@ test() //│ JS (unsanitized): //│ let test7; //│ test7 = function test() { -//│ let x1, y1, tmp18; +//│ let x1, y1, tmp19; //│ x1 = A1; //│ y1 = B1; //│ if (x1 instanceof A1.class) { -//│ tmp18 = 1; +//│ tmp19 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1129,8 +1178,8 @@ test() //│ test7 = function test() { //│ let x1, y1; //│ x1 = (y2) => { -//│ let tmp18; -//│ tmp18 = 1; +//│ let tmp19; +//│ tmp19 = 1; //│ return runtime.safeCall(y2()) //│ }; //│ y1 = () => { @@ -1138,7 +1187,7 @@ test() //│ }; //│ return runtime.safeCall(x1(y1)) //│ }; -//│ block$res36 = test7(); +//│ block$res37 = test7(); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1154,98 +1203,98 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ JS (unsanitized): -//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; //│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp28, tmp29, tmp30; +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param11 = x1.y; //│ n1 = param01; //│ m1 = param11; -//│ tmp28 = y1 + n1; -//│ tmp29 = tmp28 - m1; +//│ tmp29 = y1 + n1; +//│ tmp30 = tmp29 - m1; //│ } else if (x1 instanceof BBB1.class) { //│ param0 = x1.x; //│ param1 = x1.y; //│ n = param0; //│ m = param1; -//│ tmp30 = m + 1; -//│ tmp29 = tmp30 - n; +//│ tmp31 = m + 1; +//│ tmp30 = tmp31 - n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp29; +//│ a = tmp30; //│ return a + 3 //│ }; -//│ tmp18 = AAA1(1, 3); -//│ tmp19 = f5(tmp18, 1); -//│ tmp20 = BBB1(2, 3); -//│ tmp21 = f5(tmp20, 2); -//│ tmp22 = tmp19 + tmp21; -//│ tmp23 = AAA1(3, 2); -//│ tmp24 = f5(tmp23, 4); -//│ tmp25 = tmp22 + tmp24; -//│ tmp26 = BBB1(4, 6); -//│ tmp27 = f5(tmp26, 0); -//│ tmp25 + tmp27 +//│ tmp19 = AAA1(1, 3); +//│ tmp20 = f5(tmp19, 1); +//│ tmp21 = BBB1(2, 3); +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp24 = AAA1(3, 2); +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp27 = BBB1(4, 6); +//│ tmp28 = f5(tmp27, 0); +//│ tmp26 + tmp28 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, match_x_rest, match_x_branch_AAA, tmp30, tmp31, match_x_branch_BBB, tmp32, tmp33, tmp34, tmp35; +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; //│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp36, tmp37; +//│ let param0, param1, n, m, tmp37, tmp38; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp36 = y1 + n; -//│ tmp37 = tmp36 - m; -//│ return match_x_rest(tmp37) +//│ tmp37 = y1 + n; +//│ tmp38 = tmp37 - m; +//│ return match_x_rest(tmp38) //│ }; //│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp36, tmp37; +//│ let param0, param1, n, m, tmp37, tmp38; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp37 = m + 1; -//│ tmp36 = tmp37 - n; -//│ return match_x_rest(tmp36) +//│ tmp38 = m + 1; +//│ tmp37 = tmp38 - n; +//│ return match_x_rest(tmp37) //│ }; -//│ match_x_rest = function match_x_rest(tmp36) { +//│ match_x_rest = function match_x_rest(tmp37) { //│ let a; -//│ a = tmp36; +//│ a = tmp37; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp28 = 1; -//│ tmp29 = 3; -//│ tmp18 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp28, tmp29) -//│ }; -//│ tmp19 = f5(tmp18, 1); -//│ tmp30 = 2; -//│ tmp31 = 3; -//│ tmp20 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp30, tmp31) +//│ tmp29 = 1; +//│ tmp30 = 3; +//│ tmp19 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp29, tmp30) //│ }; -//│ tmp21 = f5(tmp20, 2); -//│ tmp22 = tmp19 + tmp21; +//│ tmp20 = f5(tmp19, 1); +//│ tmp31 = 2; //│ tmp32 = 3; -//│ tmp33 = 2; -//│ tmp23 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp32, tmp33) -//│ }; -//│ tmp24 = f5(tmp23, 4); -//│ tmp25 = tmp22 + tmp24; -//│ tmp34 = 4; -//│ tmp35 = 6; -//│ tmp26 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp34, tmp35) -//│ }; -//│ tmp27 = f5(tmp26, 0); -//│ block$res38 = tmp25 + tmp27; +//│ tmp21 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp31, tmp32) +//│ }; +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp33 = 3; +//│ tmp34 = 2; +//│ tmp24 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp33, tmp34) +//│ }; +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp35 = 4; +//│ tmp36 = 6; +//│ tmp27 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp35, tmp36) +//│ }; +//│ tmp28 = f5(tmp27, 0); +//│ block$res39 = tmp26 + tmp28; //│ undefined //│ = 21 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1262,7 +1311,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp46; +//│ let p, c11, c2, tmp47; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1284,16 +1333,16 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp47, tmp48; -//│ tmp47 = c11(a); -//│ tmp48 = c2(a); -//│ return tmp47 + tmp48 +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 //│ }; -//│ tmp46 = AA1(1); -//│ p(tmp46) +//│ tmp47 = AA1(1); +//│ p(tmp47) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp46; +//│ let p, c11, c2, tmp47; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1313,13 +1362,13 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp47, tmp48; -//│ tmp47 = c11(a); -//│ tmp48 = c2(a); -//│ return tmp47 + tmp48 +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 //│ }; -//│ tmp46 = AA1(1); -//│ block$res40 = p(tmp46); +//│ tmp47 = AA1(1); +//│ block$res41 = p(tmp47); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From d7c7e42a28d453ee45e744c46f11bed4443eb744 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 5 Mar 2025 22:01:29 +0800 Subject: [PATCH 094/303] use blocktraverser --- .../scala/hkmc2/codegen/Deforestation.scala | 35 +++++++++---------- .../src/test/mlscript/deforest/todos.mls | 19 +++++++++- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c1f155e256..8925f4939e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -88,21 +88,20 @@ extension (b: Block) def sortedFvs(using alwaysDefined: Set[Symbol]) = (b.freeVars -- b.definedVars -- alwaysDefined).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) - def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = - object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): - override def applyPath(p: Path): Path = p match - case s@Select(_, nme) if ss(s.uid) => args(nme) - case _ => p - ReplaceSelectTransformer.applyBlock(b) + // def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = + // object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): + // override def applyPath(p: Path): Path = p match + // case s@Select(_, nme) if ss(s.uid) => args(nme) + // case _ => p + // ReplaceSelectTransformer.applyBlock(b) def hasExplicitRet: Boolean = - object HasExplicitRetTransformer extends BlockTransformerShallow(new SymbolSubst()): + object HasExplicitRetTransformer extends BlockTraverserShallow(new SymbolSubst()): var flag = false - override def applyBlock(b: Block): Block = b match - case Return(_, imp) => flag = !imp; b + override def applyBlock(b: Block): Unit = b match + case Return(_, imp) => flag = !imp case Define(defn, rest) => applyBlock(rest) case _ => super.applyBlock(b) - override def applyResult(r: Result): Result = r HasExplicitRetTransformer.applyBlock(b) HasExplicitRetTransformer.flag @@ -164,7 +163,7 @@ class Deforest(using TL, Raise, Elaborator.State): override def mapBuiltInSym(s: BuiltinSymbol): BuiltinSymbol = store += s; s - object FreshVarForAllVars extends BlockTransformer(Subst) + object FreshVarForAllVars extends BlockTraverser(Subst) FreshVarForAllVars.applyBlock(b) var constraints: Ls[ProdStrat -> ConsStrat] = Nil @@ -192,7 +191,7 @@ class Deforest(using TL, Raise, Elaborator.State): store += s -> freshVar(s.nme)._1; s override def mapModuleSym(s: ModuleSymbol): ModuleSymbol = store += s -> freshVar(s.nme)._1; s - object FreshVarForAllVars extends BlockTransformer(AllVarsSymbolSubst) + object FreshVarForAllVars extends BlockTraverser(AllVarsSymbolSubst) FreshVarForAllVars.applyBlock(p) // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols @@ -543,22 +542,22 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorSym = getClsSymOfUid(ctor) val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.get._2 - object GetCtorsTransformer extends BlockTransformer(new SymbolSubst()): + object GetCtorsTransformer extends BlockTraverser(new SymbolSubst()): val ctors = mutable.Set.empty[ResultId] - override def applyResult(r: Result): Result = + override def applyResult(r: Result): Unit = handleCtors( r.uid, (id, f, clsOrMod, args) => ctors += id args.foreach { case Arg(_, value) => applyResult(value) } ) match - case Some(_) => r + case Some(_) => () case None => r match case Call(_, args) => - args.foreach { case Arg(_, value) => applyResult(value) }; r + args.foreach { case Arg(_, value) => applyResult(value) } case Instantiate(cls, args) => - args.foreach(applyResult); r - case _ => r + args.foreach(applyResult) + case _ => () GetCtorsTransformer.applyBlock(arm) GetCtorsTransformer.ctors.toSet diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index a116f5ca3d..cdfbca1aa3 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -156,6 +156,23 @@ c(AA(2)) //│ tmp = AA1(2); //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: x (class hkmc2.semantics.VarSymbol) +//│ ==== JS (deforested): ==== +//│ let c, tmp, tmp1; +//│ c = function c(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ tmp1 = 2; +//│ tmp = () => { +//│ let scrut; +//│ scrut = (x) => { +//│ return tmp1 +//│ }; +//│ return runtime.safeCall(scrut(x_not_in_scope)) +//│ }; +//│ block$res6 = c(tmp); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 From da297ea2682974807d6e2d93acc20c8f8833ae84 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 10:58:09 +0800 Subject: [PATCH 095/303] update tests --- .../src/test/mlscript/deforest/imperative.mls | 66 ++++++++----------- .../src/test/mlscript/deforest/todos.mls | 19 +----- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index 7b7a9511e9..cba18b3705 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -29,12 +29,10 @@ fun foo(x) = //│ } //│ if (x1 instanceof A1.class) { //│ return 1 +//│ } else if (x1 instanceof B1.class) { +//│ return 2 //│ } else { -//│ if (x1 instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ scrut = true; @@ -48,10 +46,9 @@ fun foo(x) = //│ ==== JS (deforested): ==== //│ let foo, x, scrut, tmp; //│ foo = function foo(x1) { -//│ let tmp1, tmp2; +//│ let tmp1; //│ if (x1 instanceof A1.class) { -//│ tmp2 = 123; -//│ tmp1 = Predef.print(tmp2); +//│ tmp1 = Predef.print(123); //│ } else { //│ tmp1 = runtime.Unit; //│ } @@ -106,16 +103,14 @@ fun foo(x) = //│ ==== JS (deforested): ==== //│ let foo1, x1, scrut1, tmp2; //│ foo1 = function foo(x2) { -//│ let tmp3, tmp4, tmp5; +//│ let tmp3; //│ if (x2 instanceof A1.class) { -//│ tmp4 = 123; -//│ tmp3 = Predef.print(tmp4); +//│ tmp3 = Predef.print(123); //│ } else { //│ tmp3 = runtime.Unit; //│ } //│ if (x2 instanceof B1.class) { -//│ tmp5 = 456; -//│ return Predef.print(tmp5) +//│ return Predef.print(456) //│ } else { //│ return runtime.Unit //│ } @@ -162,52 +157,45 @@ foo(bar, 123) //│ bar = function bar(v) { //│ if (v instanceof A1.class) { //│ return 1 +//│ } else if (v instanceof B1.class) { +//│ return 2 //│ } else { -//│ if (v instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ foo2(bar, 123) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let bar, foo2, tmp4, tmp5; +//│ let bar, foo2, match_v_branch_A; +//│ match_v_branch_A = function match_v_branch_A() { +//│ return 1 +//│ }; //│ foo2 = function foo(k, x2) { -//│ let scrut2, scrut3, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; -//│ tmp8 = x2; -//│ tmp9 = 0; -//│ scrut2 = tmp8 === tmp9; +//│ let scrut2, scrut3, tmp4, tmp5; +//│ scrut2 = x2 === 0; //│ if (scrut2 === true) { -//│ tmp10 = () => { -//│ return 1 -//│ }; -//│ tmp6 = runtime.safeCall(k(tmp10)); +//│ tmp4 = runtime.safeCall(k(() => { +//│ return match_v_branch_A() +//│ })); //│ } else { -//│ tmp6 = runtime.Unit; +//│ tmp4 = runtime.Unit; //│ } -//│ tmp11 = x2; -//│ tmp12 = 0; -//│ scrut3 = tmp11 > tmp12; +//│ scrut3 = x2 > 0; //│ if (scrut3 === true) { -//│ tmp7 = () => { -//│ return 1 +//│ tmp5 = () => { +//│ return match_v_branch_A() //│ }; //│ } else { -//│ tmp7 = () => { +//│ tmp5 = () => { //│ return 2 //│ }; //│ } -//│ tmp13 = tmp7; -//│ return runtime.safeCall(k(tmp13)) +//│ return runtime.safeCall(k(tmp5)) //│ }; //│ bar = function bar(v) { //│ return runtime.safeCall(v()) //│ }; -//│ tmp4 = bar; -//│ tmp5 = 123; -//│ block$res6 = foo2(tmp4, tmp5); +//│ block$res6 = foo2(bar, 123); //│ undefined //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index cdfbca1aa3..a116f5ca3d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -156,23 +156,6 @@ c(AA(2)) //│ tmp = AA1(2); //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, tmp, tmp1; -//│ c = function c(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ tmp1 = 2; -//│ tmp = () => { -//│ let scrut; -//│ scrut = (x) => { -//│ return tmp1 -//│ }; -//│ return runtime.safeCall(scrut(x_not_in_scope)) -//│ }; -//│ block$res6 = c(tmp); -//│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: x (class hkmc2.semantics.VarSymbol) From 8e3eebda8f212b4888ca4ea5bb9b66b104c7796a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:30:24 +0800 Subject: [PATCH 096/303] wip: restore my changes on function `Block.freeVars` --- .../src/main/scala/hkmc2/codegen/Block.scala | 6 +- .../src/test/mlscript/deforest/simple.mls | 320 ++++++------- .../src/test/mlscript/lifter/ClassInFun.mls | 379 ++++----------- .../test/mlscript/lifter/StackSafetyLift.mls | 447 ++++-------------- 4 files changed, 339 insertions(+), 813 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 84ce520446..3783e47e9e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -310,7 +310,7 @@ sealed abstract class Defn: preCtor :: ctor :: mtds.flatMap(_.subBlocks) lazy val freeVars: Set[Local] = this match - case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym -- body.definedVars + case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor) => @@ -381,7 +381,7 @@ enum Case: lazy val freeVars: Set[Local] = this match case Lit(_) => Set.empty - case Cls(_, path) => Set.empty //path.freeVars + case Cls(_, path) => path.freeVars case Tup(_, _) => Set.empty lazy val freeVarsLLIR: Set[Local] = this match @@ -417,7 +417,7 @@ sealed abstract class Result: case Value.Ref(l) => Set(l) case Value.This(sym) => Set.empty case Value.Lit(lit) => Set.empty - case Value.Lam(params, body) => body.freeVars -- body.definedVars -- params.paramSyms + case Value.Lam(params, body) => body.freeVars -- params.paramSyms case Value.Arr(elems) => elems.flatMap(_.value.freeVars).toSet case DynSelect(qual, fld, arrayIdx) => qual.freeVars ++ fld.freeVars diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 8022371905..392be07387 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -841,58 +841,18 @@ map(x => x + 4, enumFromTo(1, 4)) //│ }); //│ map2(lambda2, tmp10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; -//│ match_ls_rest = function match_ls_rest(f3, tmp11) { -//│ return runtime.safeCall(tmp11(f3)) -//│ }; -//│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12, tmp13, tmp14; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp11 = a + 1; -//│ tmp12 = enumFromTo3(tmp11, b); -//│ tmp13 = a; -//│ tmp14 = tmp12; -//│ return (f3) => { -//│ let param0, param1, h, t, tmp15, tmp16, lambda3; -//│ param0 = tmp13; -//│ param1 = tmp14; -//│ h = param0; -//│ t = param1; -//│ lambda3 = (undefined, function (f4) { -//│ let tmp17, tmp18; -//│ tmp17 = runtime.safeCall(f4(h)); -//│ tmp18 = map2(f4, t); -//│ return Cons1(tmp17, tmp18) -//│ }); -//│ tmp16 = lambda3; -//│ tmp15 = tmp16; -//│ return match_ls_rest(f3, tmp15) -//│ } -//│ } else { -//│ return (f3) => { -//│ let tmp15, lambda3; -//│ lambda3 = (undefined, function (f4) { -//│ return Nil1 -//│ }); -//│ tmp15 = lambda3; -//│ return match_ls_rest(f3, tmp15) -//│ } -//│ } -//│ }; -//│ map2 = function map(f3, ls) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ tmp10 = enumFromTo3(1, 4); -//│ lambda2 = (undefined, function (x) { -//│ return x + 4 -//│ }); -//│ block$res25 = map2(lambda2, tmp10); -//│ undefined -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:60) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:71) +//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:100) +//│ at: scala.Option.getOrElse(Option.scala:201) +//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:101) +//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) +//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) +//│ at: hkmc2.codegen.js.JSBuilder.argument(JSBuilder.scala:80) +//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$2(JSBuilder.scala:122) +//│ at: scala.collection.immutable.List.map(List.scala:251) @@ -903,20 +863,20 @@ fun sum(ls, a) = if ls is Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp12; +//│ let enumFromTo4, sum1, tmp11; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14; +//│ let scrut, tmp12, tmp13; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ return Cons1(a, tmp14) +//│ tmp12 = a + 1; +//│ tmp13 = enumFromTo4(tmp12, b); +//│ return Cons1(a, tmp13) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp13; +//│ let param0, param1, h, t, tmp12; //│ if (ls instanceof Nil1.class) { //│ return a //│ } else if (ls instanceof Cons1.class) { @@ -924,33 +884,33 @@ sum(enumFromTo(1, 10), 0) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp13 = h + a; -//│ return sum1(t, tmp13) +//│ tmp12 = h + a; +//│ return sum1(t, tmp12) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ sum1(tmp12, 0) +//│ tmp11 = enumFromTo4(1, 10); +//│ sum1(tmp11, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp12; +//│ let enumFromTo4, sum1, tmp11; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14, tmp15, tmp16; +//│ let scrut, tmp12, tmp13, tmp14, tmp15; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ tmp15 = a; -//│ tmp16 = tmp14; +//│ tmp12 = a + 1; +//│ tmp13 = enumFromTo4(tmp12, b); +//│ tmp14 = a; +//│ tmp15 = tmp13; //│ return (a1) => { -//│ let param0, param1, h, t, tmp17; -//│ param0 = tmp15; -//│ param1 = tmp16; +//│ let param0, param1, h, t, tmp16; +//│ param0 = tmp14; +//│ param1 = tmp15; //│ h = param0; //│ t = param1; -//│ tmp17 = h + a1; -//│ return sum1(t, tmp17) +//│ tmp16 = h + a1; +//│ return sum1(t, tmp16) //│ } //│ } else { //│ return (a1) => { @@ -961,8 +921,8 @@ sum(enumFromTo(1, 10), 0) //│ sum1 = function sum(ls, a) { //│ return runtime.safeCall(ls(a)) //│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ block$res27 = sum1(tmp12, 0); +//│ tmp11 = enumFromTo4(1, 10); +//│ block$res26 = sum1(tmp11, 0); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -980,36 +940,36 @@ fun c(x) = n + t c(AA(3)) //│ JS (unsanitized): -//│ let c1, tmp14; +//│ let c1, tmp13; //│ c1 = function c(x) { -//│ let t, n, tmp15; +//│ let t, n, tmp14; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp15 = 2; +//│ tmp14 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp15; +//│ n = tmp14; //│ return n + t //│ }; -//│ tmp14 = AA1(3); -//│ c1(tmp14) +//│ tmp13 = AA1(3); +//│ c1(tmp13) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp14; +//│ let c1, tmp13; //│ c1 = function c(x) { -//│ let t, n, tmp15; +//│ let t, n, tmp14; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp15 = 2; +//│ tmp14 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp15; +//│ n = tmp14; //│ return n + t //│ }; -//│ tmp14 = AA1(3); -//│ block$res29 = c1(tmp14); +//│ tmp13 = AA1(3); +//│ block$res28 = c1(tmp13); //│ undefined //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1042,7 +1002,7 @@ f(A, B) //│ f3 = function f(a, b) { //│ return runtime.safeCall(a(b)) //│ }; -//│ block$res31 = f3((b) => { +//│ block$res30 = f3((b) => { //│ return runtime.safeCall(b()) //│ }, () => { //│ return 3 @@ -1058,15 +1018,15 @@ fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f4, tmp16; +//│ let f4, tmp15; //│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; +//│ let scrut, tmp16, tmp17; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp17 = x.value - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) +//│ tmp16 = x.value - 1; +//│ tmp17 = Some1(tmp16); +//│ return f4(tmp17) //│ } else { //│ return 0 //│ } @@ -1074,19 +1034,19 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp16 = Some1(2); -//│ f4(tmp16) +//│ tmp15 = Some1(2); +//│ f4(tmp15) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f4, tmp16; +//│ let f4, tmp15; //│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; +//│ let scrut, tmp16, tmp17; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp17 = x.value - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) +//│ tmp16 = x.value - 1; +//│ tmp17 = Some1(tmp16); +//│ return f4(tmp17) //│ } else { //│ return 0 //│ } @@ -1094,8 +1054,8 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp16 = Some1(2); -//│ block$res33 = f4(tmp16); +//│ tmp15 = Some1(2); +//│ block$res32 = f4(tmp15); //│ undefined //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1111,11 +1071,11 @@ if x is if y is B then 2 //│ JS (unsanitized): -//│ let x, y, tmp18; +//│ let x, y, tmp17; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp18 = 1; +//│ tmp17 = 1; //│ } else { //│ throw new this.Error("match error"); //│ } @@ -1128,14 +1088,14 @@ if y is //│ ==== JS (deforested): ==== //│ let x, y; //│ x = (y1) => { -//│ let tmp18; -//│ tmp18 = 1; +//│ let tmp17; +//│ tmp17 = 1; //│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ block$res35 = runtime.safeCall(x(y)); +//│ block$res34 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1157,11 +1117,11 @@ test() //│ JS (unsanitized): //│ let test7; //│ test7 = function test() { -//│ let x1, y1, tmp19; +//│ let x1, y1, tmp18; //│ x1 = A1; //│ y1 = B1; //│ if (x1 instanceof A1.class) { -//│ tmp19 = 1; +//│ tmp18 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1178,8 +1138,8 @@ test() //│ test7 = function test() { //│ let x1, y1; //│ x1 = (y2) => { -//│ let tmp19; -//│ tmp19 = 1; +//│ let tmp18; +//│ tmp18 = 1; //│ return runtime.safeCall(y2()) //│ }; //│ y1 = () => { @@ -1187,7 +1147,7 @@ test() //│ }; //│ return runtime.safeCall(x1(y1)) //│ }; -//│ block$res37 = test7(); +//│ block$res36 = test7(); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1203,98 +1163,98 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ JS (unsanitized): -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; +//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; //│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp28, tmp29, tmp30; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param11 = x1.y; //│ n1 = param01; //│ m1 = param11; -//│ tmp29 = y1 + n1; -//│ tmp30 = tmp29 - m1; +//│ tmp28 = y1 + n1; +//│ tmp29 = tmp28 - m1; //│ } else if (x1 instanceof BBB1.class) { //│ param0 = x1.x; //│ param1 = x1.y; //│ n = param0; //│ m = param1; -//│ tmp31 = m + 1; -//│ tmp30 = tmp31 - n; +//│ tmp30 = m + 1; +//│ tmp29 = tmp30 - n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp30; +//│ a = tmp29; //│ return a + 3 //│ }; -//│ tmp19 = AAA1(1, 3); -//│ tmp20 = f5(tmp19, 1); -//│ tmp21 = BBB1(2, 3); -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; -//│ tmp24 = AAA1(3, 2); -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; -//│ tmp27 = BBB1(4, 6); -//│ tmp28 = f5(tmp27, 0); -//│ tmp26 + tmp28 +//│ tmp18 = AAA1(1, 3); +//│ tmp19 = f5(tmp18, 1); +//│ tmp20 = BBB1(2, 3); +//│ tmp21 = f5(tmp20, 2); +//│ tmp22 = tmp19 + tmp21; +//│ tmp23 = AAA1(3, 2); +//│ tmp24 = f5(tmp23, 4); +//│ tmp25 = tmp22 + tmp24; +//│ tmp26 = BBB1(4, 6); +//│ tmp27 = f5(tmp26, 0); +//│ tmp25 + tmp27 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; +//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, match_x_rest, match_x_branch_AAA, tmp30, tmp31, match_x_branch_BBB, tmp32, tmp33, tmp34, tmp35; //│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; +//│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp37 = y1 + n; -//│ tmp38 = tmp37 - m; -//│ return match_x_rest(tmp38) +//│ tmp36 = y1 + n; +//│ tmp37 = tmp36 - m; +//│ return match_x_rest(tmp37) //│ }; //│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; +//│ let param0, param1, n, m, tmp36, tmp37; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp38 = m + 1; -//│ tmp37 = tmp38 - n; -//│ return match_x_rest(tmp37) +//│ tmp37 = m + 1; +//│ tmp36 = tmp37 - n; +//│ return match_x_rest(tmp36) //│ }; -//│ match_x_rest = function match_x_rest(tmp37) { +//│ match_x_rest = function match_x_rest(tmp36) { //│ let a; -//│ a = tmp37; +//│ a = tmp36; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp29 = 1; -//│ tmp30 = 3; -//│ tmp19 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp29, tmp30) +//│ tmp28 = 1; +//│ tmp29 = 3; +//│ tmp18 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp28, tmp29) +//│ }; +//│ tmp19 = f5(tmp18, 1); +//│ tmp30 = 2; +//│ tmp31 = 3; +//│ tmp20 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp30, tmp31) //│ }; -//│ tmp20 = f5(tmp19, 1); -//│ tmp31 = 2; +//│ tmp21 = f5(tmp20, 2); +//│ tmp22 = tmp19 + tmp21; //│ tmp32 = 3; -//│ tmp21 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp31, tmp32) -//│ }; -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; -//│ tmp33 = 3; -//│ tmp34 = 2; -//│ tmp24 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp33, tmp34) -//│ }; -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; -//│ tmp35 = 4; -//│ tmp36 = 6; -//│ tmp27 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp35, tmp36) -//│ }; -//│ tmp28 = f5(tmp27, 0); -//│ block$res39 = tmp26 + tmp28; +//│ tmp33 = 2; +//│ tmp23 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp32, tmp33) +//│ }; +//│ tmp24 = f5(tmp23, 4); +//│ tmp25 = tmp22 + tmp24; +//│ tmp34 = 4; +//│ tmp35 = 6; +//│ tmp26 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp34, tmp35) +//│ }; +//│ tmp27 = f5(tmp26, 0); +//│ block$res38 = tmp25 + tmp27; //│ undefined //│ = 21 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1311,7 +1271,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp46; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1333,16 +1293,16 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp47, tmp48; +//│ tmp47 = c11(a); +//│ tmp48 = c2(a); +//│ return tmp47 + tmp48 //│ }; -//│ tmp47 = AA1(1); -//│ p(tmp47) +//│ tmp46 = AA1(1); +//│ p(tmp46) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp46; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1362,13 +1322,13 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp47, tmp48; +//│ tmp47 = c11(a); +//│ tmp48 = c2(a); +//│ return tmp47 + tmp48 //│ }; -//│ tmp47 = AA1(1); -//│ block$res41 = p(tmp47); +//│ tmp46 = AA1(1); +//│ block$res40 = p(tmp46); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 7e6e7dffe0..8c07179ff8 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -12,210 +12,18 @@ fun f() = h.perform() 1 f() + f() + f() -//│ JS (unsanitized): -//│ let f, Effect1, tmp, handleBlock$, Cont$func$f$ClassInFun$_mls_L7_95_118$1, Cont$handleBlock$h$1, Handler$h$1, lambda, f$, Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor, Cont$func$f$ClassInFun$_mls_L7_95_118$$, Cont$handleBlock$h$$ctor, Cont$handleBlock$h$$, lambda$; -//│ Effect1 = class Effect { -//│ constructor() {} -//│ toString() { return "Effect"; } -//│ }; -//│ lambda$ = function lambda$(Handler$h$$instance, k) { -//│ return runtime.safeCall(k()) -//│ }; -//│ lambda = (undefined, function (Handler$h$$instance) { -//│ return (k) => { -//│ return lambda$(Handler$h$$instance, k) -//│ } -//│ }); -//│ Handler$h$1 = class Handler$h$ extends Effect1 { -//│ constructor() { -//│ let tmp1; -//│ tmp1 = super(); -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = runtime.safeCall(lambda(this)); -//│ return runtime.mkEffect(this, lambda$this) -//│ } -//│ toString() { return "Handler$h$"; } -//│ }; -//│ Cont$handleBlock$h$$ = function Cont$handleBlock$h$$(h$0, tmp$1, tmp$2, tmp$3, tmp$4, pc) { -//│ let tmp1; -//│ tmp1 = new Cont$handleBlock$h$1.class(pc); -//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) -//│ }; -//│ Cont$handleBlock$h$$ctor = function Cont$handleBlock$h$$ctor(h$0, tmp$1, tmp$2, tmp$3, tmp$4) { -//│ return (pc) => { -//│ let tmp1; -//│ tmp1 = new Cont$handleBlock$h$1.class(pc); -//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) -//│ } -//│ }; -//│ Cont$handleBlock$h$1 = function Cont$handleBlock$h$(pc1) { -//│ return (h$01, tmp$11, tmp$21, tmp$31, tmp$41) => { -//│ return new Cont$handleBlock$h$.class(pc1)(h$01, tmp$11, tmp$21, tmp$31, tmp$41); -//│ } -//│ }; -//│ Cont$handleBlock$h$1.class = class Cont$handleBlock$h$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (h$0, tmp$1, tmp$2, tmp$3, tmp$4) => { -//│ let tmp1; -//│ tmp1 = super(null); -//│ this.pc = pc; -//│ this.h$0 = h$0; -//│ this.tmp$1 = tmp$1; -//│ this.tmp$2 = tmp$2; -//│ this.tmp$3 = tmp$3; -//│ this.tmp$4 = tmp$4; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 2) { -//│ this.tmp$1 = value$; -//│ } else if (this.pc === 3) { -//│ this.tmp$2 = value$; -//│ } else if (this.pc === 4) { -//│ this.tmp$4 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 7) { -//│ this.tmp$1 = f$(this.h$0); -//│ if (this.tmp$1 instanceof runtime.EffectSig.class) { -//│ this.pc = 2; -//│ this.tmp$1.contTrace.last.next = this; -//│ this.tmp$1.contTrace.last = this; -//│ return this.tmp$1 -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ this.tmp$2 = f$(this.h$0); -//│ if (this.tmp$2 instanceof runtime.EffectSig.class) { -//│ this.pc = 3; -//│ this.tmp$2.contTrace.last.next = this; -//│ this.tmp$2.contTrace.last = this; -//│ return this.tmp$2 -//│ } -//│ this.pc = 3; -//│ continue contLoop; -//│ } else if (this.pc === 3) { -//│ this.tmp$3 = this.tmp$1 + this.tmp$2; -//│ this.pc = 5; -//│ continue contLoop; -//│ } else if (this.pc === 5) { -//│ this.tmp$4 = f$(this.h$0); -//│ if (this.tmp$4 instanceof runtime.EffectSig.class) { -//│ this.pc = 4; -//│ this.tmp$4.contTrace.last.next = this; -//│ this.tmp$4.contTrace.last = this; -//│ return this.tmp$4 -//│ } -//│ this.pc = 4; -//│ continue contLoop; -//│ } else if (this.pc === 4) { -//│ return this.tmp$3 + this.tmp$4 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$handleBlock$h$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ = function Cont$func$f$ClassInFun$_mls_L7_95_118$$(h$0, tmp$1, pc) { -//│ let tmp1; -//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); -//│ return tmp1(h$0, tmp$1) -//│ }; -//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor = function Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor(h$0, tmp$1) { -//│ return (pc) => { -//│ let tmp1; -//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); -//│ return tmp1(h$0, tmp$1) -//│ } -//│ }; -//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1 = function Cont$func$f$ClassInFun$_mls_L7_95_118$(pc1) { -//│ return (h$01, tmp$11) => { -//│ return new Cont$func$f$ClassInFun$_mls_L7_95_118$.class(pc1)(h$01, tmp$11); -//│ } -//│ }; -//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1.class = class Cont$func$f$ClassInFun$_mls_L7_95_118$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (h$0, tmp$1) => { -//│ let tmp1; -//│ tmp1 = super(null); -//│ this.pc = pc; -//│ this.h$0 = h$0; -//│ this.tmp$1 = tmp$1; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ this.tmp$1 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 1) { -//│ this.tmp$1 = runtime.safeCall(this.h$0.perform()); -//│ if (this.tmp$1 instanceof runtime.EffectSig.class) { -//│ this.pc = 0; -//│ this.tmp$1.contTrace.last.next = this; -//│ this.tmp$1.contTrace.last = this; -//│ return this.tmp$1 -//│ } -//│ this.pc = 0; -//│ continue contLoop; -//│ } else if (this.pc === 0) { -//│ return 1 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$func$f$ClassInFun$_mls_L7_95_118$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ f$ = function f$(h) { -//│ let tmp1; -//│ tmp1 = runtime.safeCall(h.perform()); -//│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = Cont$func$f$ClassInFun$_mls_L7_95_118$$(h, tmp1, 0); -//│ tmp1.contTrace.last = tmp1.contTrace.last.next; -//│ return tmp1 -//│ } -//│ return 1 -//│ }; -//│ f = function f(h) { -//│ return () => { -//│ return f$(h) -//│ } -//│ }; -//│ handleBlock$ = function handleBlock$() { -//│ let h, tmp1, tmp2, tmp3, tmp4; -//│ h = new Handler$h$1(); -//│ tmp1 = f$(h); -//│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 2); -//│ return runtime.handleBlockImpl(tmp1, h) -//│ } -//│ tmp2 = f$(h); -//│ if (tmp2 instanceof runtime.EffectSig.class) { -//│ tmp2.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 3); -//│ return runtime.handleBlockImpl(tmp2, h) -//│ } -//│ tmp3 = tmp1 + tmp2; -//│ tmp4 = f$(h); -//│ if (tmp4 instanceof runtime.EffectSig.class) { -//│ tmp4.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 4); -//│ return runtime.handleBlockImpl(tmp4, h) -//│ } -//│ return tmp3 + tmp4 -//│ }; -//│ tmp = handleBlock$(); -//│ if (tmp instanceof runtime.EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ tmp -//│ = 3 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader 'app') +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :expect 1 :sjs @@ -225,17 +33,17 @@ fun f(x) = Test() f(1).get() //│ JS (unsanitized): -//│ let Test1, f1, tmp1, Test$ctor, Test$; +//│ let Test1, f, tmp, Test$ctor, Test$; //│ Test$ = function Test$(x$0) { -//│ let tmp2; -//│ tmp2 = new Test1.class(); -//│ return tmp2(x$0) +//│ let tmp1; +//│ tmp1 = new Test1.class(); +//│ return tmp1(x$0) //│ }; //│ Test$ctor = function Test$ctor(x$0) { //│ return () => { -//│ let tmp2; -//│ tmp2 = new Test1.class(); -//│ return tmp2(x$0) +//│ let tmp1; +//│ tmp1 = new Test1.class(); +//│ return tmp1(x$0) //│ } //│ }; //│ Test1 = function Test() { @@ -255,11 +63,11 @@ f(1).get() //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; -//│ f1 = function f(x) { +//│ f = function f(x) { //│ return Test$(x) //│ }; -//│ tmp1 = f1(1); -//│ runtime.safeCall(tmp1.get()) +//│ tmp = f(1); +//│ runtime.safeCall(tmp.get()) //│ = 1 :expect 1 @@ -275,7 +83,7 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let h, Test3, g, f2, tmp2, Test$ctor1, Test$1, g$, h$; +//│ let h, Test3, g, f1, tmp1, Test$ctor1, Test$1, g$, h$; //│ h$ = function h$(used3) { //│ return used3 //│ }; @@ -285,10 +93,10 @@ f(1, 2).get() //│ } //│ }; //│ g$ = function g$(used1, g_arg) { -//│ let used3, tmp3; +//│ let used3, tmp2; //│ used3 = 2; -//│ tmp3 = h$(used3); -//│ return used1 + tmp3 +//│ tmp2 = h$(used3); +//│ return used1 + tmp2 //│ }; //│ g = function g(used1) { //│ return (g_arg) => { @@ -296,15 +104,15 @@ f(1, 2).get() //│ } //│ }; //│ Test$1 = function Test$(used1$0, a) { -//│ let tmp3; -//│ tmp3 = new Test3.class(a); -//│ return tmp3(used1$0) +//│ let tmp2; +//│ tmp2 = new Test3.class(a); +//│ return tmp2(used1$0) //│ }; //│ Test$ctor1 = function Test$ctor(used1$0) { //│ return (a) => { -//│ let tmp3; -//│ tmp3 = new Test3.class(a); -//│ return tmp3(used1$0) +//│ let tmp2; +//│ tmp2 = new Test3.class(a); +//│ return tmp2(used1$0) //│ } //│ }; //│ Test3 = function Test(a1) { @@ -325,13 +133,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; -//│ f2 = function f(used1, unused1) { +//│ f1 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$1(used1, unused1) //│ }; -//│ tmp2 = f2(1, 2); -//│ runtime.safeCall(tmp2.get()) +//│ tmp1 = f1(1, 2); +//│ runtime.safeCall(tmp1.get()) //│ = 1 :expect 1 @@ -360,7 +168,7 @@ fun f(used1, unused1) = new Test f(1, 2).get() //│ JS (unsanitized): -//│ let h2, Test7, g2, f4, tmp4, Test$ctor3, Test$3, g$2, h$2; +//│ let h2, Test7, g2, f3, tmp3, Test$ctor3, Test$3, g$2, h$2; //│ h$2 = function h$(used3) { //│ return used3 //│ }; @@ -370,10 +178,10 @@ f(1, 2).get() //│ } //│ }; //│ g$2 = function g$(used1, g_arg) { -//│ let used3, tmp5; +//│ let used3, tmp4; //│ used3 = 2; -//│ tmp5 = h$2(used3); -//│ return used1 + tmp5 +//│ tmp4 = h$2(used3); +//│ return used1 + tmp4 //│ }; //│ g2 = function g(used1) { //│ return (g_arg) => { @@ -381,15 +189,15 @@ f(1, 2).get() //│ } //│ }; //│ Test$3 = function Test$(used1$0) { -//│ let tmp5; -//│ tmp5 = new Test7.class(); -//│ return tmp5(used1$0) +//│ let tmp4; +//│ tmp4 = new Test7.class(); +//│ return tmp4(used1$0) //│ }; //│ Test$ctor3 = function Test$ctor(used1$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Test7.class(); -//│ return tmp5(used1$0) +//│ let tmp4; +//│ tmp4 = new Test7.class(); +//│ return tmp4(used1$0) //│ } //│ }; //│ Test7 = function Test(used1$01) { @@ -407,13 +215,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test"; } //│ }; -//│ f4 = function f(used1, unused1) { +//│ f3 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$3(used1) //│ }; -//│ tmp4 = f4(1, 2); -//│ runtime.safeCall(tmp4.get()) +//│ tmp3 = f3(1, 2); +//│ runtime.safeCall(tmp3.get()) //│ = 1 :sjs @@ -425,17 +233,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f5, A$ctor, A$, f$capture1; +//│ let A1, f4, A$ctor, A$, f$capture1; //│ A$ = function A$(f$capture$0) { -//│ let tmp5; -//│ tmp5 = new A1.class(); -//│ return tmp5(f$capture$0) +//│ let tmp4; +//│ tmp4 = new A1.class(); +//│ return tmp4(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new A1.class(); -//│ return tmp5(f$capture$0) +//│ let tmp4; +//│ tmp4 = new A1.class(); +//│ return tmp4(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -465,14 +273,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f5 = function f(x) { -//│ let tmp5, tmp6, capture; +//│ f4 = function f(x) { +//│ let tmp4, tmp5, capture; //│ capture = new f$capture1(x); -//│ tmp5 = A$(capture); -//│ tmp6 = runtime.safeCall(tmp5.f()); +//│ tmp4 = A$(capture); +//│ tmp5 = runtime.safeCall(tmp4.f()); //│ return capture.x0$ //│ }; -//│ f5(1) +//│ f4(1) //│ = 2 // only w should be in a capture @@ -494,17 +302,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; +//│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; //│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { -//│ let tmp6; -//│ tmp6 = new Good1.class(); -//│ return tmp6(x$1, y$2, z$3, f$capture$0) +//│ let tmp5; +//│ tmp5 = new Good1.class(); +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new Good1.class(); -//│ return tmp6(x$1, y$2, z$3, f$capture$0) +//│ let tmp5; +//│ tmp5 = new Good1.class(); +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -523,24 +331,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp6, tmp7; +//│ let tmp5, tmp6; //│ this.z$3 = 100; -//│ tmp6 = this.x$1 + this.y$2; -//│ tmp7 = tmp6 + this.z$3; -//│ return tmp7 + this.f$capture$0.w0$ +//│ tmp5 = this.x$1 + this.y$2; +//│ tmp6 = tmp5 + this.z$3; +//│ return tmp6 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp6; -//│ tmp6 = new Bad1.class(); -//│ return tmp6(f$capture$0) +//│ let tmp5; +//│ tmp5 = new Bad1.class(); +//│ return tmp5(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new Bad1.class(); -//│ return tmp6(f$capture$0) +//│ let tmp5; +//│ tmp5 = new Bad1.class(); +//│ return tmp5(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -570,19 +378,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } //│ }; -//│ f6 = function f() { -//│ let x, y, z, tmp6, tmp7, capture; +//│ f5 = function f() { +//│ let x, y, z, tmp5, tmp6, capture; //│ capture = new f$capture3(null); //│ x = 1; //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp6 = Bad$(capture); -//│ tmp7 = runtime.safeCall(tmp6.foo()); +//│ tmp5 = Bad$(capture); +//│ tmp6 = runtime.safeCall(tmp5.foo()); //│ return Good$(x, y, z, capture) //│ }; -//│ tmp5 = f6(); -//│ runtime.safeCall(tmp5.foo()) +//│ tmp4 = f5(); +//│ runtime.safeCall(tmp4.foo()) //│ = 10111 :fixme @@ -623,7 +431,18 @@ fun sum(n) = else n + sum(n - 1) sum(100) -//│ = 5050 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader 'app') +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) // instance checks @@ -650,7 +469,7 @@ fun f(x) = f(2).get() //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ JS (unsanitized): -//│ let h3, f9, tmp12, h$3; +//│ let h3, f8, tmp11, h$3; //│ h$3 = function h$(Test$instance, x) { //│ return x //│ }; @@ -659,7 +478,7 @@ f(2).get() //│ return h$3(Test$instance, x) //│ } //│ }; -//│ f9 = function f(x) { +//│ f8 = function f(x) { //│ let Test10, foo1; //│ Test10 = function Test() { //│ return new Test.class(); @@ -674,8 +493,8 @@ f(2).get() //│ foo1 = Test10; //│ return runtime.safeCall(foo1()) //│ }; -//│ tmp12 = f9(2); -//│ runtime.safeCall(tmp12.get()) +//│ tmp11 = f8(2); +//│ runtime.safeCall(tmp11.get()) //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 85dbe28d85..bb975ded2f 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -20,165 +20,18 @@ fun hi(n) = if n == 0 then 0 else hi(n - 1) hi(0) -//│ JS (unsanitized): -//│ let hi, res, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1, handleBlock$, Cont$handleBlock$stackHandler$1, StackDelay$1, lambda, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$, Cont$handleBlock$stackHandler$$ctor, Cont$handleBlock$stackHandler$$, lambda$; -//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n$0, scrut$1, tmp$2, stackDelayRes$3, pc) { -//│ let tmp; -//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); -//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) -//│ }; -//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor(n$0, scrut$1, tmp$2, stackDelayRes$3) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); -//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) -//│ } -//│ }; -//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1 = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(pc1) { -//│ return (n$01, scrut$11, tmp$21, stackDelayRes$31) => { -//│ return new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$.class(pc1)(n$01, scrut$11, tmp$21, stackDelayRes$31); -//│ } -//│ }; -//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class = class Cont$func$hi$StackSafetyLift$_mls_L19_4_47$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (n$0, scrut$1, tmp$2, stackDelayRes$3) => { -//│ let tmp; -//│ tmp = super(null); -//│ this.pc = pc; -//│ this.n$0 = n$0; -//│ this.scrut$1 = scrut$1; -//│ this.tmp$2 = tmp$2; -//│ this.stackDelayRes$3 = stackDelayRes$3; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ this.stackDelayRes$3 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ this.scrut$1 = this.n$0 == 0; -//│ if (this.scrut$1 === true) { -//│ return 0 -//│ } else { -//│ this.tmp$2 = this.n$0 - 1; -//│ this.pc = 2; -//│ continue contLoop; -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ break contLoop; -//│ } else if (this.pc === 2) { -//│ runtime.stackDepth = runtime.stackDepth + 1; -//│ return hi(this.tmp$2) -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ hi = function hi(n) { -//│ let scrut, tmp, stackDelayRes; -//│ stackDelayRes = runtime.checkDepth(); -//│ if (stackDelayRes instanceof runtime.EffectSig.class) { -//│ stackDelayRes.contTrace.last.next = Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n, scrut, tmp, stackDelayRes, 0); -//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; -//│ return stackDelayRes -//│ } -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0 -//│ } else { -//│ tmp = n - 1; -//│ runtime.stackDepth = runtime.stackDepth + 1; -//│ return hi(tmp) -//│ } -//│ }; -//│ lambda$ = function lambda$(StackDelay$$instance, resume) { -//│ runtime.stackOffset = runtime.stackDepth; -//│ return resume() -//│ }; -//│ lambda = (undefined, function (StackDelay$$instance) { -//│ return (resume) => { -//│ return lambda$(StackDelay$$instance, resume) -//│ } -//│ }); -//│ StackDelay$1 = class StackDelay$ extends runtime.StackDelay { -//│ constructor() { -//│ let tmp; -//│ tmp = super(); -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = runtime.safeCall(lambda(this)); -//│ return runtime.mkEffect(this, lambda$this) -//│ } -//│ toString() { return "StackDelay$"; } -//│ }; -//│ Cont$handleBlock$stackHandler$$ = function Cont$handleBlock$stackHandler$$(res$0, pc) { -//│ let tmp; -//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); -//│ return tmp(res$0) -//│ }; -//│ Cont$handleBlock$stackHandler$$ctor = function Cont$handleBlock$stackHandler$$ctor(res$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); -//│ return tmp(res$0) -//│ } -//│ }; -//│ Cont$handleBlock$stackHandler$1 = function Cont$handleBlock$stackHandler$(pc1) { -//│ return (res$01) => { -//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); -//│ } -//│ }; -//│ Cont$handleBlock$stackHandler$1.class = class Cont$handleBlock$stackHandler$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (res$0) => { -//│ let tmp; -//│ tmp = super(null); -//│ this.pc = pc; -//│ this.res$0 = res$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 3) { -//│ this.res$0 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 3) { -//│ return this.res$0 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$ = function handleBlock$() { -//│ let stackHandler, res1; -//│ stackHandler = new StackDelay$1(); -//│ runtime.stackLimit = 5; -//│ runtime.stackOffset = 0; -//│ runtime.stackDepth = 1; -//│ runtime.stackHandler = stackHandler; -//│ res1 = hi(0); -//│ if (res1 instanceof runtime.EffectSig.class) { -//│ res1.contTrace.last.next = Cont$handleBlock$stackHandler$$(res1, 3); -//│ return runtime.handleBlockImpl(res1, stackHandler) -//│ } -//│ return res1 -//│ }; -//│ res = handleBlock$(); -//│ if (res instanceof runtime.EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ runtime.stackDepth = 0; -//│ runtime.stackHandler = null; -//│ res -//│ = 0 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :sjs :stackSafe 1000 @@ -189,188 +42,18 @@ fun sum(n) = else n + sum(n - 1) sum(10000) -//│ JS (unsanitized): -//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; -//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { -//│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class(pc); -//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) -//│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class(pc); -//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) -//│ } -//│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$(pc1) { -//│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { -//│ return new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); -//│ } -//│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L187_4_57$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { -//│ let tmp; -//│ tmp = super(null); -//│ this.pc = pc; -//│ this.n$0 = n$0; -//│ this.scrut$1 = scrut$1; -//│ this.tmp$2 = tmp$2; -//│ this.tmp$3 = tmp$3; -//│ this.curDepth$4 = curDepth$4; -//│ this.stackDelayRes$5 = stackDelayRes$5; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ this.stackDelayRes$5 = value$; -//│ } else if (this.pc === 1) { -//│ this.tmp$3 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ this.scrut$1 = this.n$0 == 0; -//│ if (this.scrut$1 === true) { -//│ return 0 -//│ } else { -//│ this.tmp$2 = this.n$0 - 1; -//│ this.pc = 3; -//│ continue contLoop; -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ break contLoop; -//│ } else if (this.pc === 3) { -//│ runtime.stackDepth = runtime.stackDepth + 1; -//│ this.tmp$3 = sum1(this.tmp$2); -//│ if (this.tmp$3 instanceof runtime.EffectSig.class) { -//│ this.pc = 1; -//│ this.tmp$3.contTrace.last.next = this; -//│ this.tmp$3.contTrace.last = this; -//│ return this.tmp$3 -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ this.tmp$3 = runtime.resetDepth(this.tmp$3, this.curDepth$4); -//│ return this.n$0 + this.tmp$3 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L187_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, curDepth, stackDelayRes; -//│ curDepth = runtime.stackDepth; -//│ stackDelayRes = runtime.checkDepth(); -//│ if (stackDelayRes instanceof runtime.EffectSig.class) { -//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); -//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; -//│ return stackDelayRes -//│ } -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0 -//│ } else { -//│ tmp = n - 1; -//│ runtime.stackDepth = runtime.stackDepth + 1; -//│ tmp1 = sum1(tmp); -//│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); -//│ tmp1.contTrace.last = tmp1.contTrace.last.next; -//│ return tmp1 -//│ } -//│ tmp1 = runtime.resetDepth(tmp1, curDepth); -//│ return n + tmp1 -//│ } -//│ }; -//│ lambda$1 = function lambda$(StackDelay$$instance, resume) { -//│ runtime.stackOffset = runtime.stackDepth; -//│ return resume() -//│ }; -//│ lambda1 = (undefined, function (StackDelay$$instance) { -//│ return (resume) => { -//│ return lambda$1(StackDelay$$instance, resume) -//│ } -//│ }); -//│ StackDelay$3 = class StackDelay$2 extends runtime.StackDelay { -//│ constructor() { -//│ let tmp; -//│ tmp = super(); -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = runtime.safeCall(lambda1(this)); -//│ return runtime.mkEffect(this, lambda$this) -//│ } -//│ toString() { return "StackDelay$"; } -//│ }; -//│ Cont$handleBlock$stackHandler$$1 = function Cont$handleBlock$stackHandler$$(res$0, pc) { -//│ let tmp; -//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); -//│ return tmp(res$0) -//│ }; -//│ Cont$handleBlock$stackHandler$$ctor1 = function Cont$handleBlock$stackHandler$$ctor(res$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); -//│ return tmp(res$0) -//│ } -//│ }; -//│ Cont$handleBlock$stackHandler$3 = function Cont$handleBlock$stackHandler$(pc1) { -//│ return (res$01) => { -//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); -//│ } -//│ }; -//│ Cont$handleBlock$stackHandler$3.class = class Cont$handleBlock$stackHandler$2 extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ return (res$0) => { -//│ let tmp; -//│ tmp = super(null); -//│ this.pc = pc; -//│ this.res$0 = res$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 4) { -//│ this.res$0 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ return this.res$0 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$1 = function handleBlock$() { -//│ let stackHandler, res2; -//│ stackHandler = new StackDelay$3(); -//│ runtime.stackLimit = 1000; -//│ runtime.stackOffset = 0; -//│ runtime.stackDepth = 1; -//│ runtime.stackHandler = stackHandler; -//│ res2 = sum1(10000); -//│ if (res2 instanceof runtime.EffectSig.class) { -//│ res2.contTrace.last.next = Cont$handleBlock$stackHandler$$1(res2, 4); -//│ return runtime.handleBlockImpl(res2, stackHandler) -//│ } -//│ return res2 -//│ }; -//│ res1 = handleBlock$1(); -//│ if (res1 instanceof runtime.EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ runtime.stackDepth = 0; -//│ runtime.stackHandler = null; -//│ res1 -//│ = 50005000 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) // stack-overflows without :stackSafe :re @@ -391,8 +74,18 @@ fun foo(f) = set ctr += 1 dummy(f(f)) foo(foo) -//│ = 0 -//│ ctr = 10001 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :stackSafe 1000 :effectHandlers @@ -403,8 +96,18 @@ val foo = else n + f(n-1) f(10000) foo -//│ = 50005000 -//│ foo = 50005000 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :re fun foo() = @@ -432,7 +135,18 @@ handle h = Eff with else n + f(n-1) resume(f(10000)) foo(h) -//│ = 50005000 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) // function call and defn inside handler :effectHandlers @@ -448,7 +162,18 @@ handle h = Eff with in fun foo(h) = h.perform foo(h) -//│ = 50005000 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :re :effectHandlers @@ -461,7 +186,18 @@ handle h = Eff with else n + f(n-1) resume(f(10000)) foo(h) -//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) :effectHandlers :stackSafe @@ -486,7 +222,7 @@ fun hi(n) = n hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ JS (unsanitized): -//│ let hi1; hi1 = function hi(n) { return n }; hi1(0) +//│ let hi; hi = function hi(n) { return n }; hi(0) //│ = 0 :stackSafe 42 @@ -504,5 +240,16 @@ fun sum(n) = n + sum(n - 1) fun bad() = sum(10000) + sum(10000) bad() -//│ = 100010000 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) From aaac95e9b53ecf91776afffed5b91cfbd583a400 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:52:01 +0800 Subject: [PATCH 097/303] use my own free var function --- .../scala/hkmc2/codegen/Deforestation.scala | 52 ++- .../src/test/mlscript/deforest/simple.mls | 320 ++++++++++-------- 2 files changed, 231 insertions(+), 141 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8925f4939e..ac2a9afeba 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -86,7 +86,57 @@ extension (b: Block) override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs(using alwaysDefined: Set[Symbol]) = (b.freeVars -- b.definedVars -- alwaysDefined).filterNot(v => v.asClsLike.isDefined).toList.sortBy(_.uid) + def sortedFvs(using alwaysDefined: Set[Symbol]) = + object DeforestationFreeVarTraverser extends BlockTraverser(new SymbolSubst()): + val ctx = mutable.Set.from(alwaysDefined) + val result = mutable.Set.empty[Symbol] + + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + applyPath(scrut) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + val realArm = Begin(a, rest).flattened + applyBlock(realArm) + + case Assign(lhs, rhs, rest) => + applyResult(rhs) + ctx += lhs + applyBlock(rest) + ctx -= lhs + case Begin(sub, rest) => applyBlock(b.flattened) + case Define(defn, rest) => defn match + case FunDefn(owner, sym, params, body) => + val paramSymbols = params.flatMap: + case ParamList(flags, params, restParam) => (params ++ restParam).map: + case Param(flags, sym, sign) => sym + ctx += sym + ctx ++= paramSymbols + applyBlock(body) + ctx --= paramSymbols + applyBlock(rest) + ctx -= sym + case ValDefn(owner, k, sym, rhs) => + ctx += sym + applyPath(rhs) + applyBlock(rest) + ctx -= sym + case c: ClsLikeDefn => result ++= c.freeVars // TODO: just use the `freeVars` lazy val for ClsLikeDefns for now... + + case _ => super.applyBlock(b) + + override def applyValue(v: Value): Unit = v match + case Value.Ref(l) => if !ctx.contains(l) then result += l + case _ => super.applyValue(v) + + override def applyLam(l: Value.Lam): Unit = + val paramSymbols = l.params.params.map(p => p.sym) + ctx ++= paramSymbols + applyBlock(l.body) + ctx --= paramSymbols + DeforestationFreeVarTraverser.applyBlock(b) + DeforestationFreeVarTraverser.result.toList.sortBy(_.uid) // def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = // object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 392be07387..8022371905 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -841,18 +841,58 @@ map(x => x + 4, enumFromTo(1, 4)) //│ }); //│ map2(lambda2, tmp10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:60) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:71) -//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:100) -//│ at: scala.Option.getOrElse(Option.scala:201) -//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:101) -//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) -//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) -//│ at: hkmc2.codegen.js.JSBuilder.argument(JSBuilder.scala:80) -//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$2(JSBuilder.scala:122) -//│ at: scala.collection.immutable.List.map(List.scala:251) +//│ ==== JS (deforested): ==== +//│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; +//│ match_ls_rest = function match_ls_rest(f3, tmp11) { +//│ return runtime.safeCall(tmp11(f3)) +//│ }; +//│ enumFromTo3 = function enumFromTo(a, b) { +//│ let scrut, tmp11, tmp12, tmp13, tmp14; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ tmp11 = a + 1; +//│ tmp12 = enumFromTo3(tmp11, b); +//│ tmp13 = a; +//│ tmp14 = tmp12; +//│ return (f3) => { +//│ let param0, param1, h, t, tmp15, tmp16, lambda3; +//│ param0 = tmp13; +//│ param1 = tmp14; +//│ h = param0; +//│ t = param1; +//│ lambda3 = (undefined, function (f4) { +//│ let tmp17, tmp18; +//│ tmp17 = runtime.safeCall(f4(h)); +//│ tmp18 = map2(f4, t); +//│ return Cons1(tmp17, tmp18) +//│ }); +//│ tmp16 = lambda3; +//│ tmp15 = tmp16; +//│ return match_ls_rest(f3, tmp15) +//│ } +//│ } else { +//│ return (f3) => { +//│ let tmp15, lambda3; +//│ lambda3 = (undefined, function (f4) { +//│ return Nil1 +//│ }); +//│ tmp15 = lambda3; +//│ return match_ls_rest(f3, tmp15) +//│ } +//│ } +//│ }; +//│ map2 = function map(f3, ls) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ tmp10 = enumFromTo3(1, 4); +//│ lambda2 = (undefined, function (x) { +//│ return x + 4 +//│ }); +//│ block$res25 = map2(lambda2, tmp10); +//│ undefined +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -863,20 +903,20 @@ fun sum(ls, a) = if ls is Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp11; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp12, tmp13; +//│ let scrut, tmp13, tmp14; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp12 = a + 1; -//│ tmp13 = enumFromTo4(tmp12, b); -//│ return Cons1(a, tmp13) +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ return Cons1(a, tmp14) //│ } else { //│ return Nil1 //│ } //│ }; //│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp12; +//│ let param0, param1, h, t, tmp13; //│ if (ls instanceof Nil1.class) { //│ return a //│ } else if (ls instanceof Cons1.class) { @@ -884,33 +924,33 @@ sum(enumFromTo(1, 10), 0) //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp12 = h + a; -//│ return sum1(t, tmp12) +//│ tmp13 = h + a; +//│ return sum1(t, tmp13) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp11 = enumFromTo4(1, 10); -//│ sum1(tmp11, 0) +//│ tmp12 = enumFromTo4(1, 10); +//│ sum1(tmp12, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp11; +//│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp12, tmp13, tmp14, tmp15; +//│ let scrut, tmp13, tmp14, tmp15, tmp16; //│ scrut = a < b; //│ if (scrut === true) { -//│ tmp12 = a + 1; -//│ tmp13 = enumFromTo4(tmp12, b); -//│ tmp14 = a; -//│ tmp15 = tmp13; +//│ tmp13 = a + 1; +//│ tmp14 = enumFromTo4(tmp13, b); +//│ tmp15 = a; +//│ tmp16 = tmp14; //│ return (a1) => { -//│ let param0, param1, h, t, tmp16; -//│ param0 = tmp14; -//│ param1 = tmp15; +//│ let param0, param1, h, t, tmp17; +//│ param0 = tmp15; +//│ param1 = tmp16; //│ h = param0; //│ t = param1; -//│ tmp16 = h + a1; -//│ return sum1(t, tmp16) +//│ tmp17 = h + a1; +//│ return sum1(t, tmp17) //│ } //│ } else { //│ return (a1) => { @@ -921,8 +961,8 @@ sum(enumFromTo(1, 10), 0) //│ sum1 = function sum(ls, a) { //│ return runtime.safeCall(ls(a)) //│ }; -//│ tmp11 = enumFromTo4(1, 10); -//│ block$res26 = sum1(tmp11, 0); +//│ tmp12 = enumFromTo4(1, 10); +//│ block$res27 = sum1(tmp12, 0); //│ undefined //│ = 45 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -940,36 +980,36 @@ fun c(x) = n + t c(AA(3)) //│ JS (unsanitized): -//│ let c1, tmp13; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp14; +//│ let t, n, tmp15; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp14 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp14; +//│ n = tmp15; //│ return n + t //│ }; -//│ tmp13 = AA1(3); -//│ c1(tmp13) +//│ tmp14 = AA1(3); +//│ c1(tmp14) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp13; +//│ let c1, tmp14; //│ c1 = function c(x) { -//│ let t, n, tmp14; +//│ let t, n, tmp15; //│ t = x.aa; //│ if (x instanceof AA1.class) { -//│ tmp14 = 2; +//│ tmp15 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp14; +//│ n = tmp15; //│ return n + t //│ }; -//│ tmp13 = AA1(3); -//│ block$res28 = c1(tmp13); +//│ tmp14 = AA1(3); +//│ block$res29 = c1(tmp14); //│ undefined //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1002,7 +1042,7 @@ f(A, B) //│ f3 = function f(a, b) { //│ return runtime.safeCall(a(b)) //│ }; -//│ block$res30 = f3((b) => { +//│ block$res31 = f3((b) => { //│ return runtime.safeCall(b()) //│ }, () => { //│ return 3 @@ -1018,15 +1058,15 @@ fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f4, tmp15; +//│ let f4, tmp16; //│ f4 = function f(x) { -//│ let scrut, tmp16, tmp17; +//│ let scrut, tmp17, tmp18; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp16 = x.value - 1; -//│ tmp17 = Some1(tmp16); -//│ return f4(tmp17) +//│ tmp17 = x.value - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) //│ } else { //│ return 0 //│ } @@ -1034,19 +1074,19 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp15 = Some1(2); -//│ f4(tmp15) +//│ tmp16 = Some1(2); +//│ f4(tmp16) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f4, tmp15; +//│ let f4, tmp16; //│ f4 = function f(x) { -//│ let scrut, tmp16, tmp17; +//│ let scrut, tmp17, tmp18; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp16 = x.value - 1; -//│ tmp17 = Some1(tmp16); -//│ return f4(tmp17) +//│ tmp17 = x.value - 1; +//│ tmp18 = Some1(tmp17); +//│ return f4(tmp18) //│ } else { //│ return 0 //│ } @@ -1054,8 +1094,8 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp15 = Some1(2); -//│ block$res32 = f4(tmp15); +//│ tmp16 = Some1(2); +//│ block$res33 = f4(tmp16); //│ undefined //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1071,11 +1111,11 @@ if x is if y is B then 2 //│ JS (unsanitized): -//│ let x, y, tmp17; +//│ let x, y, tmp18; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp17 = 1; +//│ tmp18 = 1; //│ } else { //│ throw new this.Error("match error"); //│ } @@ -1088,14 +1128,14 @@ if y is //│ ==== JS (deforested): ==== //│ let x, y; //│ x = (y1) => { -//│ let tmp17; -//│ tmp17 = 1; +//│ let tmp18; +//│ tmp18 = 1; //│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ block$res34 = runtime.safeCall(x(y)); +//│ block$res35 = runtime.safeCall(x(y)); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1117,11 +1157,11 @@ test() //│ JS (unsanitized): //│ let test7; //│ test7 = function test() { -//│ let x1, y1, tmp18; +//│ let x1, y1, tmp19; //│ x1 = A1; //│ y1 = B1; //│ if (x1 instanceof A1.class) { -//│ tmp18 = 1; +//│ tmp19 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1138,8 +1178,8 @@ test() //│ test7 = function test() { //│ let x1, y1; //│ x1 = (y2) => { -//│ let tmp18; -//│ tmp18 = 1; +//│ let tmp19; +//│ tmp19 = 1; //│ return runtime.safeCall(y2()) //│ }; //│ y1 = () => { @@ -1147,7 +1187,7 @@ test() //│ }; //│ return runtime.safeCall(x1(y1)) //│ }; -//│ block$res36 = test7(); +//│ block$res37 = test7(); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1163,98 +1203,98 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ JS (unsanitized): -//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; //│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp28, tmp29, tmp30; +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param11 = x1.y; //│ n1 = param01; //│ m1 = param11; -//│ tmp28 = y1 + n1; -//│ tmp29 = tmp28 - m1; +//│ tmp29 = y1 + n1; +//│ tmp30 = tmp29 - m1; //│ } else if (x1 instanceof BBB1.class) { //│ param0 = x1.x; //│ param1 = x1.y; //│ n = param0; //│ m = param1; -//│ tmp30 = m + 1; -//│ tmp29 = tmp30 - n; +//│ tmp31 = m + 1; +//│ tmp30 = tmp31 - n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp29; +//│ a = tmp30; //│ return a + 3 //│ }; -//│ tmp18 = AAA1(1, 3); -//│ tmp19 = f5(tmp18, 1); -//│ tmp20 = BBB1(2, 3); -//│ tmp21 = f5(tmp20, 2); -//│ tmp22 = tmp19 + tmp21; -//│ tmp23 = AAA1(3, 2); -//│ tmp24 = f5(tmp23, 4); -//│ tmp25 = tmp22 + tmp24; -//│ tmp26 = BBB1(4, 6); -//│ tmp27 = f5(tmp26, 0); -//│ tmp25 + tmp27 +//│ tmp19 = AAA1(1, 3); +//│ tmp20 = f5(tmp19, 1); +//│ tmp21 = BBB1(2, 3); +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp24 = AAA1(3, 2); +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp27 = BBB1(4, 6); +//│ tmp28 = f5(tmp27, 0); +//│ tmp26 + tmp28 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, match_x_rest, match_x_branch_AAA, tmp30, tmp31, match_x_branch_BBB, tmp32, tmp33, tmp34, tmp35; +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; //│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp36, tmp37; +//│ let param0, param1, n, m, tmp37, tmp38; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp36 = y1 + n; -//│ tmp37 = tmp36 - m; -//│ return match_x_rest(tmp37) +//│ tmp37 = y1 + n; +//│ tmp38 = tmp37 - m; +//│ return match_x_rest(tmp38) //│ }; //│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp36, tmp37; +//│ let param0, param1, n, m, tmp37, tmp38; //│ param0 = field_x; //│ param1 = field_y; //│ n = param0; //│ m = param1; -//│ tmp37 = m + 1; -//│ tmp36 = tmp37 - n; -//│ return match_x_rest(tmp36) +//│ tmp38 = m + 1; +//│ tmp37 = tmp38 - n; +//│ return match_x_rest(tmp37) //│ }; -//│ match_x_rest = function match_x_rest(tmp36) { +//│ match_x_rest = function match_x_rest(tmp37) { //│ let a; -//│ a = tmp36; +//│ a = tmp37; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp28 = 1; -//│ tmp29 = 3; -//│ tmp18 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp28, tmp29) -//│ }; -//│ tmp19 = f5(tmp18, 1); -//│ tmp30 = 2; -//│ tmp31 = 3; -//│ tmp20 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp30, tmp31) +//│ tmp29 = 1; +//│ tmp30 = 3; +//│ tmp19 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp29, tmp30) //│ }; -//│ tmp21 = f5(tmp20, 2); -//│ tmp22 = tmp19 + tmp21; +//│ tmp20 = f5(tmp19, 1); +//│ tmp31 = 2; //│ tmp32 = 3; -//│ tmp33 = 2; -//│ tmp23 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp32, tmp33) -//│ }; -//│ tmp24 = f5(tmp23, 4); -//│ tmp25 = tmp22 + tmp24; -//│ tmp34 = 4; -//│ tmp35 = 6; -//│ tmp26 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp34, tmp35) -//│ }; -//│ tmp27 = f5(tmp26, 0); -//│ block$res38 = tmp25 + tmp27; +//│ tmp21 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp31, tmp32) +//│ }; +//│ tmp22 = f5(tmp21, 2); +//│ tmp23 = tmp20 + tmp22; +//│ tmp33 = 3; +//│ tmp34 = 2; +//│ tmp24 = (y1) => { +//│ return match_x_branch_AAA(y1, tmp33, tmp34) +//│ }; +//│ tmp25 = f5(tmp24, 4); +//│ tmp26 = tmp23 + tmp25; +//│ tmp35 = 4; +//│ tmp36 = 6; +//│ tmp27 = (y1) => { +//│ return match_x_branch_BBB(y1, tmp35, tmp36) +//│ }; +//│ tmp28 = f5(tmp27, 0); +//│ block$res39 = tmp26 + tmp28; //│ undefined //│ = 21 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1271,7 +1311,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp46; +//│ let p, c11, c2, tmp47; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1293,16 +1333,16 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp47, tmp48; -//│ tmp47 = c11(a); -//│ tmp48 = c2(a); -//│ return tmp47 + tmp48 +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 //│ }; -//│ tmp46 = AA1(1); -//│ p(tmp46) +//│ tmp47 = AA1(1); +//│ p(tmp47) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp46; +//│ let p, c11, c2, tmp47; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1322,13 +1362,13 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp47, tmp48; -//│ tmp47 = c11(a); -//│ tmp48 = c2(a); -//│ return tmp47 + tmp48 +//│ let tmp48, tmp49; +//│ tmp48 = c11(a); +//│ tmp49 = c2(a); +//│ return tmp48 + tmp49 //│ }; -//│ tmp46 = AA1(1); -//│ block$res40 = p(tmp46); +//│ tmp47 = AA1(1); +//│ block$res41 = p(tmp47); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 0a02e4f50103aaef9e8d8276a00a1443f7af156a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:01:14 +0800 Subject: [PATCH 098/303] restore my changes to the definition of Block.Match --- .../src/main/scala/hkmc2/codegen/Block.scala | 4 +- .../hkmc2/codegen/BlockTransformer.scala | 2 +- .../scala/hkmc2/codegen/Deforestation.scala | 4 +- .../scala/hkmc2/codegen/HandlerLowering.scala | 4 +- .../main/scala/hkmc2/codegen/Lowering.scala | 2 +- .../src/test/mlscript/lifter/ClassInFun.mls | 379 +++++++++++---- .../test/mlscript/lifter/StackSafetyLift.mls | 447 ++++++++++++++---- 7 files changed, 638 insertions(+), 204 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 3783e47e9e..8930281dad 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -253,7 +253,7 @@ end Block sealed abstract class BlockTail extends Block case class Match( - scrut: Value.Ref, + scrut: Path, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block, @@ -487,7 +487,7 @@ extension (k: Block => Block) def define(defn: Defn) = k.chain(Define(defn, _)) def end = k.rest(End()) def ifthen(scrut: Path, cse: Case, trm: Block, els: Opt[Block] = N): Block => Block = - k.chain(Match(scrut.asInstanceOf[Value.Ref], cse -> trm :: Nil, els, _)) + k.chain(Match(scrut, cse -> trm :: Nil, els, _)) def label(label: Local, body: Block) = k.chain(Label(label, body, _)) def ret(r: Result) = k.rest(Return(r, false)) def staticif(b: Boolean, f: (Block => Block) => (Block => Block)) = if b then k.transform(f) else k diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index aaf8b1719a..24eb9eb27c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -43,7 +43,7 @@ class BlockTransformer(subst: SymbolSubst): if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) - then b else Match(scrut2.asInstanceOf[Value.Ref], arms2, dflt2, rst2) + then b else Match(scrut2, arms2, dflt2, rst2) case Label(lbl, bod, rst) => val lbl2 = applyLocal(lbl) val bod2 = applySubBlock(bod) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ac2a9afeba..489b8c3846 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -680,11 +680,11 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = resolveClashes._2.keys.flatMap{ case DtorExpr.Match(s) => - val Match(scrut, arms, dflt, rest) = matchScrutToMatchBlock(s) + val Match(Value.Ref(l), arms, dflt, rest) = matchScrutToMatchBlock(s) val fvsInallBodiesAndRest = (rest :: arms.map(_._2).appendedAll(dflt)).flatMap(b => b.sortedFvs(using globallyDefinedVars.store.toSet)) // NOTE: doesn't intersect with defined vars in dflt because it may be only `throw error` val definedInAllArms = arms.map(_._2.definedVars).reduce((a, b) => a.intersect(b)) - Some(s -> fvsInallBodiesAndRest.filterNot(a => (a.uid == scrut.l.uid) || definedInAllArms.contains(a)).distinct.sortBy(_.uid)) + Some(s -> fvsInallBodiesAndRest.filterNot(a => (a.uid == l.uid) || definedInAllArms.contains(a)).distinct.sortBy(_.uid)) case DtorExpr.Sel(s) => None }.toMap diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index dd4d2ff413..84a0a5b199 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -502,7 +502,7 @@ class HandlerLowering(paths: HandlerPaths)(using TL, Raise, Elaborator.State, El // match block representing the function body val mainMatchCases = parts.toList.map(b => (Case.Lit(Tree.IntLit(b.id)), transformPart(b.blk))) val mainMatchBlk = Match( - pcSymbol.asPath.asInstanceOf[Value.Ref], + pcSymbol.asPath, mainMatchCases, N, End() @@ -525,7 +525,7 @@ class HandlerLowering(paths: HandlerPaths)(using TL, Raise, Elaborator.State, El lbl else Match( - pcSymbol.asPath.asInstanceOf[Value.Ref], + pcSymbol.asPath, assignedResumedCases, N, lbl diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 2453897659..52847f5f41 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -454,7 +454,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): subTerm_nonTail(scrut): sr => tl.log(s"Binding scrut $scrut to $sr (${summon[Subst].map})") // val cse = - def mkMatch(cse: Case -> Block) = Match((sr.asInstanceOf[Value.Ref]), cse :: Nil, + def mkMatch(cse: Case -> Block) = Match(sr, cse :: Nil, S(go(restSplit, topLevel = true)), End() ) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 8c07179ff8..7e6e7dffe0 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -12,18 +12,210 @@ fun f() = h.perform() 1 f() + f() + f() -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader 'app') -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ JS (unsanitized): +//│ let f, Effect1, tmp, handleBlock$, Cont$func$f$ClassInFun$_mls_L7_95_118$1, Cont$handleBlock$h$1, Handler$h$1, lambda, f$, Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor, Cont$func$f$ClassInFun$_mls_L7_95_118$$, Cont$handleBlock$h$$ctor, Cont$handleBlock$h$$, lambda$; +//│ Effect1 = class Effect { +//│ constructor() {} +//│ toString() { return "Effect"; } +//│ }; +//│ lambda$ = function lambda$(Handler$h$$instance, k) { +//│ return runtime.safeCall(k()) +//│ }; +//│ lambda = (undefined, function (Handler$h$$instance) { +//│ return (k) => { +//│ return lambda$(Handler$h$$instance, k) +//│ } +//│ }); +//│ Handler$h$1 = class Handler$h$ extends Effect1 { +//│ constructor() { +//│ let tmp1; +//│ tmp1 = super(); +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda(this)); +//│ return runtime.mkEffect(this, lambda$this) +//│ } +//│ toString() { return "Handler$h$"; } +//│ }; +//│ Cont$handleBlock$h$$ = function Cont$handleBlock$h$$(h$0, tmp$1, tmp$2, tmp$3, tmp$4, pc) { +//│ let tmp1; +//│ tmp1 = new Cont$handleBlock$h$1.class(pc); +//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) +//│ }; +//│ Cont$handleBlock$h$$ctor = function Cont$handleBlock$h$$ctor(h$0, tmp$1, tmp$2, tmp$3, tmp$4) { +//│ return (pc) => { +//│ let tmp1; +//│ tmp1 = new Cont$handleBlock$h$1.class(pc); +//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) +//│ } +//│ }; +//│ Cont$handleBlock$h$1 = function Cont$handleBlock$h$(pc1) { +//│ return (h$01, tmp$11, tmp$21, tmp$31, tmp$41) => { +//│ return new Cont$handleBlock$h$.class(pc1)(h$01, tmp$11, tmp$21, tmp$31, tmp$41); +//│ } +//│ }; +//│ Cont$handleBlock$h$1.class = class Cont$handleBlock$h$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (h$0, tmp$1, tmp$2, tmp$3, tmp$4) => { +//│ let tmp1; +//│ tmp1 = super(null); +//│ this.pc = pc; +//│ this.h$0 = h$0; +//│ this.tmp$1 = tmp$1; +//│ this.tmp$2 = tmp$2; +//│ this.tmp$3 = tmp$3; +//│ this.tmp$4 = tmp$4; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 2) { +//│ this.tmp$1 = value$; +//│ } else if (this.pc === 3) { +//│ this.tmp$2 = value$; +//│ } else if (this.pc === 4) { +//│ this.tmp$4 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 7) { +//│ this.tmp$1 = f$(this.h$0); +//│ if (this.tmp$1 instanceof runtime.EffectSig.class) { +//│ this.pc = 2; +//│ this.tmp$1.contTrace.last.next = this; +//│ this.tmp$1.contTrace.last = this; +//│ return this.tmp$1 +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ this.tmp$2 = f$(this.h$0); +//│ if (this.tmp$2 instanceof runtime.EffectSig.class) { +//│ this.pc = 3; +//│ this.tmp$2.contTrace.last.next = this; +//│ this.tmp$2.contTrace.last = this; +//│ return this.tmp$2 +//│ } +//│ this.pc = 3; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ this.tmp$3 = this.tmp$1 + this.tmp$2; +//│ this.pc = 5; +//│ continue contLoop; +//│ } else if (this.pc === 5) { +//│ this.tmp$4 = f$(this.h$0); +//│ if (this.tmp$4 instanceof runtime.EffectSig.class) { +//│ this.pc = 4; +//│ this.tmp$4.contTrace.last.next = this; +//│ this.tmp$4.contTrace.last = this; +//│ return this.tmp$4 +//│ } +//│ this.pc = 4; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ return this.tmp$3 + this.tmp$4 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$handleBlock$h$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ = function Cont$func$f$ClassInFun$_mls_L7_95_118$$(h$0, tmp$1, pc) { +//│ let tmp1; +//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); +//│ return tmp1(h$0, tmp$1) +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor = function Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor(h$0, tmp$1) { +//│ return (pc) => { +//│ let tmp1; +//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); +//│ return tmp1(h$0, tmp$1) +//│ } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1 = function Cont$func$f$ClassInFun$_mls_L7_95_118$(pc1) { +//│ return (h$01, tmp$11) => { +//│ return new Cont$func$f$ClassInFun$_mls_L7_95_118$.class(pc1)(h$01, tmp$11); +//│ } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1.class = class Cont$func$f$ClassInFun$_mls_L7_95_118$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (h$0, tmp$1) => { +//│ let tmp1; +//│ tmp1 = super(null); +//│ this.pc = pc; +//│ this.h$0 = h$0; +//│ this.tmp$1 = tmp$1; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.tmp$1 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 1) { +//│ this.tmp$1 = runtime.safeCall(this.h$0.perform()); +//│ if (this.tmp$1 instanceof runtime.EffectSig.class) { +//│ this.pc = 0; +//│ this.tmp$1.contTrace.last.next = this; +//│ this.tmp$1.contTrace.last = this; +//│ return this.tmp$1 +//│ } +//│ this.pc = 0; +//│ continue contLoop; +//│ } else if (this.pc === 0) { +//│ return 1 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$func$f$ClassInFun$_mls_L7_95_118$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ f$ = function f$(h) { +//│ let tmp1; +//│ tmp1 = runtime.safeCall(h.perform()); +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$func$f$ClassInFun$_mls_L7_95_118$$(h, tmp1, 0); +//│ tmp1.contTrace.last = tmp1.contTrace.last.next; +//│ return tmp1 +//│ } +//│ return 1 +//│ }; +//│ f = function f(h) { +//│ return () => { +//│ return f$(h) +//│ } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let h, tmp1, tmp2, tmp3, tmp4; +//│ h = new Handler$h$1(); +//│ tmp1 = f$(h); +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 2); +//│ return runtime.handleBlockImpl(tmp1, h) +//│ } +//│ tmp2 = f$(h); +//│ if (tmp2 instanceof runtime.EffectSig.class) { +//│ tmp2.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 3); +//│ return runtime.handleBlockImpl(tmp2, h) +//│ } +//│ tmp3 = tmp1 + tmp2; +//│ tmp4 = f$(h); +//│ if (tmp4 instanceof runtime.EffectSig.class) { +//│ tmp4.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 4); +//│ return runtime.handleBlockImpl(tmp4, h) +//│ } +//│ return tmp3 + tmp4 +//│ }; +//│ tmp = handleBlock$(); +//│ if (tmp instanceof runtime.EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ tmp +//│ = 3 :expect 1 :sjs @@ -33,17 +225,17 @@ fun f(x) = Test() f(1).get() //│ JS (unsanitized): -//│ let Test1, f, tmp, Test$ctor, Test$; +//│ let Test1, f1, tmp1, Test$ctor, Test$; //│ Test$ = function Test$(x$0) { -//│ let tmp1; -//│ tmp1 = new Test1.class(); -//│ return tmp1(x$0) +//│ let tmp2; +//│ tmp2 = new Test1.class(); +//│ return tmp2(x$0) //│ }; //│ Test$ctor = function Test$ctor(x$0) { //│ return () => { -//│ let tmp1; -//│ tmp1 = new Test1.class(); -//│ return tmp1(x$0) +//│ let tmp2; +//│ tmp2 = new Test1.class(); +//│ return tmp2(x$0) //│ } //│ }; //│ Test1 = function Test() { @@ -63,11 +255,11 @@ f(1).get() //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; -//│ f = function f(x) { +//│ f1 = function f(x) { //│ return Test$(x) //│ }; -//│ tmp = f(1); -//│ runtime.safeCall(tmp.get()) +//│ tmp1 = f1(1); +//│ runtime.safeCall(tmp1.get()) //│ = 1 :expect 1 @@ -83,7 +275,7 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let h, Test3, g, f1, tmp1, Test$ctor1, Test$1, g$, h$; +//│ let h, Test3, g, f2, tmp2, Test$ctor1, Test$1, g$, h$; //│ h$ = function h$(used3) { //│ return used3 //│ }; @@ -93,10 +285,10 @@ f(1, 2).get() //│ } //│ }; //│ g$ = function g$(used1, g_arg) { -//│ let used3, tmp2; +//│ let used3, tmp3; //│ used3 = 2; -//│ tmp2 = h$(used3); -//│ return used1 + tmp2 +//│ tmp3 = h$(used3); +//│ return used1 + tmp3 //│ }; //│ g = function g(used1) { //│ return (g_arg) => { @@ -104,15 +296,15 @@ f(1, 2).get() //│ } //│ }; //│ Test$1 = function Test$(used1$0, a) { -//│ let tmp2; -//│ tmp2 = new Test3.class(a); -//│ return tmp2(used1$0) +//│ let tmp3; +//│ tmp3 = new Test3.class(a); +//│ return tmp3(used1$0) //│ }; //│ Test$ctor1 = function Test$ctor(used1$0) { //│ return (a) => { -//│ let tmp2; -//│ tmp2 = new Test3.class(a); -//│ return tmp2(used1$0) +//│ let tmp3; +//│ tmp3 = new Test3.class(a); +//│ return tmp3(used1$0) //│ } //│ }; //│ Test3 = function Test(a1) { @@ -133,13 +325,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; -//│ f1 = function f(used1, unused1) { +//│ f2 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$1(used1, unused1) //│ }; -//│ tmp1 = f1(1, 2); -//│ runtime.safeCall(tmp1.get()) +//│ tmp2 = f2(1, 2); +//│ runtime.safeCall(tmp2.get()) //│ = 1 :expect 1 @@ -168,7 +360,7 @@ fun f(used1, unused1) = new Test f(1, 2).get() //│ JS (unsanitized): -//│ let h2, Test7, g2, f3, tmp3, Test$ctor3, Test$3, g$2, h$2; +//│ let h2, Test7, g2, f4, tmp4, Test$ctor3, Test$3, g$2, h$2; //│ h$2 = function h$(used3) { //│ return used3 //│ }; @@ -178,10 +370,10 @@ f(1, 2).get() //│ } //│ }; //│ g$2 = function g$(used1, g_arg) { -//│ let used3, tmp4; +//│ let used3, tmp5; //│ used3 = 2; -//│ tmp4 = h$2(used3); -//│ return used1 + tmp4 +//│ tmp5 = h$2(used3); +//│ return used1 + tmp5 //│ }; //│ g2 = function g(used1) { //│ return (g_arg) => { @@ -189,15 +381,15 @@ f(1, 2).get() //│ } //│ }; //│ Test$3 = function Test$(used1$0) { -//│ let tmp4; -//│ tmp4 = new Test7.class(); -//│ return tmp4(used1$0) +//│ let tmp5; +//│ tmp5 = new Test7.class(); +//│ return tmp5(used1$0) //│ }; //│ Test$ctor3 = function Test$ctor(used1$0) { //│ return () => { -//│ let tmp4; -//│ tmp4 = new Test7.class(); -//│ return tmp4(used1$0) +//│ let tmp5; +//│ tmp5 = new Test7.class(); +//│ return tmp5(used1$0) //│ } //│ }; //│ Test7 = function Test(used1$01) { @@ -215,13 +407,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test"; } //│ }; -//│ f3 = function f(used1, unused1) { +//│ f4 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$3(used1) //│ }; -//│ tmp3 = f3(1, 2); -//│ runtime.safeCall(tmp3.get()) +//│ tmp4 = f4(1, 2); +//│ runtime.safeCall(tmp4.get()) //│ = 1 :sjs @@ -233,17 +425,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f4, A$ctor, A$, f$capture1; +//│ let A1, f5, A$ctor, A$, f$capture1; //│ A$ = function A$(f$capture$0) { -//│ let tmp4; -//│ tmp4 = new A1.class(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1.class(); +//│ return tmp5(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp4; -//│ tmp4 = new A1.class(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1.class(); +//│ return tmp5(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -273,14 +465,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f4 = function f(x) { -//│ let tmp4, tmp5, capture; +//│ f5 = function f(x) { +//│ let tmp5, tmp6, capture; //│ capture = new f$capture1(x); -//│ tmp4 = A$(capture); -//│ tmp5 = runtime.safeCall(tmp4.f()); +//│ tmp5 = A$(capture); +//│ tmp6 = runtime.safeCall(tmp5.f()); //│ return capture.x0$ //│ }; -//│ f4(1) +//│ f5(1) //│ = 2 // only w should be in a capture @@ -302,17 +494,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; +//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; //│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Good1.class(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1.class(); +//│ return tmp6(x$1, y$2, z$3, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Good1.class(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1.class(); +//│ return tmp6(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -331,24 +523,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp5, tmp6; +//│ let tmp6, tmp7; //│ this.z$3 = 100; -//│ tmp5 = this.x$1 + this.y$2; -//│ tmp6 = tmp5 + this.z$3; -//│ return tmp6 + this.f$capture$0.w0$ +//│ tmp6 = this.x$1 + this.y$2; +//│ tmp7 = tmp6 + this.z$3; +//│ return tmp7 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Bad1.class(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1.class(); +//│ return tmp6(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Bad1.class(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1.class(); +//│ return tmp6(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -378,19 +570,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } //│ }; -//│ f5 = function f() { -//│ let x, y, z, tmp5, tmp6, capture; +//│ f6 = function f() { +//│ let x, y, z, tmp6, tmp7, capture; //│ capture = new f$capture3(null); //│ x = 1; //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(capture); -//│ tmp6 = runtime.safeCall(tmp5.foo()); +//│ tmp6 = Bad$(capture); +//│ tmp7 = runtime.safeCall(tmp6.foo()); //│ return Good$(x, y, z, capture) //│ }; -//│ tmp4 = f5(); -//│ runtime.safeCall(tmp4.foo()) +//│ tmp5 = f6(); +//│ runtime.safeCall(tmp5.foo()) //│ = 10111 :fixme @@ -431,18 +623,7 @@ fun sum(n) = else n + sum(n - 1) sum(100) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader 'app') -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 5050 // instance checks @@ -469,7 +650,7 @@ fun f(x) = f(2).get() //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ JS (unsanitized): -//│ let h3, f8, tmp11, h$3; +//│ let h3, f9, tmp12, h$3; //│ h$3 = function h$(Test$instance, x) { //│ return x //│ }; @@ -478,7 +659,7 @@ f(2).get() //│ return h$3(Test$instance, x) //│ } //│ }; -//│ f8 = function f(x) { +//│ f9 = function f(x) { //│ let Test10, foo1; //│ Test10 = function Test() { //│ return new Test.class(); @@ -493,8 +674,8 @@ f(2).get() //│ foo1 = Test10; //│ return runtime.safeCall(foo1()) //│ }; -//│ tmp11 = f8(2); -//│ runtime.safeCall(tmp11.get()) +//│ tmp12 = f9(2); +//│ runtime.safeCall(tmp12.get()) //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index bb975ded2f..c71269fa12 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -20,18 +20,165 @@ fun hi(n) = if n == 0 then 0 else hi(n - 1) hi(0) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ JS (unsanitized): +//│ let hi, res, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1, handleBlock$, Cont$handleBlock$stackHandler$1, StackDelay$1, lambda, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$, Cont$handleBlock$stackHandler$$ctor, Cont$handleBlock$stackHandler$$, lambda$; +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n$0, scrut$1, tmp$2, stackDelayRes$3, pc) { +//│ let tmp; +//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); +//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) +//│ }; +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor(n$0, scrut$1, tmp$2, stackDelayRes$3) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); +//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) +//│ } +//│ }; +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1 = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(pc1) { +//│ return (n$01, scrut$11, tmp$21, stackDelayRes$31) => { +//│ return new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$.class(pc1)(n$01, scrut$11, tmp$21, stackDelayRes$31); +//│ } +//│ }; +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class = class Cont$func$hi$StackSafetyLift$_mls_L19_4_47$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (n$0, scrut$1, tmp$2, stackDelayRes$3) => { +//│ let tmp; +//│ tmp = super(null); +//│ this.pc = pc; +//│ this.n$0 = n$0; +//│ this.scrut$1 = scrut$1; +//│ this.tmp$2 = tmp$2; +//│ this.stackDelayRes$3 = stackDelayRes$3; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.stackDelayRes$3 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ this.scrut$1 = this.n$0 == 0; +//│ if (this.scrut$1 === true) { +//│ return 0 +//│ } else { +//│ this.tmp$2 = this.n$0 - 1; +//│ this.pc = 2; +//│ continue contLoop; +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ break contLoop; +//│ } else if (this.pc === 2) { +//│ runtime.stackDepth = runtime.stackDepth + 1; +//│ return hi(this.tmp$2) +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ hi = function hi(n) { +//│ let scrut, tmp, stackDelayRes; +//│ stackDelayRes = runtime.checkDepth(); +//│ if (stackDelayRes instanceof runtime.EffectSig.class) { +//│ stackDelayRes.contTrace.last.next = Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n, scrut, tmp, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; +//│ return stackDelayRes +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0 +//│ } else { +//│ tmp = n - 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; +//│ return hi(tmp) +//│ } +//│ }; +//│ lambda$ = function lambda$(StackDelay$$instance, resume) { +//│ runtime.stackOffset = runtime.stackDepth; +//│ return resume() +//│ }; +//│ lambda = (undefined, function (StackDelay$$instance) { +//│ return (resume) => { +//│ return lambda$(StackDelay$$instance, resume) +//│ } +//│ }); +//│ StackDelay$1 = class StackDelay$ extends runtime.StackDelay { +//│ constructor() { +//│ let tmp; +//│ tmp = super(); +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda(this)); +//│ return runtime.mkEffect(this, lambda$this) +//│ } +//│ toString() { return "StackDelay$"; } +//│ }; +//│ Cont$handleBlock$stackHandler$$ = function Cont$handleBlock$stackHandler$$(res$0, pc) { +//│ let tmp; +//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); +//│ return tmp(res$0) +//│ }; +//│ Cont$handleBlock$stackHandler$$ctor = function Cont$handleBlock$stackHandler$$ctor(res$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); +//│ return tmp(res$0) +//│ } +//│ }; +//│ Cont$handleBlock$stackHandler$1 = function Cont$handleBlock$stackHandler$(pc1) { +//│ return (res$01) => { +//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); +//│ } +//│ }; +//│ Cont$handleBlock$stackHandler$1.class = class Cont$handleBlock$stackHandler$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (res$0) => { +//│ let tmp; +//│ tmp = super(null); +//│ this.pc = pc; +//│ this.res$0 = res$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 3) { +//│ this.res$0 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 3) { +//│ return this.res$0 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let stackHandler, res1; +//│ stackHandler = new StackDelay$1(); +//│ runtime.stackLimit = 5; +//│ runtime.stackOffset = 0; +//│ runtime.stackDepth = 1; +//│ runtime.stackHandler = stackHandler; +//│ res1 = hi(0); +//│ if (res1 instanceof runtime.EffectSig.class) { +//│ res1.contTrace.last.next = Cont$handleBlock$stackHandler$$(res1, 3); +//│ return runtime.handleBlockImpl(res1, stackHandler) +//│ } +//│ return res1 +//│ }; +//│ res = handleBlock$(); +//│ if (res instanceof runtime.EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ runtime.stackDepth = 0; +//│ runtime.stackHandler = null; +//│ res +//│ = 0 :sjs :stackSafe 1000 @@ -42,18 +189,188 @@ fun sum(n) = else n + sum(n - 1) sum(10000) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ JS (unsanitized): +//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; +//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { +//│ let tmp; +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class(pc); +//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) +//│ }; +//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class(pc); +//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) +//│ } +//│ }; +//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$(pc1) { +//│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { +//│ return new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); +//│ } +//│ }; +//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L40_4_57$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { +//│ let tmp; +//│ tmp = super(null); +//│ this.pc = pc; +//│ this.n$0 = n$0; +//│ this.scrut$1 = scrut$1; +//│ this.tmp$2 = tmp$2; +//│ this.tmp$3 = tmp$3; +//│ this.curDepth$4 = curDepth$4; +//│ this.stackDelayRes$5 = stackDelayRes$5; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.stackDelayRes$5 = value$; +//│ } else if (this.pc === 1) { +//│ this.tmp$3 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ this.scrut$1 = this.n$0 == 0; +//│ if (this.scrut$1 === true) { +//│ return 0 +//│ } else { +//│ this.tmp$2 = this.n$0 - 1; +//│ this.pc = 3; +//│ continue contLoop; +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 3) { +//│ runtime.stackDepth = runtime.stackDepth + 1; +//│ this.tmp$3 = sum1(this.tmp$2); +//│ if (this.tmp$3 instanceof runtime.EffectSig.class) { +//│ this.pc = 1; +//│ this.tmp$3.contTrace.last.next = this; +//│ this.tmp$3.contTrace.last = this; +//│ return this.tmp$3 +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ this.tmp$3 = runtime.resetDepth(this.tmp$3, this.curDepth$4); +//│ return this.n$0 + this.tmp$3 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L40_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ sum1 = function sum(n) { +//│ let scrut, tmp, tmp1, curDepth, stackDelayRes; +//│ curDepth = runtime.stackDepth; +//│ stackDelayRes = runtime.checkDepth(); +//│ if (stackDelayRes instanceof runtime.EffectSig.class) { +//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; +//│ return stackDelayRes +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0 +//│ } else { +//│ tmp = n - 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; +//│ tmp1 = sum1(tmp); +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); +//│ tmp1.contTrace.last = tmp1.contTrace.last.next; +//│ return tmp1 +//│ } +//│ tmp1 = runtime.resetDepth(tmp1, curDepth); +//│ return n + tmp1 +//│ } +//│ }; +//│ lambda$1 = function lambda$(StackDelay$$instance, resume) { +//│ runtime.stackOffset = runtime.stackDepth; +//│ return resume() +//│ }; +//│ lambda1 = (undefined, function (StackDelay$$instance) { +//│ return (resume) => { +//│ return lambda$1(StackDelay$$instance, resume) +//│ } +//│ }); +//│ StackDelay$3 = class StackDelay$2 extends runtime.StackDelay { +//│ constructor() { +//│ let tmp; +//│ tmp = super(); +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda1(this)); +//│ return runtime.mkEffect(this, lambda$this) +//│ } +//│ toString() { return "StackDelay$"; } +//│ }; +//│ Cont$handleBlock$stackHandler$$1 = function Cont$handleBlock$stackHandler$$(res$0, pc) { +//│ let tmp; +//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); +//│ return tmp(res$0) +//│ }; +//│ Cont$handleBlock$stackHandler$$ctor1 = function Cont$handleBlock$stackHandler$$ctor(res$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); +//│ return tmp(res$0) +//│ } +//│ }; +//│ Cont$handleBlock$stackHandler$3 = function Cont$handleBlock$stackHandler$(pc1) { +//│ return (res$01) => { +//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); +//│ } +//│ }; +//│ Cont$handleBlock$stackHandler$3.class = class Cont$handleBlock$stackHandler$2 extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (res$0) => { +//│ let tmp; +//│ tmp = super(null); +//│ this.pc = pc; +//│ this.res$0 = res$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 4) { +//│ this.res$0 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ return this.res$0 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$1 = function handleBlock$() { +//│ let stackHandler, res2; +//│ stackHandler = new StackDelay$3(); +//│ runtime.stackLimit = 1000; +//│ runtime.stackOffset = 0; +//│ runtime.stackDepth = 1; +//│ runtime.stackHandler = stackHandler; +//│ res2 = sum1(10000); +//│ if (res2 instanceof runtime.EffectSig.class) { +//│ res2.contTrace.last.next = Cont$handleBlock$stackHandler$$1(res2, 4); +//│ return runtime.handleBlockImpl(res2, stackHandler) +//│ } +//│ return res2 +//│ }; +//│ res1 = handleBlock$1(); +//│ if (res1 instanceof runtime.EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ runtime.stackDepth = 0; +//│ runtime.stackHandler = null; +//│ res1 +//│ = 50005000 // stack-overflows without :stackSafe :re @@ -74,18 +391,8 @@ fun foo(f) = set ctr += 1 dummy(f(f)) foo(foo) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 0 +//│ ctr = 10001 :stackSafe 1000 :effectHandlers @@ -96,18 +403,8 @@ val foo = else n + f(n-1) f(10000) foo -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 50005000 +//│ foo = 50005000 :re fun foo() = @@ -135,18 +432,7 @@ handle h = Eff with else n + f(n-1) resume(f(10000)) foo(h) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 50005000 // function call and defn inside handler :effectHandlers @@ -162,18 +448,7 @@ handle h = Eff with in fun foo(h) = h.perform foo(h) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 50005000 :re :effectHandlers @@ -186,18 +461,7 @@ handle h = Eff with else n + f(n-1) resume(f(10000)) foo(h) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :effectHandlers :stackSafe @@ -222,7 +486,7 @@ fun hi(n) = n hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ JS (unsanitized): -//│ let hi; hi = function hi(n) { return n }; hi(0) +//│ let hi1; hi1 = function hi(n) { return n }; hi1(0) //│ = 0 :stackSafe 42 @@ -240,16 +504,5 @@ fun sum(n) = n + sum(n - 1) fun bad() = sum(10000) + sum(10000) bad() -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.lang.ClassCastException: class hkmc2.codegen.Select cannot be cast to class hkmc2.codegen.Value$Ref (hkmc2.codegen.Select and hkmc2.codegen.Value$Ref are in unnamed module of loader sbt.internal.LayeredClassLoader @719c85be) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:46) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:49) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:620) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:42) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:250) +//│ = 100010000 From f4a80b7877bf07c148175793b3280a5710802a7b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:12:57 +0800 Subject: [PATCH 099/303] update tests --- .../test/mlscript/lifter/StackSafetyLift.mls | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index c71269fa12..85dbe28d85 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -190,25 +190,25 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; -//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { +//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; +//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { //│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { +//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ } //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L40_4_57$(pc1) { +//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L187_4_57$(pc1) { //│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { -//│ return new Cont$func$sum$StackSafetyLift$_mls_L40_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); +//│ return new Cont$func$sum$StackSafetyLift$_mls_L187_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); //│ } //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L40_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L40_4_57$ extends runtime.FunctionContFrame.class { +//│ Cont$func$sum$StackSafetyLift$_mls_L187_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L187_4_57$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { //│ let tmp; @@ -261,14 +261,14 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L40_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L187_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ sum1 = function sum(n) { //│ let scrut, tmp, tmp1, curDepth, stackDelayRes; //│ curDepth = runtime.stackDepth; //│ stackDelayRes = runtime.checkDepth(); //│ if (stackDelayRes instanceof runtime.EffectSig.class) { -//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); //│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; //│ return stackDelayRes //│ } @@ -280,7 +280,7 @@ sum(10000) //│ runtime.stackDepth = runtime.stackDepth + 1; //│ tmp1 = sum1(tmp); //│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L40_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); +//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L187_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); //│ tmp1.contTrace.last = tmp1.contTrace.last.next; //│ return tmp1 //│ } From 7f07ab85a55cd659c9c1274597f064802fb7d91b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:30:27 +0800 Subject: [PATCH 100/303] revert my changes toBblockTransformer and properly implement ReplaceLocalSymTransformer --- .../src/main/scala/hkmc2/codegen/BlockTransformer.scala | 2 +- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 24eb9eb27c..33a04e1fd0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -122,7 +122,7 @@ class BlockTransformer(subst: SymbolSubst): def applyValue(v: Value): Value = v match case Value.Ref(l) => - val l2 = applyLocal(l) + val l2 = l.subst if (l2 is l) then v else Value.Ref(l2) case Value.This(sym) => val sym2 = sym.subst diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 489b8c3846..b79e2c702e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -82,8 +82,9 @@ trait StratVarTrait(stratState: StratVarState): extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): - // FIXME: depends on my hacky change (d4358d7) to blocktransformer to work... - override def applyLocal(sym: Local): Local = freeVarsAndTheirNewSyms.getOrElse(sym, sym) + override def applyValue(v: Value): Value = v match + case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) + case _ => super.applyValue(v) ReplaceLocalSymTransformer.applyBlock(b) def sortedFvs(using alwaysDefined: Set[Symbol]) = From 64994f427e60bcbe1a4636f3214a52ed3ec05d93 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:42:07 +0800 Subject: [PATCH 101/303] minor --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b79e2c702e..72d5ef3787 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -147,15 +147,15 @@ extension (b: Block) // ReplaceSelectTransformer.applyBlock(b) def hasExplicitRet: Boolean = - object HasExplicitRetTransformer extends BlockTraverserShallow(new SymbolSubst()): + object HasExplicitRetTraverser extends BlockTraverserShallow(new SymbolSubst()): var flag = false override def applyBlock(b: Block): Unit = b match case Return(_, imp) => flag = !imp case Define(defn, rest) => applyBlock(rest) case _ => super.applyBlock(b) - HasExplicitRetTransformer.applyBlock(b) - HasExplicitRetTransformer.flag + HasExplicitRetTraverser.applyBlock(b) + HasExplicitRetTraverser.flag class Deforest(using TL, Raise, Elaborator.State): @@ -593,7 +593,7 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorSym = getClsSymOfUid(ctor) val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.get._2 - object GetCtorsTransformer extends BlockTraverser(new SymbolSubst()): + object GetCtorsTraverser extends BlockTraverser(new SymbolSubst()): val ctors = mutable.Set.empty[ResultId] override def applyResult(r: Result): Unit = handleCtors( @@ -610,8 +610,8 @@ class Deforest(using TL, Raise, Elaborator.State): args.foreach(applyResult) case _ => () - GetCtorsTransformer.applyBlock(arm) - GetCtorsTransformer.ctors.toSet + GetCtorsTraverser.applyBlock(arm) + GetCtorsTraverser.ctors.toSet def findCycle(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = val cache = mutable.Set(ctor) From c6550cfa46d49d4c5a979135a42c1052de554943 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 18:34:57 +0800 Subject: [PATCH 102/303] better scopeExtrusionInfo; more tests --- .../scala/hkmc2/codegen/Deforestation.scala | 20 +++---- .../src/test/mlscript/deforest/todos.mls | 58 ++++++++++++++++++- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 72d5ef3787..e6cd813326 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -678,18 +678,6 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Match(scrut, _, _) => scrut }.toSet - lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = - resolveClashes._2.keys.flatMap{ - case DtorExpr.Match(s) => - val Match(Value.Ref(l), arms, dflt, rest) = matchScrutToMatchBlock(s) - val fvsInallBodiesAndRest = (rest :: arms.map(_._2).appendedAll(dflt)).flatMap(b => b.sortedFvs(using globallyDefinedVars.store.toSet)) - // NOTE: doesn't intersect with defined vars in dflt because it may be only `throw error` - val definedInAllArms = arms.map(_._2.definedVars).reduce((a, b) => a.intersect(b)) - Some(s -> fvsInallBodiesAndRest.filterNot(a => (a.uid == l.uid) || definedInAllArms.contains(a)).distinct.sortBy(_.uid)) - case DtorExpr.Sel(s) => None - }.toMap - - def rewrite(p: Block) = val deforestTransformer = DeforestTransformer(using globallyDefinedVars.store.toSet) val rest = deforestTransformer.applyBlock(p) @@ -699,6 +687,14 @@ class Deforest(using TL, Raise, Elaborator.State): class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): + lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = + resolveClashes._2.keys.flatMap{ + case DtorExpr.Match(s) => + val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = matchScrutToMatchBlock(s) + Some(s -> matchExpr.sortedFvs.filterNot(_.uid === l.uid)) + case DtorExpr.Sel(s) => None + }.toMap + var replaceSelInfo = Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] override def applyBlock(b: Block): Block = b match diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index a116f5ca3d..cc4f1e194b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -156,6 +156,62 @@ c(AA(2)) //│ tmp = AA1(2); //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: x (class hkmc2.semantics.VarSymbol) +//│ ==== JS (deforested): ==== +//│ let c, tmp, tmp1; +//│ c = function c(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ tmp1 = 2; +//│ tmp = () => { +//│ let scrut; +//│ scrut = (x) => { +//│ return tmp1 +//│ }; +//│ return runtime.safeCall(scrut(x_not_in_scope)) +//│ }; +//│ block$res6 = c(tmp); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +:fixme +:sjs +fun c(x, y) = if x is + AA then + if y is + A then x.x +c(AA(2), A) +//│ JS (unsanitized): +//│ let c1, tmp3; +//│ c1 = function c(x, y) { +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ return x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp3 = AA1(2); +//│ c1(tmp3, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c1, tmp3, tmp4; +//│ c1 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ tmp4 = 2; +//│ tmp3 = (y) => { +//│ return runtime.safeCall(y(x_not_in_scope)) +//│ }; +//│ block$res8 = c1(tmp3, (x) => { +//│ return x.x +//│ }); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 From ca4cdd5bd611c6e691e65c2cf2c7950a86758d09 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 19:02:38 +0800 Subject: [PATCH 103/303] add test --- .../src/test/mlscript/deforest/simple.mls | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 8022371905..6196bcc794 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -9,6 +9,7 @@ class AA(aa) class BB(bb) class AAA(x, y) class BBB(x, y) +class CCC(c) object None class Some(value) @@ -1373,3 +1374,148 @@ p(AA(1)) //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 + + + +:sjs +fun c(x, y) = if x is + AA(a) then + if y is + A then a +c(AA(2), A) +//│ JS (unsanitized): +//│ let c3, tmp49; +//│ c3 = function c(x1, y1) { +//│ let param0, a; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ a = param0; +//│ if (y1 instanceof A1.class) { +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp49 = AA1(2); +//│ c3(tmp49, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c3, tmp49, tmp50; +//│ c3 = function c(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ tmp50 = 2; +//│ tmp49 = (y1) => { +//│ let param0, a; +//│ param0 = tmp50; +//│ a = param0; +//│ return runtime.safeCall(y1(a)) +//│ }; +//│ block$res43 = c3(tmp49, (a) => { +//│ return a +//│ }); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + +:sjs +fun f(x, y) = + let a = if x is + AAA(n, m) then y + n - m + CCC(n) then n + 1 + a + 3 +f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) +//│ JS (unsanitized): +//│ let f6, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61; +//│ f6 = function f(x1, y1) { +//│ let a, param0, n, param01, param1, n1, m, tmp62, tmp63; +//│ if (x1 instanceof AAA1.class) { +//│ param01 = x1.x; +//│ param1 = x1.y; +//│ n1 = param01; +//│ m = param1; +//│ tmp62 = y1 + n1; +//│ tmp63 = tmp62 - m; +//│ } else if (x1 instanceof CCC1.class) { +//│ param0 = x1.c; +//│ n = param0; +//│ tmp63 = n + 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ a = tmp63; +//│ return a + 3 +//│ }; +//│ tmp52 = AAA1(1, 3); +//│ tmp53 = f6(tmp52, 1); +//│ tmp54 = CCC1(2); +//│ tmp55 = f6(tmp54, 2); +//│ tmp56 = tmp53 + tmp55; +//│ tmp57 = AAA1(3, 2); +//│ tmp58 = f6(tmp57, 4); +//│ tmp59 = tmp56 + tmp58; +//│ tmp60 = CCC1(4); +//│ tmp61 = f6(tmp60, 0); +//│ tmp59 + tmp61 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f6, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, match_x_rest1, match_x_branch_AAA1, tmp64, match_x_branch_CCC, tmp65, tmp66, tmp67; +//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, field_x, field_y) { +//│ let param0, param1, n, m, tmp68, tmp69; +//│ param0 = field_x; +//│ param1 = field_y; +//│ n = param0; +//│ m = param1; +//│ tmp68 = y1 + n; +//│ tmp69 = tmp68 - m; +//│ return match_x_rest1(tmp69) +//│ }; +//│ match_x_branch_CCC = function match_x_branch_CCC(y1, field_c) { +//│ let param0, n, tmp68; +//│ param0 = field_c; +//│ n = param0; +//│ tmp68 = n + 1; +//│ return match_x_rest1(tmp68) +//│ }; +//│ match_x_rest1 = function match_x_rest(tmp68) { +//│ let a; +//│ a = tmp68; +//│ return a + 3 +//│ }; +//│ f6 = function f(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ tmp62 = 1; +//│ tmp63 = 3; +//│ tmp52 = (y1) => { +//│ return match_x_branch_AAA1(y1, tmp62, tmp63) +//│ }; +//│ tmp53 = f6(tmp52, 1); +//│ tmp64 = 2; +//│ tmp54 = (y1) => { +//│ return match_x_branch_CCC(y1, tmp64) +//│ }; +//│ tmp55 = f6(tmp54, 2); +//│ tmp56 = tmp53 + tmp55; +//│ tmp65 = 3; +//│ tmp66 = 2; +//│ tmp57 = (y1) => { +//│ return match_x_branch_AAA1(y1, tmp65, tmp66) +//│ }; +//│ tmp58 = f6(tmp57, 4); +//│ tmp59 = tmp56 + tmp58; +//│ tmp67 = 4; +//│ tmp60 = (y1) => { +//│ return match_x_branch_CCC(y1, tmp67) +//│ }; +//│ tmp61 = f6(tmp60, 0); +//│ block$res45 = tmp59 + tmp61; +//│ undefined +//│ = 24 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 24 From dec504dbfbf56242e59e447e7023901477860f1a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:54:16 +0800 Subject: [PATCH 104/303] wip: proper handling of sels in nested matches --- .../scala/hkmc2/codegen/Deforestation.scala | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e6cd813326..7764cbfcf0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -49,7 +49,7 @@ case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends Con case None => Some(expr) case Some(exist) => ??? // should only update once -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Set[ResultId]) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Map[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat @@ -267,7 +267,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] = Set.empty + matching: Map[ResultId, ClsOrModSymbol] = Map.empty[ResultId, ClsOrModSymbol] ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) @@ -275,7 +275,7 @@ class Deforest(using TL, Raise, Elaborator.State): val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map { case (Case.Cls(s, _), body) => // TODO: fix this "asInstanceOf"? - processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + scrut.uid) + processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + (scrut.uid -> s)) } else arms.map{ case (_, armBody) => processBlock(armBody) } @@ -337,7 +337,7 @@ class Deforest(using TL, Raise, Elaborator.State): def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] + matching: Map[ResultId, ClsOrModSymbol] ) = val paramSyms = params.map{ case Param(_, sym, _) => sym } val paramStrats = paramSyms.map{ sym => symToStrat(sym) } @@ -348,7 +348,7 @@ class Deforest(using TL, Raise, Elaborator.State): def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: Set[ResultId] + matching: Map[ResultId, ClsOrModSymbol] ): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map { case Arg(false, value) => @@ -659,6 +659,20 @@ class Deforest(using TL, Raise, Elaborator.State): case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) case _ => false } then + val selMaps = dtors.head._2.arms.flatMap { + case (Case.Cls(c, _), body) => c.asCls.map: c => + c -> getClsFields(c).map { f => + // find the sels in inside this arm to be replaced + val selToBeReplaced = sels.filter { case fs: FieldSel => fs.inMatching(dtors.head._1) === c } + + // if a branch is used multiple times, later it will be a function, so use var here + // if a branch is used once, it will just be a lambda, so tempvar here + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 + val sym = if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"${c.name}_${f.id.name}")) + else TempSymbol(N, s"${c.name}_${f.id.name}") + (f.id -> sym) }.toMap + }.toMap Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels.map(_.expr))) else throw Error("more than one consumer") @@ -688,14 +702,14 @@ class Deforest(using TL, Raise, Elaborator.State): class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = - resolveClashes._2.keys.flatMap{ - case DtorExpr.Match(s) => - val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = matchScrutToMatchBlock(s) - Some(s -> matchExpr.sortedFvs.filterNot(_.uid === l.uid)) - case DtorExpr.Sel(s) => None + filteredCtorDests.values.flatMap{ + case CtorFinalDest.Match(scrut, expr, selInArms) => + val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr + Some(scrut -> matchExpr.sortedFvs.filterNot(_.uid === l.uid)) + case CtorFinalDest.Sel(s) => None }.toMap - var replaceSelInfo = Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] + val replaceSelInfo = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => From bf504b6b14f44731eb6fd0303cc57970e6ea6fd0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:02:49 +0800 Subject: [PATCH 105/303] wip: proper handling of sels in nested matches --- .../scala/hkmc2/codegen/Deforestation.scala | 82 ++++++++++++------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7764cbfcf0..d3166897c8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -60,7 +60,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[ClassSymbol, Map[Tree.Ident, Symbol]] -> Map[ClassSymbol, Map[ResultId, Symbol]]) case Sel(s: ResultId) trait FieldSelTrait: @@ -659,21 +659,39 @@ class Deforest(using TL, Raise, Elaborator.State): case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) case _ => false } then - val selMaps = dtors.head._2.arms.flatMap { - case (Case.Cls(c, _), body) => c.asCls.map: c => - c -> getClsFields(c).map { f => - // find the sels in inside this arm to be replaced - val selToBeReplaced = sels.filter { case fs: FieldSel => fs.inMatching(dtors.head._1) === c } - - // if a branch is used multiple times, later it will be a function, so use var here - // if a branch is used once, it will just be a lambda, so tempvar here - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 - val sym = if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${f.id.name}")) - else TempSymbol(N, s"${c.name}_${f.id.name}") - (f.id -> sym) }.toMap - }.toMap - Some(CtorFinalDest.Match(dtors.head._1, dtors.head._2, sels.map(_.expr))) + val fieldNameToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[Tree.Ident, Symbol]] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[ResultId, Symbol]] + dtors.head._2.arms.foreach: + case (Case.Cls(c, _), body) => c.asCls.foreach: c => + // if this arm is used more than once, should be var symbol because the arm body will be + // extracted to a function, otherwise just temp symbol + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } + selsInArms.foreach: fs => + assert(getClsFields(c).map(_.id).contains(fs.field)) + fieldNameToSymToBeReplaced.updateWith(c): + case None => + val sym = if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + else TempSymbol(N, s"${c.name}_${fs.field.name}") + Some(Map(fs.field -> sym)) + case Some(v) => + if !v.contains(fs.field) then + val sym = if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + else TempSymbol(N, s"${c.name}_${fs.field.name}") + Some(v + (fs.field -> sym)) + else Some(v) + val sym = fieldNameToSymToBeReplaced(c)(fs.field) + selectionUidsToSymToBeReplaced.updateWith(c): + case None => Some(Map(fs.expr -> sym)) + case Some(v) => Some(v + (fs.expr -> sym)) + Some(CtorFinalDest.Match( + dtors.head._1, + dtors.head._2, + sels.map(_.expr), + fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap + )) else throw Error("more than one consumer") None @@ -684,12 +702,12 @@ class Deforest(using TL, Raise, Elaborator.State): res.toMap lazy val rewritingSelConsumer = filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, sels) => None + case CtorFinalDest.Match(_, _, _, _) => None case CtorFinalDest.Sel(s) => Some(s) }.toSet lazy val filteredDtors = filteredCtorDests.values.collect { - case CtorFinalDest.Match(scrut, _, _) => scrut + case CtorFinalDest.Match(scrut, _, _, _) => scrut }.toSet def rewrite(p: Block) = @@ -703,13 +721,17 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = filteredCtorDests.values.flatMap{ - case CtorFinalDest.Match(scrut, expr, selInArms) => + case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr Some(scrut -> matchExpr.sortedFvs.filterNot(_.uid === l.uid)) case CtorFinalDest.Sel(s) => None }.toMap - val replaceSelInfo = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] + val replaceSelInfo = + filteredCtorDests.values.flatMap { + case CtorFinalDest.Match(_, _, _, selMaps) => selMaps._2.values.flatMap(m => m.toList) + case CtorFinalDest.Sel(s) => Nil + }.toMap override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => @@ -736,6 +758,7 @@ class Deforest(using TL, Raise, Elaborator.State): // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms def getOrElseUpdate(scrut: ResultId, m: Match, cls: ClsOrModSymbol, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = + // FIXME: change to use pre-determined symbols assert(scrut === m.scrut.uid) val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) store.get(scrut).flatMap(_.get(cls)) match @@ -769,9 +792,9 @@ class Deforest(using TL, Raise, Elaborator.State): val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - replaceSelInfo += scrut -> (sel -> newSelMaps) + // replaceSelInfo += scrut -> (sel -> newSelMaps) val bodyReplaceSel = applyBlock(body) - replaceSelInfo -= scrut + // replaceSelInfo -= scrut // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) @@ -800,9 +823,9 @@ class Deforest(using TL, Raise, Elaborator.State): else // make a lambda, and replace the selections - replaceSelInfo += scrut -> (sel -> selMap) + // replaceSelInfo += scrut -> (sel -> selMap) val bodyReplaceSel = applyBlock(body) - replaceSelInfo -= scrut + // replaceSelInfo -= scrut // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) @@ -879,12 +902,13 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(call.uid) match case None => handleNormalCall(args) - case Some(CtorFinalDest.Match(scrut, expr, sels)) => + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 // tl.log(call.toString() + " ----> " + body) val newArgs = args.map(_ => TempSymbol(N)) + // FIXME: use pre-determined symbols val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) @@ -915,14 +939,14 @@ class Deforest(using TL, Raise, Elaborator.State): if rewritingSelConsumer.contains(s.uid) then k(p) else - replaceSelInfo.values.find(v => v._1.contains(s.uid)) match + replaceSelInfo.get(s.uid) match case None => k(s) - case Some(v) => k(v._2(nme)) + case Some(v) => k(Value.Ref(v)) case Some(mod) => filteredCtorDests.get(s.uid) match case None => k(s) - case Some(CtorFinalDest.Match(scrut, expr, sels)) => + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) @@ -935,7 +959,7 @@ class Deforest(using TL, Raise, Elaborator.State): filteredCtorDests.get(r.uid) match case None => k(r) - case Some(CtorFinalDest.Match(scrut, expr, sels)) => + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get // tl.log(mod.toString + " ----> " + body) From 291b868dedeb52d91a2616af0590e0248da7e1f8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:42:58 +0800 Subject: [PATCH 106/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 39 ++++++++-- .../src/test/mlscript/deforest/simple.mls | 78 +++++++++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index d3166897c8..caf6e3ecd1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -733,6 +733,8 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Sel(s) => Nil }.toMap + val replaceSelInfo2 = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] + override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then @@ -792,9 +794,9 @@ class Deforest(using TL, Raise, Elaborator.State): val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - // replaceSelInfo += scrut -> (sel -> newSelMaps) + replaceSelInfo2 += scrut -> (sel -> newSelMaps) val bodyReplaceSel = applyBlock(body) - // replaceSelInfo -= scrut + replaceSelInfo2 -= scrut // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) @@ -823,9 +825,9 @@ class Deforest(using TL, Raise, Elaborator.State): else // make a lambda, and replace the selections - // replaceSelInfo += scrut -> (sel -> selMap) + replaceSelInfo2 += scrut -> (sel -> selMap) val bodyReplaceSel = applyBlock(body) - // replaceSelInfo -= scrut + replaceSelInfo2 -= scrut // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) @@ -906,13 +908,33 @@ class Deforest(using TL, Raise, Elaborator.State): val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 // tl.log(call.toString() + " ----> " + body) + // use pre-determined symbols, create temp symbols for un-used fields + val usedFieldIdentToSymbolsToBeReplaced = selsMap._1(c) + val allFieldIdentToSymbolsToBeReplaced = getClsFields(c).map: f => + f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"field_${f.id}_unused")) + + // if all vars are temp vars, no need to create more temp vars + // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) + val assignedTempSyms = if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then + allFieldIdentToSymbolsToBeReplaced.map(_._2.asInstanceOf[TempSymbol]) + else + allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match + case ts: TempSymbol => ts + case vs: VarSymbol => TempSymbol(N, s"${vs.name}_tmp") + } + + + val newArgs = args.map(_ => TempSymbol(N)) - // FIXME: use pre-determined symbols + // FIXME: use pre-determined symbols, create temp symbols for un-used fields val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) + // args.zip(assignedTempSyms).foldRight[Block](k(bodyAndRestInLam)): + // case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } + args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => applyResult2(a.value): r => Assign(tmp, r, rest) @@ -939,9 +961,12 @@ class Deforest(using TL, Raise, Elaborator.State): if rewritingSelConsumer.contains(s.uid) then k(p) else - replaceSelInfo.get(s.uid) match + // val _ = replaceSelInfo.get(s.uid) match + // case None => k(s) + // case Some(v) => k(Value.Ref(v)) + replaceSelInfo2.values.find(v => v._1.contains(s.uid)) match case None => k(s) - case Some(v) => k(Value.Ref(v)) + case Some(v) => k(v._2(nme)) case Some(mod) => filteredCtorDests.get(s.uid) match case None => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 6196bcc794..fc3787f23c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1519,3 +1519,81 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ = 24 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 24 + + + + +:sjs +fun f(x, y) = + let a = if x is + AAA then y + x.y + CCC(n) then n + 1 + a + 3 +f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) +//│ JS (unsanitized): +//│ let f7, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84; +//│ f7 = function f(x1, y1) { +//│ let a, param0, n, tmp85; +//│ if (x1 instanceof AAA1.class) { +//│ tmp85 = y1 + x1.y; +//│ } else if (x1 instanceof CCC1.class) { +//│ param0 = x1.c; +//│ n = param0; +//│ tmp85 = n + 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ a = tmp85; +//│ return a + 3 +//│ }; +//│ tmp78 = AAA1(1, 3); +//│ tmp79 = f7(tmp78, 1); +//│ tmp80 = CCC1(2); +//│ tmp81 = f7(tmp80, 2); +//│ tmp82 = tmp79 + tmp81; +//│ tmp83 = AAA1(3, 2); +//│ tmp84 = f7(tmp83, 4); +//│ tmp82 + tmp84 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f7, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84, tmp85, tmp86, match_x_rest2, match_x_branch_AAA2, tmp87, tmp88, tmp89; +//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, field_x, field_y) { +//│ let tmp90; +//│ tmp90 = y1 + field_y; +//│ return match_x_rest2(tmp90) +//│ }; +//│ match_x_rest2 = function match_x_rest(tmp90) { +//│ let a; +//│ a = tmp90; +//│ return a + 3 +//│ }; +//│ f7 = function f(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ tmp85 = 1; +//│ tmp86 = 3; +//│ tmp78 = (y1) => { +//│ return match_x_branch_AAA2(y1, tmp85, tmp86) +//│ }; +//│ tmp79 = f7(tmp78, 1); +//│ tmp87 = 2; +//│ tmp80 = (y1) => { +//│ let param0, n, tmp90; +//│ param0 = tmp87; +//│ n = param0; +//│ tmp90 = n + 1; +//│ return match_x_rest2(tmp90) +//│ }; +//│ tmp81 = f7(tmp80, 2); +//│ tmp82 = tmp79 + tmp81; +//│ tmp88 = 3; +//│ tmp89 = 2; +//│ tmp83 = (y1) => { +//│ return match_x_branch_AAA2(y1, tmp88, tmp89) +//│ }; +//│ tmp84 = f7(tmp83, 4); +//│ block$res47 = tmp82 + tmp84; +//│ undefined +//│ = 22 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 22 From f0214378efeac6930c599ddb0c64f11b1a18237e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Mar 2025 21:01:38 +0800 Subject: [PATCH 107/303] wip: use pre-computed symbols for rewriting, also prevent passing unused fields to matching arm functions --- .../scala/hkmc2/codegen/Deforestation.scala | 173 ++++--- .../src/test/mlscript/deforest/simple.mls | 440 +++++++++--------- .../src/test/mlscript/deforest/todos.mls | 72 ++- 3 files changed, 386 insertions(+), 299 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index caf6e3ecd1..69680b3346 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -644,6 +644,10 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = val res = mutable.Map.empty[CtorExpr, CtorFinalDest] + + // we need only one CtorFinalDest per arm + val handledMatches = mutable.Map.empty[ClsOrModSymbol, Opt[CtorFinalDest]] + resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head.expr)) @@ -654,47 +658,56 @@ class Deforest(using TL, Raise, Elaborator.State): throw Error("more than one consumer") None else if dtors.size == 1 then - val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) - if sels.forall{ s => ResultUid(s.expr) match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) - case _ => false - } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[Tree.Ident, Symbol]] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[ResultId, Symbol]] - dtors.head._2.arms.foreach: - case (Case.Cls(c, _), body) => c.asCls.foreach: c => - // if this arm is used more than once, should be var symbol because the arm body will be - // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } - selsInArms.foreach: fs => - assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(c): - case None => - val sym = if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - else TempSymbol(N, s"${c.name}_${fs.field.name}") - Some(Map(fs.field -> sym)) - case Some(v) => - if !v.contains(fs.field) then + val currentCtorCls = getClsSymOfUid(ctor) + handledMatches.getOrElseUpdate(currentCtorCls, { + val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + + if sels.forall{ s => ResultUid(s.expr) match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) + case _ => false + } then + val fieldNameToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[Tree.Ident, Symbol]] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[ResultId, Symbol]] + println(dtors.head._2.arms.length) + dtors.head._2.arms.foreach: + case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => + val c = cOrMod.asCls.get + // if this arm is used more than once, should be var symbol because the arm body will be + // extracted to a function, otherwise just temp symbol + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c }.distinct + println(s"in arm: ${c}; sel in arms: ${selsInArms}; all sels: ${sels}") + selsInArms.foreach: fs => + assert(getClsFields(c).map(_.id).contains(fs.field)) + fieldNameToSymToBeReplaced.updateWith(c): + case None => val sym = if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - else TempSymbol(N, s"${c.name}_${fs.field.name}") - Some(v + (fs.field -> sym)) - else Some(v) - val sym = fieldNameToSymToBeReplaced(c)(fs.field) - selectionUidsToSymToBeReplaced.updateWith(c): - case None => Some(Map(fs.expr -> sym)) - case Some(v) => Some(v + (fs.expr -> sym)) - Some(CtorFinalDest.Match( - dtors.head._1, - dtors.head._2, - sels.map(_.expr), - fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )) - else - throw Error("more than one consumer") - None + then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + else TempSymbol(N, s"${c.name}_${fs.field.name}") + Some(Map(fs.field -> sym)) + case Some(v) => + if !v.contains(fs.field) then + val sym = if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + else TempSymbol(N, s"${c.name}_${fs.field.name}") + Some(v + (fs.field -> sym)) + else Some(v) + val sym = fieldNameToSymToBeReplaced(c)(fs.field) + println("herer" + sym) + selectionUidsToSymToBeReplaced.updateWith(c): + case None => Some(Map(fs.expr -> sym)) + case Some(v) => Some(v + (fs.expr -> sym)) + case _ => () + Some(CtorFinalDest.Match( + dtors.head._1, + dtors.head._2, + sels.map(_.expr), + fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap + )) + else + throw Error("more than one consumer") + None + }) else ??? } res.updateWith(ctor){_ => filteredDtor} @@ -732,6 +745,7 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Match(_, _, _, selMaps) => selMaps._2.values.flatMap(m => m.toList) case CtorFinalDest.Sel(s) => Nil }.toMap + println(replaceSelInfo.map(x => x._2)) val replaceSelInfo2 = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] @@ -759,7 +773,14 @@ class Deforest(using TL, Raise, Elaborator.State): val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms - def getOrElseUpdate(scrut: ResultId, m: Match, cls: ClsOrModSymbol, sel: Set[ResultId], selMap: Map[Tree.Ident, Value.Ref]) = + def getOrElseUpdate( + scrut: ResultId, + m: Match, + cls: ClsOrModSymbol, + sel: Set[ResultId], + currentUsedCtorArgsToFields: Map[Tree.Ident, Value.Ref], + preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty + ) = // FIXME: change to use pre-determined symbols assert(scrut === m.scrut.uid) val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) @@ -791,12 +812,15 @@ class Deforest(using TL, Raise, Elaborator.State): // make a function, and register, and return a lambda calling that function with correct arguments // arguments for lambda: free vars // arguments for that function: free vars and pattern vars - val newSelMapSyms = selMap.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } - val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - replaceSelInfo2 += scrut -> (sel -> newSelMaps) + // FIXME: use pre determined var symbols here + // val newSelMapSyms = currentUsedCtorArgsToFields.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } + + // val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } + + // replaceSelInfo2 += scrut -> (sel -> newSelMaps) val bodyReplaceSel = applyBlock(body) - replaceSelInfo2 -= scrut + // replaceSelInfo2 -= scrut // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) @@ -809,7 +833,11 @@ class Deforest(using TL, Raise, Elaborator.State): funSym, ParamList( ParamListFlags.empty, - freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList ::: newSelMapSyms.toList.sortBy(_._1.name).map(v => Param(FldFlags.empty, v._2, N)), + freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList + ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => + println(s"in param list: ${v}") + Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N) + ), N ) :: Nil, funBody @@ -818,18 +846,18 @@ class Deforest(using TL, Raise, Elaborator.State): Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N)), N), Return( - Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), false ) ) else // make a lambda, and replace the selections - replaceSelInfo2 += scrut -> (sel -> selMap) + // replaceSelInfo2 += scrut -> (sel -> currentUsedCtorArgsToFields) val bodyReplaceSel = applyBlock(body) - replaceSelInfo2 -= scrut + // replaceSelInfo2 -= scrut - // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, selMap) + // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, currentUsedCtorArgsToFields) val lambdaBody = makeBody(bodyReplaceSel) Value.Lam( @@ -842,7 +870,7 @@ class Deforest(using TL, Raise, Elaborator.State): Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)), N), Return( - Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: selMap.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), false ) ) @@ -911,16 +939,16 @@ class Deforest(using TL, Raise, Elaborator.State): // use pre-determined symbols, create temp symbols for un-used fields val usedFieldIdentToSymbolsToBeReplaced = selsMap._1(c) val allFieldIdentToSymbolsToBeReplaced = getClsFields(c).map: f => - f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"field_${f.id}_unused")) + f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) // if all vars are temp vars, no need to create more temp vars // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) val assignedTempSyms = if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then - allFieldIdentToSymbolsToBeReplaced.map(_._2.asInstanceOf[TempSymbol]) + allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) else allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match - case ts: TempSymbol => ts - case vs: VarSymbol => TempSymbol(N, s"${vs.name}_tmp") + case ts: TempSymbol => id -> ts + case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") } @@ -928,17 +956,24 @@ class Deforest(using TL, Raise, Elaborator.State): val newArgs = args.map(_ => TempSymbol(N)) // FIXME: use pre-determined symbols, create temp symbols for un-used fields - val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap + // val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap - val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) + val bodyAndRestInLam = matchArms.getOrElseUpdate( + scrut, + expr, + c, + sels.toSet, + assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, + selsMap._1(c) -> selsMap._2(c)) + // val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) - // args.zip(assignedTempSyms).foldRight[Block](k(bodyAndRestInLam)): - // case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } + args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): + case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } - args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => - applyResult2(a.value): r => - Assign(tmp, r, rest) - } + // args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => + // applyResult2(a.value): r => + // Assign(tmp, r, rest) + // } case Some(CtorFinalDest.Sel(s)) => val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } val idx = getClsFields(c).indexWhere(s => s.id === selFieldName) @@ -961,12 +996,14 @@ class Deforest(using TL, Raise, Elaborator.State): if rewritingSelConsumer.contains(s.uid) then k(p) else - // val _ = replaceSelInfo.get(s.uid) match - // case None => k(s) - // case Some(v) => k(Value.Ref(v)) - replaceSelInfo2.values.find(v => v._1.contains(s.uid)) match + replaceSelInfo.get(s.uid) match case None => k(s) - case Some(v) => k(v._2(nme)) + case Some(v) => + println(v) + k(Value.Ref(v)) + // replaceSelInfo2.values.find(v => v._1.contains(s.uid)) match + // case None => k(s) + // case Some(v) => k(v._2(nme)) case Some(mod) => filteredCtorDests.get(s.uid) match case None => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index fc3787f23c..38d82a0eaa 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -99,21 +99,21 @@ test() //│ ==== JS (deforested): ==== //│ let test1; //│ test1 = function test() { -//│ let x, scrut, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, AA_aa, BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = A1; +//│ AA_aa = A1; //│ tmp = () => { //│ let param0, x1; -//│ param0 = tmp1; +//│ param0 = AA_aa; //│ x1 = param0; //│ return x1 //│ }; //│ } else { -//│ tmp2 = B1; +//│ BB_bb = B1; //│ tmp = () => { //│ let param0, x1; -//│ param0 = tmp2; +//│ param0 = BB_bb; //│ x1 = param0; //│ return x1 //│ }; @@ -178,25 +178,25 @@ test() //│ return runtime.safeCall(a()) //│ }; //│ test2 = function test() { -//│ let x, scrut, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, AA_aa, BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = () => { +//│ AA_aa = () => { //│ return 1 //│ }; //│ tmp = () => { //│ let param0, x1; -//│ param0 = tmp1; +//│ param0 = AA_aa; //│ x1 = param0; //│ return f(x1) //│ }; //│ } else { -//│ tmp2 = () => { +//│ BB_bb = () => { //│ return 2 //│ }; //│ tmp = () => { //│ let param0, x1; -//│ param0 = tmp2; +//│ param0 = BB_bb; //│ x1 = param0; //│ return f(x1) //│ }; @@ -279,21 +279,21 @@ test() //│ return runtime.safeCall(a()) //│ }; //│ test3 = function test() { -//│ let x, scrut, tmp, tmp1, tmp2; +//│ let x, scrut, tmp, AA_aa, BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = () => { +//│ AA_aa = () => { //│ return 1 //│ }; //│ tmp = () => { -//│ return f1(tmp1) +//│ return f1(AA_aa) //│ }; //│ } else { -//│ tmp2 = () => { +//│ BB_bb = () => { //│ return 5 //│ }; //│ tmp = () => { -//│ return f2(tmp2) +//│ return f2(BB_bb) //│ }; //│ } //│ x = tmp; @@ -351,21 +351,21 @@ test() //│ test4 = function test() { //│ let c, g, tmp; //│ g = function g(x) { -//│ let scrut, tmp1, tmp2; +//│ let scrut, AA_aa, BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ tmp1 = 11; +//│ AA_aa = 11; //│ return () => { //│ let param0, x1; -//│ param0 = tmp1; +//│ param0 = AA_aa; //│ x1 = param0; //│ return x1 //│ } //│ } else { -//│ tmp2 = 22; +//│ BB_bb = 22; //│ return () => { //│ let param0, x1; -//│ param0 = tmp2; +//│ param0 = BB_bb; //│ x1 = param0; //│ return x1 //│ } @@ -434,22 +434,22 @@ map(enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo, map, tmp; //│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2, tmp3, tmp4; +//│ let scrut, tmp1, tmp2, Cons_t, Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp1 = a + 1; //│ tmp2 = enumFromTo(tmp1, b); -//│ tmp3 = a; -//│ tmp4 = tmp2; +//│ Cons_h = a; +//│ Cons_t = tmp2; //│ return () => { -//│ let param0, param1, h, t, tmp5, tmp6; -//│ param0 = tmp3; -//│ param1 = tmp4; +//│ let param0, param1, h, t, tmp3, tmp4; +//│ param0 = Cons_h; +//│ param1 = Cons_t; //│ h = param0; //│ t = param1; -//│ tmp5 = h + 4; -//│ tmp6 = map(t); -//│ return Cons1(tmp5, tmp6) +//│ tmp3 = h + 4; +//│ tmp4 = map(t); +//│ return Cons1(tmp3, tmp4) //│ } //│ } else { //│ return () => { @@ -510,21 +510,21 @@ sum(enumFromTo(1,10)) //│ ==== JS (deforested): ==== //│ let enumFromTo1, sum, tmp2; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp3, tmp4, tmp5, tmp6; +//│ let scrut, tmp3, tmp4, Cons_t, Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp3 = a + 1; //│ tmp4 = enumFromTo1(tmp3, b); -//│ tmp5 = a; -//│ tmp6 = tmp4; +//│ Cons_h = a; +//│ Cons_t = tmp4; //│ return () => { -//│ let param0, param1, h, t, tmp7; -//│ param0 = tmp5; -//│ param1 = tmp6; +//│ let param0, param1, h, t, tmp5; +//│ param0 = Cons_h; +//│ param1 = Cons_t; //│ h = param0; //│ t = param1; -//│ tmp7 = sum(t); -//│ return h + tmp7 +//│ tmp5 = sum(t); +//│ return h + tmp5 //│ } //│ } else { //│ return () => { @@ -754,22 +754,22 @@ map(x => x + 4, enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo2, map1, tmp8, lambda; //│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp9, tmp10, tmp11, tmp12; +//│ let scrut, tmp9, tmp10, Cons_t, Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp9 = a + 1; //│ tmp10 = enumFromTo2(tmp9, b); -//│ tmp11 = a; -//│ tmp12 = tmp10; +//│ Cons_h = a; +//│ Cons_t = tmp10; //│ return (f3) => { -//│ let param0, param1, h, t, tmp13, tmp14; -//│ param0 = tmp11; -//│ param1 = tmp12; +//│ let param0, param1, h, t, tmp11, tmp12; +//│ param0 = Cons_h; +//│ param1 = Cons_t; //│ h = param0; //│ t = param1; -//│ tmp13 = runtime.safeCall(f3(h)); -//│ tmp14 = map1(f3, t); -//│ return Cons1(tmp13, tmp14) +//│ tmp11 = runtime.safeCall(f3(h)); +//│ tmp12 = map1(f3, t); +//│ return Cons1(tmp11, tmp12) //│ } //│ } else { //│ return (f3) => { @@ -848,37 +848,37 @@ map(x => x + 4, enumFromTo(1, 4)) //│ return runtime.safeCall(tmp11(f3)) //│ }; //│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12, tmp13, tmp14; +//│ let scrut, tmp11, tmp12, Cons_t, Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp11 = a + 1; //│ tmp12 = enumFromTo3(tmp11, b); -//│ tmp13 = a; -//│ tmp14 = tmp12; +//│ Cons_h = a; +//│ Cons_t = tmp12; //│ return (f3) => { -//│ let param0, param1, h, t, tmp15, tmp16, lambda3; -//│ param0 = tmp13; -//│ param1 = tmp14; +//│ let param0, param1, h, t, tmp13, tmp14, lambda3; +//│ param0 = Cons_h; +//│ param1 = Cons_t; //│ h = param0; //│ t = param1; //│ lambda3 = (undefined, function (f4) { -//│ let tmp17, tmp18; -//│ tmp17 = runtime.safeCall(f4(h)); -//│ tmp18 = map2(f4, t); -//│ return Cons1(tmp17, tmp18) +//│ let tmp15, tmp16; +//│ tmp15 = runtime.safeCall(f4(h)); +//│ tmp16 = map2(f4, t); +//│ return Cons1(tmp15, tmp16) //│ }); -//│ tmp16 = lambda3; -//│ tmp15 = tmp16; -//│ return match_ls_rest(f3, tmp15) +//│ tmp14 = lambda3; +//│ tmp13 = tmp14; +//│ return match_ls_rest(f3, tmp13) //│ } //│ } else { //│ return (f3) => { -//│ let tmp15, lambda3; +//│ let tmp13, lambda3; //│ lambda3 = (undefined, function (f4) { //│ return Nil1 //│ }); -//│ tmp15 = lambda3; -//│ return match_ls_rest(f3, tmp15) +//│ tmp13 = lambda3; +//│ return match_ls_rest(f3, tmp13) //│ } //│ } //│ }; @@ -937,21 +937,21 @@ sum(enumFromTo(1, 10), 0) //│ ==== JS (deforested): ==== //│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14, tmp15, tmp16; +//│ let scrut, tmp13, tmp14, Cons_t, Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp13 = a + 1; //│ tmp14 = enumFromTo4(tmp13, b); -//│ tmp15 = a; -//│ tmp16 = tmp14; +//│ Cons_h = a; +//│ Cons_t = tmp14; //│ return (a1) => { -//│ let param0, param1, h, t, tmp17; -//│ param0 = tmp15; -//│ param1 = tmp16; +//│ let param0, param1, h, t, tmp15; +//│ param0 = Cons_h; +//│ param1 = Cons_t; //│ h = param0; //│ t = param1; -//│ tmp17 = h + a1; -//│ return sum1(t, tmp17) +//│ tmp15 = h + a1; +//│ return sum1(t, tmp15) //│ } //│ } else { //│ return (a1) => { @@ -1240,59 +1240,59 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ tmp26 + tmp28 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, match_x_rest, match_x_branch_AAA, tmp31, tmp32, match_x_branch_BBB, tmp33, tmp34, tmp35, tmp36; -//│ match_x_branch_AAA = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; -//│ param0 = field_x; -//│ param1 = field_y; +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, AAA_x_tmp, AAA_y_tmp, match_x_rest, match_x_branch_AAA, BBB_x_tmp, BBB_y_tmp, match_x_branch_BBB, AAA_x_tmp1, AAA_y_tmp1, BBB_x_tmp1, BBB_y_tmp1; +//│ match_x_branch_AAA = function match_x_branch_AAA(y1, AAA_x, AAA_y) { +//│ let param0, param1, n, m, tmp29, tmp30; +//│ param0 = AAA_x; +//│ param1 = AAA_y; //│ n = param0; //│ m = param1; -//│ tmp37 = y1 + n; -//│ tmp38 = tmp37 - m; -//│ return match_x_rest(tmp38) -//│ }; -//│ match_x_branch_BBB = function match_x_branch_BBB(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp37, tmp38; -//│ param0 = field_x; -//│ param1 = field_y; +//│ tmp29 = y1 + n; +//│ tmp30 = tmp29 - m; +//│ return match_x_rest(tmp30) +//│ }; +//│ match_x_branch_BBB = function match_x_branch_BBB(y1, BBB_x, BBB_y) { +//│ let param0, param1, n, m, tmp29, tmp30; +//│ param0 = BBB_x; +//│ param1 = BBB_y; //│ n = param0; //│ m = param1; -//│ tmp38 = m + 1; -//│ tmp37 = tmp38 - n; -//│ return match_x_rest(tmp37) +//│ tmp30 = m + 1; +//│ tmp29 = tmp30 - n; +//│ return match_x_rest(tmp29) //│ }; -//│ match_x_rest = function match_x_rest(tmp37) { +//│ match_x_rest = function match_x_rest(tmp29) { //│ let a; -//│ a = tmp37; +//│ a = tmp29; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp29 = 1; -//│ tmp30 = 3; +//│ AAA_x_tmp = 1; +//│ AAA_y_tmp = 3; //│ tmp19 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp29, tmp30) +//│ return match_x_branch_AAA(y1, AAA_x_tmp, AAA_y_tmp) //│ }; //│ tmp20 = f5(tmp19, 1); -//│ tmp31 = 2; -//│ tmp32 = 3; +//│ BBB_x_tmp = 2; +//│ BBB_y_tmp = 3; //│ tmp21 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp31, tmp32) +//│ return match_x_branch_BBB(y1, BBB_x_tmp, BBB_y_tmp) //│ }; //│ tmp22 = f5(tmp21, 2); //│ tmp23 = tmp20 + tmp22; -//│ tmp33 = 3; -//│ tmp34 = 2; +//│ AAA_x_tmp1 = 3; +//│ AAA_y_tmp1 = 2; //│ tmp24 = (y1) => { -//│ return match_x_branch_AAA(y1, tmp33, tmp34) +//│ return match_x_branch_AAA(y1, AAA_x_tmp1, AAA_y_tmp1) //│ }; //│ tmp25 = f5(tmp24, 4); //│ tmp26 = tmp23 + tmp25; -//│ tmp35 = 4; -//│ tmp36 = 6; +//│ BBB_x_tmp1 = 4; +//│ BBB_y_tmp1 = 6; //│ tmp27 = (y1) => { -//│ return match_x_branch_BBB(y1, tmp35, tmp36) +//│ return match_x_branch_BBB(y1, BBB_x_tmp1, BBB_y_tmp1) //│ }; //│ tmp28 = f5(tmp27, 0); //│ block$res39 = tmp26 + tmp28; @@ -1312,7 +1312,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp39; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1334,16 +1334,16 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp40, tmp41; +//│ tmp40 = c11(a); +//│ tmp41 = c2(a); +//│ return tmp40 + tmp41 //│ }; -//│ tmp47 = AA1(1); -//│ p(tmp47) +//│ tmp39 = AA1(1); +//│ p(tmp39) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp47; +//│ let p, c11, c2, tmp39; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1363,13 +1363,13 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp48, tmp49; -//│ tmp48 = c11(a); -//│ tmp49 = c2(a); -//│ return tmp48 + tmp49 +//│ let tmp40, tmp41; +//│ tmp40 = c11(a); +//│ tmp41 = c2(a); +//│ return tmp40 + tmp41 //│ }; -//│ tmp47 = AA1(1); -//│ block$res41 = p(tmp47); +//│ tmp39 = AA1(1); +//│ block$res41 = p(tmp39); //│ undefined //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1384,7 +1384,7 @@ fun c(x, y) = if x is A then a c(AA(2), A) //│ JS (unsanitized): -//│ let c3, tmp49; +//│ let c3, tmp41; //│ c3 = function c(x1, y1) { //│ let param0, a; //│ if (x1 instanceof AA1.class) { @@ -1399,22 +1399,22 @@ c(AA(2), A) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp49 = AA1(2); -//│ c3(tmp49, A1) +//│ tmp41 = AA1(2); +//│ c3(tmp41, A1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c3, tmp49, tmp50; +//│ let c3, tmp41, AA_aa; //│ c3 = function c(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp50 = 2; -//│ tmp49 = (y1) => { +//│ AA_aa = 2; +//│ tmp41 = (y1) => { //│ let param0, a; -//│ param0 = tmp50; +//│ param0 = AA_aa; //│ a = param0; //│ return runtime.safeCall(y1(a)) //│ }; -//│ block$res43 = c3(tmp49, (a) => { +//│ block$res43 = c3(tmp41, (a) => { //│ return a //│ }); //│ undefined @@ -1431,90 +1431,90 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ JS (unsanitized): -//│ let f6, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61; +//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52; //│ f6 = function f(x1, y1) { -//│ let a, param0, n, param01, param1, n1, m, tmp62, tmp63; +//│ let a, param0, n, param01, param1, n1, m, tmp53, tmp54; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param1 = x1.y; //│ n1 = param01; //│ m = param1; -//│ tmp62 = y1 + n1; -//│ tmp63 = tmp62 - m; +//│ tmp53 = y1 + n1; +//│ tmp54 = tmp53 - m; //│ } else if (x1 instanceof CCC1.class) { //│ param0 = x1.c; //│ n = param0; -//│ tmp63 = n + 1; +//│ tmp54 = n + 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp63; +//│ a = tmp54; //│ return a + 3 //│ }; -//│ tmp52 = AAA1(1, 3); -//│ tmp53 = f6(tmp52, 1); -//│ tmp54 = CCC1(2); -//│ tmp55 = f6(tmp54, 2); -//│ tmp56 = tmp53 + tmp55; -//│ tmp57 = AAA1(3, 2); -//│ tmp58 = f6(tmp57, 4); -//│ tmp59 = tmp56 + tmp58; -//│ tmp60 = CCC1(4); -//│ tmp61 = f6(tmp60, 0); -//│ tmp59 + tmp61 +//│ tmp43 = AAA1(1, 3); +//│ tmp44 = f6(tmp43, 1); +//│ tmp45 = CCC1(2); +//│ tmp46 = f6(tmp45, 2); +//│ tmp47 = tmp44 + tmp46; +//│ tmp48 = AAA1(3, 2); +//│ tmp49 = f6(tmp48, 4); +//│ tmp50 = tmp47 + tmp49; +//│ tmp51 = CCC1(4); +//│ tmp52 = f6(tmp51, 0); +//│ tmp50 + tmp52 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f6, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, match_x_rest1, match_x_branch_AAA1, tmp64, match_x_branch_CCC, tmp65, tmp66, tmp67; -//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let param0, param1, n, m, tmp68, tmp69; -//│ param0 = field_x; -//│ param1 = field_y; +//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, AAA_x_tmp2, AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, CCC_c_tmp, match_x_branch_CCC, AAA_x_tmp3, AAA_y_tmp3, CCC_c_tmp1; +//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, AAA_x, AAA_y) { +//│ let param0, param1, n, m, tmp53, tmp54; +//│ param0 = AAA_x; +//│ param1 = AAA_y; //│ n = param0; //│ m = param1; -//│ tmp68 = y1 + n; -//│ tmp69 = tmp68 - m; -//│ return match_x_rest1(tmp69) +//│ tmp53 = y1 + n; +//│ tmp54 = tmp53 - m; +//│ return match_x_rest1(tmp54) //│ }; -//│ match_x_branch_CCC = function match_x_branch_CCC(y1, field_c) { -//│ let param0, n, tmp68; -//│ param0 = field_c; +//│ match_x_branch_CCC = function match_x_branch_CCC(y1, CCC_c) { +//│ let param0, n, tmp53; +//│ param0 = CCC_c; //│ n = param0; -//│ tmp68 = n + 1; -//│ return match_x_rest1(tmp68) +//│ tmp53 = n + 1; +//│ return match_x_rest1(tmp53) //│ }; -//│ match_x_rest1 = function match_x_rest(tmp68) { +//│ match_x_rest1 = function match_x_rest(tmp53) { //│ let a; -//│ a = tmp68; +//│ a = tmp53; //│ return a + 3 //│ }; //│ f6 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp62 = 1; -//│ tmp63 = 3; -//│ tmp52 = (y1) => { -//│ return match_x_branch_AAA1(y1, tmp62, tmp63) -//│ }; -//│ tmp53 = f6(tmp52, 1); -//│ tmp64 = 2; -//│ tmp54 = (y1) => { -//│ return match_x_branch_CCC(y1, tmp64) -//│ }; -//│ tmp55 = f6(tmp54, 2); -//│ tmp56 = tmp53 + tmp55; -//│ tmp65 = 3; -//│ tmp66 = 2; -//│ tmp57 = (y1) => { -//│ return match_x_branch_AAA1(y1, tmp65, tmp66) -//│ }; -//│ tmp58 = f6(tmp57, 4); -//│ tmp59 = tmp56 + tmp58; -//│ tmp67 = 4; -//│ tmp60 = (y1) => { -//│ return match_x_branch_CCC(y1, tmp67) -//│ }; -//│ tmp61 = f6(tmp60, 0); -//│ block$res45 = tmp59 + tmp61; +//│ AAA_x_tmp2 = 1; +//│ AAA_y_tmp2 = 3; +//│ tmp43 = (y1) => { +//│ return match_x_branch_AAA1(y1, AAA_x_tmp2, AAA_y_tmp2) +//│ }; +//│ tmp44 = f6(tmp43, 1); +//│ CCC_c_tmp = 2; +//│ tmp45 = (y1) => { +//│ return match_x_branch_CCC(y1, CCC_c_tmp) +//│ }; +//│ tmp46 = f6(tmp45, 2); +//│ tmp47 = tmp44 + tmp46; +//│ AAA_x_tmp3 = 3; +//│ AAA_y_tmp3 = 2; +//│ tmp48 = (y1) => { +//│ return match_x_branch_AAA1(y1, AAA_x_tmp3, AAA_y_tmp3) +//│ }; +//│ tmp49 = f6(tmp48, 4); +//│ tmp50 = tmp47 + tmp49; +//│ CCC_c_tmp1 = 4; +//│ tmp51 = (y1) => { +//│ return match_x_branch_CCC(y1, CCC_c_tmp1) +//│ }; +//│ tmp52 = f6(tmp51, 0); +//│ block$res45 = tmp50 + tmp52; //│ undefined //│ = 24 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1531,68 +1531,68 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ JS (unsanitized): -//│ let f7, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84; +//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69; //│ f7 = function f(x1, y1) { -//│ let a, param0, n, tmp85; +//│ let a, param0, n, tmp70; //│ if (x1 instanceof AAA1.class) { -//│ tmp85 = y1 + x1.y; +//│ tmp70 = y1 + x1.y; //│ } else if (x1 instanceof CCC1.class) { //│ param0 = x1.c; //│ n = param0; -//│ tmp85 = n + 1; +//│ tmp70 = n + 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp85; +//│ a = tmp70; //│ return a + 3 //│ }; -//│ tmp78 = AAA1(1, 3); -//│ tmp79 = f7(tmp78, 1); -//│ tmp80 = CCC1(2); -//│ tmp81 = f7(tmp80, 2); -//│ tmp82 = tmp79 + tmp81; -//│ tmp83 = AAA1(3, 2); -//│ tmp84 = f7(tmp83, 4); -//│ tmp82 + tmp84 +//│ tmp63 = AAA1(1, 3); +//│ tmp64 = f7(tmp63, 1); +//│ tmp65 = CCC1(2); +//│ tmp66 = f7(tmp65, 2); +//│ tmp67 = tmp64 + tmp66; +//│ tmp68 = AAA1(3, 2); +//│ tmp69 = f7(tmp68, 4); +//│ tmp67 + tmp69 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f7, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84, tmp85, tmp86, match_x_rest2, match_x_branch_AAA2, tmp87, tmp88, tmp89; -//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, field_x, field_y) { -//│ let tmp90; -//│ tmp90 = y1 + field_y; -//│ return match_x_rest2(tmp90) +//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, CCC_c, AAA_x_unused, AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, AAA_x_unused1, AAA_y_tmp5; +//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, AAA_y) { +//│ let tmp70; +//│ tmp70 = y1 + AAA_y; +//│ return match_x_rest2(tmp70) //│ }; -//│ match_x_rest2 = function match_x_rest(tmp90) { +//│ match_x_rest2 = function match_x_rest(tmp70) { //│ let a; -//│ a = tmp90; +//│ a = tmp70; //│ return a + 3 //│ }; //│ f7 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ tmp85 = 1; -//│ tmp86 = 3; -//│ tmp78 = (y1) => { -//│ return match_x_branch_AAA2(y1, tmp85, tmp86) +//│ AAA_x_unused = 1; +//│ AAA_y_tmp4 = 3; +//│ tmp63 = (y1) => { +//│ return match_x_branch_AAA2(y1, AAA_y_tmp4) //│ }; -//│ tmp79 = f7(tmp78, 1); -//│ tmp87 = 2; -//│ tmp80 = (y1) => { -//│ let param0, n, tmp90; -//│ param0 = tmp87; +//│ tmp64 = f7(tmp63, 1); +//│ CCC_c = 2; +//│ tmp65 = (y1) => { +//│ let param0, n, tmp70; +//│ param0 = CCC_c; //│ n = param0; -//│ tmp90 = n + 1; -//│ return match_x_rest2(tmp90) -//│ }; -//│ tmp81 = f7(tmp80, 2); -//│ tmp82 = tmp79 + tmp81; -//│ tmp88 = 3; -//│ tmp89 = 2; -//│ tmp83 = (y1) => { -//│ return match_x_branch_AAA2(y1, tmp88, tmp89) -//│ }; -//│ tmp84 = f7(tmp83, 4); -//│ block$res47 = tmp82 + tmp84; +//│ tmp70 = n + 1; +//│ return match_x_rest2(tmp70) +//│ }; +//│ tmp66 = f7(tmp65, 2); +//│ tmp67 = tmp64 + tmp66; +//│ AAA_x_unused1 = 3; +//│ AAA_y_tmp5 = 2; +//│ tmp68 = (y1) => { +//│ return match_x_branch_AAA2(y1, AAA_y_tmp5) +//│ }; +//│ tmp69 = f7(tmp68, 4); +//│ block$res47 = tmp67 + tmp69; //│ undefined //│ = 22 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index cc4f1e194b..7145e59ecc 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -157,15 +157,15 @@ c(AA(2)) //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp, tmp1; +//│ let c, tmp, AA_x; //│ c = function c(x) { //│ return runtime.safeCall(x()) //│ }; -//│ tmp1 = 2; +//│ AA_x = 2; //│ tmp = () => { //│ let scrut; //│ scrut = (x) => { -//│ return tmp1 +//│ return AA_x //│ }; //│ return runtime.safeCall(scrut(x_not_in_scope)) //│ }; @@ -184,7 +184,7 @@ fun c(x, y) = if x is A then x.x c(AA(2), A) //│ JS (unsanitized): -//│ let c1, tmp3; +//│ let c1, tmp2; //│ c1 = function c(x, y) { //│ if (x instanceof AA1.class) { //│ if (y instanceof A1.class) { @@ -196,20 +196,70 @@ c(AA(2), A) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp3 = AA1(2); -//│ c1(tmp3, A1) +//│ tmp2 = AA1(2); +//│ c1(tmp2, A1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp3, tmp4; +//│ let c1, tmp2, AA_x1; //│ c1 = function c(x, y) { //│ return runtime.safeCall(x(y)) //│ }; -//│ tmp4 = 2; -//│ tmp3 = (y) => { +//│ AA_x1 = 2; +//│ tmp2 = (y) => { //│ return runtime.safeCall(y(x_not_in_scope)) //│ }; -//│ block$res8 = c1(tmp3, (x) => { -//│ return x.x +//│ block$res8 = c1(tmp2, (x) => { +//│ return AA_x1 +//│ }); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + + +:fixme +:sjs +fun c(x, y) = if x is + AA then + if y is + A then x.x +fun p() = AA(2) +c(p(), A) +//│ JS (unsanitized): +//│ let p, c2, tmp4; +//│ c2 = function c(x, y) { +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ return x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p = function p() { +//│ return AA1(2) +//│ }; +//│ tmp4 = p(); +//│ c2(tmp4, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p, c2, tmp4; +//│ c2 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ p = function p() { +//│ let AA_x2; +//│ AA_x2 = 2; +//│ return (y) => { +//│ return runtime.safeCall(y(x_not_in_scope)) +//│ } +//│ }; +//│ tmp4 = p(); +//│ block$res10 = c2(tmp4, (x) => { +//│ return AA_x_not_in_scope //│ }); //│ undefined //│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined From d144789c2be456b3ba5ee7c3928a71944a5eb7f7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:41:46 +0800 Subject: [PATCH 108/303] seems to work; need cleanup --- .../scala/hkmc2/codegen/Deforestation.scala | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 69680b3346..b5876ebd9c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -87,7 +87,7 @@ extension (b: Block) case _ => super.applyValue(v) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs(using alwaysDefined: Set[Symbol]) = + def sortedFvs(using alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol] = Map.empty) = object DeforestationFreeVarTraverser extends BlockTraverser(new SymbolSubst()): val ctx = mutable.Set.from(alwaysDefined) val result = mutable.Set.empty[Symbol] @@ -127,6 +127,11 @@ extension (b: Block) case _ => super.applyBlock(b) + override def applyPath(p: Path): Unit = p match + case p @ Select(qual, name) => + selsToBeReplaced.get(p.uid).fold(super.applyPath(p))(s => result += s) + case _ => super.applyPath(p) + override def applyValue(v: Value): Unit = v match case Value.Ref(l) => if !ctx.contains(l) then result += l case _ => super.applyValue(v) @@ -731,20 +736,28 @@ class Deforest(using TL, Raise, Elaborator.State): newDefsArms(newDefsRest(rest)) class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): - + val replaceSelInfo: Map[ResultId, Symbol] = + filteredCtorDests.values.flatMap { + case CtorFinalDest.Match(_, _, _, selMaps) => selMaps._2.values.flatMap(m => m.toList) + case CtorFinalDest.Sel(s) => Nil + }.toMap + lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = + val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) + filteredCtorDests.values.foreach: + case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => + toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2.values.flatMap(m => m.iterator)) + + filteredCtorDests.values.flatMap{ case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr - Some(scrut -> matchExpr.sortedFvs.filterNot(_.uid === l.uid)) + val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys + Some(scrut -> matchExpr.sortedFvs(using nonFreeVars, selReplacementNotForThisSel).filterNot(_.uid === l.uid)) case CtorFinalDest.Sel(s) => None }.toMap - val replaceSelInfo = - filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, _, selMaps) => selMaps._2.values.flatMap(m => m.toList) - case CtorFinalDest.Sel(s) => Nil - }.toMap + println(replaceSelInfo.map(x => x._2)) val replaceSelInfo2 = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] From b6ee0011d056aa95a8f1533641f1371be1c2a747 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:44:51 +0800 Subject: [PATCH 109/303] wip: update tests --- .../src/test/mlscript/deforest/todos.mls | 84 +++++++++++++++---- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 7145e59ecc..52445cefd1 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -131,7 +131,6 @@ module Test with // NOTE: if we replace `x.x` first, the nested pat mat will not be fused... // we can perform the replacing of `x.x` and rewriting at the same time, but // then there will be a non-existing free var `x` -:fixme :sjs fun c(x) = if x is AA then @@ -164,19 +163,18 @@ c(AA(2)) //│ AA_x = 2; //│ tmp = () => { //│ let scrut; -//│ scrut = (x) => { -//│ return AA_x +//│ scrut = (AA_x1) => { +//│ return AA_x1 //│ }; -//│ return runtime.safeCall(scrut(x_not_in_scope)) +//│ return runtime.safeCall(scrut(AA_x)) //│ }; //│ block$res6 = c(tmp); //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 -:fixme :sjs fun c(x, y) = if x is AA then @@ -206,19 +204,18 @@ c(AA(2), A) //│ }; //│ AA_x1 = 2; //│ tmp2 = (y) => { -//│ return runtime.safeCall(y(x_not_in_scope)) +//│ return runtime.safeCall(y(AA_x1)) //│ }; -//│ block$res8 = c1(tmp2, (x) => { -//│ return AA_x1 +//│ block$res8 = c1(tmp2, (AA_x2) => { +//│ return AA_x2 //│ }); //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 -:fixme :sjs fun c(x, y) = if x is AA then @@ -254,14 +251,71 @@ c(p(), A) //│ let AA_x2; //│ AA_x2 = 2; //│ return (y) => { -//│ return runtime.safeCall(y(x_not_in_scope)) +//│ return runtime.safeCall(y(AA_x2)) //│ } //│ }; //│ tmp4 = p(); -//│ block$res10 = c2(tmp4, (x) => { -//│ return AA_x_not_in_scope +//│ block$res10 = c2(tmp4, (AA_x2) => { +//│ return AA_x2 //│ }); //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: x_not_in_scope is not defined +//│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 + + + + +:sjs +fun c(x, y) = if x is + AA then + let a = if y is + A then 1 + a + x.x +fun p() = AA(2) +c(p(), A) +//│ JS (unsanitized): +//│ let p1, c3, tmp6; +//│ c3 = function c(x, y) { +//│ let a, tmp7; +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ tmp7 = 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ a = tmp7; +//│ return a + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p1 = function p() { +//│ return AA1(2) +//│ }; +//│ tmp6 = p1(); +//│ c3(tmp6, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p1, c3, tmp6; +//│ c3 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ p1 = function p() { +//│ let AA_x2; +//│ AA_x2 = 2; +//│ return (y) => { +//│ return runtime.safeCall(y(AA_x2)) +//│ } +//│ }; +//│ tmp6 = p1(); +//│ block$res12 = c3(tmp6, (AA_x2) => { +//│ let a, tmp7; +//│ tmp7 = 1; +//│ a = tmp7; +//│ return a + AA_x2 +//│ }); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 From d626ecf67079d7931bb9b0090c62c9f9d93edeac Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Mar 2025 00:52:06 +0800 Subject: [PATCH 110/303] ctordest and ctorfinaldests are already branch-specific --- .../scala/hkmc2/codegen/Deforestation.scala | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b5876ebd9c..015f058d7e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -60,7 +60,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[ClassSymbol, Map[Tree.Ident, Symbol]] -> Map[ClassSymbol, Map[ResultId, Symbol]]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) case Sel(s: ResultId) trait FieldSelTrait: @@ -671,8 +671,8 @@ class Deforest(using TL, Raise, Elaborator.State): case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) case _ => false } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[Tree.Ident, Symbol]] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ClassSymbol, Map[ResultId, Symbol]] + val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] println(dtors.head._2.arms.length) dtors.head._2.arms.foreach: case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => @@ -684,24 +684,29 @@ class Deforest(using TL, Raise, Elaborator.State): println(s"in arm: ${c}; sel in arms: ${selsInArms}; all sels: ${sels}") selsInArms.foreach: fs => assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(c): - case None => - val sym = if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - else TempSymbol(N, s"${c.name}_${fs.field.name}") - Some(Map(fs.field -> sym)) - case Some(v) => - if !v.contains(fs.field) then - val sym = if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - else TempSymbol(N, s"${c.name}_${fs.field.name}") - Some(v + (fs.field -> sym)) - else Some(v) - val sym = fieldNameToSymToBeReplaced(c)(fs.field) + // fieldNameToSymToBeReplaced.updateWith(c): + // case None => + // val sym = if varSymInsteadOfTempSym + // then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + // else TempSymbol(N, s"${c.name}_${fs.field.name}") + // Some(Map(fs.field -> sym)) + // case Some(v) => + // if !v.contains(fs.field) then + // val sym = if varSymInsteadOfTempSym + // then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + // else TempSymbol(N, s"${c.name}_${fs.field.name}") + // Some(v + (fs.field -> sym)) + // else Some(v) + fieldNameToSymToBeReplaced.updateWith(fs.field): + case Some(v) => Some(v) + case None => Some(if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) + else TempSymbol(N, s"${c.name}_${fs.field.name}")) + val sym = fieldNameToSymToBeReplaced(fs.field) println("herer" + sym) - selectionUidsToSymToBeReplaced.updateWith(c): - case None => Some(Map(fs.expr -> sym)) - case Some(v) => Some(v + (fs.expr -> sym)) + selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) + // case None => Some(Map(fs.expr -> sym)) + // case Some(v) => Some(v + (fs.expr -> sym)) case _ => () Some(CtorFinalDest.Match( dtors.head._1, @@ -738,7 +743,10 @@ class Deforest(using TL, Raise, Elaborator.State): class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): val replaceSelInfo: Map[ResultId, Symbol] = filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, _, selMaps) => selMaps._2.values.flatMap(m => m.toList) + case CtorFinalDest.Match(_, _, _, selMaps) => + // assert(selMaps._1.size <= 1) + // assert(selMaps._2.size <= 1) + selMaps._2 case CtorFinalDest.Sel(s) => Nil }.toMap @@ -746,7 +754,7 @@ class Deforest(using TL, Raise, Elaborator.State): val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) filteredCtorDests.values.foreach: case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2.values.flatMap(m => m.iterator)) + toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) filteredCtorDests.values.flatMap{ @@ -950,7 +958,7 @@ class Deforest(using TL, Raise, Elaborator.State): // tl.log(call.toString() + " ----> " + body) // use pre-determined symbols, create temp symbols for un-used fields - val usedFieldIdentToSymbolsToBeReplaced = selsMap._1(c) + val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 val allFieldIdentToSymbolsToBeReplaced = getClsFields(c).map: f => f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) @@ -977,7 +985,7 @@ class Deforest(using TL, Raise, Elaborator.State): c, sels.toSet, assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, - selsMap._1(c) -> selsMap._2(c)) + selsMap._1 -> selsMap._2) // val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): From 8eca4423a7671686ae76a5480c00c1edc0a5ea47 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Mar 2025 02:34:13 +0800 Subject: [PATCH 111/303] some cleanup and update tests --- .../scala/hkmc2/codegen/Deforestation.scala | 227 +++++------------- .../deforest/selectionsInNestedMatch.mls | 206 ++++++++++++++++ .../src/test/mlscript/deforest/simple.mls | 38 ++- .../src/test/mlscript/deforest/todos.mls | 193 --------------- 4 files changed, 307 insertions(+), 357 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 015f058d7e..1aecf0f3a3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -35,14 +35,14 @@ object StratVarState: type CtorExpr = ResultId -// enum ProdStrat: + case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat -// enum ConsStrat: + case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends ConsStrat: assert(scrut === expr.scrut.uid) d.matchScrutToMatchBlock.updateWith(scrut): @@ -123,7 +123,7 @@ extension (b: Block) applyPath(rhs) applyBlock(rest) ctx -= sym - case c: ClsLikeDefn => result ++= c.freeVars // TODO: just use the `freeVars` lazy val for ClsLikeDefns for now... + case c: ClsLikeDefn => ??? // not supported case _ => super.applyBlock(b) @@ -143,13 +143,6 @@ extension (b: Block) ctx --= paramSymbols DeforestationFreeVarTraverser.applyBlock(b) DeforestationFreeVarTraverser.result.toList.sortBy(_.uid) - - // def replaceSelect(using ss: Set[ResultId], args: Map[Tree.Ident, Value.Ref]): Block = - // object ReplaceSelectTransformer extends BlockTransformer(new SymbolSubst()): - // override def applyPath(p: Path): Path = p match - // case s@Select(_, nme) if ss(s.uid) => args(nme) - // case _ => p - // ReplaceSelectTransformer.applyBlock(b) def hasExplicitRet: Boolean = object HasExplicitRetTraverser extends BlockTraverserShallow(new SymbolSubst()): @@ -186,10 +179,6 @@ class Deforest(using TL, Raise, Elaborator.State): lowerBounds.foreach(l => tl.log("\t" + l)) tl.log("-----------------------------------------") - // tl.log("ctor -> dtor:") - // ctorDests.foreach(l => tl.log("\t" + l._1.toString() + " ===> " + l._2.size)) - // tl.log("dtor -> ctor:") - // dtorSources.foreach(l => tl.log("\t" + l._1.toString().take(20) + " ===> " + l._2.toString().take(20))) tl.log("ctor -> dtor") resolveClashes._1.foreach(u => tl.log("\t" + u)) @@ -202,8 +191,7 @@ class Deforest(using TL, Raise, Elaborator.State): ) // these are never considered as free vars (because of their symbol type) - // currently cannot distinguish toplevel `let` and function params (both are varsymbol) - // so toplevel `let`s are not considered globally defined vars for now... + // consider TopLevelSym, BlockMemberSymbols and BuiltInSyms as globally defined... object globallyDefinedVars: val store = mutable.Set.empty[Symbol] @@ -211,7 +199,7 @@ class Deforest(using TL, Raise, Elaborator.State): def init(b: Block) = object Subst extends SymbolSubst: - // only consider block member symbols as globally defined + override def mapTopLevelSym(s: TopLevelSymbol): TopLevelSymbol = store += s; s override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = @@ -250,15 +238,13 @@ class Deforest(using TL, Raise, Elaborator.State): object FreshVarForAllVars extends BlockTraverser(AllVarsSymbolSubst) FreshVarForAllVars.applyBlock(p) - // currently, symbols that shouldn't be read from ctx are symbols for ctors (class/object) blkMem symbols // TODO: ctor as a function? def getStratOfSym(s: Symbol) = s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd - case _: BlockMemberSymbol => store.getOrElse(s, {tl.log(s"${s.nme} no strat"); NoProd}) // For `fun` and `let` only, not classes or modules? - case _: LocalSymbol => store.getOrElse(s, NoProd) - case _: FlowSymbol => store(s) + case _: BlockMemberSymbol => store(s) + case _: LocalSymbol => store(s) def get(s: Symbol) = store.get(s) def +=(e: Symbol -> ProdVar) = store += e def addAll(es: Iterable[Symbol -> ProdVar]) = es.foreach(store += _) @@ -278,13 +264,15 @@ class Deforest(using TL, Raise, Elaborator.State): val scrutStrat = processResult(scrut) constrain(scrutStrat, Dtor(scrut.uid)(m)(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then - arms.map { case (Case.Cls(s, _), body) => - // TODO: fix this "asInstanceOf"? - processBlock(body)(using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + (scrut.uid -> s)) - } + arms.map: + case (Case.Cls(s, _), body) => + processBlock(body)( + using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), + matching + (scrut.uid -> s) + ) else - arms.map{ case (_, armBody) => processBlock(armBody) } - // TODO: dflt? + arms.map: + case (_, armBody) => processBlock(armBody) val dfltRes = dflt.map(processBlock) rest match case End(msg) => @@ -296,13 +284,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Return(res, implct) => processResult(res) case Assign(lhs, rhs, rest) => - symToStrat.get(lhs) match - // case None => - // val lhsTpeVar = freshVar(lhs.nme) - // constrain(processResult(rhs), lhsTpeVar._2) - // symToStrat += lhs -> lhsTpeVar._1 - case Some(v) => - constrain(processResult(rhs), v.asConsStrat) + constrain(processResult(rhs), symToStrat(lhs).asConsStrat) processBlock(rest) case Begin(sub, rest) => processBlock(sub) @@ -310,42 +292,38 @@ class Deforest(using TL, Raise, Elaborator.State): case Define(defn, rest) => defn match case FunDefn(_, sym, params, body) => - // val funSymStratVar = freshVar(sym.nme) - // symToStrat += sym -> funSymStratVar._1 val funSymStratVar = symToStrat(sym) val param = params.head match case ParamList(flags, params, restParam) => params val funStrat = constrFun(param, body) // TODO: handle mutiple param list constrain(funStrat, funSymStratVar.asConsStrat) funSymStratVar - case ValDefn(owner, k, sym, rhs) => NoProd // TODO: + case ValDefn(owner, k, sym, rhs) => ??? + // only handle code inside module for now to show the + // todo case of if scrut being not the same as what the user writes case c: ClsLikeDefn if c.sym.asMod.isDefined => - c.methods.foreach{ case FunDefn(_, sym, params, body) => - val funSymStratVar = freshVar(sym.nme) - symToStrat += sym -> funSymStratVar._1 - val param = params.head match - case ParamList(flags, params, restParam) => params - val funStrat = constrFun(param, body) // TODO: handle mutiple param list - constrain(funStrat, funSymStratVar._2) - funSymStratVar._1 - } + c.methods.foreach: + case FunDefn(_, sym, params, body) => + val funSymStratVar = freshVar(sym.nme) + symToStrat += sym -> funSymStratVar._1 + val param = params.head match + case ParamList(flags, params, restParam) => params + val funStrat = constrFun(param, body) // TODO: handle mutiple param list + constrain(funStrat, funSymStratVar._2) + funSymStratVar._1 processBlock(c.ctor) - case _ => ??? // TODO: + case _ => ??? processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd - case AssignField(lhs, nme, rhs, rest) => ??? - case Label(label, body, rest) => ??? - case Break(label) => ??? - case Continue(label) => ??? - case TryBlock(sub, finallyDo, rest) => ??? def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], matching: Map[ResultId, ClsOrModSymbol] ) = - val paramSyms = params.map{ case Param(_, sym, _) => sym } - val paramStrats = paramSyms.map{ sym => symToStrat(sym) } + val paramSyms = params.map: + case Param(_, sym, _) => sym + val paramStrats = paramSyms.map(symToStrat.apply) symToStrat.addAll(paramSyms.zip(paramStrats)) val res = freshVar() constrain(processBlock(body), res._2) @@ -356,9 +334,9 @@ class Deforest(using TL, Raise, Elaborator.State): matching: Map[ResultId, ClsOrModSymbol] ): ProdStrat = r match case c@Call(f, args) => - val argsTpe = args.map { case Arg(false, value) => - processResult(value) - } + val argsTpe = args.map: + case Arg(false, value) => processResult(value) + f match case s@Select(p, nme) => s.symbol.map(_.asCls) match @@ -396,9 +374,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Value.Lit(lit) => ??? case Value.Arr(elems) => ??? - case Instantiate(cls, args) => - // TODO: - freshVar()._1 + case Instantiate(cls, args) => ??? case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => @@ -416,18 +392,6 @@ class Deforest(using TL, Raise, Elaborator.State): val tpeVar = freshVar() constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 - - // if inArm.contains(pStrat) then - // // assert(sel.symbol.exists(_.isInstanceOf[TermSymbol])) - // val tpeVar = freshVar() - // val selStrat = FieldSel(nme, tpeVar._2)(sel.uid) - // selStrat.updateFilter(pStrat, inArm(pStrat) :: Nil) - // constrain(pStrat, selStrat) - // tpeVar._1 - // else - // val tpeVar = freshVar() - // constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid)) - // tpeVar._1 case v@Value.Ref(l) => l.asObj match case None => symToStrat.getStratOfSym(l) @@ -483,20 +447,15 @@ class Deforest(using TL, Raise, Elaborator.State): ctorDests.update(expr, dtorStrat.expr) dtorSources.update(scrut, expr) case (Ctor(ctor, args, expr), selDtor@FieldSel(field, consVar)) => - // if clsSym.isDefined then - // args.get(clsSym.get).map(p => handle(p -> consVar)) - // else ctorDests.update(expr, selDtor) dtorSources.update(selDtor.expr, expr) - args.find(a => a._1.id == field).map(p => - // rewritingSel.add(sel) + args.find(a => a._1.id == field).map: p => handle(p._2 -> consVar) - ) case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? case (p: ProdVar, _) => upperBounds += p.uid -> (cons :: upperBounds(p.uid)) - lowerBounds(p.uid).foreach{ l => + lowerBounds(p.uid).foreach: l => (l, cons) match case (l: ProdVar, sel@FieldSel(field, consVar)) => sel.updateFilter(l, sel.filter(p)) @@ -507,10 +466,9 @@ class Deforest(using TL, Raise, Elaborator.State): else () case _ => handle(l -> cons) - } case (_, c: ConsVar) => lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) - upperBounds(c.uid).foreach { u => + upperBounds(c.uid).foreach: u => (prod, u) match case (Ctor(ctor, args, _), sel@FieldSel(field, consVar)) => if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then @@ -519,8 +477,6 @@ class Deforest(using TL, Raise, Elaborator.State): () case (_: ProdVar, _) => ??? // unreachable, should be handled above case _ => handle(prod -> u) - } - // upperBounds(uid).foreach(c => handle(prod -> c)) case (Ctor(ctor, args, _), NoCons) => () case (ProdFun(l, r), Dtor(cls)) => ??? case (ProdFun(l, r), FieldSel(field, consVar)) => ??? @@ -596,7 +552,7 @@ class Deforest(using TL, Raise, Elaborator.State): val removeCycle = { def getCtorInArm(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = val ctorSym = getClsSymOfUid(ctor) - val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.get._2 + val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get object GetCtorsTraverser extends BlockTraverser(new SymbolSubst()): val ctors = mutable.Set.empty[ResultId] @@ -673,40 +629,25 @@ class Deforest(using TL, Raise, Elaborator.State): } then val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] - println(dtors.head._2.arms.length) + dtors.head._2.arms.foreach: case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => val c = cOrMod.asCls.get // if this arm is used more than once, should be var symbol because the arm body will be // extracted to a function, otherwise just temp symbol val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c }.distinct - println(s"in arm: ${c}; sel in arms: ${selsInArms}; all sels: ${sels}") + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } + selsInArms.foreach: fs => assert(getClsFields(c).map(_.id).contains(fs.field)) - // fieldNameToSymToBeReplaced.updateWith(c): - // case None => - // val sym = if varSymInsteadOfTempSym - // then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - // else TempSymbol(N, s"${c.name}_${fs.field.name}") - // Some(Map(fs.field -> sym)) - // case Some(v) => - // if !v.contains(fs.field) then - // val sym = if varSymInsteadOfTempSym - // then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - // else TempSymbol(N, s"${c.name}_${fs.field.name}") - // Some(v + (fs.field -> sym)) - // else Some(v) fieldNameToSymToBeReplaced.updateWith(fs.field): case Some(v) => Some(v) case None => Some(if varSymInsteadOfTempSym then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) else TempSymbol(N, s"${c.name}_${fs.field.name}")) val sym = fieldNameToSymToBeReplaced(fs.field) - println("herer" + sym) + selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) - // case None => Some(Map(fs.expr -> sym)) - // case Some(v) => Some(v + (fs.expr -> sym)) case _ => () Some(CtorFinalDest.Match( dtors.head._1, @@ -744,12 +685,10 @@ class Deforest(using TL, Raise, Elaborator.State): val replaceSelInfo: Map[ResultId, Symbol] = filteredCtorDests.values.flatMap { case CtorFinalDest.Match(_, _, _, selMaps) => - // assert(selMaps._1.size <= 1) - // assert(selMaps._2.size <= 1) selMaps._2 case CtorFinalDest.Sel(s) => Nil }.toMap - + lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) filteredCtorDests.values.foreach: @@ -765,10 +704,7 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Sel(s) => None }.toMap - - println(replaceSelInfo.map(x => x._2)) - - val replaceSelInfo2 = mutable.Map.empty[ResultId, Set[ResultId] -> Map[Tree.Ident, Value.Ref]] + override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => @@ -802,12 +738,11 @@ class Deforest(using TL, Raise, Elaborator.State): currentUsedCtorArgsToFields: Map[Tree.Ident, Value.Ref], preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty ) = - // FIXME: change to use pre-determined symbols assert(scrut === m.scrut.uid) val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) store.get(scrut).flatMap(_.get(cls)) match case None => // not registered before, or this branch of this match will only appear once - val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.get._2 // TODO: handle wildcard case + val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get val rest = m.rest @@ -834,19 +769,9 @@ class Deforest(using TL, Raise, Elaborator.State): // arguments for lambda: free vars // arguments for that function: free vars and pattern vars - // FIXME: use pre determined var symbols here - // val newSelMapSyms = currentUsedCtorArgsToFields.map { case (id, r) => id -> VarSymbol(Tree.Ident("field_" + id.name)) } - - // val newSelMaps = newSelMapSyms.map { case (id, s) => id -> Value.Ref(s).asInstanceOf[Value.Ref] } - - // replaceSelInfo2 += scrut -> (sel -> newSelMaps) val bodyReplaceSel = applyBlock(body) - // replaceSelInfo2 -= scrut - - // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, newSelMaps) val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) - val funBody = makeBody(bodyReplaceSel) val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) val newDef = FunDefn( @@ -856,7 +781,7 @@ class Deforest(using TL, Raise, Elaborator.State): ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => - println(s"in param list: ${v}") + Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N) ), N @@ -872,14 +797,7 @@ class Deforest(using TL, Raise, Elaborator.State): ) ) else - // make a lambda, and replace the selections - - // replaceSelInfo2 += scrut -> (sel -> currentUsedCtorArgsToFields) val bodyReplaceSel = applyBlock(body) - // replaceSelInfo2 -= scrut - - // val bodyReplaceSel = bodyInitRewritten.replaceSelect(using sel, currentUsedCtorArgsToFields) - val lambdaBody = makeBody(bodyReplaceSel) Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), @@ -949,13 +867,11 @@ class Deforest(using TL, Raise, Elaborator.State): k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects)) def handleCtorCall(c: ClassSymbol) = - // assert(ctorDests(call).size == 1, s"$call has more than one destination") filteredCtorDests.get(call.uid) match case None => handleNormalCall(args) case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.get._2 - // tl.log(call.toString() + " ----> " + body) + val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.map(_._2).orElse(expr.dflt).get // use pre-determined symbols, create temp symbols for un-used fields val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 @@ -964,21 +880,17 @@ class Deforest(using TL, Raise, Elaborator.State): // if all vars are temp vars, no need to create more temp vars // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) - val assignedTempSyms = if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then - allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) - else - allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match - case ts: TempSymbol => id -> ts - case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") - } - - - + val assignedTempSyms = + if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then + allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) + else + allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match + case ts: TempSymbol => id -> ts + case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") + } + val newArgs = args.map(_ => TempSymbol(N)) - // FIXME: use pre-determined symbols, create temp symbols for un-used fields - // val idsToArgs = getClsFields(c).map(s => s.id).zip(newArgs.map(s => Value.Ref(s).asInstanceOf[Value.Ref])).toMap - val bodyAndRestInLam = matchArms.getOrElseUpdate( scrut, expr, @@ -986,15 +898,10 @@ class Deforest(using TL, Raise, Elaborator.State): sels.toSet, assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, selsMap._1 -> selsMap._2) - // val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, c, sels.toSet, idsToArgs) args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } - // args.zip(newArgs).foldRight[Block](k(bodyAndRestInLam)){ case ((a, tmp), rest) => - // applyResult2(a.value): r => - // Assign(tmp, r, rest) - // } case Some(CtorFinalDest.Sel(s)) => val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } val idx = getClsFields(c).indexWhere(s => s.id === selFieldName) @@ -1019,22 +926,17 @@ class Deforest(using TL, Raise, Elaborator.State): else replaceSelInfo.get(s.uid) match case None => k(s) - case Some(v) => - println(v) - k(Value.Ref(v)) - // replaceSelInfo2.values.find(v => v._1.contains(s.uid)) match - // case None => k(s) - // case Some(v) => k(v._2(nme)) + case Some(v) => k(Value.Ref(v)) + case Some(mod) => filteredCtorDests.get(s.uid) match case None => k(s) case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - // tl.log(mod.toString + " ----> " + body) + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) k(bodyAndRestInLam) - case Some(_) => ??? // TODO: a selection on a module consumes it + case Some(_) => ??? // a selection on a module consumes it case r@Value.Ref(l) => l.asObj match case None => k(r) @@ -1043,12 +945,11 @@ class Deforest(using TL, Raise, Elaborator.State): case None => k(r) case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.get - // tl.log(mod.toString + " ----> " + body) + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) k(bodyAndRestInLam) - case Some(_) => ??? // TODO: a selection on a module consumes it + case Some(_) => ??? // a selection on a module consumes it case Value.This(sym) => k(Value.This(sym)) case Value.Lit(lit) => k(Value.Lit(lit)) case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls new file mode 100644 index 0000000000..adc8751a2f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -0,0 +1,206 @@ +:js +:deforest + +object A +object B +object C +class AA(x) +class BB(x) + +object Nil +class Cons(h, t) + +object None +class Some(x) + + + + +:sjs +fun c(x) = if x is + AA then + if A is + A then x.x +c(AA(2)) +//│ JS (unsanitized): +//│ let c, tmp; +//│ c = function c(x) { +//│ let scrut; +//│ if (x instanceof AA1.class) { +//│ scrut = A1; +//│ if (scrut instanceof A1.class) { +//│ return x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = AA1(2); +//│ c(tmp) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c, tmp, AA_x; +//│ c = function c(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x = 2; +//│ tmp = () => { +//│ let scrut; +//│ scrut = (AA_x1) => { +//│ return AA_x1 +//│ }; +//│ return runtime.safeCall(scrut(AA_x)) +//│ }; +//│ block$res4 = c(tmp); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + +:sjs +fun c(x, y) = if x is + AA then + if y is + A then x.x +c(AA(2), A) +//│ JS (unsanitized): +//│ let c1, tmp2; +//│ c1 = function c(x, y) { +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ return x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp2 = AA1(2); +//│ c1(tmp2, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c1, tmp2, AA_x1; +//│ c1 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ AA_x1 = 2; +//│ tmp2 = (y) => { +//│ return runtime.safeCall(y(AA_x1)) +//│ }; +//│ block$res6 = c1(tmp2, (AA_x2) => { +//│ return AA_x2 +//│ }); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + + +:sjs +fun c(x, y) = if x is + AA then + if y is + A then x.x +fun p() = AA(2) +c(p(), A) +//│ JS (unsanitized): +//│ let p, c2, tmp4; +//│ c2 = function c(x, y) { +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ return x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p = function p() { +//│ return AA1(2) +//│ }; +//│ tmp4 = p(); +//│ c2(tmp4, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p, c2, tmp4; +//│ c2 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ p = function p() { +//│ let AA_x2; +//│ AA_x2 = 2; +//│ return (y) => { +//│ return runtime.safeCall(y(AA_x2)) +//│ } +//│ }; +//│ tmp4 = p(); +//│ block$res8 = c2(tmp4, (AA_x2) => { +//│ return AA_x2 +//│ }); +//│ undefined +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 + + + + +:sjs +fun c(x, y) = if x is + AA then + let a = if y is + A then 1 + a + x.x +fun p() = AA(2) +c(p(), A) +//│ JS (unsanitized): +//│ let p1, c3, tmp6; +//│ c3 = function c(x, y) { +//│ let a, tmp7; +//│ if (x instanceof AA1.class) { +//│ if (y instanceof A1.class) { +//│ tmp7 = 1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ a = tmp7; +//│ return a + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p1 = function p() { +//│ return AA1(2) +//│ }; +//│ tmp6 = p1(); +//│ c3(tmp6, A1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p1, c3, tmp6; +//│ c3 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ p1 = function p() { +//│ let AA_x2; +//│ AA_x2 = 2; +//│ return (y) => { +//│ return runtime.safeCall(y(AA_x2)) +//│ } +//│ }; +//│ tmp6 = p1(); +//│ block$res10 = c3(tmp6, (AA_x2) => { +//│ let a, tmp7; +//│ tmp7 = 1; +//│ a = tmp7; +//│ return a + AA_x2 +//│ }); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 38d82a0eaa..1073e327eb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -650,7 +650,7 @@ test() :sjs fun c(a) = - val x = if a is + let x = if a is A then 1 B then 2 print(x) @@ -1597,3 +1597,39 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ = 22 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 22 + + +:sjs +fun c(x, m) = if x is + AA(n) then n + 1 + else m +c(BB(3), 0) +//│ JS (unsanitized): +//│ let c4, tmp77; +//│ c4 = function c(x1, m) { +//│ let param0, n; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ n = param0; +//│ return n + 1 +//│ } else { +//│ return m +//│ } +//│ }; +//│ tmp77 = BB1(3); +//│ c4(tmp77, 0) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c4, tmp77, BB_bb_unused; +//│ c4 = function c(x1, m) { +//│ return runtime.safeCall(x1(m)) +//│ }; +//│ BB_bb_unused = 3; +//│ tmp77 = (m) => { +//│ return m +//│ }; +//│ block$res49 = c4(tmp77, 0); +//│ undefined +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 52445cefd1..3a6ec1c342 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -126,196 +126,3 @@ module Test with //│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// NOTE: if we replace `x.x` first, the nested pat mat will not be fused... -// we can perform the replacing of `x.x` and rewriting at the same time, but -// then there will be a non-existing free var `x` -:sjs -fun c(x) = if x is - AA then - if A is - A then x.x -c(AA(2)) -//│ JS (unsanitized): -//│ let c, tmp; -//│ c = function c(x) { -//│ let scrut; -//│ if (x instanceof AA1.class) { -//│ scrut = A1; -//│ if (scrut instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp = AA1(2); -//│ c(tmp) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, tmp, AA_x; -//│ c = function c(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ AA_x = 2; -//│ tmp = () => { -//│ let scrut; -//│ scrut = (AA_x1) => { -//│ return AA_x1 -//│ }; -//│ return runtime.safeCall(scrut(AA_x)) -//│ }; -//│ block$res6 = c(tmp); -//│ undefined -//│ = 2 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 - - -:sjs -fun c(x, y) = if x is - AA then - if y is - A then x.x -c(AA(2), A) -//│ JS (unsanitized): -//│ let c1, tmp2; -//│ c1 = function c(x, y) { -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp2 = AA1(2); -//│ c1(tmp2, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c1, tmp2, AA_x1; -//│ c1 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ AA_x1 = 2; -//│ tmp2 = (y) => { -//│ return runtime.safeCall(y(AA_x1)) -//│ }; -//│ block$res8 = c1(tmp2, (AA_x2) => { -//│ return AA_x2 -//│ }); -//│ undefined -//│ = 2 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 - - - -:sjs -fun c(x, y) = if x is - AA then - if y is - A then x.x -fun p() = AA(2) -c(p(), A) -//│ JS (unsanitized): -//│ let p, c2, tmp4; -//│ c2 = function c(x, y) { -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p = function p() { -//│ return AA1(2) -//│ }; -//│ tmp4 = p(); -//│ c2(tmp4, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p, c2, tmp4; -//│ c2 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ p = function p() { -//│ let AA_x2; -//│ AA_x2 = 2; -//│ return (y) => { -//│ return runtime.safeCall(y(AA_x2)) -//│ } -//│ }; -//│ tmp4 = p(); -//│ block$res10 = c2(tmp4, (AA_x2) => { -//│ return AA_x2 -//│ }); -//│ undefined -//│ = 2 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 - - - - -:sjs -fun c(x, y) = if x is - AA then - let a = if y is - A then 1 - a + x.x -fun p() = AA(2) -c(p(), A) -//│ JS (unsanitized): -//│ let p1, c3, tmp6; -//│ c3 = function c(x, y) { -//│ let a, tmp7; -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ tmp7 = 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ a = tmp7; -//│ return a + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p1 = function p() { -//│ return AA1(2) -//│ }; -//│ tmp6 = p1(); -//│ c3(tmp6, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p1, c3, tmp6; -//│ c3 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ p1 = function p() { -//│ let AA_x2; -//│ AA_x2 = 2; -//│ return (y) => { -//│ return runtime.safeCall(y(AA_x2)) -//│ } -//│ }; -//│ tmp6 = p1(); -//│ block$res12 = c3(tmp6, (AA_x2) => { -//│ let a, tmp7; -//│ tmp7 = 1; -//│ a = tmp7; -//│ return a + AA_x2 -//│ }); -//│ undefined -//│ = 3 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 From eb0a9552e75789220223b4500af7ad992567b531 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Mar 2025 03:16:39 +0800 Subject: [PATCH 112/303] fix non determinism --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1aecf0f3a3..a8a3a87bc9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -609,7 +609,7 @@ class Deforest(using TL, Raise, Elaborator.State): // we need only one CtorFinalDest per arm val handledMatches = mutable.Map.empty[ClsOrModSymbol, Opt[CtorFinalDest]] - resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels)) => + resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head.expr)) else if dtors.size == 0 && sels.size > 1 then diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index a2a2ead9d2..fec6c4ffce 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -310,4 +310,4 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case _ => output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") - + From 06a4ddbf60a869d4d338a600ca6bb5c3d0e7e513 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Mar 2025 13:01:26 +0800 Subject: [PATCH 113/303] further cleanup --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a8a3a87bc9..90fd2f1092 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -221,20 +221,12 @@ class Deforest(using TL, Raise, Elaborator.State): object AllVarsSymbolSubst extends SymbolSubst: override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = store += s -> freshVar(s.nme)._1; s - override def mapFlowSym(s: FlowSymbol): FlowSymbol = - store += s -> freshVar(s.nme)._1; s override def mapTempSym(s: TempSymbol): TempSymbol = store += s -> freshVar(s.nme)._1; s override def mapVarSym(s: VarSymbol): VarSymbol = store += s -> freshVar(s.nme)._1; s - override def mapInstSym(s: InstSymbol): InstSymbol = - store += s -> freshVar(s.nme)._1; s override def mapTermSym(s: TermSymbol): TermSymbol = store += s -> freshVar(s.nme)._1; s - override def mapClsSym(s: ClassSymbol): ClassSymbol = - store += s -> freshVar(s.nme)._1; s - override def mapModuleSym(s: ModuleSymbol): ModuleSymbol = - store += s -> freshVar(s.nme)._1; s object FreshVarForAllVars extends BlockTraverser(AllVarsSymbolSubst) FreshVarForAllVars.applyBlock(p) @@ -245,9 +237,8 @@ class Deforest(using TL, Raise, Elaborator.State): case _: TopLevelSymbol => NoProd case _: BlockMemberSymbol => store(s) case _: LocalSymbol => store(s) - def get(s: Symbol) = store.get(s) def +=(e: Symbol -> ProdVar) = store += e - def addAll(es: Iterable[Symbol -> ProdVar]) = es.foreach(store += _) + def addAll(es: Iterable[Symbol -> ProdVar]) = store.addAll(es) def apply(s: Symbol) = store(s) def getClsFields(s: ClassSymbol) = s.tree.clsParams From 4c472fd5682f545b68ae5dae77b88b9108ad7dcb Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:24:36 +0800 Subject: [PATCH 114/303] fix: there should be one CtorFinalDest per arm for each pat mat expr --- .../scala/hkmc2/codegen/Deforestation.scala | 16 +- .../src/test/mlscript/deforest/simple.mls | 166 ++++++++++++++++++ 2 files changed, 175 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 90fd2f1092..d81c8c945d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -178,12 +178,14 @@ class Deforest(using TL, Raise, Elaborator.State): tl.log("lower:") lowerBounds.foreach(l => tl.log("\t" + l)) tl.log("-----------------------------------------") - - tl.log("ctor -> dtor") resolveClashes._1.foreach(u => tl.log("\t" + u)) tl.log("dtor -> ctor") resolveClashes._2.foreach(l => tl.log("\t" + l)) + tl.log("-----------------------------------------") + filteredCtorDests.foreach: + case (ctorUid, CtorFinalDest.Sel(s)) => tl.log("\t" + ctorUid + " --sel--> " + s) + case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log("\t" + ctorUid + " --mat-->" + scrut ) Program( p.imports, @@ -597,8 +599,8 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = val res = mutable.Map.empty[CtorExpr, CtorFinalDest] - // we need only one CtorFinalDest per arm - val handledMatches = mutable.Map.empty[ClsOrModSymbol, Opt[CtorFinalDest]] + // we need only one CtorFinalDest per arm for each pat mat expr + val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, Opt[CtorFinalDest]] resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels)) => val filteredDtor = { @@ -611,8 +613,8 @@ class Deforest(using TL, Raise, Elaborator.State): None else if dtors.size == 1 then val currentCtorCls = getClsSymOfUid(ctor) - handledMatches.getOrElseUpdate(currentCtorCls, { - val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + handledMatches.getOrElseUpdate(scrutRef.uid -> currentCtorCls, { if sels.forall{ s => ResultUid(s.expr) match case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) @@ -691,7 +693,7 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys - Some(scrut -> matchExpr.sortedFvs(using nonFreeVars, selReplacementNotForThisSel).filterNot(_.uid === l.uid)) + Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) case CtorFinalDest.Sel(s) => None }.toMap diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 1073e327eb..735f790609 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1633,3 +1633,169 @@ c(BB(3), 0) //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 + +:sjs +fun f(a) = if a is + AA(BB(B)) then 3 +f(AA(BB(B))) +//│ JS (unsanitized): +//│ let f8, tmp79, tmp80; +//│ f8 = function f(a) { +//│ let param0, param01; +//│ if (a instanceof AA1.class) { +//│ param0 = a.aa; +//│ if (param0 instanceof BB1.class) { +//│ param01 = param0.bb; +//│ if (param01 instanceof B1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp79 = BB1(B1); +//│ tmp80 = AA1(tmp79); +//│ f8(tmp80) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f8, tmp79, tmp80, BB_bb, AA_aa1; +//│ f8 = function f(a) { +//│ return runtime.safeCall(a(BB_bb)) +//│ }; +//│ BB_bb = () => { +//│ return 3 +//│ }; +//│ tmp79 = () => { +//│ let param0; +//│ param0 = BB_bb; +//│ return runtime.safeCall(param0()) +//│ }; +//│ AA_aa1 = tmp79; +//│ tmp80 = (BB_bb1) => { +//│ let param0; +//│ param0 = AA_aa1; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res51 = f8(tmp80); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 + + +:sjs +fun f(x) = if x is + AA(b) then g(b) +fun g(b) = if b is + AA(a) then a + 1 +f(AA(AA(0))) +//│ JS (unsanitized): +//│ let f9, g, tmp83, tmp84; +//│ f9 = function f(x1) { +//│ let param0, b; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ b = param0; +//│ return g(b) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ g = function g(b) { +//│ let param0, a; +//│ if (b instanceof AA1.class) { +//│ param0 = b.aa; +//│ a = param0; +//│ return a + 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp83 = AA1(0); +//│ tmp84 = AA1(tmp83); +//│ f9(tmp84) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f9, g, tmp83, tmp84, AA_aa2, AA_aa3; +//│ f9 = function f(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ g = function g(b) { +//│ return runtime.safeCall(b()) +//│ }; +//│ AA_aa2 = 0; +//│ tmp83 = () => { +//│ let param0, a; +//│ param0 = AA_aa2; +//│ a = param0; +//│ return a + 1 +//│ }; +//│ AA_aa3 = tmp83; +//│ tmp84 = () => { +//│ let param0, b; +//│ param0 = AA_aa3; +//│ b = param0; +//│ return g(b) +//│ }; +//│ block$res53 = f9(tmp84); +//│ undefined +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 + + +:sjs +fun c2(x) = if x is + AA(AA(a)) then a +c2(AA(AA(0))) +//│ JS (unsanitized): +//│ let c21, tmp87, tmp88; +//│ c21 = function c2(x1) { +//│ let param0, param01, a; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.aa; +//│ a = param01; +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp87 = AA1(0); +//│ tmp88 = AA1(tmp87); +//│ c21(tmp88) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c21, tmp87, tmp88, AA_aa4, AA_aa5; +//│ c21 = function c2(x1) { +//│ return runtime.safeCall(x1(AA_aa4)) +//│ }; +//│ AA_aa4 = 0; +//│ tmp87 = () => { +//│ let param0, a; +//│ param0 = AA_aa4; +//│ a = param0; +//│ return a +//│ }; +//│ AA_aa5 = tmp87; +//│ tmp88 = (AA_aa6) => { +//│ let param0; +//│ param0 = AA_aa5; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res55 = c21(tmp88); +//│ undefined +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 + + From 8a0fcc65b9eff430ea6890dfc62c4eeec2178e5d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 9 Mar 2025 15:01:25 +0800 Subject: [PATCH 115/303] wip: add problematic tests --- .../src/test/mlscript/deforest/todos.mls | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 3a6ec1c342..a2dbd21695 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -126,3 +126,171 @@ module Test with //│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +:fixme +:sjs +fun f(x) = if x is + AA(AA(a)) then g(a) +fun g(x) = if x is + AA(b) then f(b) + A then 42 +f(AA(AA(AA(AA(AA(A)))))) +//│ JS (unsanitized): +//│ let f, g, tmp, tmp1, tmp2, tmp3, tmp4; +//│ f = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return g(a) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ g = function g(x) { +//│ let param0, b; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ b = param0; +//│ return f(b) +//│ } else if (x instanceof A1.class) { +//│ return 42 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = AA1(A1); +//│ tmp1 = AA1(tmp); +//│ tmp2 = AA1(tmp1); +//│ tmp3 = AA1(tmp2); +//│ tmp4 = AA1(tmp3); +//│ f(tmp4) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: AA_x (class hkmc2.semantics.VarSymbol) + + + +// FIXME: tmp11 takes an unused parameter AA_x3 +:sjs +fun f(x) = if x is + AA(AA(a)) then a +f(AA(AA(A))) +//│ JS (unsanitized): +//│ let f1, tmp5, tmp6; +//│ f1 = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp5 = AA1(A1); +//│ tmp6 = AA1(tmp5); +//│ f1(tmp6) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f1, tmp5, tmp6, AA_x1, AA_x2; +//│ f1 = function f(x) { +//│ return runtime.safeCall(x(AA_x1)) +//│ }; +//│ AA_x1 = A1; +//│ tmp5 = () => { +//│ let param0, a; +//│ param0 = AA_x1; +//│ a = param0; +//│ return a +//│ }; +//│ AA_x2 = tmp5; +//│ tmp6 = (AA_x3) => { +//│ let param0; +//│ param0 = AA_x2; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res7 = f1(tmp6); +//│ undefined +//│ = A +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A + +:fixme +:sjs +fun f(x) = if x is + AA(AA(a)) then a +fun p() = AA(AA(A)) +f(p()) +//│ JS (unsanitized): +//│ let p, f2, tmp9; +//│ f2 = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p = function p() { +//│ let tmp10; +//│ tmp10 = AA1(A1); +//│ return AA1(tmp10) +//│ }; +//│ tmp9 = p(); +//│ f2(tmp9) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $AA_x (class hkmc2.semantics.TempSymbol) + + + +:fixme +:sjs +fun f(x) = + if x is + AA(AA(a)) then a + 3 +f(AA(AA(A))) + f(AA(AA(A))) +//│ JS (unsanitized): +//│ let f3, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ f3 = function f(x) { +//│ let param0, param01, a, tmp16; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ tmp16 = a; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return 3 +//│ }; +//│ tmp10 = AA1(A1); +//│ tmp11 = AA1(tmp10); +//│ tmp12 = f3(tmp11); +//│ tmp13 = AA1(A1); +//│ tmp14 = AA1(tmp13); +//│ tmp15 = f3(tmp14); +//│ tmp12 + tmp15 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: AA_x (class hkmc2.semantics.VarSymbol) From 42977e60e18b2e67c5b879b403550a6835206235 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:34:02 +0800 Subject: [PATCH 116/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 44 +++-- .../src/test/mlscript/deforest/todos.mls | 171 +++++++++++++++--- 2 files changed, 173 insertions(+), 42 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index d81c8c945d..53803d741f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -682,28 +682,44 @@ class Deforest(using TL, Raise, Elaborator.State): case CtorFinalDest.Sel(s) => Nil }.toMap - lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = - val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) - filteredCtorDests.values.foreach: - case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) + // lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = + // val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) + // filteredCtorDests.values.foreach: + // case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => + // toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - filteredCtorDests.values.flatMap{ + // filteredCtorDests.values.flatMap{ + // case CtorFinalDest.Match(scrut, expr, _, _) => + // val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr + // val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys + // Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) + // case CtorFinalDest.Sel(s) => None + // }.toMap + object scopeExtrusionInfo: + val store = mutable.Map.empty[ResultId, List[Symbol]] + + private val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) + filteredCtorDests.values.foreach: case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr - val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys - Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) - case CtorFinalDest.Sel(s) => None - }.toMap - + toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) + + def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( + scrutExprId, + { + assert(m.scrut.uid === scrutExprId) + val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m + val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys + matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel) + } + ) override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = scopeExtrusionInfo(scrut.uid).map(v => Arg(false, Value.Ref(v))) + val freeVars = scopeExtrusionInfo(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) @@ -732,7 +748,7 @@ class Deforest(using TL, Raise, Elaborator.State): preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty ) = assert(scrut === m.scrut.uid) - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut).map(s => s -> VarSymbol(Tree.Ident(s.nme))) + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) store.get(scrut).flatMap(_.get(cls)) match case None => // not registered before, or this branch of this match will only appear once val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index a2dbd21695..831505c56c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -171,7 +171,55 @@ f(AA(AA(AA(AA(AA(A)))))) //│ tmp4 = AA1(tmp3); //│ f(tmp4) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: AA_x (class hkmc2.semantics.VarSymbol) +//│ ==== JS (deforested): ==== +//│ let f, g, tmp, tmp1, tmp2, tmp3, tmp4, AA_x, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; +//│ match_x_branch_AA = function match_x_branch_AA(AA_x1, AA_x2) { +//│ let param0; +//│ param0 = AA_x2; +//│ return runtime.safeCall(param0()) +//│ }; +//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x1) { +//│ let param0, a; +//│ param0 = AA_x1; +//│ a = param0; +//│ return g(a) +//│ }; +//│ f = function f(x) { +//│ return runtime.safeCall(x(AA_x_not_in_scope)) +//│ }; +//│ g = function g(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x_tmp = () => { +//│ return 42 +//│ }; +//│ tmp = () => { +//│ return match_param0_branch_AA(AA_x_tmp) +//│ }; +//│ AA_x_tmp1 = tmp; +//│ tmp1 = (AA_x1) => { +//│ return match_x_branch_AA(AA_x1, AA_x_tmp1) +//│ }; +//│ AA_x = tmp1; +//│ tmp2 = () => { +//│ let param0, b; +//│ param0 = AA_x; +//│ b = param0; +//│ return f(b) +//│ }; +//│ AA_x_tmp2 = tmp2; +//│ tmp3 = () => { +//│ return match_param0_branch_AA(AA_x_tmp2) +//│ }; +//│ AA_x_tmp3 = tmp3; +//│ tmp4 = (AA_x1) => { +//│ return match_x_branch_AA(AA_x1, AA_x_tmp3) +//│ }; +//│ block$res6 = f(tmp4); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 42 @@ -181,7 +229,7 @@ fun f(x) = if x is AA(AA(a)) then a f(AA(AA(A))) //│ JS (unsanitized): -//│ let f1, tmp5, tmp6; +//│ let f1, tmp10, tmp11; //│ f1 = function f(x) { //│ let param0, param01, a; //│ if (x instanceof AA1.class) { @@ -197,29 +245,29 @@ f(AA(AA(A))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp5 = AA1(A1); -//│ tmp6 = AA1(tmp5); -//│ f1(tmp6) +//│ tmp10 = AA1(A1); +//│ tmp11 = AA1(tmp10); +//│ f1(tmp11) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, tmp5, tmp6, AA_x1, AA_x2; +//│ let f1, tmp10, tmp11, AA_x1, AA_x2; //│ f1 = function f(x) { //│ return runtime.safeCall(x(AA_x1)) //│ }; //│ AA_x1 = A1; -//│ tmp5 = () => { +//│ tmp10 = () => { //│ let param0, a; //│ param0 = AA_x1; //│ a = param0; //│ return a //│ }; -//│ AA_x2 = tmp5; -//│ tmp6 = (AA_x3) => { +//│ AA_x2 = tmp10; +//│ tmp11 = (AA_x3) => { //│ let param0; //│ param0 = AA_x2; //│ return runtime.safeCall(param0()) //│ }; -//│ block$res7 = f1(tmp6); +//│ block$res8 = f1(tmp11); //│ undefined //│ = A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -232,7 +280,7 @@ fun f(x) = if x is fun p() = AA(AA(A)) f(p()) //│ JS (unsanitized): -//│ let p, f2, tmp9; +//│ let p, f2, tmp14; //│ f2 = function f(x) { //│ let param0, param01, a; //│ if (x instanceof AA1.class) { @@ -249,14 +297,40 @@ f(p()) //│ } //│ }; //│ p = function p() { -//│ let tmp10; -//│ tmp10 = AA1(A1); -//│ return AA1(tmp10) +//│ let tmp15; +//│ tmp15 = AA1(A1); +//│ return AA1(tmp15) //│ }; -//│ tmp9 = p(); -//│ f2(tmp9) +//│ tmp14 = p(); +//│ f2(tmp14) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $AA_x (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let p, f2, tmp14; +//│ f2 = function f(x) { +//│ return runtime.safeCall(x(AA_x_not_in_scope)) +//│ }; +//│ p = function p() { +//│ let tmp15, AA_x3, AA_x4; +//│ AA_x3 = A1; +//│ tmp15 = () => { +//│ let param0, a; +//│ param0 = AA_x3; +//│ a = param0; +//│ return a +//│ }; +//│ AA_x4 = tmp15; +//│ return (AA_x5) => { +//│ let param0; +//│ param0 = AA_x4; +//│ return runtime.safeCall(param0()) +//│ } +//│ }; +//│ tmp14 = p(); +//│ block$res10 = f2(tmp14); +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A @@ -268,15 +342,15 @@ fun f(x) = 3 f(AA(AA(A))) + f(AA(AA(A))) //│ JS (unsanitized): -//│ let f3, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; +//│ let f3, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; //│ f3 = function f(x) { -//│ let param0, param01, a, tmp16; +//│ let param0, param01, a, tmp22; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; //│ a = param01; -//│ tmp16 = a; +//│ tmp22 = a; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -285,12 +359,53 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ } //│ return 3 //│ }; -//│ tmp10 = AA1(A1); -//│ tmp11 = AA1(tmp10); -//│ tmp12 = f3(tmp11); -//│ tmp13 = AA1(A1); -//│ tmp14 = AA1(tmp13); -//│ tmp15 = f3(tmp14); -//│ tmp12 + tmp15 +//│ tmp16 = AA1(A1); +//│ tmp17 = AA1(tmp16); +//│ tmp18 = f3(tmp17); +//│ tmp19 = AA1(A1); +//│ tmp20 = AA1(tmp19); +//│ tmp21 = f3(tmp20); +//│ tmp18 + tmp21 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: AA_x (class hkmc2.semantics.VarSymbol) +//│ ==== JS (deforested): ==== +//│ let f3, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, AA_x_tmp4, match_param0_branch_AA1, AA_x_tmp5, match_x_rest, match_x_branch_AA1, AA_x_tmp6, AA_x_tmp7; +//│ match_x_branch_AA1 = function match_x_branch_AA(AA_x3, AA_x4) { +//│ let param0; +//│ param0 = AA_x4; +//│ return runtime.safeCall(param0()) +//│ }; +//│ match_param0_branch_AA1 = function match_param0_branch_AA(AA_x3) { +//│ let param0, a, tmp22; +//│ param0 = AA_x3; +//│ a = param0; +//│ tmp22 = a; +//│ }; +//│ match_x_rest = function match_x_rest() { +//│ return 3 +//│ }; +//│ f3 = function f(x) { +//│ return runtime.safeCall(x(AA_x_not_in_scope)) +//│ }; +//│ AA_x_tmp4 = A1; +//│ tmp16 = () => { +//│ return match_param0_branch_AA1(AA_x_tmp4) +//│ }; +//│ AA_x_tmp5 = tmp16; +//│ tmp17 = (AA_x3) => { +//│ return match_x_branch_AA1(AA_x3, AA_x_tmp5) +//│ }; +//│ tmp18 = f3(tmp17); +//│ AA_x_tmp6 = A1; +//│ tmp19 = () => { +//│ return match_param0_branch_AA1(AA_x_tmp6) +//│ }; +//│ AA_x_tmp7 = tmp19; +//│ tmp20 = (AA_x3) => { +//│ return match_x_branch_AA1(AA_x3, AA_x_tmp7) +//│ }; +//│ tmp21 = f3(tmp20); +//│ block$res12 = tmp18 + tmp21; +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 6 From 8b2496ef8bfc3bb4742c1cf36b81207be15a3428 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:49:23 +0800 Subject: [PATCH 117/303] wip: move DeforestTransformer out --- .../scala/hkmc2/codegen/Deforestation.scala | 555 +++++++++--------- 1 file changed, 279 insertions(+), 276 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 53803d741f..72a8c4e53f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -87,7 +87,7 @@ extension (b: Block) case _ => super.applyValue(v) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs(using alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol] = Map.empty) = + def sortedFvs(using alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol] = Map.empty, dt: DeforestTransformer) = object DeforestationFreeVarTraverser extends BlockTraverser(new SymbolSubst()): val ctx = mutable.Set.from(alwaysDefined) val result = mutable.Set.empty[Symbol] @@ -668,300 +668,303 @@ class Deforest(using TL, Raise, Elaborator.State): }.toSet def rewrite(p: Block) = - val deforestTransformer = DeforestTransformer(using globallyDefinedVars.store.toSet) + val deforestTransformer = DeforestTransformer(using this) val rest = deforestTransformer.applyBlock(p) val newDefsRest = deforestTransformer.matchRest.getAllFunDefs val newDefsArms = deforestTransformer.matchArms.getAllFunDefs newDefsArms(newDefsRest(rest)) - class DeforestTransformer(using nonFreeVars: Set[Symbol]) extends BlockTransformer(new SymbolSubst()): - val replaceSelInfo: Map[ResultId, Symbol] = - filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, _, selMaps) => - selMaps._2 - case CtorFinalDest.Sel(s) => Nil - }.toMap +class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): + given DeforestTransformer = this + given nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet + + val replaceSelInfo: Map[ResultId, Symbol] = + d.filteredCtorDests.values.flatMap { + case CtorFinalDest.Match(_, _, _, selMaps) => + selMaps._2 + case CtorFinalDest.Sel(s) => Nil + }.toMap + + // lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = + // val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) + // filteredCtorDests.values.foreach: + // case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => + // toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) + + + // filteredCtorDests.values.flatMap{ + // case CtorFinalDest.Match(scrut, expr, _, _) => + // val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr + // val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys + // Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) + // case CtorFinalDest.Sel(s) => None + // }.toMap + object scopeExtrusionInfo: + val store = mutable.Map.empty[ResultId, List[Symbol]] - // lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = - // val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) - // filteredCtorDests.values.foreach: - // case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - // toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - + private val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) + d.filteredCtorDests.values.foreach: + case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => + toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - // filteredCtorDests.values.flatMap{ - // case CtorFinalDest.Match(scrut, expr, _, _) => - // val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr - // val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys - // Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) - // case CtorFinalDest.Sel(s) => None - // }.toMap - object scopeExtrusionInfo: - val store = mutable.Map.empty[ResultId, List[Symbol]] - - private val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) - filteredCtorDests.values.foreach: - case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - - def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( - scrutExprId, - { - assert(m.scrut.uid === scrutExprId) - val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m - val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys - matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel) - } - ) + def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( + scrutExprId, + { + assert(m.scrut.uid === scrutExprId) + val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m + val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys + matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel) + } + ) + + override def applyBlock(b: Block): Block = b match + case mat@Match(scrut, arms, dflt, rest) => + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) then + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) + val freeVars = scopeExtrusionInfo(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) + Return(Call(scrut, freeVars)(false, false), !needExplicitRet) + else + Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) + case Return(res, implct) => + applyResult2(res)(r => Return(r, implct)) + case Assign(lhs, rhs, rest) => + applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) + case d@Define(defn, rest) => + defn match + case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) + case _ => super.applyBlock(d) + case End(msg) => End(msg) + case Throw(exc) => applyResult2(exc)(Throw.apply) + case _ => super.applyBlock(b) + + object matchArms: + val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) - override def applyBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && filteredDtors.contains(scrut.uid) then - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = scopeExtrusionInfo(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) - Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - else - Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) - case Return(res, implct) => - applyResult2(res)(r => Return(r, implct)) - case Assign(lhs, rhs, rest) => - applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) - case d@Define(defn, rest) => - defn match - case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) - case _ => super.applyBlock(d) - case End(msg) => End(msg) - case Throw(exc) => applyResult2(exc)(Throw.apply) - case _ => super.applyBlock(b) - - object matchArms: - val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) - - // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms - def getOrElseUpdate( - scrut: ResultId, - m: Match, - cls: ClsOrModSymbol, - sel: Set[ResultId], - currentUsedCtorArgsToFields: Map[Tree.Ident, Value.Ref], - preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty - ) = - assert(scrut === m.scrut.uid) - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) - store.get(scrut).flatMap(_.get(cls)) match - case None => // not registered before, or this branch of this match will only appear once - val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get - val rest = m.rest - - - val makeBody = matchRest.getOrElseUpdate(scrut, rest) match - case N -> rewrittenRest => (bodyBlk: Block) => - Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: - case Return(res, implct) => Return(res, false) - case t => t - case Some(f) -> rewrittenRest => (bodyBlk: Block) => - Begin( - bodyBlk, - Return( - Call( - Value.Ref(f), - rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), - false - ) - ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: - case Return(res, implct) => Return(res, false) - case t => t - - if resolveClashes._2(DtorExpr.Match(scrut)).count(getClsSymOfUid(_) === cls) > 1 then - // make a function, and register, and return a lambda calling that function with correct arguments - // arguments for lambda: free vars - // arguments for that function: free vars and pattern vars - - val bodyReplaceSel = applyBlock(body) - - val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) - val funBody = makeBody(bodyReplaceSel) - val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) - val newDef = FunDefn( - N, - funSym, - ParamList( - ParamListFlags.empty, - freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList - ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => - - Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N) - ), - N - ) :: Nil, - funBody - ) - store += (scrut -> (store(scrut) + (cls -> newDef))) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N)), N), + // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms + def getOrElseUpdate( + scrut: ResultId, + m: Match, + cls: ClsOrModSymbol, + sel: Set[ResultId], + currentUsedCtorArgsToFields: Map[Tree.Ident, Value.Ref], + preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty + ) = + assert(scrut === m.scrut.uid) + val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) + store.get(scrut).flatMap(_.get(cls)) match + case None => // not registered before, or this branch of this match will only appear once + val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get + val rest = m.rest + + + val makeBody = matchRest.getOrElseUpdate(scrut, rest) match + case N -> rewrittenRest => (bodyBlk: Block) => + Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: + case Return(res, implct) => Return(res, false) + case t => t + case Some(f) -> rewrittenRest => (bodyBlk: Block) => + Begin( + bodyBlk, Return( - Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + Call( + Value.Ref(f), + rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), false ) - ) - else - val bodyReplaceSel = applyBlock(body) - val lambdaBody = makeBody(bodyReplaceSel) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), - lambdaBody - ) + ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: + case Return(res, implct) => Return(res, false) + case t => t + + if d.resolveClashes._2(DtorExpr.Match(scrut)).count(d.getClsSymOfUid(_) === cls) > 1 then + // make a function, and register, and return a lambda calling that function with correct arguments + // arguments for lambda: free vars + // arguments for that function: free vars and pattern vars + + val bodyReplaceSel = applyBlock(body) - case Some(f) => - // return a lambda that calls f with correct arguments + val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) + val funBody = makeBody(bodyReplaceSel) + val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) + val newDef = FunDefn( + N, + funSym, + ParamList( + ParamListFlags.empty, + freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList + ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => + + Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N) + ), + N + ) :: Nil, + funBody + ) + store += (scrut -> (store(scrut) + (cls -> newDef))) Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)), N), + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N)), N), Return( - Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), - false - ) + Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + false + ) ) - - def getAllFunDefs: Block => Block = - store.values.flatMap(v => v.values).foldRight(identity: Block => Block): - case (defn, k) => r => Define(defn, k(r)) - - object matchRest: - val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] - - // returns the symbol for the rest function (if any), and the rewritten rest block - def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = - store.get(s) match - case Some(f, b) => f.map(_.sym) -> b - case None if restBeforeRewriting.isInstanceOf[End] || (resolveClashes._2(DtorExpr.Match(s)).size == 1) => - val res = N -> applyBlock(restBeforeRewriting) - store += s -> res - res - case _ => // now need to build a new function and update the store - val restRewritten = applyBlock(restBeforeRewriting) - val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme - val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - - val newFunDef = FunDefn( - N, - sym, - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, - restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) + else + val bodyReplaceSel = applyBlock(body) + val lambdaBody = makeBody(bodyReplaceSel) + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N), + lambdaBody ) - store += s -> (Some(newFunDef) -> restRewritten) - Some(sym) -> restRewritten - - def getAllFunDefs: Block => Block = - store.values.foldRight(identity: Block => Block): - case (defn -> _, k) => - r => defn match - case None => k(r) - case Some(defn) => Define(defn, k(r)) + + case Some(f) => + // return a lambda that calls f with correct arguments + Value.Lam( + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)), N), + Return( + Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), + false + ) + ) - override def applyResult2(r: Result)(k: Result => Block): Block = r match - case call@Call(f, args) => - def handleNormalCall(args: List[Arg]) = - var newArgs: Ls[Arg] = Nil - args.foreach: - case Arg(spread, value) => applyResult2(value): r => - // since the arguments must be paths, - // and calls with parameters are not paths, - // so paths will always be rewritten to paths, - // and there won't be more blocks added by `applyResult2(value)` - // so just use a dummy `End` here, to use `applyResult2` as `applyResult` - newArgs = Arg(spread, r.asInstanceOf[Path]) :: newArgs - End() - k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects)) + def getAllFunDefs: Block => Block = + store.values.flatMap(v => v.values).foldRight(identity: Block => Block): + case (defn, k) => r => Define(defn, k(r)) + + object matchRest: + val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] + + // returns the symbol for the rest function (if any), and the rewritten rest block + def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = + store.get(s) match + case Some(f, b) => f.map(_.sym) -> b + case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => + val res = N -> applyBlock(restBeforeRewriting) + store += s -> res + res + case _ => // now need to build a new function and update the store + val restRewritten = applyBlock(restBeforeRewriting) + val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme + val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) + val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - def handleCtorCall(c: ClassSymbol) = - filteredCtorDests.get(call.uid) match - case None => - handleNormalCall(args) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.map(_._2).orElse(expr.dflt).get - - // use pre-determined symbols, create temp symbols for un-used fields - val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 - val allFieldIdentToSymbolsToBeReplaced = getClsFields(c).map: f => - f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) - - // if all vars are temp vars, no need to create more temp vars - // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) - val assignedTempSyms = - if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then - allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) - else - allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match - case ts: TempSymbol => id -> ts - case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") - } - - val newArgs = args.map(_ => TempSymbol(N)) - - val bodyAndRestInLam = matchArms.getOrElseUpdate( - scrut, - expr, - c, - sels.toSet, - assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, - selsMap._1 -> selsMap._2) - - args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): - case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } - - case Some(CtorFinalDest.Sel(s)) => - val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } - val idx = getClsFields(c).indexWhere(s => s.id === selFieldName) - k(args(idx).value) + val newFunDef = FunDefn( + N, + sym, + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, + restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) + ) + store += s -> (Some(newFunDef) -> restRewritten) + Some(sym) -> restRewritten + + def getAllFunDefs: Block => Block = + store.values.foldRight(identity: Block => Block): + case (defn -> _, k) => + r => defn match + case None => k(r) + case Some(defn) => Define(defn, k(r)) + + override def applyResult2(r: Result)(k: Result => Block): Block = r match + case call@Call(f, args) => + def handleNormalCall(args: List[Arg]) = + var newArgs: Ls[Arg] = Nil + args.foreach: + case Arg(spread, value) => applyResult2(value): r => + // since the arguments must be paths, + // and calls with parameters are not paths, + // so paths will always be rewritten to paths, + // and there won't be more blocks added by `applyResult2(value)` + // so just use a dummy `End` here, to use `applyResult2` as `applyResult` + newArgs = Arg(spread, r.asInstanceOf[Path]) :: newArgs + End() + k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects)) - f match - case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case None => - handleNormalCall(args) - case Some(c) => handleCtorCall(c) - case Value.Ref(l) => l.asCls match - case None => - handleNormalCall(args) - case Some(c) => handleCtorCall(c) - case Value.Lam(params, body) => - k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun, call.mayRaiseEffects)) - case Instantiate(cls, args) => k(r) - case s@Select(p, nme) => s.symbol.flatMap(f => f.asObj) match - case None => - if rewritingSelConsumer.contains(s.uid) then - k(p) - else - replaceSelInfo.get(s.uid) match - case None => k(s) - case Some(v) => k(Value.Ref(v)) - - case Some(mod) => - filteredCtorDests.get(s.uid) match - case None => - k(s) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get - val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) - k(bodyAndRestInLam) - case Some(_) => ??? // a selection on a module consumes it + def handleCtorCall(c: ClassSymbol) = + d.filteredCtorDests.get(call.uid) match + case None => + handleNormalCall(args) + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => + val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.map(_._2).orElse(expr.dflt).get + + // use pre-determined symbols, create temp symbols for un-used fields + val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 + val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => + f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) + + // if all vars are temp vars, no need to create more temp vars + // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) + val assignedTempSyms = + if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then + allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) + else + allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match + case ts: TempSymbol => id -> ts + case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") + } + + val newArgs = args.map(_ => TempSymbol(N)) + + val bodyAndRestInLam = matchArms.getOrElseUpdate( + scrut, + expr, + c, + sels.toSet, + assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, + selsMap._1 -> selsMap._2) + + args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): + case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } + + case Some(CtorFinalDest.Sel(s)) => + val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } + val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) + k(args(idx).value) - case r@Value.Ref(l) => l.asObj match - case None => k(r) - case Some(mod) => - filteredCtorDests.get(r.uid) match - case None => - k(r) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get - - val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) - k(bodyAndRestInLam) - case Some(_) => ??? // a selection on a module consumes it - case Value.This(sym) => k(Value.This(sym)) - case Value.Lit(lit) => k(Value.Lit(lit)) - case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) - case Value.Arr(elems) => k(Value.Arr(elems)) - + f match + case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match + case None => + handleNormalCall(args) + case Some(c) => handleCtorCall(c) + case Value.Ref(l) => l.asCls match + case None => + handleNormalCall(args) + case Some(c) => handleCtorCall(c) + case Value.Lam(params, body) => + k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun, call.mayRaiseEffects)) + case Instantiate(cls, args) => k(r) + case s@Select(p, nme) => s.symbol.flatMap(f => f.asObj) match + case None => + if d.rewritingSelConsumer.contains(s.uid) then + k(p) + else + replaceSelInfo.get(s.uid) match + case None => k(s) + case Some(v) => k(Value.Ref(v)) + + case Some(mod) => + d.filteredCtorDests.get(s.uid) match + case None => + k(s) + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get + val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) + k(bodyAndRestInLam) + case Some(_) => ??? // a selection on a module consumes it + case r@Value.Ref(l) => l.asObj match + case None => k(r) + case Some(mod) => + d.filteredCtorDests.get(r.uid) match + case None => + k(r) + case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get + + val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) + k(bodyAndRestInLam) + case Some(_) => ??? // a selection on a module consumes it + case Value.This(sym) => k(Value.This(sym)) + case Value.Lit(lit) => k(Value.Lit(lit)) + case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) + case Value.Arr(elems) => k(Value.Arr(elems)) + + From c39b54dcb62e112854e97d7f2faa4bc4edc76847 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 10 Mar 2025 18:19:06 +0800 Subject: [PATCH 118/303] further fixes to free vars of matches; found the new problem of missing `rest` --- .../scala/hkmc2/codegen/Deforestation.scala | 166 ++++++----- .../deforest/selectionsInNestedMatch.mls | 203 +++++++++++++ .../src/test/mlscript/deforest/simple.mls | 8 +- .../src/test/mlscript/deforest/todos.mls | 280 +++--------------- 4 files changed, 337 insertions(+), 320 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 72a8c4e53f..3f24cad209 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -77,6 +77,81 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid +class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser(new SymbolSubst()): + val ctx = mutable.Set.from(alwaysDefined) + val result = mutable.Set.empty[Symbol] + + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + applyPath(scrut) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + val realArm = Begin(a, rest).flattened + applyBlock(realArm) + + case Assign(lhs, rhs, rest) => + applyResult(rhs) + ctx += lhs + applyBlock(rest) + ctx -= lhs + case Begin(sub, rest) => applyBlock(b.flattened) + case Define(defn, rest) => defn match + case FunDefn(owner, sym, params, body) => + val paramSymbols = params.flatMap: + case ParamList(flags, params, restParam) => (params ++ restParam).map: + case Param(flags, sym, sign) => sym + ctx += sym + ctx ++= paramSymbols + applyBlock(body) + ctx --= paramSymbols + applyBlock(rest) + ctx -= sym + case ValDefn(owner, k, sym, rhs) => + ctx += sym + applyPath(rhs) + applyBlock(rest) + ctx -= sym + case c: ClsLikeDefn => ??? // not supported + + case _ => super.applyBlock(b) + + override def applyValue(v: Value): Unit = v match + case Value.Ref(l) => if !ctx.contains(l) then result += l + case _ => super.applyValue(v) + + override def applyLam(l: Value.Lam): Unit = + val paramSymbols = l.params.params.map(p => p.sym) + ctx ++= paramSymbols + applyBlock(l.body) + ctx --= paramSymbols + +class DeforestationFreeVarTraverser(using + alwaysDefined: Set[Symbol], + selsToBeReplaced: Map[ResultId, Symbol] = Map.empty, + selsReplacementByCurrentMatch: Iterable[Symbol], + dt: DeforestTransformer +) extends FreeVarTraverser(alwaysDefined): + override def applyBlock(b: Block): Unit = b match + // a nested match + case m@Match(scrut, arms, dflt, rest) => + result ++= dt.freeVarsOfNonTransformedMatches(scrut.uid, m) + + // scruts of sub-matches are also free vars + val Value.Ref(l) = scrut + if !ctx(l) then result += l + + // free vars in nested-matches reported by freeVarsOfNonTransformedMatches may also contain + // spurious ones: those that are going to be substitued by the current match, + // and those that are in the ctx + result --= selsReplacementByCurrentMatch + result --= ctx + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Unit = p match + case p @ Select(qual, name) => + selsToBeReplaced.get(p.uid).fold(super.applyPath(p))(s => result += s) + case _ => super.applyPath(p) extension (b: Block) @@ -87,62 +162,10 @@ extension (b: Block) case _ => super.applyValue(v) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs(using alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol] = Map.empty, dt: DeforestTransformer) = - object DeforestationFreeVarTraverser extends BlockTraverser(new SymbolSubst()): - val ctx = mutable.Set.from(alwaysDefined) - val result = mutable.Set.empty[Symbol] - - override def applyBlock(b: Block): Unit = b match - case Match(scrut, arms, dflt, rest) => - applyPath(scrut) - (arms.map(_._2) ++ dflt).foreach: a => - // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. - // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - val realArm = Begin(a, rest).flattened - applyBlock(realArm) - - case Assign(lhs, rhs, rest) => - applyResult(rhs) - ctx += lhs - applyBlock(rest) - ctx -= lhs - case Begin(sub, rest) => applyBlock(b.flattened) - case Define(defn, rest) => defn match - case FunDefn(owner, sym, params, body) => - val paramSymbols = params.flatMap: - case ParamList(flags, params, restParam) => (params ++ restParam).map: - case Param(flags, sym, sign) => sym - ctx += sym - ctx ++= paramSymbols - applyBlock(body) - ctx --= paramSymbols - applyBlock(rest) - ctx -= sym - case ValDefn(owner, k, sym, rhs) => - ctx += sym - applyPath(rhs) - applyBlock(rest) - ctx -= sym - case c: ClsLikeDefn => ??? // not supported - - case _ => super.applyBlock(b) - - override def applyPath(p: Path): Unit = p match - case p @ Select(qual, name) => - selsToBeReplaced.get(p.uid).fold(super.applyPath(p))(s => result += s) - case _ => super.applyPath(p) - - override def applyValue(v: Value): Unit = v match - case Value.Ref(l) => if !ctx.contains(l) then result += l - case _ => super.applyValue(v) - - override def applyLam(l: Value.Lam): Unit = - val paramSymbols = l.params.params.map(p => p.sym) - ctx ++= paramSymbols - applyBlock(l.body) - ctx --= paramSymbols - DeforestationFreeVarTraverser.applyBlock(b) - DeforestationFreeVarTraverser.result.toList.sortBy(_.uid) + def sortedFvs(using alwaysDefined: Set[Symbol]) = + val traverser = FreeVarTraverser(alwaysDefined) + traverser.applyBlock(b) + traverser.result.toList.sortBy(_.uid) def hasExplicitRet: Boolean = object HasExplicitRetTraverser extends BlockTraverserShallow(new SymbolSubst()): @@ -685,21 +708,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case CtorFinalDest.Sel(s) => Nil }.toMap - // lazy val scopeExtrusionInfo: Map[ResultId, List[Symbol]] = - // val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) - // filteredCtorDests.values.foreach: - // case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - // toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - - - // filteredCtorDests.values.flatMap{ - // case CtorFinalDest.Match(scrut, expr, _, _) => - // val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = expr - // val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrut).keys - // Some(scrut -> matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel)) - // case CtorFinalDest.Sel(s) => None - // }.toMap - object scopeExtrusionInfo: + object freeVarsOfNonTransformedMatches: val store = mutable.Map.empty[ResultId, List[Symbol]] private val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) @@ -713,7 +722,16 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend assert(m.scrut.uid === scrutExprId) val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys - matchExpr.sortedFvs(using nonFreeVars + l, selReplacementNotForThisSel) + + val traverser = DeforestationFreeVarTraverser(using nonFreeVars + l, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId).values) + traverser.applyPath(m.scrut) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + val realArm = Begin(a, rest).flattened + traverser.applyBlock(realArm) + + traverser.result.toList.sortBy(_.uid) } ) @@ -722,7 +740,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case mat@Match(scrut, arms, dflt, rest) => if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) then val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = scopeExtrusionInfo(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) + val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) else Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) @@ -751,7 +769,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty ) = assert(scrut === m.scrut.uid) - val freeVarsAndTheirNewSyms = scopeExtrusionInfo(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) + val freeVarsAndTheirNewSyms = freeVarsOfNonTransformedMatches(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) store.get(scrut).flatMap(_.get(cls)) match case None => // not registered before, or this branch of this match will only appear once val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index adc8751a2f..29c159b05f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -204,3 +204,206 @@ c(p(), A) //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 + + + + +:sjs +fun f(x) = if x is + AA(AA(a)) then g(a) +fun g(x) = if x is + AA(b) then f(b) + A then 42 +f(AA(AA(AA(AA(AA(A)))))) +//│ JS (unsanitized): +//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12; +//│ f = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return g(a) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ g = function g(x) { +//│ let param0, b; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ b = param0; +//│ return f(b) +//│ } else if (x instanceof A1.class) { +//│ return 42 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp8 = AA1(A1); +//│ tmp9 = AA1(tmp8); +//│ tmp10 = AA1(tmp9); +//│ tmp11 = AA1(tmp10); +//│ tmp12 = AA1(tmp11); +//│ f(tmp12) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12, AA_x2, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; +//│ match_x_branch_AA = function match_x_branch_AA(AA_x3) { +//│ let param0; +//│ param0 = AA_x3; +//│ return runtime.safeCall(param0()) +//│ }; +//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x3) { +//│ let param0, a; +//│ param0 = AA_x3; +//│ a = param0; +//│ return g(a) +//│ }; +//│ f = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ g = function g(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x_tmp = () => { +//│ return 42 +//│ }; +//│ tmp8 = () => { +//│ return match_param0_branch_AA(AA_x_tmp) +//│ }; +//│ AA_x_tmp1 = tmp8; +//│ tmp9 = () => { +//│ return match_x_branch_AA(AA_x_tmp1) +//│ }; +//│ AA_x2 = tmp9; +//│ tmp10 = () => { +//│ let param0, b; +//│ param0 = AA_x2; +//│ b = param0; +//│ return f(b) +//│ }; +//│ AA_x_tmp2 = tmp10; +//│ tmp11 = () => { +//│ return match_param0_branch_AA(AA_x_tmp2) +//│ }; +//│ AA_x_tmp3 = tmp11; +//│ tmp12 = () => { +//│ return match_x_branch_AA(AA_x_tmp3) +//│ }; +//│ block$res12 = f(tmp12); +//│ undefined +//│ = 42 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 42 + +:sjs +fun f(x) = if x is + AA(AA(a)) then a +f(AA(AA(A))) +//│ JS (unsanitized): +//│ let f1, tmp18, tmp19; +//│ f1 = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp18 = AA1(A1); +//│ tmp19 = AA1(tmp18); +//│ f1(tmp19) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f1, tmp18, tmp19, AA_x3, AA_x4; +//│ f1 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x3 = A1; +//│ tmp18 = () => { +//│ let param0, a; +//│ param0 = AA_x3; +//│ a = param0; +//│ return a +//│ }; +//│ AA_x4 = tmp18; +//│ tmp19 = () => { +//│ let param0; +//│ param0 = AA_x4; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res14 = f1(tmp19); +//│ undefined +//│ = A +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A + +:sjs +fun f(x) = if x is + AA(AA(a)) then a +fun p() = AA(AA(A)) +f(p()) +//│ JS (unsanitized): +//│ let p2, f2, tmp22; +//│ f2 = function f(x) { +//│ let param0, param01, a; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a = param01; +//│ return a +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p2 = function p() { +//│ let tmp23; +//│ tmp23 = AA1(A1); +//│ return AA1(tmp23) +//│ }; +//│ tmp22 = p2(); +//│ f2(tmp22) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p2, f2, tmp22; +//│ f2 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ p2 = function p() { +//│ let tmp23, AA_x5, AA_x6; +//│ AA_x5 = A1; +//│ tmp23 = () => { +//│ let param0, a; +//│ param0 = AA_x5; +//│ a = param0; +//│ return a +//│ }; +//│ AA_x6 = tmp23; +//│ return () => { +//│ let param0; +//│ param0 = AA_x6; +//│ return runtime.safeCall(param0()) +//│ } +//│ }; +//│ tmp22 = p2(); +//│ block$res16 = f2(tmp22); +//│ undefined +//│ = A +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 735f790609..6f64ec118d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1665,7 +1665,7 @@ f(AA(BB(B))) //│ ==== JS (deforested): ==== //│ let f8, tmp79, tmp80, BB_bb, AA_aa1; //│ f8 = function f(a) { -//│ return runtime.safeCall(a(BB_bb)) +//│ return runtime.safeCall(a()) //│ }; //│ BB_bb = () => { //│ return 3 @@ -1676,7 +1676,7 @@ f(AA(BB(B))) //│ return runtime.safeCall(param0()) //│ }; //│ AA_aa1 = tmp79; -//│ tmp80 = (BB_bb1) => { +//│ tmp80 = () => { //│ let param0; //│ param0 = AA_aa1; //│ return runtime.safeCall(param0()) @@ -1777,7 +1777,7 @@ c2(AA(AA(0))) //│ ==== JS (deforested): ==== //│ let c21, tmp87, tmp88, AA_aa4, AA_aa5; //│ c21 = function c2(x1) { -//│ return runtime.safeCall(x1(AA_aa4)) +//│ return runtime.safeCall(x1()) //│ }; //│ AA_aa4 = 0; //│ tmp87 = () => { @@ -1787,7 +1787,7 @@ c2(AA(AA(0))) //│ return a //│ }; //│ AA_aa5 = tmp87; -//│ tmp88 = (AA_aa6) => { +//│ tmp88 = () => { //│ let param0; //│ param0 = AA_aa5; //│ return runtime.safeCall(param0()) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 831505c56c..dcf501c0aa 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -127,285 +127,81 @@ module Test with //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:fixme +// FIXME: `rest` is missing after the transformation... :sjs -fun f(x) = if x is - AA(AA(a)) then g(a) -fun g(x) = if x is - AA(b) then f(b) - A then 42 -f(AA(AA(AA(AA(AA(A)))))) +fun f(x) = + let t = if x is + AA(AA(a)) then a + t + 3 +f(AA(AA(A))) + f(AA(AA(A))) //│ JS (unsanitized): -//│ let f, g, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; //│ f = function f(x) { -//│ let param0, param01, a; +//│ let t, param0, param01, a, tmp6; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; //│ a = param01; -//│ return g(a) +//│ tmp6 = a; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; -//│ g = function g(x) { -//│ let param0, b; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ b = param0; -//│ return f(b) -//│ } else if (x instanceof A1.class) { -//│ return 42 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ t = tmp6; +//│ return t + 3 //│ }; //│ tmp = AA1(A1); //│ tmp1 = AA1(tmp); -//│ tmp2 = AA1(tmp1); -//│ tmp3 = AA1(tmp2); +//│ tmp2 = f(tmp1); +//│ tmp3 = AA1(A1); //│ tmp4 = AA1(tmp3); -//│ f(tmp4) +//│ tmp5 = f(tmp4); +//│ tmp2 + tmp5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, g, tmp, tmp1, tmp2, tmp3, tmp4, AA_x, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; -//│ match_x_branch_AA = function match_x_branch_AA(AA_x1, AA_x2) { +//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_rest, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; +//│ match_x_branch_AA = function match_x_branch_AA(AA_x) { //│ let param0; -//│ param0 = AA_x2; +//│ param0 = AA_x; //│ return runtime.safeCall(param0()) //│ }; -//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x1) { -//│ let param0, a; -//│ param0 = AA_x1; +//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x) { +//│ let param0, a, tmp6; +//│ param0 = AA_x; //│ a = param0; -//│ return g(a) +//│ tmp6 = a; //│ }; -//│ f = function f(x) { -//│ return runtime.safeCall(x(AA_x_not_in_scope)) +//│ match_x_rest = function match_x_rest(tmp6) { +//│ let t; +//│ t = tmp6; +//│ return t + 3 //│ }; -//│ g = function g(x) { +//│ f = function f(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x_tmp = () => { -//│ return 42 -//│ }; +//│ AA_x_tmp = A1; //│ tmp = () => { //│ return match_param0_branch_AA(AA_x_tmp) //│ }; //│ AA_x_tmp1 = tmp; -//│ tmp1 = (AA_x1) => { -//│ return match_x_branch_AA(AA_x1, AA_x_tmp1) -//│ }; -//│ AA_x = tmp1; -//│ tmp2 = () => { -//│ let param0, b; -//│ param0 = AA_x; -//│ b = param0; -//│ return f(b) +//│ tmp1 = () => { +//│ return match_x_branch_AA(AA_x_tmp1) //│ }; -//│ AA_x_tmp2 = tmp2; +//│ tmp2 = f(tmp1); +//│ AA_x_tmp2 = A1; //│ tmp3 = () => { //│ return match_param0_branch_AA(AA_x_tmp2) //│ }; //│ AA_x_tmp3 = tmp3; -//│ tmp4 = (AA_x1) => { -//│ return match_x_branch_AA(AA_x1, AA_x_tmp3) -//│ }; -//│ block$res6 = f(tmp4); -//│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 42 - - - -// FIXME: tmp11 takes an unused parameter AA_x3 -:sjs -fun f(x) = if x is - AA(AA(a)) then a -f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f1, tmp10, tmp11; -//│ f1 = function f(x) { -//│ let param0, param01, a; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp10 = AA1(A1); -//│ tmp11 = AA1(tmp10); -//│ f1(tmp11) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f1, tmp10, tmp11, AA_x1, AA_x2; -//│ f1 = function f(x) { -//│ return runtime.safeCall(x(AA_x1)) -//│ }; -//│ AA_x1 = A1; -//│ tmp10 = () => { -//│ let param0, a; -//│ param0 = AA_x1; -//│ a = param0; -//│ return a -//│ }; -//│ AA_x2 = tmp10; -//│ tmp11 = (AA_x3) => { -//│ let param0; -//│ param0 = AA_x2; -//│ return runtime.safeCall(param0()) -//│ }; -//│ block$res8 = f1(tmp11); -//│ undefined -//│ = A -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A - -:fixme -:sjs -fun f(x) = if x is - AA(AA(a)) then a -fun p() = AA(AA(A)) -f(p()) -//│ JS (unsanitized): -//│ let p, f2, tmp14; -//│ f2 = function f(x) { -//│ let param0, param01, a; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p = function p() { -//│ let tmp15; -//│ tmp15 = AA1(A1); -//│ return AA1(tmp15) -//│ }; -//│ tmp14 = p(); -//│ f2(tmp14) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p, f2, tmp14; -//│ f2 = function f(x) { -//│ return runtime.safeCall(x(AA_x_not_in_scope)) -//│ }; -//│ p = function p() { -//│ let tmp15, AA_x3, AA_x4; -//│ AA_x3 = A1; -//│ tmp15 = () => { -//│ let param0, a; -//│ param0 = AA_x3; -//│ a = param0; -//│ return a -//│ }; -//│ AA_x4 = tmp15; -//│ return (AA_x5) => { -//│ let param0; -//│ param0 = AA_x4; -//│ return runtime.safeCall(param0()) -//│ } -//│ }; -//│ tmp14 = p(); -//│ block$res10 = f2(tmp14); -//│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A - - - -:fixme -:sjs -fun f(x) = - if x is - AA(AA(a)) then a - 3 -f(AA(AA(A))) + f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f3, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; -//│ f3 = function f(x) { -//│ let param0, param01, a, tmp22; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ tmp22 = a; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return 3 -//│ }; -//│ tmp16 = AA1(A1); -//│ tmp17 = AA1(tmp16); -//│ tmp18 = f3(tmp17); -//│ tmp19 = AA1(A1); -//│ tmp20 = AA1(tmp19); -//│ tmp21 = f3(tmp20); -//│ tmp18 + tmp21 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f3, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, AA_x_tmp4, match_param0_branch_AA1, AA_x_tmp5, match_x_rest, match_x_branch_AA1, AA_x_tmp6, AA_x_tmp7; -//│ match_x_branch_AA1 = function match_x_branch_AA(AA_x3, AA_x4) { -//│ let param0; -//│ param0 = AA_x4; -//│ return runtime.safeCall(param0()) -//│ }; -//│ match_param0_branch_AA1 = function match_param0_branch_AA(AA_x3) { -//│ let param0, a, tmp22; -//│ param0 = AA_x3; -//│ a = param0; -//│ tmp22 = a; -//│ }; -//│ match_x_rest = function match_x_rest() { -//│ return 3 -//│ }; -//│ f3 = function f(x) { -//│ return runtime.safeCall(x(AA_x_not_in_scope)) -//│ }; -//│ AA_x_tmp4 = A1; -//│ tmp16 = () => { -//│ return match_param0_branch_AA1(AA_x_tmp4) -//│ }; -//│ AA_x_tmp5 = tmp16; -//│ tmp17 = (AA_x3) => { -//│ return match_x_branch_AA1(AA_x3, AA_x_tmp5) -//│ }; -//│ tmp18 = f3(tmp17); -//│ AA_x_tmp6 = A1; -//│ tmp19 = () => { -//│ return match_param0_branch_AA1(AA_x_tmp6) -//│ }; -//│ AA_x_tmp7 = tmp19; -//│ tmp20 = (AA_x3) => { -//│ return match_x_branch_AA1(AA_x3, AA_x_tmp7) +//│ tmp4 = () => { +//│ return match_x_branch_AA(AA_x_tmp3) //│ }; -//│ tmp21 = f3(tmp20); -//│ block$res12 = tmp18 + tmp21; +//│ tmp5 = f(tmp4); +//│ block$res6 = tmp2 + tmp5; //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: AA_x_not_in_scope is not defined +//│ = "()()" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 6 +//│ = "A3A3" From 23654711950aced338d33ebccb0c83ba26721ce9 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:03:54 +0800 Subject: [PATCH 119/303] update test --- .../src/test/mlscript/deforest/todos.mls | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index dcf501c0aa..b17ade48d3 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -205,3 +205,62 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ = "()()" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "A3A3" + + +:sjs +fun f(x) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 +f(AA(AA(3))) +//│ JS (unsanitized): +//│ let f1, tmp12, tmp13; +//│ f1 = function f(x) { +//│ let n, param0, a, param01, m, tmp14, tmp15; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a = param0; +//│ if (a instanceof AA1.class) { +//│ param01 = a.x; +//│ m = param01; +//│ tmp14 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp15 = tmp14; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp15; +//│ return n + 2 +//│ }; +//│ tmp12 = AA1(3); +//│ tmp13 = AA1(tmp12); +//│ f1(tmp13) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f1, tmp12, tmp13, AA_x, AA_x1; +//│ f1 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x = 3; +//│ tmp12 = () => { +//│ let param0, m, tmp14, tmp15; +//│ param0 = AA_x; +//│ m = param0; +//│ tmp14 = m; +//│ tmp15 = tmp14; +//│ }; +//│ AA_x1 = tmp12; +//│ tmp13 = () => { +//│ let param0, a; +//│ param0 = AA_x1; +//│ a = param0; +//│ return runtime.safeCall(a()) +//│ }; +//│ block$res8 = f1(tmp13); +//│ undefined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 From 44239ab832cd4eec95b793670d2580e3929d2d8d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:21:08 +0800 Subject: [PATCH 120/303] keep track of how matches are nested --- .../scala/hkmc2/codegen/Deforestation.scala | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 3f24cad209..b529e0f8e8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -7,6 +7,7 @@ import syntax.{Literal, Tree} import utils.{TL, tl, SymbolSubst} import mlscript.utils.*, shorthands.* import scala.collection.mutable +import scala.collection.mutable.LinkedHashMap type StratVar type StratVarId = Uid[StratVar] @@ -43,13 +44,16 @@ case object NoProd extends ProdStrat -case class Dtor(scrut: ResultId)(val expr: Match)(using d: Deforest) extends ConsStrat: +case class Dtor(scrut: ResultId)(val expr: Match, val outterMatch: Option[ResultId])(using d: Deforest) extends ConsStrat: assert(scrut === expr.scrut.uid) d.matchScrutToMatchBlock.updateWith(scrut): case None => Some(expr) case Some(exist) => ??? // should only update once + d.matchScrutToParentMatchScrut.updateWith(scrut): + case None => Some(outterMatch) + case Some(_) => ??? // should only update once -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: Map[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait +case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: LinkedHashMap[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) case object NoCons extends ConsStrat @@ -60,7 +64,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol])(val inMatch: Option[ResultId]) case Sel(s: ResultId) trait FieldSelTrait: @@ -238,6 +242,7 @@ class Deforest(using TL, Raise, Elaborator.State): var constraints: Ls[ProdStrat -> ConsStrat] = Nil val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] + val matchScrutToParentMatchScrut = mutable.Map.empty[ResultId, Option[ResultId]] object symToStrat: val store = mutable.Map.empty[Symbol, ProdVar] @@ -273,12 +278,12 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def processBlock(b: Block)(using - inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: Map[ResultId, ClsOrModSymbol] = Map.empty[ResultId, ClsOrModSymbol] + inArm: LinkedHashMap[ProdVar, ClsOrModSymbol] = LinkedHashMap.empty[ProdVar, ClsOrModSymbol], + matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol] ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(scrut.uid)(m)(using this)) + constrain(scrutStrat, Dtor(scrut.uid)(m, matching.lastOption.map(_._1))(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: case (Case.Cls(s, _), body) => @@ -334,8 +339,8 @@ class Deforest(using TL, Raise, Elaborator.State): case Throw(exc) => NoProd def constrFun(params: Ls[Param], body: Block)(using - inArm: Map[ProdVar, ClsOrModSymbol], - matching: Map[ResultId, ClsOrModSymbol] + inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], + matching: LinkedHashMap[ResultId, ClsOrModSymbol] ) = val paramSyms = params.map: case Param(_, sym, _) => sym @@ -346,8 +351,8 @@ class Deforest(using TL, Raise, Elaborator.State): ProdFun(paramStrats.map(s => s.asConsStrat), res._1) def processResult(r: Result)(using - inArm: Map[ProdVar, ClsOrModSymbol], - matching: Map[ResultId, ClsOrModSymbol] + inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], + matching: LinkedHashMap[ResultId, ClsOrModSymbol] ): ProdStrat = r match case c@Call(f, args) => val argsTpe = args.map: @@ -670,7 +675,7 @@ class Deforest(using TL, Raise, Elaborator.State): dtors.head._2, sels.map(_.expr), fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )) + )(matchScrutToParentMatchScrut(dtors.head._1))) else throw Error("more than one consumer") None From 75cf3af5c8e3e444afa7b9991cf2ea18d7932c59 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 11 Mar 2025 21:56:18 +0800 Subject: [PATCH 121/303] wip: seems to fix the missing `rest`s --- .../scala/hkmc2/codegen/Deforestation.scala | 35 +++++- .../deforest/selectionsInNestedMatch.mls | 10 +- .../src/test/mlscript/deforest/todos.mls | 119 ++++++++++++++++-- 3 files changed, 146 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b529e0f8e8..2b686e6bf7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -91,6 +91,8 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser(new Sy (arms.map(_._2) ++ dflt).foreach: a => // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + // this also makes sure that the free vars in the `rest` of outter + // matches can be counted as free vars for the inner match val realArm = Begin(a, rest).flattened applyBlock(realArm) @@ -762,7 +764,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case _ => super.applyBlock(b) object matchArms: - val store = mutable.Map.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) + val store = LinkedHashMap.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms def getOrElseUpdate( @@ -854,21 +856,44 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case (defn, k) => r => Define(defn, k(r)) object matchRest: - val store = mutable.Map.empty[ResultId, Opt[FunDefn] -> Block] + val store = LinkedHashMap.empty[ResultId, Opt[FunDefn] -> Block] + + def getAllDefined = store.valuesIterator.flatMap(_._1.map(_.sym)) // returns the symbol for the rest function (if any), and the rewritten rest block def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = store.get(s) match case Some(f, b) => f.map(_.sym) -> b case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => - val res = N -> applyBlock(restBeforeRewriting) + val parentRest = d.matchScrutToParentMatchScrut(s).map: p => + getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest) + val res = + N -> + (parentRest match + case None => applyBlock(restBeforeRewriting) + case Some(Some(s), b) => Begin( + applyBlock(restBeforeRewriting), + Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + ) + + // val res = N -> applyBlock(restBeforeRewriting) store += s -> res res case _ => // now need to build a new function and update the store - val restRewritten = applyBlock(restBeforeRewriting) + val parentRest = d.matchScrutToParentMatchScrut(s).map: p => + getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest) + val restRewritten = parentRest match + case None => applyBlock(restBeforeRewriting) + case Some(Some(s), b) => Begin( + applyBlock(restBeforeRewriting), + Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + + // val restRewritten = applyBlock(restBeforeRewriting) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvs.map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val freeVarsAndTheirNewSyms = restRewritten.sortedFvs(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap val newFunDef = FunDefn( N, diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 29c159b05f..6090903f75 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -253,17 +253,17 @@ f(AA(AA(AA(AA(AA(A)))))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12, AA_x2, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; -//│ match_x_branch_AA = function match_x_branch_AA(AA_x3) { -//│ let param0; -//│ param0 = AA_x3; -//│ return runtime.safeCall(param0()) -//│ }; //│ match_param0_branch_AA = function match_param0_branch_AA(AA_x3) { //│ let param0, a; //│ param0 = AA_x3; //│ a = param0; //│ return g(a) //│ }; +//│ match_x_branch_AA = function match_x_branch_AA(AA_x3) { +//│ let param0; +//│ param0 = AA_x3; +//│ return runtime.safeCall(param0()) +//│ }; //│ f = function f(x) { //│ return runtime.safeCall(x()) //│ }; diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index b17ade48d3..9cbd398e4d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -162,17 +162,18 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ tmp2 + tmp5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_rest, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; -//│ match_x_branch_AA = function match_x_branch_AA(AA_x) { -//│ let param0; -//│ param0 = AA_x; -//│ return runtime.safeCall(param0()) -//│ }; +//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, AA_x_tmp, match_x_rest, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; //│ match_param0_branch_AA = function match_param0_branch_AA(AA_x) { //│ let param0, a, tmp6; //│ param0 = AA_x; //│ a = param0; //│ tmp6 = a; +//│ return match_x_rest(tmp6) +//│ }; +//│ match_x_branch_AA = function match_x_branch_AA(AA_x) { +//│ let param0; +//│ param0 = AA_x; +//│ return runtime.safeCall(param0()) //│ }; //│ match_x_rest = function match_x_rest(tmp6) { //│ let t; @@ -202,7 +203,7 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ tmp5 = f(tmp4); //│ block$res6 = tmp2 + tmp5; //│ undefined -//│ = "()()" +//│ = "A3A3" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "A3A3" @@ -247,11 +248,13 @@ f(AA(AA(3))) //│ }; //│ AA_x = 3; //│ tmp12 = () => { -//│ let param0, m, tmp14, tmp15; +//│ let n, param0, m, tmp14, tmp15; //│ param0 = AA_x; //│ m = param0; //│ tmp14 = m; //│ tmp15 = tmp14; +//│ n = tmp15; +//│ return n + 2 //│ }; //│ AA_x1 = tmp12; //│ tmp13 = () => { @@ -262,5 +265,105 @@ f(AA(AA(3))) //│ }; //│ block$res8 = f1(tmp13); //│ undefined +//│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 + + + + + + + + + +:sjs +fun f(x, y) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 + y +f(AA(AA(3)), 9) + f(AA(AA(4)), 10) +//│ JS (unsanitized): +//│ let f2, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; +//│ f2 = function f(x, y) { +//│ let n, param0, a, param01, m, tmp22, tmp23, tmp24; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a = param0; +//│ if (a instanceof AA1.class) { +//│ param01 = a.x; +//│ m = param01; +//│ tmp22 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp23 = tmp22; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp23; +//│ tmp24 = n + 2; +//│ return tmp24 + y +//│ }; +//│ tmp16 = AA1(3); +//│ tmp17 = AA1(tmp16); +//│ tmp18 = f2(tmp17, 9); +//│ tmp19 = AA1(4); +//│ tmp20 = AA1(tmp19); +//│ tmp21 = f2(tmp20, 10); +//│ tmp18 + tmp21 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f2, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, AA_x_tmp4, match_x_rest1, match_a_rest, match_a_branch_AA, AA_x_tmp5, match_x_branch_AA1, AA_x_tmp6, AA_x_tmp7; +//│ match_a_branch_AA = function match_a_branch_AA(y, AA_x2) { +//│ let param0, m, tmp22; +//│ param0 = AA_x2; +//│ m = param0; +//│ tmp22 = m; +//│ return match_a_rest(y, tmp22, match_x_rest1) +//│ }; +//│ match_x_branch_AA1 = function match_x_branch_AA(y, AA_x2) { +//│ let param0, a; +//│ param0 = AA_x2; +//│ a = param0; +//│ return runtime.safeCall(a(y)) +//│ }; +//│ match_x_rest1 = function match_x_rest(y, tmp22) { +//│ let n, tmp23; +//│ n = tmp22; +//│ tmp23 = n + 2; +//│ return tmp23 + y +//│ }; +//│ match_a_rest = function match_a_rest(y, tmp22) { +//│ let tmp23; +//│ tmp23 = tmp22; +//│ return match_x_rest1(y, tmp23) +//│ }; +//│ f2 = function f(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ AA_x_tmp4 = 3; +//│ tmp16 = (y) => { +//│ return match_a_branch_AA(y, AA_x_tmp4) +//│ }; +//│ AA_x_tmp5 = tmp16; +//│ tmp17 = (y) => { +//│ return match_x_branch_AA1(y, AA_x_tmp5) +//│ }; +//│ tmp18 = f2(tmp17, 9); +//│ AA_x_tmp6 = 4; +//│ tmp19 = (y) => { +//│ return match_a_branch_AA(y, AA_x_tmp6) +//│ }; +//│ AA_x_tmp7 = tmp19; +//│ tmp20 = (y) => { +//│ return match_x_branch_AA1(y, AA_x_tmp7) +//│ }; +//│ tmp21 = f2(tmp20, 10); +//│ block$res10 = tmp18 + tmp21; +//│ undefined +//│ = 30 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 30 From 43f738e1370f62de9ce174d1222e4957d68536bd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Mar 2025 12:03:39 +0800 Subject: [PATCH 122/303] update tests --- .../src/test/mlscript/deforest/simple.mls | 235 +++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 241 ------------------ 2 files changed, 235 insertions(+), 241 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 6f64ec118d..cdd7bf3a87 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1799,3 +1799,238 @@ c2(AA(AA(0))) //│ = 0 + + +:sjs +fun f(x) = + let t = if x is + AA(AA(a)) then a + t + 3 +f(AA(AA(A))) + f(AA(AA(A))) +//│ JS (unsanitized): +//│ let f10, tmp91, tmp92, tmp93, tmp94, tmp95, tmp96; +//│ f10 = function f(x1) { +//│ let t, param0, param01, a, tmp97; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.aa; +//│ a = param01; +//│ tmp97 = a; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp97; +//│ return t + 3 +//│ }; +//│ tmp91 = AA1(A1); +//│ tmp92 = AA1(tmp91); +//│ tmp93 = f10(tmp92); +//│ tmp94 = AA1(A1); +//│ tmp95 = AA1(tmp94); +//│ tmp96 = f10(tmp95); +//│ tmp93 + tmp96 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f10, tmp91, tmp92, tmp93, tmp94, tmp95, tmp96, AA_aa_tmp, match_x_rest3, match_param0_branch_AA, AA_aa_tmp1, match_x_branch_AA, AA_aa_tmp2, AA_aa_tmp3; +//│ match_param0_branch_AA = function match_param0_branch_AA(AA_aa6) { +//│ let param0, a, tmp97; +//│ param0 = AA_aa6; +//│ a = param0; +//│ tmp97 = a; +//│ return match_x_rest3(tmp97) +//│ }; +//│ match_x_branch_AA = function match_x_branch_AA(AA_aa6) { +//│ let param0; +//│ param0 = AA_aa6; +//│ return runtime.safeCall(param0()) +//│ }; +//│ match_x_rest3 = function match_x_rest(tmp97) { +//│ let t; +//│ t = tmp97; +//│ return t + 3 +//│ }; +//│ f10 = function f(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ AA_aa_tmp = A1; +//│ tmp91 = () => { +//│ return match_param0_branch_AA(AA_aa_tmp) +//│ }; +//│ AA_aa_tmp1 = tmp91; +//│ tmp92 = () => { +//│ return match_x_branch_AA(AA_aa_tmp1) +//│ }; +//│ tmp93 = f10(tmp92); +//│ AA_aa_tmp2 = A1; +//│ tmp94 = () => { +//│ return match_param0_branch_AA(AA_aa_tmp2) +//│ }; +//│ AA_aa_tmp3 = tmp94; +//│ tmp95 = () => { +//│ return match_x_branch_AA(AA_aa_tmp3) +//│ }; +//│ tmp96 = f10(tmp95); +//│ block$res57 = tmp93 + tmp96; +//│ undefined +//│ = "A3A3" +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "A3A3" + + +:sjs +fun f(x) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 +f(AA(AA(3))) +//│ JS (unsanitized): +//│ let f11, tmp103, tmp104; +//│ f11 = function f(x1) { +//│ let n, param0, a, param01, m, tmp105, tmp106; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ a = param0; +//│ if (a instanceof AA1.class) { +//│ param01 = a.aa; +//│ m = param01; +//│ tmp105 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp106 = tmp105; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp106; +//│ return n + 2 +//│ }; +//│ tmp103 = AA1(3); +//│ tmp104 = AA1(tmp103); +//│ f11(tmp104) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f11, tmp103, tmp104, AA_aa6, AA_aa7; +//│ f11 = function f(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ AA_aa6 = 3; +//│ tmp103 = () => { +//│ let n, param0, m, tmp105, tmp106; +//│ param0 = AA_aa6; +//│ m = param0; +//│ tmp105 = m; +//│ tmp106 = tmp105; +//│ n = tmp106; +//│ return n + 2 +//│ }; +//│ AA_aa7 = tmp103; +//│ tmp104 = () => { +//│ let param0, a; +//│ param0 = AA_aa7; +//│ a = param0; +//│ return runtime.safeCall(a()) +//│ }; +//│ block$res59 = f11(tmp104); +//│ undefined +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 + + + +:sjs +fun f(x, y) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 + y +f(AA(AA(3)), 9) + f(AA(AA(4)), 10) +//│ JS (unsanitized): +//│ let f12, tmp107, tmp108, tmp109, tmp110, tmp111, tmp112; +//│ f12 = function f(x1, y1) { +//│ let n, param0, a, param01, m, tmp113, tmp114, tmp115; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ a = param0; +//│ if (a instanceof AA1.class) { +//│ param01 = a.aa; +//│ m = param01; +//│ tmp113 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp114 = tmp113; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp114; +//│ tmp115 = n + 2; +//│ return tmp115 + y1 +//│ }; +//│ tmp107 = AA1(3); +//│ tmp108 = AA1(tmp107); +//│ tmp109 = f12(tmp108, 9); +//│ tmp110 = AA1(4); +//│ tmp111 = AA1(tmp110); +//│ tmp112 = f12(tmp111, 10); +//│ tmp109 + tmp112 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f12, tmp107, tmp108, tmp109, tmp110, tmp111, tmp112, AA_aa_tmp4, match_x_rest4, match_a_rest1, match_a_branch_AA, AA_aa_tmp5, match_x_branch_AA1, AA_aa_tmp6, AA_aa_tmp7; +//│ match_a_branch_AA = function match_a_branch_AA(y1, AA_aa8) { +//│ let param0, m, tmp113; +//│ param0 = AA_aa8; +//│ m = param0; +//│ tmp113 = m; +//│ return match_a_rest1(y1, tmp113, match_x_rest4) +//│ }; +//│ match_x_branch_AA1 = function match_x_branch_AA(y1, AA_aa8) { +//│ let param0, a; +//│ param0 = AA_aa8; +//│ a = param0; +//│ return runtime.safeCall(a(y1)) +//│ }; +//│ match_x_rest4 = function match_x_rest(y1, tmp113) { +//│ let n, tmp114; +//│ n = tmp113; +//│ tmp114 = n + 2; +//│ return tmp114 + y1 +//│ }; +//│ match_a_rest1 = function match_a_rest(y1, tmp113) { +//│ let tmp114; +//│ tmp114 = tmp113; +//│ return match_x_rest4(y1, tmp114) +//│ }; +//│ f12 = function f(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ AA_aa_tmp4 = 3; +//│ tmp107 = (y1) => { +//│ return match_a_branch_AA(y1, AA_aa_tmp4) +//│ }; +//│ AA_aa_tmp5 = tmp107; +//│ tmp108 = (y1) => { +//│ return match_x_branch_AA1(y1, AA_aa_tmp5) +//│ }; +//│ tmp109 = f12(tmp108, 9); +//│ AA_aa_tmp6 = 4; +//│ tmp110 = (y1) => { +//│ return match_a_branch_AA(y1, AA_aa_tmp6) +//│ }; +//│ AA_aa_tmp7 = tmp110; +//│ tmp111 = (y1) => { +//│ return match_x_branch_AA1(y1, AA_aa_tmp7) +//│ }; +//│ tmp112 = f12(tmp111, 10); +//│ block$res61 = tmp109 + tmp112; +//│ undefined +//│ = 30 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 30 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 9cbd398e4d..3a6ec1c342 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -126,244 +126,3 @@ module Test with //│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - -// FIXME: `rest` is missing after the transformation... -:sjs -fun f(x) = - let t = if x is - AA(AA(a)) then a - t + 3 -f(AA(AA(A))) + f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; -//│ f = function f(x) { -//│ let t, param0, param01, a, tmp6; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ tmp6 = a; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp6; -//│ return t + 3 -//│ }; -//│ tmp = AA1(A1); -//│ tmp1 = AA1(tmp); -//│ tmp2 = f(tmp1); -//│ tmp3 = AA1(A1); -//│ tmp4 = AA1(tmp3); -//│ tmp5 = f(tmp4); -//│ tmp2 + tmp5 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, AA_x_tmp, match_x_rest, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; -//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x) { -//│ let param0, a, tmp6; -//│ param0 = AA_x; -//│ a = param0; -//│ tmp6 = a; -//│ return match_x_rest(tmp6) -//│ }; -//│ match_x_branch_AA = function match_x_branch_AA(AA_x) { -//│ let param0; -//│ param0 = AA_x; -//│ return runtime.safeCall(param0()) -//│ }; -//│ match_x_rest = function match_x_rest(tmp6) { -//│ let t; -//│ t = tmp6; -//│ return t + 3 -//│ }; -//│ f = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ AA_x_tmp = A1; -//│ tmp = () => { -//│ return match_param0_branch_AA(AA_x_tmp) -//│ }; -//│ AA_x_tmp1 = tmp; -//│ tmp1 = () => { -//│ return match_x_branch_AA(AA_x_tmp1) -//│ }; -//│ tmp2 = f(tmp1); -//│ AA_x_tmp2 = A1; -//│ tmp3 = () => { -//│ return match_param0_branch_AA(AA_x_tmp2) -//│ }; -//│ AA_x_tmp3 = tmp3; -//│ tmp4 = () => { -//│ return match_x_branch_AA(AA_x_tmp3) -//│ }; -//│ tmp5 = f(tmp4); -//│ block$res6 = tmp2 + tmp5; -//│ undefined -//│ = "A3A3" -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "A3A3" - - -:sjs -fun f(x) = - let n = if x is - AA(a) then - if a is - AA(m) then m - n + 2 -f(AA(AA(3))) -//│ JS (unsanitized): -//│ let f1, tmp12, tmp13; -//│ f1 = function f(x) { -//│ let n, param0, a, param01, m, tmp14, tmp15; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a = param0; -//│ if (a instanceof AA1.class) { -//│ param01 = a.x; -//│ m = param01; -//│ tmp14 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp15 = tmp14; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp15; -//│ return n + 2 -//│ }; -//│ tmp12 = AA1(3); -//│ tmp13 = AA1(tmp12); -//│ f1(tmp13) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f1, tmp12, tmp13, AA_x, AA_x1; -//│ f1 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ AA_x = 3; -//│ tmp12 = () => { -//│ let n, param0, m, tmp14, tmp15; -//│ param0 = AA_x; -//│ m = param0; -//│ tmp14 = m; -//│ tmp15 = tmp14; -//│ n = tmp15; -//│ return n + 2 -//│ }; -//│ AA_x1 = tmp12; -//│ tmp13 = () => { -//│ let param0, a; -//│ param0 = AA_x1; -//│ a = param0; -//│ return runtime.safeCall(a()) -//│ }; -//│ block$res8 = f1(tmp13); -//│ undefined -//│ = 5 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 5 - - - - - - - - - -:sjs -fun f(x, y) = - let n = if x is - AA(a) then - if a is - AA(m) then m - n + 2 + y -f(AA(AA(3)), 9) + f(AA(AA(4)), 10) -//│ JS (unsanitized): -//│ let f2, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21; -//│ f2 = function f(x, y) { -//│ let n, param0, a, param01, m, tmp22, tmp23, tmp24; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a = param0; -//│ if (a instanceof AA1.class) { -//│ param01 = a.x; -//│ m = param01; -//│ tmp22 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp23 = tmp22; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp23; -//│ tmp24 = n + 2; -//│ return tmp24 + y -//│ }; -//│ tmp16 = AA1(3); -//│ tmp17 = AA1(tmp16); -//│ tmp18 = f2(tmp17, 9); -//│ tmp19 = AA1(4); -//│ tmp20 = AA1(tmp19); -//│ tmp21 = f2(tmp20, 10); -//│ tmp18 + tmp21 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f2, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, AA_x_tmp4, match_x_rest1, match_a_rest, match_a_branch_AA, AA_x_tmp5, match_x_branch_AA1, AA_x_tmp6, AA_x_tmp7; -//│ match_a_branch_AA = function match_a_branch_AA(y, AA_x2) { -//│ let param0, m, tmp22; -//│ param0 = AA_x2; -//│ m = param0; -//│ tmp22 = m; -//│ return match_a_rest(y, tmp22, match_x_rest1) -//│ }; -//│ match_x_branch_AA1 = function match_x_branch_AA(y, AA_x2) { -//│ let param0, a; -//│ param0 = AA_x2; -//│ a = param0; -//│ return runtime.safeCall(a(y)) -//│ }; -//│ match_x_rest1 = function match_x_rest(y, tmp22) { -//│ let n, tmp23; -//│ n = tmp22; -//│ tmp23 = n + 2; -//│ return tmp23 + y -//│ }; -//│ match_a_rest = function match_a_rest(y, tmp22) { -//│ let tmp23; -//│ tmp23 = tmp22; -//│ return match_x_rest1(y, tmp23) -//│ }; -//│ f2 = function f(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ AA_x_tmp4 = 3; -//│ tmp16 = (y) => { -//│ return match_a_branch_AA(y, AA_x_tmp4) -//│ }; -//│ AA_x_tmp5 = tmp16; -//│ tmp17 = (y) => { -//│ return match_x_branch_AA1(y, AA_x_tmp5) -//│ }; -//│ tmp18 = f2(tmp17, 9); -//│ AA_x_tmp6 = 4; -//│ tmp19 = (y) => { -//│ return match_a_branch_AA(y, AA_x_tmp6) -//│ }; -//│ AA_x_tmp7 = tmp19; -//│ tmp20 = (y) => { -//│ return match_x_branch_AA1(y, AA_x_tmp7) -//│ }; -//│ tmp21 = f2(tmp20, 10); -//│ block$res10 = tmp18 + tmp21; -//│ undefined -//│ = 30 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 30 From 79c489fdbe59e0cb409f2959790c9777d54ceba6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:29:01 +0800 Subject: [PATCH 123/303] minor fixes for selections --- .../scala/hkmc2/codegen/Deforestation.scala | 4 +-- .../src/test/mlscript/deforest/simple.mls | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 2b686e6bf7..cf981dcd76 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -982,10 +982,10 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case s@Select(p, nme) => s.symbol.flatMap(f => f.asObj) match case None => if d.rewritingSelConsumer.contains(s.uid) then - k(p) + applyResult2(p)(k) else replaceSelInfo.get(s.uid) match - case None => k(s) + case None => applyResult2(p)(r => k(Select(r.asInstanceOf[Path], nme)(N))) case Some(v) => k(Value.Ref(v)) case Some(mod) => diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index cdd7bf3a87..77030544b9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -2034,3 +2034,30 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ = 30 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 30 + + + +:sjs +fun f(x) = x.aa.aa +f(AA(AA(3))) +//│ JS (unsanitized): +//│ let f13, tmp119, tmp120; +//│ f13 = function f(x1) { +//│ return x1.aa.aa +//│ }; +//│ tmp119 = AA1(3); +//│ tmp120 = AA1(tmp119); +//│ f13(tmp120) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f13, tmp119, tmp120; +//│ f13 = function f(x1) { +//│ return x1 +//│ }; +//│ tmp119 = 3; +//│ tmp120 = tmp119; +//│ block$res63 = f13(tmp120); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 From bf718c8178fc9c32a72e1791343620cb3f6aba91 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Mar 2025 16:07:44 +0800 Subject: [PATCH 124/303] update todos with more test cases and comments --- .../src/test/mlscript/deforest/todos.mls | 142 +++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 3a6ec1c342..a1fba2d3d6 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -15,7 +15,12 @@ class Some(x) -// TODO: how to keep track of the correct s.x +// TODO: keep track of the equality between `Test.#s` and scrut. +// The `s` in `if s is ...` is actually `Test.#s`, +// and `s.x`s in branches are actually `Test.#s.x`. +// This causes that `Test.#s` has 3 consumption sites, +// and the `A` or `B` in `Test.#s.x` cannot get the branch-specific information +// to be considered as having only 1 consumption site. :sjs module Test with fun f1(a) = if a is @@ -126,3 +131,138 @@ module Test with //│ block$res4 = undefined; //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +// if `Test.#s.x` is always consumed by `f` +// the `A` and `B` can be fused, but not the outer `AA` or `BB` +:sjs +module Test with + fun f(a) = if a is + A then 1 + B then 2 + C then 3 + let s = if true then AA(A) else BB(B) + if s is + AA then f(s.x) + BB then f(s.x) +//│ JS (unsanitized): +//│ let Test3; +//│ Test3 = class Test { +//│ static #s; +//│ static { +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = AA1(A1); +//│ } else { +//│ tmp = BB1(B1); +//│ } +//│ Test.#s = tmp; +//│ scrut1 = Test.#s; +//│ if (scrut1 instanceof AA1.class) { +//│ Test.f(Test.#s.x) +//│ } else if (scrut1 instanceof BB1.class) { +//│ Test.f(Test.#s.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static f(a) { +//│ if (a instanceof A1.class) { +//│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else if (a instanceof C1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static toString() { return "Test"; } +//│ }; +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let Test3; +//│ Test3 = class Test { +//│ static #s1; +//│ static { +//│ let scrut, scrut1, tmp; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = AA1(() => { +//│ return 1 +//│ }); +//│ } else { +//│ tmp = BB1(() => { +//│ return 2 +//│ }); +//│ } +//│ Test.#s1 = tmp; +//│ scrut1 = Test.#s1; +//│ if (scrut1 instanceof AA1.class) { +//│ Test.f(Test.#s1.x) +//│ } else if (scrut1 instanceof BB1.class) { +//│ Test.f(Test.#s1.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } +//│ static f(a) { +//│ return runtime.safeCall(a()) +//│ } +//│ static toString() { return "Test"; } +//│ }; +//│ block$res6 = undefined; +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// similar as above, since `x.x` is considered as being consumed +// at two places,the inner match is not fused +:sjs +fun f(x) = if x is + AA then if x.x is + AA then x.x.x +f(AA(AA(3))) +//│ JS (unsanitized): +//│ let f, tmp, tmp1; +//│ f = function f(x) { +//│ let scrut; +//│ if (x instanceof AA1.class) { +//│ scrut = x.x; +//│ if (scrut instanceof AA1.class) { +//│ return x.x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = AA1(3); +//│ tmp1 = AA1(tmp); +//│ f(tmp1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f, tmp, tmp1, AA_x; +//│ f = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ tmp = AA1(3); +//│ AA_x = tmp; +//│ tmp1 = () => { +//│ let scrut; +//│ scrut = AA_x; +//│ if (scrut instanceof AA1.class) { +//│ return AA_x.x +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ }; +//│ block$res8 = f(tmp1); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 + + + + From 3f3fe05ad400038b8f55726a631b72635667f137 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 12 Mar 2025 17:49:01 +0800 Subject: [PATCH 125/303] cleanup and more tests --- .../scala/hkmc2/codegen/Deforestation.scala | 47 ++--- .../src/test/mlscript/deforest/simple.mls | 199 ++++++++++++++++++ 2 files changed, 222 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cf981dcd76..971a835020 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -742,27 +742,6 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend } ) - - override def applyBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) then - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) - Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - else - Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) - case Return(res, implct) => - applyResult2(res)(r => Return(r, implct)) - case Assign(lhs, rhs, rest) => - applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) - case d@Define(defn, rest) => - defn match - case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) - case _ => super.applyBlock(d) - case End(msg) => End(msg) - case Throw(exc) => applyResult2(exc)(Throw.apply) - case _ => super.applyBlock(b) - object matchArms: val store = LinkedHashMap.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) @@ -877,7 +856,6 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) ) - // val res = N -> applyBlock(restBeforeRewriting) store += s -> res res case _ => // now need to build a new function and update the store @@ -889,8 +867,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) - - // val restRewritten = applyBlock(restBeforeRewriting) + val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) val freeVarsAndTheirNewSyms = restRewritten.sortedFvs(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap @@ -911,6 +888,28 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case None => k(r) case Some(defn) => Define(defn, k(r)) + + override def applyBlock(b: Block): Block = b match + case mat@Match(scrut, arms, dflt, rest) => + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) then + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) + val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) + Return(Call(scrut, freeVars)(false, false), !needExplicitRet) + else + Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) + case Return(res, implct) => + applyResult2(res)(r => Return(r, implct)) + case Assign(lhs, rhs, rest) => + applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) + case d@Define(defn, rest) => + defn match + case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) + case _ => super.applyBlock(d) + case End(msg) => End(msg) + case Throw(exc) => applyResult2(exc)(Throw.apply) + case _ => super.applyBlock(b) + + override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) => def handleNormalCall(args: List[Arg]) = diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 77030544b9..7a68daa05a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -2061,3 +2061,202 @@ f(AA(AA(3))) //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 + +:sjs +fun inner(x, y) = + let t = if x is + AA(a) then a + y + BB(b) then b - y + t * y +fun outer1(x, y, z) = + let t = if x is + AA(a) then inner(a, y) + BB(b) then inner(b, y) + t + z +fun outer2(x, y, z) = + let t = if x is + AA(a) then inner(a, z) + BB(b) then inner(b, y) + t + y +let p = AA(AA(3)) +outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) +//│ JS (unsanitized): +//│ let outer1, outer2, inner, p1, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129; +//│ inner = function inner(x1, y1) { +//│ let t, param0, b, param01, a, tmp130; +//│ if (x1 instanceof AA1.class) { +//│ param01 = x1.aa; +//│ a = param01; +//│ tmp130 = a + y1; +//│ } else if (x1 instanceof BB1.class) { +//│ param0 = x1.bb; +//│ b = param0; +//│ tmp130 = b - y1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp130; +//│ return t * y1 +//│ }; +//│ outer1 = function outer1(x1, y1, z) { +//│ let t, param0, b, param01, a, tmp130; +//│ if (x1 instanceof AA1.class) { +//│ param01 = x1.aa; +//│ a = param01; +//│ tmp130 = inner(a, y1); +//│ } else if (x1 instanceof BB1.class) { +//│ param0 = x1.bb; +//│ b = param0; +//│ tmp130 = inner(b, y1); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp130; +//│ return t + z +//│ }; +//│ outer2 = function outer2(x1, y1, z) { +//│ let t, param0, b, param01, a, tmp130; +//│ if (x1 instanceof AA1.class) { +//│ param01 = x1.aa; +//│ a = param01; +//│ tmp130 = inner(a, z); +//│ } else if (x1 instanceof BB1.class) { +//│ param0 = x1.bb; +//│ b = param0; +//│ tmp130 = inner(b, y1); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp130; +//│ return t + y1 +//│ }; +//│ tmp123 = AA1(3); +//│ tmp124 = AA1(tmp123); +//│ p1 = tmp124; +//│ tmp125 = outer1(p1, 1, 2); +//│ tmp126 = outer2(p1, 3, 4); +//│ tmp127 = tmp125 + tmp126; +//│ tmp128 = AA1(5); +//│ tmp129 = inner(tmp128, 6); +//│ tmp127 + tmp129 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let outer1, outer2, inner, p1, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, AA_aa_tmp8, match_x_rest5, match_x_branch_AA2, AA_aa_tmp9; +//│ match_x_branch_AA2 = function match_x_branch_AA(y1, AA_aa8) { +//│ let param0, a, tmp130; +//│ param0 = AA_aa8; +//│ a = param0; +//│ tmp130 = a + y1; +//│ return match_x_rest5(y1, tmp130) +//│ }; +//│ match_x_rest5 = function match_x_rest(y1, tmp130) { +//│ let t; +//│ t = tmp130; +//│ return t * y1 +//│ }; +//│ inner = function inner(x1, y1) { +//│ return runtime.safeCall(x1(y1)) +//│ }; +//│ outer1 = function outer1(x1, y1, z) { +//│ let t, param0, b, param01, a, tmp130; +//│ if (x1 instanceof AA1.class) { +//│ param01 = x1.aa; +//│ a = param01; +//│ tmp130 = inner(a, y1); +//│ } else if (x1 instanceof BB1.class) { +//│ param0 = x1.bb; +//│ b = param0; +//│ tmp130 = inner(b, y1); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp130; +//│ return t + z +//│ }; +//│ outer2 = function outer2(x1, y1, z) { +//│ let t, param0, b, param01, a, tmp130; +//│ if (x1 instanceof AA1.class) { +//│ param01 = x1.aa; +//│ a = param01; +//│ tmp130 = inner(a, z); +//│ } else if (x1 instanceof BB1.class) { +//│ param0 = x1.bb; +//│ b = param0; +//│ tmp130 = inner(b, y1); +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp130; +//│ return t + y1 +//│ }; +//│ AA_aa_tmp8 = 3; +//│ tmp123 = (y1) => { +//│ return match_x_branch_AA2(y1, AA_aa_tmp8) +//│ }; +//│ tmp124 = AA1(tmp123); +//│ p1 = tmp124; +//│ tmp125 = outer1(p1, 1, 2); +//│ tmp126 = outer2(p1, 3, 4); +//│ tmp127 = tmp125 + tmp126; +//│ AA_aa_tmp9 = 5; +//│ tmp128 = (y1) => { +//│ return match_x_branch_AA2(y1, AA_aa_tmp9) +//│ }; +//│ tmp129 = inner(tmp128, 6); +//│ block$res65 = tmp127 + tmp129; +//│ undefined +//│ = 103 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 103 +//│ p = AA(AA(3)) + + +:sjs +fun mapHead(f, x) = if x is + AAA(h, t) then f(h) +mapHead(x => x, AAA(1, AAA(2, None))) +//│ JS (unsanitized): +//│ let mapHead, tmp137, tmp138, lambda4; +//│ mapHead = function mapHead(f14, x1) { +//│ let param0, param1, h, t; +//│ if (x1 instanceof AAA1.class) { +//│ param0 = x1.x; +//│ param1 = x1.y; +//│ h = param0; +//│ t = param1; +//│ return runtime.safeCall(f14(h)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp137 = AAA1(2, None1); +//│ tmp138 = AAA1(1, tmp137); +//│ lambda4 = (undefined, function (x1) { +//│ return x1 +//│ }); +//│ mapHead(lambda4, tmp138) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let mapHead, tmp137, tmp138, lambda4, AAA_x, AAA_y; +//│ mapHead = function mapHead(f14, x1) { +//│ return runtime.safeCall(x1(f14)) +//│ }; +//│ tmp137 = AAA1(2, None1); +//│ AAA_x = 1; +//│ AAA_y = tmp137; +//│ tmp138 = (f14) => { +//│ let param0, param1, h, t; +//│ param0 = AAA_x; +//│ param1 = AAA_y; +//│ h = param0; +//│ t = param1; +//│ return runtime.safeCall(f14(h)) +//│ }; +//│ lambda4 = (undefined, function (x1) { +//│ return x1 +//│ }); +//│ block$res67 = mapHead(lambda4, tmp138); +//│ undefined +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 From 5d96ecb370261f502ef879cf722ef80fbfdca828 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Mar 2025 13:42:28 +0800 Subject: [PATCH 126/303] better usage of blocktransformer --- .../scala/hkmc2/codegen/Deforestation.scala | 138 +++++++----------- 1 file changed, 50 insertions(+), 88 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 971a835020..8681f2678f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -890,53 +890,32 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend override def applyBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) => - if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) then - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) - val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) - Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - else - Match(scrut, arms.map{ (cse, blk) => (cse, applyBlock(blk)) }, dflt.map(applyBlock), applyBlock(rest)) - case Return(res, implct) => - applyResult2(res)(r => Return(r, implct)) - case Assign(lhs, rhs, rest) => - applyResult2(rhs)(r => Assign(lhs, r, applyBlock(rest))) - case d@Define(defn, rest) => - defn match - case FunDefn(o, sym, params, body) => Define(FunDefn(o, sym, params, applyBlock(body)), applyBlock(rest)) - case _ => super.applyBlock(d) - case End(msg) => End(msg) - case Throw(exc) => applyResult2(exc)(Throw.apply) + case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) => + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) + val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) + Return(Call(scrut, freeVars)(false, false), !needExplicitRet) case _ => super.applyBlock(b) + override def applyResult(r: Result): Result = r match + case _: Call => + // calls to fusing contructors are handled in `applyResult2` + // here we only handle calls to non-fusing constructors and functions + assert(!d.filteredCtorDests.isDefinedAt(r.uid)) + super.applyResult(r) + case _ => super.applyResult(r) override def applyResult2(r: Result)(k: Result => Block): Block = r match - case call@Call(f, args) => - def handleNormalCall(args: List[Arg]) = - var newArgs: Ls[Arg] = Nil - args.foreach: - case Arg(spread, value) => applyResult2(value): r => - // since the arguments must be paths, - // and calls with parameters are not paths, - // so paths will always be rewritten to paths, - // and there won't be more blocks added by `applyResult2(value)` - // so just use a dummy `End` here, to use `applyResult2` as `applyResult` - newArgs = Arg(spread, r.asInstanceOf[Path]) :: newArgs - End() - k(Call(f, newArgs.reverse)(call.isMlsFun, call.mayRaiseEffects)) - + case call@Call(f, args) if d.filteredCtorDests.isDefinedAt(call.uid) => def handleCtorCall(c: ClassSymbol) = - d.filteredCtorDests.get(call.uid) match - case None => - handleNormalCall(args) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => + d.filteredCtorDests.get(call.uid).get match + case CtorFinalDest.Match(scrut, expr, sels, selsMap) => val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.map(_._2).orElse(expr.dflt).get - + // use pre-determined symbols, create temp symbols for un-used fields val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) - + // if all vars are temp vars, no need to create more temp vars // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) val assignedTempSyms = @@ -949,7 +928,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend } val newArgs = args.map(_ => TempSymbol(N)) - + val bodyAndRestInLam = matchArms.getOrElseUpdate( scrut, expr, @@ -957,61 +936,44 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend sels.toSet, assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, selsMap._1 -> selsMap._2) - + args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } - - case Some(CtorFinalDest.Sel(s)) => + + case CtorFinalDest.Sel(s) => val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) k(args(idx).value) - f match - case s@Select(p, nme) => s.symbol.flatMap(_.asCls) match - case None => - handleNormalCall(args) - case Some(c) => handleCtorCall(c) - case Value.Ref(l) => l.asCls match - case None => - handleNormalCall(args) - case Some(c) => handleCtorCall(c) - case Value.Lam(params, body) => - k(Call(Value.Lam(params, applyBlock(body)), args)(call.isMlsFun, call.mayRaiseEffects)) - case Instantiate(cls, args) => k(r) - case s@Select(p, nme) => s.symbol.flatMap(f => f.asObj) match - case None => - if d.rewritingSelConsumer.contains(s.uid) then - applyResult2(p)(k) - else - replaceSelInfo.get(s.uid) match - case None => applyResult2(p)(r => k(Select(r.asInstanceOf[Path], nme)(N))) - case Some(v) => k(Value.Ref(v)) - - case Some(mod) => - d.filteredCtorDests.get(s.uid) match - case None => - k(s) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get - val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) - k(bodyAndRestInLam) - case Some(_) => ??? // a selection on a module consumes it - - case r@Value.Ref(l) => l.asObj match - case None => k(r) - case Some(mod) => - d.filteredCtorDests.get(r.uid) match - case None => - k(r) - case Some(CtorFinalDest.Match(scrut, expr, sels, selsMap)) => - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === mod }.map(_._2).orElse(expr.dflt).get - - val bodyAndRestInLam = matchArms.getOrElseUpdate(scrut, expr, mod, Set.empty, Map.empty) - k(bodyAndRestInLam) - case Some(_) => ??? // a selection on a module consumes it - case Value.This(sym) => k(Value.This(sym)) - case Value.Lit(lit) => k(Value.Lit(lit)) - case Value.Lam(params, body) => k(Value.Lam(params, applyBlock(body))) - case Value.Arr(elems) => k(Value.Arr(elems)) + case s: Select => handleCtorCall(s.symbol.get.asCls.get) + case Value.Ref(l) => handleCtorCall(l.asCls.get) + case _ => ??? + case _ => super.applyResult2(r)(k) + def handleObjFusing(objCallExprUid: CtorExpr, objClsSym: ModuleSymbol) = + // must be a pat mat on objects; no support for selection on objects yet + val CtorFinalDest.Match(scrut, expr, sels, selsMap) = d.filteredCtorDests(objCallExprUid): @unchecked + val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === objClsSym }.map(_._2).orElse(expr.dflt).get + matchArms.getOrElseUpdate(scrut, expr, objClsSym, Set.empty, Map.empty) + + override def applyPath(p: Path): Path = p match + // a selection which is a consumer on its own + case s@Select(p, nme) if d.rewritingSelConsumer.contains(s.uid) => applyPath(p) + + // a selection inside a fusing match that needs to be replaced by pre-computed symbols + case s@Select(p, nme) if replaceSelInfo.get(s.uid).isDefined => Value.Ref(replaceSelInfo(s.uid)) + + case s@Select(p, nme) => s.symbol.flatMap(_.asObj) match + // a fusing object constructor + case Some(obj) if d.filteredCtorDests.isDefinedAt(s.uid) => handleObjFusing(s.uid, obj) + case _ => super.applyPath(s) + + case v: Value => applyValue(v) + case _ => super.applyPath(p) + override def applyValue(v: Value): Value = v match + case r@Value.Ref(l) => l.asObj match + case None => r + case Some(obj) if d.filteredCtorDests.isDefinedAt(r.uid) => handleObjFusing(r.uid, obj) + case _ => super.applyValue(v) + case _ => super.applyValue(v) From 2268f3cca8aa0121c750d88fed6d7e801a6c1cba Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:03:56 +0800 Subject: [PATCH 127/303] further fix to take care of the `rest`s from non-fusing parent match blocks --- .../scala/hkmc2/codegen/Deforestation.scala | 21 +- .../test/mlscript/deforest/nestedMatch.mls | 482 ++++++++++++++++ .../src/test/mlscript/deforest/simple.mls | 522 +++--------------- 3 files changed, 586 insertions(+), 439 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8681f2678f..66850db305 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -728,6 +728,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend { assert(m.scrut.uid === scrutExprId) val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m + val parentMatchRest = d.matchScrutToParentMatchScrut(m.scrut.uid).flatMap: p => + if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) + else None val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys val traverser = DeforestationFreeVarTraverser(using nonFreeVars + l, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId).values) @@ -735,7 +738,8 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend (arms.map(_._2) ++ dflt).foreach: a => // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - val realArm = Begin(a, rest).flattened + // Also take care of the `rest` of its parent match block if it's not getting fused. + val realArm = Begin(a, parentMatchRest.fold(rest)(p => Begin(rest, p))).flattened traverser.applyBlock(realArm) traverser.result.toList.sortBy(_.uid) @@ -844,8 +848,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store.get(s) match case Some(f, b) => f.map(_.sym) -> b case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => - val parentRest = d.matchScrutToParentMatchScrut(s).map: p => - getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest) + val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => + if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) + else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) val res = N -> (parentRest match @@ -859,8 +864,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store += s -> res res case _ => // now need to build a new function and update the store - val parentRest = d.matchScrutToParentMatchScrut(s).map: p => - getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest) + val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => + if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) + else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) val restRewritten = parentRest match case None => applyBlock(restBeforeRewriting) case Some(Some(s), b) => Begin( @@ -891,7 +897,10 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) => - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) + val parentMatchRest = d.matchScrutToParentMatchScrut(scrut.uid).flatMap: p => + if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) + else None + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || parentMatchRest.fold(false)(_.hasExplicitRet) val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) case _ => super.applyBlock(b) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls new file mode 100644 index 0000000000..741e796875 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -0,0 +1,482 @@ +:js +:deforest + +object A +object B +object C +class AA(x) +class BB(x) + +object Nil +class Cons(h, t) + +object None +class Some(x) + + +:sjs +fun f(x, y) = + let t = if x is + AA(a) then + let m = if a is + AA(x) then x + m + 9 + BB(b) then b + t + y +fun g(x) = if x is + AA then 0 +let a = AA(AA(3)) +f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) +//│ JS (unsanitized): +//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; +//│ f = function f(x, y) { +//│ let t, param0, b, param01, a1, m, param02, x1, tmp11, tmp12; +//│ if (x instanceof AA1.class) { +//│ param01 = x.x; +//│ a1 = param01; +//│ if (a1 instanceof AA1.class) { +//│ param02 = a1.x; +//│ x1 = param02; +//│ tmp11 = x1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp11; +//│ tmp12 = m + 9; +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.x; +//│ b = param0; +//│ tmp12 = b; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp12; +//│ return t + y +//│ }; +//│ g = function g(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = AA1(3); +//│ tmp1 = AA1(tmp); +//│ a = tmp1; +//│ tmp2 = f(a, 2); +//│ tmp3 = g(a); +//│ tmp4 = tmp2 + tmp3; +//│ tmp5 = BB1(3); +//│ tmp6 = f(tmp5, 2); +//│ tmp7 = tmp4 + tmp6; +//│ tmp8 = AA1(4); +//│ tmp9 = AA1(tmp8); +//│ tmp10 = f(tmp9, 5); +//│ tmp7 + tmp10 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, AA_x_tmp, match_a_rest, match_a_branch_AA, AA_x_tmp1; +//│ match_a_branch_AA = function match_a_branch_AA(y, AA_x) { +//│ let param0, x, tmp11; +//│ param0 = AA_x; +//│ x = param0; +//│ tmp11 = x; +//│ return match_a_rest(y, tmp11) +//│ }; +//│ match_a_rest = function match_a_rest(y, tmp11) { +//│ let t, m, tmp12; +//│ m = tmp11; +//│ tmp12 = m + 9; +//│ t = tmp12; +//│ return t + y +//│ }; +//│ f = function f(x, y) { +//│ let t, param0, b, param01, a1, tmp11; +//│ if (x instanceof AA1.class) { +//│ param01 = x.x; +//│ a1 = param01; +//│ return runtime.safeCall(a1(y)) +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.x; +//│ b = param0; +//│ tmp11 = b; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp11; +//│ return t + y +//│ }; +//│ g = function g(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ AA_x_tmp = 3; +//│ tmp = (y) => { +//│ return match_a_branch_AA(y, AA_x_tmp) +//│ }; +//│ tmp1 = AA1(tmp); +//│ a = tmp1; +//│ tmp2 = f(a, 2); +//│ tmp3 = g(a); +//│ tmp4 = tmp2 + tmp3; +//│ tmp5 = BB1(3); +//│ tmp6 = f(tmp5, 2); +//│ tmp7 = tmp4 + tmp6; +//│ AA_x_tmp1 = 4; +//│ tmp8 = (y) => { +//│ return match_a_branch_AA(y, AA_x_tmp1) +//│ }; +//│ tmp9 = AA1(tmp8); +//│ tmp10 = f(tmp9, 5); +//│ block$res4 = tmp7 + tmp10; +//│ undefined +//│ = 37 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 37 +//│ a = AA(AA(3)) + + + +:sjs +fun f(x, y) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 + y +f(AA(AA(3)), 9) + f(AA(AA(4)), 10) +//│ JS (unsanitized): +//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; +//│ f1 = function f(x, y) { +//│ let n, param0, a1, param01, m, tmp28, tmp29, tmp30; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a1 = param0; +//│ if (a1 instanceof AA1.class) { +//│ param01 = a1.x; +//│ m = param01; +//│ tmp28 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp29 = tmp28; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp29; +//│ tmp30 = n + 2; +//│ return tmp30 + y +//│ }; +//│ tmp22 = AA1(3); +//│ tmp23 = AA1(tmp22); +//│ tmp24 = f1(tmp23, 9); +//│ tmp25 = AA1(4); +//│ tmp26 = AA1(tmp25); +//│ tmp27 = f1(tmp26, 10); +//│ tmp24 + tmp27 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, AA_x_tmp2, match_x_rest, match_a_rest1, match_a_branch_AA1, AA_x_tmp3, match_x_branch_AA, AA_x_tmp4, AA_x_tmp5; +//│ match_a_branch_AA1 = function match_a_branch_AA(y, AA_x) { +//│ let param0, m, tmp28; +//│ param0 = AA_x; +//│ m = param0; +//│ tmp28 = m; +//│ return match_a_rest1(y, tmp28, match_x_rest) +//│ }; +//│ match_x_branch_AA = function match_x_branch_AA(y, AA_x) { +//│ let param0, a1; +//│ param0 = AA_x; +//│ a1 = param0; +//│ return runtime.safeCall(a1(y)) +//│ }; +//│ match_x_rest = function match_x_rest(y, tmp28) { +//│ let n, tmp29; +//│ n = tmp28; +//│ tmp29 = n + 2; +//│ return tmp29 + y +//│ }; +//│ match_a_rest1 = function match_a_rest(y, tmp28) { +//│ let tmp29; +//│ tmp29 = tmp28; +//│ return match_x_rest(y, tmp29) +//│ }; +//│ f1 = function f(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ AA_x_tmp2 = 3; +//│ tmp22 = (y) => { +//│ return match_a_branch_AA1(y, AA_x_tmp2) +//│ }; +//│ AA_x_tmp3 = tmp22; +//│ tmp23 = (y) => { +//│ return match_x_branch_AA(y, AA_x_tmp3) +//│ }; +//│ tmp24 = f1(tmp23, 9); +//│ AA_x_tmp4 = 4; +//│ tmp25 = (y) => { +//│ return match_a_branch_AA1(y, AA_x_tmp4) +//│ }; +//│ AA_x_tmp5 = tmp25; +//│ tmp26 = (y) => { +//│ return match_x_branch_AA(y, AA_x_tmp5) +//│ }; +//│ tmp27 = f1(tmp26, 10); +//│ block$res6 = tmp24 + tmp27; +//│ undefined +//│ = 30 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 30 + + + +:sjs +fun f(x) = + let n = if x is + AA(a) then + if a is + AA(m) then m + n + 2 +f(AA(AA(3))) +//│ JS (unsanitized): +//│ let f2, tmp34, tmp35; +//│ f2 = function f(x) { +//│ let n, param0, a1, param01, m, tmp36, tmp37; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a1 = param0; +//│ if (a1 instanceof AA1.class) { +//│ param01 = a1.x; +//│ m = param01; +//│ tmp36 = m; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp37 = tmp36; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp37; +//│ return n + 2 +//│ }; +//│ tmp34 = AA1(3); +//│ tmp35 = AA1(tmp34); +//│ f2(tmp35) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f2, tmp34, tmp35, AA_x, AA_x1; +//│ f2 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x = 3; +//│ tmp34 = () => { +//│ let n, param0, m, tmp36, tmp37; +//│ param0 = AA_x; +//│ m = param0; +//│ tmp36 = m; +//│ tmp37 = tmp36; +//│ n = tmp37; +//│ return n + 2 +//│ }; +//│ AA_x1 = tmp34; +//│ tmp35 = () => { +//│ let param0, a1; +//│ param0 = AA_x1; +//│ a1 = param0; +//│ return runtime.safeCall(a1()) +//│ }; +//│ block$res8 = f2(tmp35); +//│ undefined +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 + + + +:sjs +fun f(x) = + let t = if x is + AA(AA(a)) then a + t + 3 +f(AA(AA(A))) + f(AA(AA(A))) +//│ JS (unsanitized): +//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; +//│ f3 = function f(x) { +//│ let t, param0, param01, a1, tmp44; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a1 = param01; +//│ tmp44 = a1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp44; +//│ return t + 3 +//│ }; +//│ tmp38 = AA1(A1); +//│ tmp39 = AA1(tmp38); +//│ tmp40 = f3(tmp39); +//│ tmp41 = AA1(A1); +//│ tmp42 = AA1(tmp41); +//│ tmp43 = f3(tmp42); +//│ tmp40 + tmp43 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, AA_x_tmp6, match_x_rest1, match_param0_branch_AA, AA_x_tmp7, match_x_branch_AA1, AA_x_tmp8, AA_x_tmp9; +//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x2) { +//│ let param0, a1, tmp44; +//│ param0 = AA_x2; +//│ a1 = param0; +//│ tmp44 = a1; +//│ return match_x_rest1(tmp44) +//│ }; +//│ match_x_branch_AA1 = function match_x_branch_AA(AA_x2) { +//│ let param0; +//│ param0 = AA_x2; +//│ return runtime.safeCall(param0()) +//│ }; +//│ match_x_rest1 = function match_x_rest(tmp44) { +//│ let t; +//│ t = tmp44; +//│ return t + 3 +//│ }; +//│ f3 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x_tmp6 = A1; +//│ tmp38 = () => { +//│ return match_param0_branch_AA(AA_x_tmp6) +//│ }; +//│ AA_x_tmp7 = tmp38; +//│ tmp39 = () => { +//│ return match_x_branch_AA1(AA_x_tmp7) +//│ }; +//│ tmp40 = f3(tmp39); +//│ AA_x_tmp8 = A1; +//│ tmp41 = () => { +//│ return match_param0_branch_AA(AA_x_tmp8) +//│ }; +//│ AA_x_tmp9 = tmp41; +//│ tmp42 = () => { +//│ return match_x_branch_AA1(AA_x_tmp9) +//│ }; +//│ tmp43 = f3(tmp42); +//│ block$res10 = tmp40 + tmp43; +//│ undefined +//│ = "A3A3" +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "A3A3" + + + +:sjs +fun c2(x) = if x is + AA(AA(a)) then a +c2(AA(AA(0))) +//│ JS (unsanitized): +//│ let c2, tmp50, tmp51; +//│ c2 = function c2(x) { +//│ let param0, param01, a1; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ a1 = param01; +//│ return a1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp50 = AA1(0); +//│ tmp51 = AA1(tmp50); +//│ c2(tmp51) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c2, tmp50, tmp51, AA_x2, AA_x3; +//│ c2 = function c2(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ AA_x2 = 0; +//│ tmp50 = () => { +//│ let param0, a1; +//│ param0 = AA_x2; +//│ a1 = param0; +//│ return a1 +//│ }; +//│ AA_x3 = tmp50; +//│ tmp51 = () => { +//│ let param0; +//│ param0 = AA_x3; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res12 = c2(tmp51); +//│ undefined +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 + + + +:sjs +fun f(a) = if a is + AA(BB(B)) then 3 +f(AA(BB(B))) +//│ JS (unsanitized): +//│ let f4, tmp54, tmp55; +//│ f4 = function f(a1) { +//│ let param0, param01; +//│ if (a1 instanceof AA1.class) { +//│ param0 = a1.x; +//│ if (param0 instanceof BB1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof B1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp54 = BB1(B1); +//│ tmp55 = AA1(tmp54); +//│ f4(tmp55) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f4, tmp54, tmp55, BB_x, AA_x4; +//│ f4 = function f(a1) { +//│ return runtime.safeCall(a1()) +//│ }; +//│ BB_x = () => { +//│ return 3 +//│ }; +//│ tmp54 = () => { +//│ let param0; +//│ param0 = BB_x; +//│ return runtime.safeCall(param0()) +//│ }; +//│ AA_x4 = tmp54; +//│ tmp55 = () => { +//│ let param0; +//│ param0 = AA_x4; +//│ return runtime.safeCall(param0()) +//│ }; +//│ block$res14 = f4(tmp55); +//│ undefined +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 7a68daa05a..d71e634e6b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1634,58 +1634,7 @@ c(BB(3), 0) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 -:sjs -fun f(a) = if a is - AA(BB(B)) then 3 -f(AA(BB(B))) -//│ JS (unsanitized): -//│ let f8, tmp79, tmp80; -//│ f8 = function f(a) { -//│ let param0, param01; -//│ if (a instanceof AA1.class) { -//│ param0 = a.aa; -//│ if (param0 instanceof BB1.class) { -//│ param01 = param0.bb; -//│ if (param01 instanceof B1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp79 = BB1(B1); -//│ tmp80 = AA1(tmp79); -//│ f8(tmp80) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f8, tmp79, tmp80, BB_bb, AA_aa1; -//│ f8 = function f(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ BB_bb = () => { -//│ return 3 -//│ }; -//│ tmp79 = () => { -//│ let param0; -//│ param0 = BB_bb; -//│ return runtime.safeCall(param0()) -//│ }; -//│ AA_aa1 = tmp79; -//│ tmp80 = () => { -//│ let param0; -//│ param0 = AA_aa1; -//│ return runtime.safeCall(param0()) -//│ }; -//│ block$res51 = f8(tmp80); -//│ undefined -//│ = 3 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 + :sjs @@ -1695,8 +1644,8 @@ fun g(b) = if b is AA(a) then a + 1 f(AA(AA(0))) //│ JS (unsanitized): -//│ let f9, g, tmp83, tmp84; -//│ f9 = function f(x1) { +//│ let f8, g, tmp79, tmp80; +//│ f8 = function f(x1) { //│ let param0, b; //│ if (x1 instanceof AA1.class) { //│ param0 = x1.aa; @@ -1716,348 +1665,55 @@ f(AA(AA(0))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp83 = AA1(0); -//│ tmp84 = AA1(tmp83); -//│ f9(tmp84) +//│ tmp79 = AA1(0); +//│ tmp80 = AA1(tmp79); +//│ f8(tmp80) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f9, g, tmp83, tmp84, AA_aa2, AA_aa3; -//│ f9 = function f(x1) { +//│ let f8, g, tmp79, tmp80, AA_aa1, AA_aa2; +//│ f8 = function f(x1) { //│ return runtime.safeCall(x1()) //│ }; //│ g = function g(b) { //│ return runtime.safeCall(b()) //│ }; -//│ AA_aa2 = 0; -//│ tmp83 = () => { +//│ AA_aa1 = 0; +//│ tmp79 = () => { //│ let param0, a; -//│ param0 = AA_aa2; +//│ param0 = AA_aa1; //│ a = param0; //│ return a + 1 //│ }; -//│ AA_aa3 = tmp83; -//│ tmp84 = () => { +//│ AA_aa2 = tmp79; +//│ tmp80 = () => { //│ let param0, b; -//│ param0 = AA_aa3; +//│ param0 = AA_aa2; //│ b = param0; //│ return g(b) //│ }; -//│ block$res53 = f9(tmp84); +//│ block$res51 = f8(tmp80); //│ undefined //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 -:sjs -fun c2(x) = if x is - AA(AA(a)) then a -c2(AA(AA(0))) -//│ JS (unsanitized): -//│ let c21, tmp87, tmp88; -//│ c21 = function c2(x1) { -//│ let param0, param01, a; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.aa; -//│ a = param01; -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp87 = AA1(0); -//│ tmp88 = AA1(tmp87); -//│ c21(tmp88) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c21, tmp87, tmp88, AA_aa4, AA_aa5; -//│ c21 = function c2(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ AA_aa4 = 0; -//│ tmp87 = () => { -//│ let param0, a; -//│ param0 = AA_aa4; -//│ a = param0; -//│ return a -//│ }; -//│ AA_aa5 = tmp87; -//│ tmp88 = () => { -//│ let param0; -//│ param0 = AA_aa5; -//│ return runtime.safeCall(param0()) -//│ }; -//│ block$res55 = c21(tmp88); -//│ undefined -//│ = 0 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 0 - - - - -:sjs -fun f(x) = - let t = if x is - AA(AA(a)) then a - t + 3 -f(AA(AA(A))) + f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f10, tmp91, tmp92, tmp93, tmp94, tmp95, tmp96; -//│ f10 = function f(x1) { -//│ let t, param0, param01, a, tmp97; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.aa; -//│ a = param01; -//│ tmp97 = a; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp97; -//│ return t + 3 -//│ }; -//│ tmp91 = AA1(A1); -//│ tmp92 = AA1(tmp91); -//│ tmp93 = f10(tmp92); -//│ tmp94 = AA1(A1); -//│ tmp95 = AA1(tmp94); -//│ tmp96 = f10(tmp95); -//│ tmp93 + tmp96 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f10, tmp91, tmp92, tmp93, tmp94, tmp95, tmp96, AA_aa_tmp, match_x_rest3, match_param0_branch_AA, AA_aa_tmp1, match_x_branch_AA, AA_aa_tmp2, AA_aa_tmp3; -//│ match_param0_branch_AA = function match_param0_branch_AA(AA_aa6) { -//│ let param0, a, tmp97; -//│ param0 = AA_aa6; -//│ a = param0; -//│ tmp97 = a; -//│ return match_x_rest3(tmp97) -//│ }; -//│ match_x_branch_AA = function match_x_branch_AA(AA_aa6) { -//│ let param0; -//│ param0 = AA_aa6; -//│ return runtime.safeCall(param0()) -//│ }; -//│ match_x_rest3 = function match_x_rest(tmp97) { -//│ let t; -//│ t = tmp97; -//│ return t + 3 -//│ }; -//│ f10 = function f(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ AA_aa_tmp = A1; -//│ tmp91 = () => { -//│ return match_param0_branch_AA(AA_aa_tmp) -//│ }; -//│ AA_aa_tmp1 = tmp91; -//│ tmp92 = () => { -//│ return match_x_branch_AA(AA_aa_tmp1) -//│ }; -//│ tmp93 = f10(tmp92); -//│ AA_aa_tmp2 = A1; -//│ tmp94 = () => { -//│ return match_param0_branch_AA(AA_aa_tmp2) -//│ }; -//│ AA_aa_tmp3 = tmp94; -//│ tmp95 = () => { -//│ return match_x_branch_AA(AA_aa_tmp3) -//│ }; -//│ tmp96 = f10(tmp95); -//│ block$res57 = tmp93 + tmp96; -//│ undefined -//│ = "A3A3" -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "A3A3" - - -:sjs -fun f(x) = - let n = if x is - AA(a) then - if a is - AA(m) then m - n + 2 -f(AA(AA(3))) -//│ JS (unsanitized): -//│ let f11, tmp103, tmp104; -//│ f11 = function f(x1) { -//│ let n, param0, a, param01, m, tmp105, tmp106; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ a = param0; -//│ if (a instanceof AA1.class) { -//│ param01 = a.aa; -//│ m = param01; -//│ tmp105 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp106 = tmp105; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp106; -//│ return n + 2 -//│ }; -//│ tmp103 = AA1(3); -//│ tmp104 = AA1(tmp103); -//│ f11(tmp104) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f11, tmp103, tmp104, AA_aa6, AA_aa7; -//│ f11 = function f(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ AA_aa6 = 3; -//│ tmp103 = () => { -//│ let n, param0, m, tmp105, tmp106; -//│ param0 = AA_aa6; -//│ m = param0; -//│ tmp105 = m; -//│ tmp106 = tmp105; -//│ n = tmp106; -//│ return n + 2 -//│ }; -//│ AA_aa7 = tmp103; -//│ tmp104 = () => { -//│ let param0, a; -//│ param0 = AA_aa7; -//│ a = param0; -//│ return runtime.safeCall(a()) -//│ }; -//│ block$res59 = f11(tmp104); -//│ undefined -//│ = 5 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 5 - - - -:sjs -fun f(x, y) = - let n = if x is - AA(a) then - if a is - AA(m) then m - n + 2 + y -f(AA(AA(3)), 9) + f(AA(AA(4)), 10) -//│ JS (unsanitized): -//│ let f12, tmp107, tmp108, tmp109, tmp110, tmp111, tmp112; -//│ f12 = function f(x1, y1) { -//│ let n, param0, a, param01, m, tmp113, tmp114, tmp115; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ a = param0; -//│ if (a instanceof AA1.class) { -//│ param01 = a.aa; -//│ m = param01; -//│ tmp113 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp114 = tmp113; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp114; -//│ tmp115 = n + 2; -//│ return tmp115 + y1 -//│ }; -//│ tmp107 = AA1(3); -//│ tmp108 = AA1(tmp107); -//│ tmp109 = f12(tmp108, 9); -//│ tmp110 = AA1(4); -//│ tmp111 = AA1(tmp110); -//│ tmp112 = f12(tmp111, 10); -//│ tmp109 + tmp112 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f12, tmp107, tmp108, tmp109, tmp110, tmp111, tmp112, AA_aa_tmp4, match_x_rest4, match_a_rest1, match_a_branch_AA, AA_aa_tmp5, match_x_branch_AA1, AA_aa_tmp6, AA_aa_tmp7; -//│ match_a_branch_AA = function match_a_branch_AA(y1, AA_aa8) { -//│ let param0, m, tmp113; -//│ param0 = AA_aa8; -//│ m = param0; -//│ tmp113 = m; -//│ return match_a_rest1(y1, tmp113, match_x_rest4) -//│ }; -//│ match_x_branch_AA1 = function match_x_branch_AA(y1, AA_aa8) { -//│ let param0, a; -//│ param0 = AA_aa8; -//│ a = param0; -//│ return runtime.safeCall(a(y1)) -//│ }; -//│ match_x_rest4 = function match_x_rest(y1, tmp113) { -//│ let n, tmp114; -//│ n = tmp113; -//│ tmp114 = n + 2; -//│ return tmp114 + y1 -//│ }; -//│ match_a_rest1 = function match_a_rest(y1, tmp113) { -//│ let tmp114; -//│ tmp114 = tmp113; -//│ return match_x_rest4(y1, tmp114) -//│ }; -//│ f12 = function f(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ AA_aa_tmp4 = 3; -//│ tmp107 = (y1) => { -//│ return match_a_branch_AA(y1, AA_aa_tmp4) -//│ }; -//│ AA_aa_tmp5 = tmp107; -//│ tmp108 = (y1) => { -//│ return match_x_branch_AA1(y1, AA_aa_tmp5) -//│ }; -//│ tmp109 = f12(tmp108, 9); -//│ AA_aa_tmp6 = 4; -//│ tmp110 = (y1) => { -//│ return match_a_branch_AA(y1, AA_aa_tmp6) -//│ }; -//│ AA_aa_tmp7 = tmp110; -//│ tmp111 = (y1) => { -//│ return match_x_branch_AA1(y1, AA_aa_tmp7) -//│ }; -//│ tmp112 = f12(tmp111, 10); -//│ block$res61 = tmp109 + tmp112; -//│ undefined -//│ = 30 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 30 - - :sjs fun f(x) = x.aa.aa f(AA(AA(3))) //│ JS (unsanitized): -//│ let f13, tmp119, tmp120; -//│ f13 = function f(x1) { +//│ let f9, tmp83, tmp84; +//│ f9 = function f(x1) { //│ return x1.aa.aa //│ }; -//│ tmp119 = AA1(3); -//│ tmp120 = AA1(tmp119); -//│ f13(tmp120) +//│ tmp83 = AA1(3); +//│ tmp84 = AA1(tmp83); +//│ f9(tmp84) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f13, tmp119, tmp120; -//│ f13 = function f(x1) { -//│ return x1 -//│ }; -//│ tmp119 = 3; -//│ tmp120 = tmp119; -//│ block$res63 = f13(tmp120); -//│ undefined +//│ let f9, tmp83, tmp84; +//│ f9 = function f(x1) { return x1 }; tmp83 = 3; tmp84 = tmp83; block$res53 = f9(tmp84); undefined //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -2081,129 +1737,129 @@ fun outer2(x, y, z) = let p = AA(AA(3)) outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ JS (unsanitized): -//│ let outer1, outer2, inner, p1, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129; +//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93; //│ inner = function inner(x1, y1) { -//│ let t, param0, b, param01, a, tmp130; +//│ let t, param0, b, param01, a, tmp94; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp130 = a + y1; +//│ tmp94 = a + y1; //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp130 = b - y1; +//│ tmp94 = b - y1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp130; +//│ t = tmp94; //│ return t * y1 //│ }; //│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp130; +//│ let t, param0, b, param01, a, tmp94; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp130 = inner(a, y1); +//│ tmp94 = inner(a, y1); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp130 = inner(b, y1); +//│ tmp94 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp130; +//│ t = tmp94; //│ return t + z //│ }; //│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp130; +//│ let t, param0, b, param01, a, tmp94; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp130 = inner(a, z); +//│ tmp94 = inner(a, z); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp130 = inner(b, y1); +//│ tmp94 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp130; +//│ t = tmp94; //│ return t + y1 //│ }; -//│ tmp123 = AA1(3); -//│ tmp124 = AA1(tmp123); -//│ p1 = tmp124; -//│ tmp125 = outer1(p1, 1, 2); -//│ tmp126 = outer2(p1, 3, 4); -//│ tmp127 = tmp125 + tmp126; -//│ tmp128 = AA1(5); -//│ tmp129 = inner(tmp128, 6); -//│ tmp127 + tmp129 +//│ tmp87 = AA1(3); +//│ tmp88 = AA1(tmp87); +//│ p1 = tmp88; +//│ tmp89 = outer1(p1, 1, 2); +//│ tmp90 = outer2(p1, 3, 4); +//│ tmp91 = tmp89 + tmp90; +//│ tmp92 = AA1(5); +//│ tmp93 = inner(tmp92, 6); +//│ tmp91 + tmp93 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let outer1, outer2, inner, p1, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, AA_aa_tmp8, match_x_rest5, match_x_branch_AA2, AA_aa_tmp9; -//│ match_x_branch_AA2 = function match_x_branch_AA(y1, AA_aa8) { -//│ let param0, a, tmp130; -//│ param0 = AA_aa8; +//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, AA_aa_tmp, match_x_rest3, match_x_branch_AA, AA_aa_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y1, AA_aa3) { +//│ let param0, a, tmp94; +//│ param0 = AA_aa3; //│ a = param0; -//│ tmp130 = a + y1; -//│ return match_x_rest5(y1, tmp130) +//│ tmp94 = a + y1; +//│ return match_x_rest3(y1, tmp94) //│ }; -//│ match_x_rest5 = function match_x_rest(y1, tmp130) { +//│ match_x_rest3 = function match_x_rest(y1, tmp94) { //│ let t; -//│ t = tmp130; +//│ t = tmp94; //│ return t * y1 //│ }; //│ inner = function inner(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; //│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp130; +//│ let t, param0, b, param01, a, tmp94; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp130 = inner(a, y1); +//│ tmp94 = inner(a, y1); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp130 = inner(b, y1); +//│ tmp94 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp130; +//│ t = tmp94; //│ return t + z //│ }; //│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp130; +//│ let t, param0, b, param01, a, tmp94; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp130 = inner(a, z); +//│ tmp94 = inner(a, z); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp130 = inner(b, y1); +//│ tmp94 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp130; +//│ t = tmp94; //│ return t + y1 //│ }; -//│ AA_aa_tmp8 = 3; -//│ tmp123 = (y1) => { -//│ return match_x_branch_AA2(y1, AA_aa_tmp8) -//│ }; -//│ tmp124 = AA1(tmp123); -//│ p1 = tmp124; -//│ tmp125 = outer1(p1, 1, 2); -//│ tmp126 = outer2(p1, 3, 4); -//│ tmp127 = tmp125 + tmp126; -//│ AA_aa_tmp9 = 5; -//│ tmp128 = (y1) => { -//│ return match_x_branch_AA2(y1, AA_aa_tmp9) -//│ }; -//│ tmp129 = inner(tmp128, 6); -//│ block$res65 = tmp127 + tmp129; +//│ AA_aa_tmp = 3; +//│ tmp87 = (y1) => { +//│ return match_x_branch_AA(y1, AA_aa_tmp) +//│ }; +//│ tmp88 = AA1(tmp87); +//│ p1 = tmp88; +//│ tmp89 = outer1(p1, 1, 2); +//│ tmp90 = outer2(p1, 3, 4); +//│ tmp91 = tmp89 + tmp90; +//│ AA_aa_tmp1 = 5; +//│ tmp92 = (y1) => { +//│ return match_x_branch_AA(y1, AA_aa_tmp1) +//│ }; +//│ tmp93 = inner(tmp92, 6); +//│ block$res55 = tmp91 + tmp93; //│ undefined //│ = 103 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -2216,46 +1872,46 @@ fun mapHead(f, x) = if x is AAA(h, t) then f(h) mapHead(x => x, AAA(1, AAA(2, None))) //│ JS (unsanitized): -//│ let mapHead, tmp137, tmp138, lambda4; -//│ mapHead = function mapHead(f14, x1) { +//│ let mapHead, tmp101, tmp102, lambda4; +//│ mapHead = function mapHead(f10, x1) { //│ let param0, param1, h, t; //│ if (x1 instanceof AAA1.class) { //│ param0 = x1.x; //│ param1 = x1.y; //│ h = param0; //│ t = param1; -//│ return runtime.safeCall(f14(h)) +//│ return runtime.safeCall(f10(h)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp137 = AAA1(2, None1); -//│ tmp138 = AAA1(1, tmp137); +//│ tmp101 = AAA1(2, None1); +//│ tmp102 = AAA1(1, tmp101); //│ lambda4 = (undefined, function (x1) { //│ return x1 //│ }); -//│ mapHead(lambda4, tmp138) +//│ mapHead(lambda4, tmp102) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let mapHead, tmp137, tmp138, lambda4, AAA_x, AAA_y; -//│ mapHead = function mapHead(f14, x1) { -//│ return runtime.safeCall(x1(f14)) +//│ let mapHead, tmp101, tmp102, lambda4, AAA_x, AAA_y; +//│ mapHead = function mapHead(f10, x1) { +//│ return runtime.safeCall(x1(f10)) //│ }; -//│ tmp137 = AAA1(2, None1); +//│ tmp101 = AAA1(2, None1); //│ AAA_x = 1; -//│ AAA_y = tmp137; -//│ tmp138 = (f14) => { +//│ AAA_y = tmp101; +//│ tmp102 = (f10) => { //│ let param0, param1, h, t; //│ param0 = AAA_x; //│ param1 = AAA_y; //│ h = param0; //│ t = param1; -//│ return runtime.safeCall(f14(h)) +//│ return runtime.safeCall(f10(h)) //│ }; //│ lambda4 = (undefined, function (x1) { //│ return x1 //│ }); -//│ block$res67 = mapHead(lambda4, tmp138); +//│ block$res57 = mapHead(lambda4, tmp102); //│ undefined //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 7f85ac127997690ad74bd858807f184e58846e2a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:53:50 +0800 Subject: [PATCH 128/303] fix after merge: add val to public class fields; remove subst for blocktraversers --- .../scala/hkmc2/codegen/Deforestation.scala | 47 ++++++++----------- .../test/mlscript/deforest/nestedMatch.mls | 8 ++-- .../deforest/selectionsInNestedMatch.mls | 8 ++-- .../src/test/mlscript/deforest/simple.mls | 14 +++--- .../src/test/mlscript/deforest/todos.mls | 8 ++-- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 1 - 6 files changed, 39 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 66850db305..cde2e02e6c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -81,7 +81,7 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid -class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser(new SymbolSubst()): +class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: val ctx = mutable.Set.from(alwaysDefined) val result = mutable.Set.empty[Symbol] @@ -174,7 +174,7 @@ extension (b: Block) traverser.result.toList.sortBy(_.uid) def hasExplicitRet: Boolean = - object HasExplicitRetTraverser extends BlockTraverserShallow(new SymbolSubst()): + object HasExplicitRetTraverser extends BlockTraverserShallow: var flag = false override def applyBlock(b: Block): Unit = b match case Return(_, imp) => flag = !imp @@ -228,17 +228,13 @@ class Deforest(using TL, Raise, Elaborator.State): def apply(s: Symbol) = store.contains(s) - def init(b: Block) = - object Subst extends SymbolSubst: - - override def mapTopLevelSym(s: TopLevelSymbol): TopLevelSymbol = - store += s; s - override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = - store += s; s - override def mapBuiltInSym(s: BuiltinSymbol): BuiltinSymbol = - store += s; s - - object FreshVarForAllVars extends BlockTraverser(Subst) + def init(b: Block) = + object FreshVarForAllVars extends BlockTraverser: + override def applySymbol(sym: Symbol): Unit = sym match + case _: TopLevelSymbol => store += sym + case _: BlockMemberSymbol => store += sym + case _: BuiltinSymbol => store += sym + case _ => () FreshVarForAllVars.applyBlock(b) var constraints: Ls[ProdStrat -> ConsStrat] = Nil @@ -248,19 +244,16 @@ class Deforest(using TL, Raise, Elaborator.State): object symToStrat: val store = mutable.Map.empty[Symbol, ProdVar] - def init(p: Block) = - if store.isEmpty then - object AllVarsSymbolSubst extends SymbolSubst: - override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = - store += s -> freshVar(s.nme)._1; s - override def mapTempSym(s: TempSymbol): TempSymbol = - store += s -> freshVar(s.nme)._1; s - override def mapVarSym(s: VarSymbol): VarSymbol = - store += s -> freshVar(s.nme)._1; s - override def mapTermSym(s: TermSymbol): TermSymbol = - store += s -> freshVar(s.nme)._1; s - object FreshVarForAllVars extends BlockTraverser(AllVarsSymbolSubst) - FreshVarForAllVars.applyBlock(p) + def init(p: Block) = if store.isEmpty then + object FreshVarForAllVars extends BlockTraverser: + override def applySymbol(s: Symbol): Unit = s match + case _: BlockMemberSymbol => store += s -> freshVar(s.nme)._1 + case _: TempSymbol => store += s -> freshVar(s.nme)._1 + case _: VarSymbol => store += s -> freshVar(s.nme)._1 + case _: TermSymbol => store += s -> freshVar(s.nme)._1 + case _ => () + + FreshVarForAllVars.applyBlock(p) // TODO: ctor as a function? def getStratOfSym(s: Symbol) = @@ -577,7 +570,7 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorSym = getClsSymOfUid(ctor) val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - object GetCtorsTraverser extends BlockTraverser(new SymbolSubst()): + object GetCtorsTraverser extends BlockTraverser: val ctors = mutable.Set.empty[ResultId] override def applyResult(r: Result): Unit = handleCtors( diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 741e796875..8a2a01db78 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -4,14 +4,14 @@ object A object B object C -class AA(x) -class BB(x) +class AA(val x) +class BB(val x) object Nil -class Cons(h, t) +class Cons(val h, val t) object None -class Some(x) +class Some(val x) :sjs diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 6090903f75..2a575aa61d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -4,14 +4,14 @@ object A object B object C -class AA(x) -class BB(x) +class AA(val x) +class BB(val x) object Nil -class Cons(h, t) +class Cons(val h, val t) object None -class Some(x) +class Some(val x) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index d71e634e6b..1e9464898d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -5,13 +5,13 @@ object A object B object C -class AA(aa) -class BB(bb) -class AAA(x, y) -class BBB(x, y) -class CCC(c) +class AA(val aa) +class BB(val bb) +class AAA(val x, val y) +class BBB(val x, val y) +class CCC(val c) object None -class Some(value) +class Some(val value) :sjs fun test() = @@ -391,7 +391,7 @@ test() object Nil -class Cons(h, t) +class Cons(val h, val t) :sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index a1fba2d3d6..14e37e3a0a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -4,14 +4,14 @@ object A object B object C -class AA(x) -class BB(x) +class AA(val x) +class BB(val x) object Nil -class Cons(h, t) +class Cons(val h, val t) object None -class Some(x) +class Some(val x) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 8ee6b00bfd..f6b8b23d43 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -147,7 +147,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case Return(res, implct) => assert(implct) Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) - case _: HandleBlockReturn => ??? case tl: (Throw | Break | Continue) => tl ) From f117a6848183871c167a8e2209ae0cfdcbeffe8d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:02:35 +0800 Subject: [PATCH 129/303] another problematic test --- .../test/mlscript/deforest/nestedMatch.mls | 115 ++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 125 ++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 8a2a01db78..8c197d5b76 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -480,3 +480,118 @@ f(AA(BB(B))) //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 + + +:sjs +fun test(x, y, z, i) = if x is + AA(a) then + let m = if y is + AA(a1) then a1 + i + let n = if z is + BB(a2) then a2 - i + m + n +test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) +//│ JS (unsanitized): +//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65; +//│ test = function test(x, y, z, i) { +//│ let param0, a1, m, param01, a11, n, param02, a2, tmp66, tmp67; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a1 = param0; +//│ if (y instanceof AA1.class) { +//│ param01 = y.x; +//│ a11 = param01; +//│ tmp66 = a11 + i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp66; +//│ if (z instanceof BB1.class) { +//│ param02 = z.x; +//│ a2 = param02; +//│ tmp67 = a2 - i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp67; +//│ return m + n +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp58 = AA1(1); +//│ tmp59 = AA1(2); +//│ tmp60 = BB1(3); +//│ tmp61 = test(tmp58, tmp59, tmp60, 4); +//│ tmp62 = AA1(1); +//│ tmp63 = AA1(2); +//│ tmp64 = BB1(3); +//│ tmp65 = test(tmp62, tmp63, tmp64, 4); +//│ tmp61 + tmp65 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, AA_x_tmp10, match_x_branch_AA2, AA_x_tmp11, match_y_rest, match_y_branch_AA, BB_x_tmp, match_z_rest, match_z_branch_BB, AA_x_tmp12, AA_x_tmp13, BB_x_tmp1; +//│ match_x_branch_AA2 = function match_x_branch_AA(y, z, i, AA_x5) { +//│ let param0, a1; +//│ param0 = AA_x5; +//│ a1 = param0; +//│ return runtime.safeCall(y(z, i)) +//│ }; +//│ match_y_branch_AA = function match_y_branch_AA(z, i, AA_x5) { +//│ let param0, a1, tmp66; +//│ param0 = AA_x5; +//│ a1 = param0; +//│ tmp66 = a1 + i; +//│ return match_y_rest(z, i, tmp66) +//│ }; +//│ match_z_branch_BB = function match_z_branch_BB(i, m, BB_x1) { +//│ let param0, a2, tmp66; +//│ param0 = BB_x1; +//│ a2 = param0; +//│ tmp66 = a2 - i; +//│ return match_z_rest(m, tmp66) +//│ }; +//│ match_y_rest = function match_y_rest(z, i, tmp66) { +//│ let m; +//│ m = tmp66; +//│ return runtime.safeCall(z(i, m)); +//│ }; +//│ match_z_rest = function match_z_rest(m, tmp66) { +//│ let n; +//│ n = tmp66; +//│ return m + n; +//│ }; +//│ test = function test(x, y, z, i) { +//│ return runtime.safeCall(x(y, z, i)) +//│ }; +//│ AA_x_tmp10 = 1; +//│ tmp58 = (y, z, i) => { +//│ return match_x_branch_AA2(y, z, i, AA_x_tmp10) +//│ }; +//│ AA_x_tmp11 = 2; +//│ tmp59 = (z, i) => { +//│ return match_y_branch_AA(z, i, AA_x_tmp11) +//│ }; +//│ BB_x_tmp = 3; +//│ tmp60 = (i, m) => { +//│ return match_z_branch_BB(i, m, BB_x_tmp) +//│ }; +//│ tmp61 = test(tmp58, tmp59, tmp60, 4); +//│ AA_x_tmp12 = 1; +//│ tmp62 = (y, z, i) => { +//│ return match_x_branch_AA2(y, z, i, AA_x_tmp12) +//│ }; +//│ AA_x_tmp13 = 2; +//│ tmp63 = (z, i) => { +//│ return match_y_branch_AA(z, i, AA_x_tmp13) +//│ }; +//│ BB_x_tmp1 = 3; +//│ tmp64 = (i, m) => { +//│ return match_z_branch_BB(i, m, BB_x_tmp1) +//│ }; +//│ tmp65 = test(tmp62, tmp63, tmp64, 4); +//│ block$res16 = tmp61 + tmp65; +//│ undefined +//│ = 10 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 10 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 14e37e3a0a..281256d6f8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -266,3 +266,128 @@ f(AA(AA(3))) +:fixme +:sjs +fun test(x, y, z, i) = + let k = if x is + AA(a) then + let m = if y is + AA(a1) then a1 + i + let n = if z is + BB(a2) then a2 - i + m + n + k + i +test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) +//│ JS (unsanitized): +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; +//│ test = function test(x, y, z, i) { +//│ let k, param0, a, m, param01, a1, n, param02, a2, tmp12, tmp13, tmp14; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a = param0; +//│ if (y instanceof AA1.class) { +//│ param01 = y.x; +//│ a1 = param01; +//│ tmp12 = a1 + i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp12; +//│ if (z instanceof BB1.class) { +//│ param02 = z.x; +//│ a2 = param02; +//│ tmp13 = a2 - i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp13; +//│ tmp14 = m + n; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ k = tmp14; +//│ return k + i +//│ }; +//│ tmp4 = AA1(1); +//│ tmp5 = AA1(2); +//│ tmp6 = BB1(3); +//│ tmp7 = test(tmp4, tmp5, tmp6, 4); +//│ tmp8 = AA1(1); +//│ tmp9 = AA1(2); +//│ tmp10 = BB1(3); +//│ tmp11 = test(tmp8, tmp9, tmp10, 4); +//│ tmp7 + tmp11 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, AA_x_tmp, match_x_rest, match_x_branch_AA, AA_x_tmp1, match_y_rest, match_y_branch_AA, BB_x_tmp, match_z_rest, match_z_branch_BB, AA_x_tmp2, AA_x_tmp3, BB_x_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, AA_x1) { +//│ let param0, a; +//│ param0 = AA_x1; +//│ a = param0; +//│ return runtime.safeCall(y(z, i)) +//│ }; +//│ match_y_branch_AA = function match_y_branch_AA(z, i, AA_x1) { +//│ let param0, a1, tmp12; +//│ param0 = AA_x1; +//│ a1 = param0; +//│ tmp12 = a1 + i; +//│ return match_y_rest(z, i, tmp12) +//│ }; +//│ match_z_branch_BB = function match_z_branch_BB(i, m, BB_x) { +//│ let param0, a2, tmp12; +//│ param0 = BB_x; +//│ a2 = param0; +//│ tmp12 = a2 - i; +//│ return match_z_rest(i, m, tmp12, match_x_rest) +//│ }; +//│ match_x_rest = function match_x_rest(i, tmp12) { +//│ let k; +//│ k = tmp12; +//│ return k + i +//│ }; +//│ match_y_rest = function match_y_rest(z, i, tmp12) { +//│ let m; +//│ m = tmp12; +//│ runtime.safeCall(z(i, m)); +//│ return match_x_rest(i, tmp_not_in_scope) +//│ }; +//│ match_z_rest = function match_z_rest(i, m, tmp12) { +//│ let n, tmp13; +//│ n = tmp12; +//│ tmp13 = m + n; +//│ return match_x_rest(i, tmp13) +//│ }; +//│ test = function test(x, y, z, i) { +//│ return runtime.safeCall(x(y, z, i)) +//│ }; +//│ AA_x_tmp = 1; +//│ tmp4 = (y, z, i) => { +//│ return match_x_branch_AA(y, z, i, AA_x_tmp) +//│ }; +//│ AA_x_tmp1 = 2; +//│ tmp5 = (z, i) => { +//│ return match_y_branch_AA(z, i, AA_x_tmp1) +//│ }; +//│ BB_x_tmp = 3; +//│ tmp6 = (i, m) => { +//│ return match_z_branch_BB(i, m, BB_x_tmp) +//│ }; +//│ tmp7 = test(tmp4, tmp5, tmp6, 4); +//│ AA_x_tmp2 = 1; +//│ tmp8 = (y, z, i) => { +//│ return match_x_branch_AA(y, z, i, AA_x_tmp2) +//│ }; +//│ AA_x_tmp3 = 2; +//│ tmp9 = (z, i) => { +//│ return match_y_branch_AA(z, i, AA_x_tmp3) +//│ }; +//│ BB_x_tmp1 = 3; +//│ tmp10 = (i, m) => { +//│ return match_z_branch_BB(i, m, BB_x_tmp1) +//│ }; +//│ tmp11 = test(tmp8, tmp9, tmp10, 4); +//│ block$res10 = tmp7 + tmp11; +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 18 From 640cc597b0dcc82adc27ee7477383baabe0c8c13 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:04:19 +0800 Subject: [PATCH 130/303] more comments on tests --- .../src/test/mlscript/deforest/todos.mls | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 281256d6f8..b61ed3cf5b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -266,6 +266,7 @@ f(AA(AA(3))) +// FIXME: fused matches should always be considered as in tail positions :fixme :sjs fun test(x, y, z, i) = @@ -391,3 +392,224 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 18 + + +// FIXME: +// - need to include the computation in the `rest` from +// matches multiple levels up +// - how to deal with the original `rest` from matches multiple levels up? +:fixme +:sjs +fun test(x) = + let t = if x is + AA(AA(AA(a))) then a + t + 5 +fun f(a) = if a is + AA(AA) then 0 +let p = AA(AA(AA(10))) +test(p) + f(p) +//│ JS (unsanitized): +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24; +//│ test1 = function test(x) { +//│ let t, param0, param01, param02, a, tmp25; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a = param02; +//│ tmp25 = a; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp25; +//│ return t + 5 +//│ }; +//│ f1 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp20 = AA1(10); +//│ tmp21 = AA1(tmp20); +//│ tmp22 = AA1(tmp21); +//│ p = tmp22; +//│ tmp23 = test1(p); +//│ tmp24 = f1(p); +//│ tmp23 + tmp24 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, AA_x1; +//│ test1 = function test(x) { +//│ let t, param0, param01; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ runtime.safeCall(param01()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp_not_in_scope; +//│ return t + 5 +//│ }; +//│ f1 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ AA_x1 = 10; +//│ tmp20 = () => { +//│ let param0, a, tmp25; +//│ param0 = AA_x1; +//│ a = param0; +//│ tmp25 = a; +//│ }; +//│ tmp21 = AA1(tmp20); +//│ tmp22 = AA1(tmp21); +//│ p = tmp22; +//│ tmp23 = test1(p); +//│ tmp24 = f1(p); +//│ block$res12 = tmp23 + tmp24; +//│ undefined +//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 15 +//│ p = AA(AA(AA(10))) + + + + +:expect 15 +:fixme +:sjs +fun test(x) = + let t = if x is + AA(AA(AA(a))) then a + AA(_) then 5 + t + 5 +fun f(a) = if a is + AA(AA) then 0 +let p = AA(AA(AA(10))) +test(p) + f(p) +//│ JS (unsanitized): +//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ test2 = function test(x) { +//│ let t, param0, param01, param02, a, tmp35; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a = param02; +//│ tmp35 = a; +//│ } else { +//│ tmp35 = 5; +//│ } +//│ } else { +//│ tmp35 = 5; +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp35; +//│ return t + 5 +//│ }; +//│ f2 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp30 = AA1(10); +//│ tmp31 = AA1(tmp30); +//│ tmp32 = AA1(tmp31); +//│ p1 = tmp32; +//│ tmp33 = test2(p1); +//│ tmp34 = f2(p1); +//│ tmp33 + tmp34 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, AA_x2; +//│ test2 = function test(x) { +//│ let t, param0, param01, tmp35; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ runtime.safeCall(param01()) +//│ } else { +//│ tmp35 = 5; +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp35; +//│ return t + 5 +//│ }; +//│ f2 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ AA_x2 = 10; +//│ tmp30 = () => { +//│ let param0, a, tmp35; +//│ param0 = AA_x2; +//│ a = param0; +//│ tmp35 = a; +//│ }; +//│ tmp31 = AA1(tmp30); +//│ tmp32 = AA1(tmp31); +//│ p1 = tmp32; +//│ tmp33 = test2(p1); +//│ tmp34 = f2(p1); +//│ block$res14 = tmp33 + tmp34; +//│ undefined +//│ ═══[RUNTIME ERROR] Expected: '15', got: 'NaN' +//│ = NaN +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 15 +//│ p = AA(AA(AA(10))) From 87781c30c726fcc3be797e45fb8b9c0d596a0544 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:26:03 +0800 Subject: [PATCH 131/303] check and compare outputs from deforestated programs --- hkmc2/shared/src/test/mlscript/deforest/todos.mls | 5 +++-- .../src/test/scala/hkmc2/JSBackendDiffMaker.scala | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index b61ed3cf5b..09306c0d3a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -391,6 +391,7 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ undefined //│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("18") //│ = 18 @@ -500,13 +501,13 @@ test(p) + f(p) //│ undefined //│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("15") //│ = 15 //│ p = AA(AA(AA(10))) -:expect 15 :fixme :sjs fun test(x) = @@ -608,8 +609,8 @@ test(p) + f(p) //│ tmp34 = f2(p1); //│ block$res14 = tmp33 + tmp34; //│ undefined -//│ ═══[RUNTIME ERROR] Expected: '15', got: 'NaN' //│ = NaN //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ ═══[RUNTIME ERROR] The result from deforestated program ("NaN") is different from the one computed by the original prorgam ("15") //│ = 15 //│ p = AA(AA(AA(10))) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index f6b8b23d43..25e31d98f4 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -103,6 +103,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val outerRaise: Raise = summon val reportedMessages = mutable.Set.empty[Str] + var deforestResult: Opt[Str] = None if showJS.isSet then given Raise = @@ -196,6 +197,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, source = Diagnostic.Source.Runtime) case _ => () + if sym === resSym then deforestResult = S(result) result match case "undefined" => case "()" => @@ -301,6 +303,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, source = Diagnostic.Source.Runtime) case _ => () + if sym === resSym && deforestFlag.isSet && deforestResult.fold(false)(_ != result) then raise: + ErrorReport( + msg"The result from deforestated program (\"${deforestResult.get}\") is different from the one computed by the original prorgam (\"${result}\")" -> N :: Nil, + source = Diagnostic.Source.Runtime) val anon = nme.isEmpty result match case "undefined" if anon => From c5afea787254ddea210251fbb34d49df02d74437 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 18 Mar 2025 19:06:24 +0800 Subject: [PATCH 132/303] update test --- .../test/mlscript/deforest/nestedMatch.mls | 124 ++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 83 +++++++----- 2 files changed, 173 insertions(+), 34 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 8c197d5b76..446cbd1bde 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -6,6 +6,7 @@ object B object C class AA(val x) class BB(val x) +class CC(val x) object Nil class Cons(val h, val t) @@ -595,3 +596,126 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ = 10 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 + + +// only the match in the middle is fused +:sjs +fun f1(x) = if x is + AA(x1) then + if x1 is + BB(x2) then + if x2 is + CC(x3) then x3 +fun f2(x) = if x is AA then "f2" + x.x +fun f3(x) = if x is CC then "f3" + x.x +fun aa(x) = AA(x) +let cc = CC("cc") +f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) +//│ JS (unsanitized): +//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81; +//│ f11 = function f1(x) { +//│ let param0, x1, param01, x2, param02, x3; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ if (x1 instanceof BB1.class) { +//│ param01 = x1.x; +//│ x2 = param01; +//│ if (x2 instanceof CC1.class) { +//│ param02 = x2.x; +//│ x3 = param02; +//│ return x3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f21 = function f2(x) { +//│ if (x instanceof AA1.class) { +//│ return "f2" + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f31 = function f3(x) { +//│ if (x instanceof CC1.class) { +//│ return "f3" + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ aa = function aa(x) { +//│ return AA1(x) +//│ }; +//│ tmp74 = CC1("cc"); +//│ cc = tmp74; +//│ tmp75 = BB1(cc); +//│ tmp76 = aa(tmp75); +//│ tmp77 = f11(tmp76); +//│ tmp78 = aa("0"); +//│ tmp79 = f21(tmp78); +//│ tmp80 = tmp77 + tmp79; +//│ tmp81 = f31(cc); +//│ tmp80 + tmp81 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, BB_x1; +//│ f11 = function f1(x) { +//│ let param0, x1; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ x1 = param0; +//│ return runtime.safeCall(x1()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f21 = function f2(x) { +//│ if (x instanceof AA1.class) { +//│ return "f2" + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f31 = function f3(x) { +//│ if (x instanceof CC1.class) { +//│ return "f3" + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ aa = function aa(x) { +//│ return AA1(x) +//│ }; +//│ tmp74 = CC1("cc"); +//│ cc = tmp74; +//│ BB_x1 = cc; +//│ tmp75 = () => { +//│ let param0, x2, param01, x3; +//│ param0 = BB_x1; +//│ x2 = param0; +//│ if (x2 instanceof CC1.class) { +//│ param01 = x2.x; +//│ x3 = param01; +//│ return x3 +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ }; +//│ tmp76 = aa(tmp75); +//│ tmp77 = f11(tmp76); +//│ tmp78 = aa("0"); +//│ tmp79 = f21(tmp78); +//│ tmp80 = tmp77 + tmp79; +//│ tmp81 = f31(cc); +//│ block$res18 = tmp80 + tmp81; +//│ undefined +//│ = "ccf20f3cc" +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "ccf20f3cc" +//│ cc = CC("cc") diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 09306c0d3a..790aaf3cf5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -6,6 +6,7 @@ object B object C class AA(val x) class BB(val x) +class CC(val x) object Nil class Cons(val h, val t) @@ -507,22 +508,27 @@ test(p) + f(p) - +// TODO: need to include computations +// from matches multiple levels up. +// and a `return` is missing: deciding whether +// a `return` is needed requires looking up all +// the computations of the specific branch up to +// all its continutaions. :fixme :sjs fun test(x) = let t = if x is AA(AA(AA(a))) then a - AA(_) then 5 - t + 5 + AA(x) then x + t + "5" fun f(a) = if a is - AA(AA) then 0 -let p = AA(AA(AA(10))) -test(p) + f(p) + AA(AA) then "0" +let p = AA(AA(AA("10"))) +test(p) + f(p) + test(AA("3")) //│ JS (unsanitized): -//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; //│ test2 = function test(x) { -//│ let t, param0, param01, param02, a, tmp35; +//│ let t, param0, x1, param01, param02, a, tmp38; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -530,25 +536,27 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp35 = a; +//│ tmp38 = a; //│ } else { -//│ tmp35 = 5; +//│ x1 = param0; +//│ tmp38 = x1; //│ } //│ } else { -//│ tmp35 = 5; +//│ x1 = param0; +//│ tmp38 = x1; //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp35; -//│ return t + 5 +//│ t = tmp38; +//│ return t + "5" //│ }; //│ f2 = function f(a) { //│ let param0; //│ if (a instanceof AA1.class) { //│ param0 = a.x; //│ if (param0 instanceof AA1.class) { -//│ return 0 +//│ return "0" //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -556,38 +564,42 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp30 = AA1(10); +//│ tmp30 = AA1("10"); //│ tmp31 = AA1(tmp30); //│ tmp32 = AA1(tmp31); //│ p1 = tmp32; //│ tmp33 = test2(p1); //│ tmp34 = f2(p1); -//│ tmp33 + tmp34 +//│ tmp35 = tmp33 + tmp34; +//│ tmp36 = AA1("3"); +//│ tmp37 = test2(tmp36); +//│ tmp35 + tmp37 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, AA_x2; +//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, AA_x2; //│ test2 = function test(x) { -//│ let t, param0, param01, tmp35; +//│ let t, param0, x1, param01, tmp38; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; -//│ runtime.safeCall(param01()) +//│ runtime.safeCall(param01(param0)) //│ } else { -//│ tmp35 = 5; +//│ x1 = param0; +//│ tmp38 = x1; //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp35; -//│ return t + 5 +//│ t = tmp38; +//│ return t + "5" //│ }; //│ f2 = function f(a) { //│ let param0; //│ if (a instanceof AA1.class) { //│ param0 = a.x; //│ if (param0 instanceof AA1.class) { -//│ return 0 +//│ return "0" //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -595,22 +607,25 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ AA_x2 = 10; -//│ tmp30 = () => { -//│ let param0, a, tmp35; -//│ param0 = AA_x2; -//│ a = param0; -//│ tmp35 = a; +//│ AA_x2 = "10"; +//│ tmp30 = (param0) => { +//│ let param01, a, tmp38; +//│ param01 = AA_x2; +//│ a = param01; +//│ tmp38 = a; //│ }; //│ tmp31 = AA1(tmp30); //│ tmp32 = AA1(tmp31); //│ p1 = tmp32; //│ tmp33 = test2(p1); //│ tmp34 = f2(p1); -//│ block$res14 = tmp33 + tmp34; +//│ tmp35 = tmp33 + tmp34; +//│ tmp36 = AA1("3"); +//│ tmp37 = test2(tmp36); +//│ block$res14 = tmp35 + tmp37; //│ undefined -//│ = NaN +//│ = "undefined5035" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ ═══[RUNTIME ERROR] The result from deforestated program ("NaN") is different from the one computed by the original prorgam ("15") -//│ = 15 -//│ p = AA(AA(AA(10))) +//│ ═══[RUNTIME ERROR] The result from deforestated program (""undefined5035"") is different from the one computed by the original prorgam (""105035"") +//│ = "105035" +//│ p = AA(AA(AA("10"))) From 921665df08dd735e9cb47139bb609abaae7ef6df Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:21:08 +0800 Subject: [PATCH 133/303] remove a useless field from CtorFinalDest.Match --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cde2e02e6c..90f4d9112c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -64,7 +64,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol])(val inMatch: Option[ResultId]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) case Sel(s: ResultId) trait FieldSelTrait: @@ -670,7 +670,7 @@ class Deforest(using TL, Raise, Elaborator.State): dtors.head._2, sels.map(_.expr), fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )(matchScrutToParentMatchScrut(dtors.head._1))) + )) else throw Error("more than one consumer") None From f901a80eee4f92898110a7bb126992c6529c5d8b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:03:55 +0800 Subject: [PATCH 134/303] add another test --- .../test/mlscript/deforest/nestedMatch.mls | 124 ++++++++++++++++++ .../deforest/selectionsInNestedMatch.mls | 54 ++++++++ 2 files changed, 178 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 446cbd1bde..20a6ce5d36 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -719,3 +719,127 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "ccf20f3cc" //│ cc = CC("cc") + + + +:sjs +fun c(x, y) = if x is + AA then + let t = if y is + A then 2 + t + x.x +let y = A +c(AA(2), y) +//│ JS (unsanitized): +//│ let c, y, tmp90; +//│ c = function c(x, y1) { +//│ let t, tmp91; +//│ if (x instanceof AA1.class) { +//│ if (y1 instanceof A1.class) { +//│ tmp91 = 2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp91; +//│ return t + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ y = A1; +//│ tmp90 = AA1(2); +//│ c(tmp90, y) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c, y, tmp90, AA_x5; +//│ c = function c(x, y1) { +//│ return runtime.safeCall(x(y1)) +//│ }; +//│ y = (AA_x6) => { +//│ let t, tmp91; +//│ tmp91 = 2; +//│ t = tmp91; +//│ return t + AA_x6 +//│ }; +//│ AA_x5 = 2; +//│ tmp90 = (y1) => { +//│ return runtime.safeCall(y1(AA_x5)) +//│ }; +//│ block$res20 = c(tmp90, y); +//│ undefined +//│ = 4 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 4 +//│ y = A + + +:sjs +fun c(x, y) = if x is + AA then + let t = if y is + A then 2 + x.x + t + x.x +fun c2(y) = if y is A then 3 +let y = A +c(AA(2), y) + c2(y) +//│ JS (unsanitized): +//│ let c1, c21, y1, tmp92, tmp93, tmp94; +//│ c1 = function c(x, y2) { +//│ let t, tmp95; +//│ if (x instanceof AA1.class) { +//│ if (y2 instanceof A1.class) { +//│ tmp95 = 2 + x.x; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp95; +//│ return t + x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c21 = function c2(y2) { +//│ if (y2 instanceof A1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ y1 = A1; +//│ tmp92 = AA1(2); +//│ tmp93 = c1(tmp92, y1); +//│ tmp94 = c21(y1); +//│ tmp93 + tmp94 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c1, c21, y1, tmp92, tmp93, tmp94, AA_x6; +//│ c1 = function c(x, y2) { +//│ return runtime.safeCall(x(y2)) +//│ }; +//│ c21 = function c2(y2) { +//│ if (y2 instanceof A1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ y1 = A1; +//│ AA_x6 = 2; +//│ tmp92 = (y2) => { +//│ let t, tmp95; +//│ if (y2 instanceof A1.class) { +//│ tmp95 = 2 + AA_x6; +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ t = tmp95; +//│ return t + AA_x6 +//│ }; +//│ tmp93 = c1(tmp92, y1); +//│ tmp94 = c21(y1); +//│ block$res22 = tmp93 + tmp94; +//│ undefined +//│ = 9 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 9 +//│ y = A diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 2a575aa61d..8875b7f08a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -407,3 +407,57 @@ f(p()) //│ = A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A + + + +:sjs +fun c(x, y) = + let t = if x is + AA then + if A is + A then x.x + t + y +c(AA(2), 10) +//│ JS (unsanitized): +//│ let c4, tmp24; +//│ c4 = function c(x, y) { +//│ let t, scrut, tmp25, tmp26; +//│ if (x instanceof AA1.class) { +//│ scrut = A1; +//│ if (scrut instanceof A1.class) { +//│ tmp25 = x.x; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp26 = tmp25; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp26; +//│ return t + y +//│ }; +//│ tmp24 = AA1(2); +//│ c4(tmp24, 10) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c4, tmp24, AA_x5; +//│ c4 = function c(x, y) { +//│ return runtime.safeCall(x(y)) +//│ }; +//│ AA_x5 = 2; +//│ tmp24 = (y) => { +//│ let scrut; +//│ scrut = (y1, AA_x6) => { +//│ let t, tmp25, tmp26; +//│ tmp25 = AA_x6; +//│ tmp26 = tmp25; +//│ t = tmp26; +//│ return t + y1 +//│ }; +//│ return runtime.safeCall(scrut(y, AA_x5)) +//│ }; +//│ block$res18 = c4(tmp24, 10); +//│ undefined +//│ = 12 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 12 From 120d7be1c9cc299859a0e76d8338c7d3fd96987d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 20 Mar 2025 23:53:29 +0800 Subject: [PATCH 135/303] wip: try to take care of matches from multiple levels up --- .../scala/hkmc2/codegen/Deforestation.scala | 107 ++++++++++++++---- .../test/mlscript/deforest/nestedMatch.mls | 6 +- .../src/test/mlscript/deforest/simple.mls | 12 +- .../src/test/mlscript/deforest/todos.mls | 28 +++-- 4 files changed, 107 insertions(+), 46 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 90f4d9112c..5b3b859855 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -708,6 +708,21 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case CtorFinalDest.Sel(s) => Nil }.toMap + def parentMatchesUptoAFusingOne(scrutId: ResultId) = + def go(scrutId: ResultId): List[ResultId] -> Opt[ResultId] = + d.matchScrutToParentMatchScrut(scrutId).fold(Nil -> N): r => + if d.filteredDtors.contains(r) + then Nil -> S(r) + else + val res = go(r) + (r :: res._1) -> res._2 + go(scrutId) + + def allParentMatches(scrutId: ResultId) = + def go(scrutId: ResultId): List[ResultId] = + d.matchScrutToParentMatchScrut(scrutId).fold(Nil)(r => r :: go(r)) + go(scrutId) + object freeVarsOfNonTransformedMatches: val store = mutable.Map.empty[ResultId, List[Symbol]] @@ -721,9 +736,15 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend { assert(m.scrut.uid === scrutExprId) val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m - val parentMatchRest = d.matchScrutToParentMatchScrut(m.scrut.uid).flatMap: p => - if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) - else None + // val parentMatchRest = d.matchScrutToParentMatchScrut(m.scrut.uid).flatMap: p => + // if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) + // else None + // val parentMatchRest = parentMatchesUptoAFusingOne(m.scrut.uid).foldRight[Opt[Block]](None): (p, acc) => + // acc.fold(Some(d.matchScrutToMatchBlock(p).rest))(accBlk => Some(Begin(d.matchScrutToMatchBlock(p).rest, accBlk))) + val parentMatchRest = allParentMatches(m.scrut.uid).foldRight[Block](End("")): (p, acc) => + Begin(d.matchScrutToMatchBlock(p).rest, acc) + + val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys val traverser = DeforestationFreeVarTraverser(using nonFreeVars + l, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId).values) @@ -732,7 +753,8 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. // Also take care of the `rest` of its parent match block if it's not getting fused. - val realArm = Begin(a, parentMatchRest.fold(rest)(p => Begin(rest, p))).flattened + // val realArm = Begin(a, parentMatchRest.fold(rest)(p => Begin(rest, p))).flattened + val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened traverser.applyBlock(realArm) traverser.result.toList.sortBy(_.uid) @@ -841,31 +863,64 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store.get(s) match case Some(f, b) => f.map(_.sym) -> b case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => - val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => - if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) - else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) + // val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => + // if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) + // else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) + + val parentRestInfo = parentMatchesUptoAFusingOne(s) match + case ps -> Some(theFusingOne) => + ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> + getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) + case ps -> None => + ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None + val res = N -> - (parentRest match - case None => applyBlock(restBeforeRewriting) - case Some(Some(s), b) => Begin( + (parentRestInfo match + case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) - ) + Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b)) + )) + + // val res = + // N -> + // (parentRest match + // case None => applyBlock(restBeforeRewriting) + // case Some(Some(s), b) => Begin( + // applyBlock(restBeforeRewriting), + // Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + // ) store += s -> res res case _ => // now need to build a new function and update the store - val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => - if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) - else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) - val restRewritten = parentRest match - case None => applyBlock(restBeforeRewriting) - case Some(Some(s), b) => Begin( + // val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => + // if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) + // else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) + + val parentRestInfo = parentMatchesUptoAFusingOne(s) match + case ps -> Some(theFusingOne) => + ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> + getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) + case ps -> None => + ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None + + // val restRewritten = parentRest match + // case None => applyBlock(restBeforeRewriting) + // case Some(Some(s), b) => Begin( + // applyBlock(restBeforeRewriting), + // Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + + val restRewritten = parentRestInfo match + case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b))) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) @@ -890,10 +945,12 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) => - val parentMatchRest = d.matchScrutToParentMatchScrut(scrut.uid).flatMap: p => - if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) - else None - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || parentMatchRest.fold(false)(_.hasExplicitRet) + // val parentMatchRest = d.matchScrutToParentMatchScrut(scrut.uid).flatMap: p => + // if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) + // else None + val oneOfParentMatchRestHasExplicitRet = allParentMatches(scrut.uid).foldRight(false) { (pid, acc) => acc || d.matchScrutToMatchBlock(pid).rest.hasExplicitRet } + // val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || parentMatchRest.fold(false)(_.hasExplicitRet) + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) case _ => super.applyBlock(b) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 20a6ce5d36..1d8701c4aa 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -89,7 +89,7 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ m = tmp11; //│ tmp12 = m + 9; //│ t = tmp12; -//│ return t + y +//│ return t + y; //│ }; //│ f = function f(x, y) { //│ let t, param0, b, param01, a1, tmp11; @@ -198,7 +198,7 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ let n, tmp29; //│ n = tmp28; //│ tmp29 = n + 2; -//│ return tmp29 + y +//│ return tmp29 + y; //│ }; //│ match_a_rest1 = function match_a_rest(y, tmp28) { //│ let tmp29; @@ -347,7 +347,7 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ match_x_rest1 = function match_x_rest(tmp44) { //│ let t; //│ t = tmp44; -//│ return t + 3 +//│ return t + 3; //│ }; //│ f3 = function f(x) { //│ return runtime.safeCall(x()) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 1e9464898d..90e57eeaa0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -681,7 +681,7 @@ c(A) + c(B) //│ let x, tmp7; //│ x = tmp6; //│ tmp7 = Predef.print(x); -//│ return x +//│ return x; //│ }; //│ c = function c(a) { //│ return runtime.safeCall(a()) @@ -845,7 +845,7 @@ map(x => x + 4, enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; //│ match_ls_rest = function match_ls_rest(f3, tmp11) { -//│ return runtime.safeCall(tmp11(f3)) +//│ return runtime.safeCall(tmp11(f3)); //│ }; //│ enumFromTo3 = function enumFromTo(a, b) { //│ let scrut, tmp11, tmp12, Cons_t, Cons_h; @@ -1264,7 +1264,7 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ match_x_rest = function match_x_rest(tmp29) { //│ let a; //│ a = tmp29; -//│ return a + 3 +//│ return a + 3; //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) @@ -1485,7 +1485,7 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ match_x_rest1 = function match_x_rest(tmp53) { //│ let a; //│ a = tmp53; -//│ return a + 3 +//│ return a + 3; //│ }; //│ f6 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) @@ -1565,7 +1565,7 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ match_x_rest2 = function match_x_rest(tmp70) { //│ let a; //│ a = tmp70; -//│ return a + 3 +//│ return a + 3; //│ }; //│ f7 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) @@ -1808,7 +1808,7 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ match_x_rest3 = function match_x_rest(y1, tmp94) { //│ let t; //│ t = tmp94; -//│ return t * y1 +//│ return t * y1; //│ }; //│ inner = function inner(x1, y1) { //│ return runtime.safeCall(x1(y1)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 790aaf3cf5..19df3192b0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -345,12 +345,12 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ match_x_rest = function match_x_rest(i, tmp12) { //│ let k; //│ k = tmp12; -//│ return k + i +//│ return k + i; //│ }; //│ match_y_rest = function match_y_rest(z, i, tmp12) { //│ let m; //│ m = tmp12; -//│ runtime.safeCall(z(i, m)); +//│ return runtime.safeCall(z(i, m)); //│ return match_x_rest(i, tmp_not_in_scope) //│ }; //│ match_z_rest = function match_z_rest(i, m, tmp12) { @@ -390,10 +390,10 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ block$res10 = tmp7 + tmp11; //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined +//│ = 18 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("18") //│ = 18 +//│ FAILURE: Unexpected lack of error to fix // FIXME: @@ -463,7 +463,7 @@ test(p) + f(p) //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; -//│ runtime.safeCall(param01()) +//│ return runtime.safeCall(param01()) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -488,10 +488,12 @@ test(p) + f(p) //│ }; //│ AA_x1 = 10; //│ tmp20 = () => { -//│ let param0, a, tmp25; +//│ let t, param0, a, tmp25; //│ param0 = AA_x1; //│ a = param0; //│ tmp25 = a; +//│ t = tmp25; +//│ return t + 5 //│ }; //│ tmp21 = AA1(tmp20); //│ tmp22 = AA1(tmp21); @@ -500,11 +502,11 @@ test(p) + f(p) //│ tmp24 = f1(p); //│ block$res12 = tmp23 + tmp24; //│ undefined -//│ ═══[RUNTIME ERROR] ReferenceError: tmp_not_in_scope is not defined +//│ = 15 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("15") //│ = 15 //│ p = AA(AA(AA(10))) +//│ FAILURE: Unexpected lack of error to fix @@ -583,7 +585,7 @@ test(p) + f(p) + test(AA("3")) //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; -//│ runtime.safeCall(param01(param0)) +//│ return runtime.safeCall(param01(param0)) //│ } else { //│ x1 = param0; //│ tmp38 = x1; @@ -609,10 +611,12 @@ test(p) + f(p) + test(AA("3")) //│ }; //│ AA_x2 = "10"; //│ tmp30 = (param0) => { -//│ let param01, a, tmp38; +//│ let t, param01, a, tmp38; //│ param01 = AA_x2; //│ a = param01; //│ tmp38 = a; +//│ t = tmp38; +//│ return t + "5" //│ }; //│ tmp31 = AA1(tmp30); //│ tmp32 = AA1(tmp31); @@ -624,8 +628,8 @@ test(p) + f(p) + test(AA("3")) //│ tmp37 = test2(tmp36); //│ block$res14 = tmp35 + tmp37; //│ undefined -//│ = "undefined5035" +//│ = "105035" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ ═══[RUNTIME ERROR] The result from deforestated program (""undefined5035"") is different from the one computed by the original prorgam (""105035"") //│ = "105035" //│ p = AA(AA(AA("10"))) +//│ FAILURE: Unexpected lack of error to fix From 5615ba8225e6136a192b0388c8e5089165a43ab8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:48:01 +0800 Subject: [PATCH 136/303] update test; need to clean undefined vars in dead code... --- .../test/mlscript/deforest/nestedMatch.mls | 118 +++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 125 ------------------ 2 files changed, 118 insertions(+), 125 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 1d8701c4aa..9dce5adb27 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -843,3 +843,121 @@ c(AA(2), y) + c2(y) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 9 //│ y = A + + +// need to include computations of `rest` from more than one levels of parent matches +:sjs +fun test(x) = + let t = if x is + AA(AA(AA(a))) then a + AA(x) then x + t + "5" +fun f(a) = if a is + AA(AA) then "0" +let p = AA(AA(AA("10"))) +test(p) + f(p) + test(AA("3")) +//│ JS (unsanitized): +//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105; +//│ test1 = function test(x) { +//│ let t, param0, x1, param01, param02, a1, tmp106; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a1 = param02; +//│ tmp106 = a1; +//│ } else { +//│ x1 = param0; +//│ tmp106 = x1; +//│ } +//│ } else { +//│ x1 = param0; +//│ tmp106 = x1; +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp106; +//│ return t + "5" +//│ }; +//│ f5 = function f(a1) { +//│ let param0; +//│ if (a1 instanceof AA1.class) { +//│ param0 = a1.x; +//│ if (param0 instanceof AA1.class) { +//│ return "0" +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp98 = AA1("10"); +//│ tmp99 = AA1(tmp98); +//│ tmp100 = AA1(tmp99); +//│ p = tmp100; +//│ tmp101 = test1(p); +//│ tmp102 = f5(p); +//│ tmp103 = tmp101 + tmp102; +//│ tmp104 = AA1("3"); +//│ tmp105 = test1(tmp104); +//│ tmp103 + tmp105 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105, AA_x7; +//│ test1 = function test(x) { +//│ let t, param0, x1, param01, tmp106; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ return runtime.safeCall(param01(param0)) +//│ } else { +//│ x1 = param0; +//│ tmp106 = x1; +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp106; +//│ return t + "5" +//│ }; +//│ f5 = function f(a1) { +//│ let param0; +//│ if (a1 instanceof AA1.class) { +//│ param0 = a1.x; +//│ if (param0 instanceof AA1.class) { +//│ return "0" +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ AA_x7 = "10"; +//│ tmp98 = (param0) => { +//│ let t, param01, a1, tmp106; +//│ param01 = AA_x7; +//│ a1 = param01; +//│ tmp106 = a1; +//│ t = tmp106; +//│ return t + "5" +//│ }; +//│ tmp99 = AA1(tmp98); +//│ tmp100 = AA1(tmp99); +//│ p = tmp100; +//│ tmp101 = test1(p); +//│ tmp102 = f5(p); +//│ tmp103 = tmp101 + tmp102; +//│ tmp104 = AA1("3"); +//│ tmp105 = test1(tmp104); +//│ block$res24 = tmp103 + tmp105; +//│ undefined +//│ = "105035" +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "105035" +//│ p = AA(AA(AA("10"))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 19df3192b0..e2c1fe2ecb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -508,128 +508,3 @@ test(p) + f(p) //│ p = AA(AA(AA(10))) //│ FAILURE: Unexpected lack of error to fix - - -// TODO: need to include computations -// from matches multiple levels up. -// and a `return` is missing: deciding whether -// a `return` is needed requires looking up all -// the computations of the specific branch up to -// all its continutaions. -:fixme -:sjs -fun test(x) = - let t = if x is - AA(AA(AA(a))) then a - AA(x) then x - t + "5" -fun f(a) = if a is - AA(AA) then "0" -let p = AA(AA(AA("10"))) -test(p) + f(p) + test(AA("3")) -//│ JS (unsanitized): -//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; -//│ test2 = function test(x) { -//│ let t, param0, x1, param01, param02, a, tmp38; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ if (param01 instanceof AA1.class) { -//│ param02 = param01.x; -//│ a = param02; -//│ tmp38 = a; -//│ } else { -//│ x1 = param0; -//│ tmp38 = x1; -//│ } -//│ } else { -//│ x1 = param0; -//│ tmp38 = x1; -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp38; -//│ return t + "5" -//│ }; -//│ f2 = function f(a) { -//│ let param0; -//│ if (a instanceof AA1.class) { -//│ param0 = a.x; -//│ if (param0 instanceof AA1.class) { -//│ return "0" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp30 = AA1("10"); -//│ tmp31 = AA1(tmp30); -//│ tmp32 = AA1(tmp31); -//│ p1 = tmp32; -//│ tmp33 = test2(p1); -//│ tmp34 = f2(p1); -//│ tmp35 = tmp33 + tmp34; -//│ tmp36 = AA1("3"); -//│ tmp37 = test2(tmp36); -//│ tmp35 + tmp37 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test2, f2, p1, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, AA_x2; -//│ test2 = function test(x) { -//│ let t, param0, x1, param01, tmp38; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ return runtime.safeCall(param01(param0)) -//│ } else { -//│ x1 = param0; -//│ tmp38 = x1; -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp38; -//│ return t + "5" -//│ }; -//│ f2 = function f(a) { -//│ let param0; -//│ if (a instanceof AA1.class) { -//│ param0 = a.x; -//│ if (param0 instanceof AA1.class) { -//│ return "0" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ AA_x2 = "10"; -//│ tmp30 = (param0) => { -//│ let t, param01, a, tmp38; -//│ param01 = AA_x2; -//│ a = param01; -//│ tmp38 = a; -//│ t = tmp38; -//│ return t + "5" -//│ }; -//│ tmp31 = AA1(tmp30); -//│ tmp32 = AA1(tmp31); -//│ p1 = tmp32; -//│ tmp33 = test2(p1); -//│ tmp34 = f2(p1); -//│ tmp35 = tmp33 + tmp34; -//│ tmp36 = AA1("3"); -//│ tmp37 = test2(tmp36); -//│ block$res14 = tmp35 + tmp37; -//│ undefined -//│ = "105035" -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "105035" -//│ p = AA(AA(AA("10"))) -//│ FAILURE: Unexpected lack of error to fix From 6a664fc7fa8cd56bc34b41236f1b33ca303f8e9d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 24 Mar 2025 12:07:56 +0800 Subject: [PATCH 137/303] better name; wip: tidy up computation of free vars --- .../main/scala/hkmc2/codegen/Deforestation.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5b3b859855..ada7b5bbc2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -168,7 +168,7 @@ extension (b: Block) case _ => super.applyValue(v) ReplaceLocalSymTransformer.applyBlock(b) - def sortedFvs(using alwaysDefined: Set[Symbol]) = + def sortedFvsForTransformedBlocks(using alwaysDefined: Set[Symbol]) = val traverser = FreeVarTraverser(alwaysDefined) traverser.applyBlock(b) traverser.result.toList.sortBy(_.uid) @@ -792,7 +792,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend Return( Call( Value.Ref(f), - rewrittenRest.sortedFvs.map(a => Arg(false, Value.Ref(a))))(true, false), + rewrittenRest.sortedFvsForTransformedBlocks.map(a => Arg(false, Value.Ref(a))))(true, false), false ) ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: @@ -880,7 +880,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b)) )) @@ -890,7 +890,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend // case None => applyBlock(restBeforeRewriting) // case Some(Some(s), b) => Begin( // applyBlock(restBeforeRewriting), - // Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) // ) @@ -912,19 +912,19 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend // case None => applyBlock(restBeforeRewriting) // case Some(Some(s), b) => Begin( // applyBlock(restBeforeRewriting), - // Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) val restRewritten = parentRestInfo match case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvs(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b))) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvs(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap val newFunDef = FunDefn( N, From 7d9ce980fda266a02256e6c8120439798023d2f7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:49:46 +0800 Subject: [PATCH 138/303] remove wrong fixmes; add more comments --- .../scala/hkmc2/codegen/Deforestation.scala | 8 +- .../test/mlscript/deforest/nestedMatch.mls | 95 ++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 121 ++++++++++++++++++ 3 files changed, 222 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ada7b5bbc2..0f2f105d0b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -880,7 +880,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b)) )) @@ -903,9 +903,13 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => + // return the original rests from unfused parent matches, + // and the function symbol for the `rest` of the fusing parent match (if any) + // and the rewritten `rest` block of that fusing parent match ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) case ps -> None => + // return the original rests from unfused parent matches, and none (meaning that there is no fusing parent match) ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None // val restRewritten = parentRest match @@ -919,7 +923,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // FIXME: b is a pre-rewritten block, should use another f.v. traverser + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b))) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 9dce5adb27..67dd77cb9a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -961,3 +961,98 @@ test(p) + f(p) + test(AA("3")) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "105035" //│ p = AA(AA(AA("10"))) + + + + +:sjs +fun f(x, y) = + let tmp = BB(y + 1) + if tmp is + BB then + let m = if x is + AA then + if AA(2) is + AA(yf) then yf + if AA(3) is + AA then m + tmp.x +let aa = AA(3) +f(aa, 3) + f(aa, 4) +//│ JS (unsanitized): +//│ let f6, aa1, tmp114, tmp115, tmp116; +//│ f6 = function f(x, y2) { +//│ let tmp117, m, scrut, param0, yf, scrut1, tmp118, tmp119, tmp120, tmp121; +//│ tmp118 = y2 + 1; +//│ tmp119 = BB1(tmp118); +//│ tmp117 = tmp119; +//│ if (tmp117 instanceof BB1.class) { +//│ if (x instanceof AA1.class) { +//│ scrut = AA1(2); +//│ if (scrut instanceof AA1.class) { +//│ param0 = scrut.x; +//│ yf = param0; +//│ tmp120 = yf; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp121 = tmp120; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp121; +//│ scrut1 = AA1(3); +//│ if (scrut1 instanceof AA1.class) { +//│ return m + tmp117.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp114 = AA1(3); +//│ aa1 = tmp114; +//│ tmp115 = f6(aa1, 3); +//│ tmp116 = f6(aa1, 4); +//│ tmp115 + tmp116 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f6, aa1, tmp114, tmp115, tmp116, AA_x_unused; +//│ f6 = function f(x, y2) { +//│ let tmp117, tmp118, tmp119, BB_x2; +//│ tmp118 = y2 + 1; +//│ BB_x2 = tmp118; +//│ tmp119 = (x1) => { +//│ return runtime.safeCall(x1(BB_x2)) +//│ }; +//│ tmp117 = tmp119; +//│ return runtime.safeCall(tmp117(x)) +//│ }; +//│ AA_x_unused = 3; +//│ tmp114 = (BB_x2) => { +//│ let scrut, AA_x8; +//│ AA_x8 = 2; +//│ scrut = (BB_x3) => { +//│ let m, param0, yf, scrut1, tmp117, tmp118, AA_x_unused1; +//│ param0 = AA_x8; +//│ yf = param0; +//│ tmp117 = yf; +//│ tmp118 = tmp117; +//│ m = tmp118; +//│ AA_x_unused1 = 3; +//│ scrut1 = (m1, BB_x4) => { +//│ return m1 + BB_x4 +//│ }; +//│ return runtime.safeCall(scrut1(m, BB_x3)) +//│ }; +//│ return runtime.safeCall(scrut(BB_x2)) +//│ }; +//│ aa1 = tmp114; +//│ tmp115 = f6(aa1, 3); +//│ tmp116 = f6(aa1, 4); +//│ block$res26 = tmp115 + tmp116; +//│ undefined +//│ = 13 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 13 +//│ aa = AA(3) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index e2c1fe2ecb..39e0bacc2a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -508,3 +508,124 @@ test(p) + f(p) //│ p = AA(AA(AA(10))) //│ FAILURE: Unexpected lack of error to fix + + + +:sjs +fun f(x, y, z, k) = + let tmp = if z then BB(y + 1) else BB(y - 1) + tmp.x + + if tmp is + BB then + let m = if x is + AA then + if AA(2) is + AA(yf) then yf + if AA(3) is + AA then m + k +f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) +//│ JS (unsanitized): +//│ let f2, tmp30, tmp31, tmp32, tmp33; +//│ f2 = function f(x, y, z, k) { +//│ let tmp34, m, scrut, param0, yf, scrut1, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41; +//│ if (z === true) { +//│ tmp35 = y + 1; +//│ tmp36 = BB1(tmp35); +//│ } else { +//│ tmp37 = y - 1; +//│ tmp36 = BB1(tmp37); +//│ } +//│ tmp34 = tmp36; +//│ if (tmp34 instanceof BB1.class) { +//│ if (x instanceof AA1.class) { +//│ scrut = AA1(2); +//│ if (scrut instanceof AA1.class) { +//│ param0 = scrut.x; +//│ yf = param0; +//│ tmp38 = yf; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp39 = tmp38; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp39; +//│ scrut1 = AA1(3); +//│ if (scrut1 instanceof AA1.class) { +//│ tmp40 = m + k; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp41 = tmp40; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return tmp34.x + tmp41 +//│ }; +//│ tmp30 = AA1(3); +//│ tmp31 = f2(tmp30, 3, true, 10); +//│ tmp32 = AA1(5); +//│ tmp33 = f2(tmp32, 4, false, 20); +//│ tmp31 + tmp33 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f2, tmp30, tmp31, tmp32, tmp33, AA_x_unused, match_x_rest1, match_x_branch_AA1, AA_x_unused1; +//│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { +//│ let scrut, AA_x2; +//│ AA_x2 = 2; +//│ scrut = (k1, tmp35) => { +//│ let param0, yf, tmp36, tmp37; +//│ param0 = AA_x2; +//│ yf = param0; +//│ tmp36 = yf; +//│ tmp37 = tmp36; +//│ return match_x_rest1(k1, tmp35, tmp37) +//│ }; +//│ return runtime.safeCall(scrut(k, tmp34)) +//│ }; +//│ match_x_rest1 = function match_x_rest(k, tmp34, tmp35) { +//│ let m, scrut, AA_x_unused2; +//│ m = tmp35; +//│ AA_x_unused2 = 3; +//│ scrut = (k1, tmp36, m1) => { +//│ let tmp37, tmp38; +//│ tmp37 = m1 + k1; +//│ tmp38 = tmp37; +//│ return tmp36.x + tmp38 +//│ }; +//│ return runtime.safeCall(scrut(k, tmp34, m)); +//│ return tmp34.x + tmp_not_in_scope; +//│ }; +//│ f2 = function f(x, y, z, k) { +//│ let tmp34, tmp35, tmp36, tmp37; +//│ if (z === true) { +//│ tmp35 = y + 1; +//│ tmp36 = BB1(tmp35); +//│ } else { +//│ tmp37 = y - 1; +//│ tmp36 = BB1(tmp37); +//│ } +//│ tmp34 = tmp36; +//│ if (tmp34 instanceof BB1.class) { +//│ return runtime.safeCall(x(k, tmp34)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return tmp34.x + tmp_not_in_scope +//│ }; +//│ AA_x_unused = 3; +//│ tmp30 = (k, tmp34) => { +//│ return match_x_branch_AA1(k, tmp34) +//│ }; +//│ tmp31 = f2(tmp30, 3, true, 10); +//│ AA_x_unused1 = 5; +//│ tmp32 = (k, tmp34) => { +//│ return match_x_branch_AA1(k, tmp34) +//│ }; +//│ tmp33 = f2(tmp32, 4, false, 20); +//│ block$res14 = tmp31 + tmp33; +//│ undefined +//│ = 41 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 41 From 4d337fdeabc237cdaaf39e55dd7b3876e3ebb4d4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:10:26 +0800 Subject: [PATCH 139/303] don't rewrite already rewritten blocks --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0f2f105d0b..f1a6f5c6d1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -881,8 +881,8 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b)) - )) + case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) + ) // val res = // N -> @@ -924,7 +924,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - case bd -> (None, b) => applyBlock(Begin(restBeforeRewriting, Begin(bd, b))) + case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) From b258545a78691854337bf8a10296aa657f1735b8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 24 Mar 2025 23:02:35 +0800 Subject: [PATCH 140/303] revisit free var computation and update comments... --- .../scala/hkmc2/codegen/Deforestation.scala | 69 ++++++++----------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f1a6f5c6d1..7851415f23 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -81,6 +81,12 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid + +// Compute free vars for a block, without considering deforestation, used on transformed blocks +// This means that for matches we don't need to consider the extra +// free vars that may be introduced by deforestation: +// 1. the free vars from the `rest` of the their parent matches +// 2. the free vars caused by the substitution of selections of scrutinees of their parent matches class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: val ctx = mutable.Set.from(alwaysDefined) val result = mutable.Set.empty[Symbol] @@ -91,9 +97,7 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: (arms.map(_._2) ++ dflt).foreach: a => // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - // this also makes sure that the free vars in the `rest` of outter - // matches can be counted as free vars for the inner match - val realArm = Begin(a, rest).flattened + val realArm = Begin(a, rest) applyBlock(realArm) case Assign(lhs, rhs, rest) => @@ -132,6 +136,14 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: applyBlock(l.body) ctx --= paramSymbols +// Compute free vars for a Match block, considering deforestations. Used on non-transformed blocks +// Make use of `freeVarsOfNonTransformedMatches`, which computes the free vars _after transformation_ +// from non-transformed matches (either fusing or un-fusing matches). +// Sources of additional free vars for fusing matches: +// - the free vars from the `rest` of the their parent matches +// - the free vars caused by the substitution of selections of scrutinees of their parent matches +// Otherwise, additional free vars come from fusing matches that are contained in the block, +// and this is handled by freeVarsOfNonTransformedMatches class DeforestationFreeVarTraverser(using alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol] = Map.empty, @@ -143,7 +155,8 @@ class DeforestationFreeVarTraverser(using case m@Match(scrut, arms, dflt, rest) => result ++= dt.freeVarsOfNonTransformedMatches(scrut.uid, m) - // scruts of sub-matches are also free vars + // sub-matches' scruts (which are not included in freeVarsOfNonTransformedMatches) + // are also free vars val Value.Ref(l) = scrut if !ctx(l) then result += l @@ -736,11 +749,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend { assert(m.scrut.uid === scrutExprId) val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m - // val parentMatchRest = d.matchScrutToParentMatchScrut(m.scrut.uid).flatMap: p => - // if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) - // else None - // val parentMatchRest = parentMatchesUptoAFusingOne(m.scrut.uid).foldRight[Opt[Block]](None): (p, acc) => - // acc.fold(Some(d.matchScrutToMatchBlock(p).rest))(accBlk => Some(Begin(d.matchScrutToMatchBlock(p).rest, accBlk))) + val parentMatchRest = allParentMatches(m.scrut.uid).foldRight[Block](End("")): (p, acc) => Begin(d.matchScrutToMatchBlock(p).rest, acc) @@ -752,8 +761,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend (arms.map(_._2) ++ dflt).foreach: a => // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - // Also take care of the `rest` of its parent match block if it's not getting fused. - // val realArm = Begin(a, parentMatchRest.fold(rest)(p => Begin(rest, p))).flattened + // Also take care of the `rest`s of its parent match blocks. val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened traverser.applyBlock(realArm) @@ -863,10 +871,6 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store.get(s) match case Some(f, b) => f.map(_.sym) -> b case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => - // val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => - // if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) - // else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) - val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> @@ -876,31 +880,22 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val res = N -> + // bd: original `rest`s of non-fusing parent matches (parentRestInfo match + // None: there is no fusing parent match case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, + // and the transformed `rest` is b case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) ) - // val res = - // N -> - // (parentRest match - // case None => applyBlock(restBeforeRewriting) - // case Some(Some(s), b) => Begin( - // applyBlock(restBeforeRewriting), - // Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) - // ) - store += s -> res res - case _ => // now need to build a new function and update the store - // val parentRest = d.matchScrutToParentMatchScrut(s).flatMap: p => - // if !d.filteredDtors.contains(p) then Some(None, d.matchScrutToMatchBlock(p).rest) - // else Some(getOrElseUpdate(p, d.matchScrutToMatchBlock(p).rest)) - + case _ => // now need to build a new function and update the store val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => // return the original rests from unfused parent matches, @@ -911,13 +906,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case ps -> None => // return the original rests from unfused parent matches, and none (meaning that there is no fusing parent match) ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None - - // val restRewritten = parentRest match - // case None => applyBlock(restBeforeRewriting) - // case Some(Some(s), b) => Begin( - // applyBlock(restBeforeRewriting), - // Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - // case Some(None, b) => Begin(applyBlock(restBeforeRewriting), b) + val restRewritten = parentRestInfo match case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) @@ -949,11 +938,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend override def applyBlock(b: Block): Block = b match case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) => - // val parentMatchRest = d.matchScrutToParentMatchScrut(scrut.uid).flatMap: p => - // if !d.filteredDtors.contains(p) then Some(d.matchScrutToMatchBlock(p).rest) - // else None + // since all fusing matches will be considered to be in the tail position, + // if any of the parent `rest`s has explicit return, the rewritten match will have explicit return val oneOfParentMatchRestHasExplicitRet = allParentMatches(scrut.uid).foldRight(false) { (pid, acc) => acc || d.matchScrutToMatchBlock(pid).rest.hasExplicitRet } - // val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || parentMatchRest.fold(false)(_.hasExplicitRet) val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) From 2373e06ceb0933cccfa809b5ca407146dd60c8f3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:39:18 +0800 Subject: [PATCH 141/303] improve `matchRest` impl --- .../scala/hkmc2/codegen/Deforestation.scala | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7851415f23..6e6ef4e8cf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -870,32 +870,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = store.get(s) match case Some(f, b) => f.map(_.sym) -> b - case None if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) => - val parentRestInfo = parentMatchesUptoAFusingOne(s) match - case ps -> Some(theFusingOne) => - ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> - getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) - case ps -> None => - ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None - - val res = - N -> - // bd: original `rest`s of non-fusing parent matches - (parentRestInfo match - // None: there is no fusing parent match - case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) - // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, - // and the transformed `rest` is b - case bd -> (Some(s), b) => Begin( - applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function - case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) - ) - - store += s -> res - res - case _ => // now need to build a new function and update the store + case _ => val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => // return the original rests from unfused parent matches, @@ -906,27 +881,36 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case ps -> None => // return the original rests from unfused parent matches, and none (meaning that there is no fusing parent match) ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None - + // bd: original `rest`s of non-fusing parent matches val restRewritten = parentRestInfo match + // None: there is no fusing parent match case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, + // and the transformed `rest` is b case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) - val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme - val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - - val newFunDef = FunDefn( - N, - sym, - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, - restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) - ) - store += s -> (Some(newFunDef) -> restRewritten) - Some(sym) -> restRewritten + // no need to build a new function for empty rest, or if the rest is only going to be used once + if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) then + val res = N -> restRewritten + store += s -> res + res + else // build a new function and update the store + val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme + val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) + val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val newFunDef = FunDefn( + N, + sym, + ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N)).toList, N) :: Nil, + restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) + ) + store += s -> (Some(newFunDef) -> restRewritten) + Some(sym) -> restRewritten def getAllFunDefs: Block => Block = store.values.foldRight(identity: Block => Block): From 58cc209d92827a13eaf49e7a3ad4d9948254b92f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:04:26 +0800 Subject: [PATCH 142/303] minor fix and a new test --- .../src/test/mlscript/deforest/todos.mls | 106 ++++++++++++++++++ .../test/scala/hkmc2/JSBackendDiffMaker.scala | 2 +- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 39e0bacc2a..5a20124701 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -629,3 +629,109 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ = 41 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 41 + + + +:sjs +fun test(n, p, q) = + n + + if AA(0) is + AA then + let k = if p is + AA(x) then x + if q is + AA(y) then k + y +test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) +//│ JS (unsanitized): +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; +//│ test2 = function test(n, p1, q) { +//│ let scrut, k, param0, x, param01, y, tmp44, tmp45, tmp46; +//│ scrut = AA1(0); +//│ if (scrut instanceof AA1.class) { +//│ if (p1 instanceof AA1.class) { +//│ param0 = p1.x; +//│ x = param0; +//│ tmp44 = x; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ k = tmp44; +//│ if (q instanceof AA1.class) { +//│ param01 = q.x; +//│ y = param01; +//│ tmp45 = k + y; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp46 = tmp45; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return n + tmp46 +//│ }; +//│ tmp38 = AA1(2); +//│ tmp39 = AA1(3); +//│ tmp40 = test2(3, tmp38, tmp39); +//│ tmp41 = AA1(2); +//│ tmp42 = AA1(3); +//│ tmp43 = test2(3, tmp41, tmp42); +//│ tmp40 + tmp43 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, AA_x_tmp4, match_p_rest, match_p_branch_AA, AA_x_tmp5, match_q_rest, match_q_branch_AA, AA_x_tmp6, AA_x_tmp7; +//│ match_p_branch_AA = function match_p_branch_AA(n, q, AA_x2) { +//│ let param0, x, tmp44; +//│ param0 = AA_x2; +//│ x = param0; +//│ tmp44 = x; +//│ return match_p_rest(n, q, tmp44) +//│ }; +//│ match_q_branch_AA = function match_q_branch_AA(n, k, AA_x2) { +//│ let param0, y, tmp44; +//│ param0 = AA_x2; +//│ y = param0; +//│ tmp44 = k + y; +//│ return match_q_rest(n, tmp44) +//│ }; +//│ match_p_rest = function match_p_rest(n, q, tmp44) { +//│ let k; +//│ k = tmp44; +//│ return runtime.safeCall(q(n, k)); +//│ return n + tmp_not_in_scope; +//│ }; +//│ match_q_rest = function match_q_rest(n, tmp44) { +//│ let tmp45; +//│ tmp45 = tmp44; +//│ return n + tmp45; +//│ }; +//│ test2 = function test(n, p1, q) { +//│ let scrut, AA_x_unused2; +//│ AA_x_unused2 = 0; +//│ scrut = (n1, p2, q1) => { +//│ return runtime.safeCall(p2(n1, q1)) +//│ }; +//│ return runtime.safeCall(scrut(n, p1, q)) +//│ }; +//│ AA_x_tmp4 = 2; +//│ tmp38 = (n, q) => { +//│ return match_p_branch_AA(n, q, AA_x_tmp4) +//│ }; +//│ AA_x_tmp5 = 3; +//│ tmp39 = (n, k) => { +//│ return match_q_branch_AA(n, k, AA_x_tmp5) +//│ }; +//│ tmp40 = test2(3, tmp38, tmp39); +//│ AA_x_tmp6 = 2; +//│ tmp41 = (n, q) => { +//│ return match_p_branch_AA(n, q, AA_x_tmp6) +//│ }; +//│ AA_x_tmp7 = 3; +//│ tmp42 = (n, k) => { +//│ return match_q_branch_AA(n, k, AA_x_tmp7) +//│ }; +//│ tmp43 = test2(3, tmp41, tmp42); +//│ block$res16 = tmp40 + tmp43; +//│ undefined +//│ = 16 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 16 diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 25e31d98f4..1cae3bbc4f 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -276,7 +276,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if silent.isUnset then import Elaborator.Ctx.* def definedValues = curCtx.env.iterator.flatMap: - case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => + case (nme, e @ (_: RefElem | SelElem(base = RefElem(_: InnerSymbol)))) => e.symbol match case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) case S(ts: BlockMemberSymbol) From 35679feb2d515a64e414e558ed86a7e112f19c44 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 25 Mar 2025 23:15:09 +0800 Subject: [PATCH 143/303] include runtimeSymbol as always in scope --- .../scala/hkmc2/codegen/Deforestation.scala | 6 +-- .../src/test/mlscript/deforest/simple.mls | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6e6ef4e8cf..b21cf822ab 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -237,18 +237,18 @@ class Deforest(using TL, Raise, Elaborator.State): // these are never considered as free vars (because of their symbol type) // consider TopLevelSym, BlockMemberSymbols and BuiltInSyms as globally defined... object globallyDefinedVars: - val store = mutable.Set.empty[Symbol] + val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) def apply(s: Symbol) = store.contains(s) def init(b: Block) = - object FreshVarForAllVars extends BlockTraverser: + (new BlockTraverser: override def applySymbol(sym: Symbol): Unit = sym match case _: TopLevelSymbol => store += sym case _: BlockMemberSymbol => store += sym case _: BuiltinSymbol => store += sym case _ => () - FreshVarForAllVars.applyBlock(b) + ).applyBlock(b) var constraints: Ls[ProdStrat -> ConsStrat] = Nil diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 90e57eeaa0..d7862edd56 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1916,3 +1916,47 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 + + +:sjs +fun test() = + let k + if A is + A then + k = 3 + k + 2 +test() +//│ JS (unsanitized): +//│ let test8; +//│ test8 = function test() { +//│ let k, scrut, tmp105; +//│ k = undefined; +//│ scrut = A1; +//│ if (scrut instanceof A1.class) { +//│ k = 3; +//│ tmp105 = runtime.Unit; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return k + 2 +//│ }; +//│ test8() +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test8; +//│ test8 = function test() { +//│ let k, scrut; +//│ k = undefined; +//│ scrut = () => { +//│ let tmp105; +//│ k = 3; +//│ tmp105 = runtime.Unit; +//│ return k + 2 +//│ }; +//│ return runtime.safeCall(scrut()) +//│ }; +//│ block$res59 = test8(); +//│ undefined +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 From 4614a0d70b4102a929e3ae86bb04a352f36cba58 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Mar 2025 15:58:23 +0800 Subject: [PATCH 144/303] better handling for dflt arms --- .../scala/hkmc2/codegen/Deforestation.scala | 17 +++++---- .../src/test/mlscript/deforest/simple.mls | 38 +++++++++++++++++++ .../test/scala/hkmc2/JSBackendDiffMaker.scala | 9 ----- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b21cf822ab..6ee5942bdf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -770,7 +770,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend ) object matchArms: - val store = LinkedHashMap.empty[ResultId, Map[ClsOrModSymbol, FunDefn]].withDefaultValue(Map.empty) + val store = LinkedHashMap.empty[ResultId, Map[ClsOrModSymbol | None.type, FunDefn]].withDefaultValue(Map.empty) // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms def getOrElseUpdate( @@ -783,12 +783,10 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend ) = assert(scrut === m.scrut.uid) val freeVarsAndTheirNewSyms = freeVarsOfNonTransformedMatches(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) - store.get(scrut).flatMap(_.get(cls)) match + val (body, isDflt) = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2 -> false).orElse(m.dflt.map(_ -> true)).get + store.get(scrut).flatMap(_.get(if isDflt then None else cls)) match case None => // not registered before, or this branch of this match will only appear once - val body = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2).orElse(m.dflt).get val rest = m.rest - - val makeBody = matchRest.getOrElseUpdate(scrut, rest) match case N -> rewrittenRest => (bodyBlk: Block) => Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: @@ -807,7 +805,10 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case Return(res, implct) => Return(res, false) case t => t - if d.resolveClashes._2(DtorExpr.Match(scrut)).count(d.getClsSymOfUid(_) === cls) > 1 then + if d.resolveClashes._2(DtorExpr.Match(scrut)).count{c => + if !isDflt then d.getClsSymOfUid(c) === cls + else m.arms.find{ case (Case.Cls(c1, _), _) => c1 === d.getClsSymOfUid(c) }.isEmpty + } > 1 then // make a function, and register, and return a lambda calling that function with correct arguments // arguments for lambda: free vars // arguments for that function: free vars and pattern vars @@ -816,7 +817,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) val funBody = makeBody(bodyReplaceSel) - val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${cls.nme}", Nil) + val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${if isDflt then "dflt" else cls.nme}", Nil) val newDef = FunDefn( N, funSym, @@ -831,7 +832,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend ) :: Nil, funBody ) - store += (scrut -> (store(scrut) + (cls -> newDef))) + store += (scrut -> (store(scrut) + ((if isDflt then None else cls) -> newDef))) Value.Lam( ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N)), N), Return( diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index d7862edd56..563dedb4a9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1960,3 +1960,41 @@ test() //│ = 5 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 + +:sjs +fun test(a) = if a is + AA then 1 + else 2 +test(B) + test(C) +//│ JS (unsanitized): +//│ let test9, tmp105, tmp106; +//│ test9 = function test(a) { +//│ if (a instanceof AA1.class) { +//│ return 1 +//│ } else { +//│ return 2 +//│ } +//│ }; +//│ tmp105 = test9(B1); +//│ tmp106 = test9(C1); +//│ tmp105 + tmp106 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test9, tmp105, tmp106, match_a_branch_dflt; +//│ match_a_branch_dflt = function match_a_branch_dflt() { +//│ return 2 +//│ }; +//│ test9 = function test(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ tmp105 = test9(() => { +//│ return match_a_branch_dflt() +//│ }); +//│ tmp106 = test9(() => { +//│ return match_a_branch_dflt() +//│ }); +//│ block$res61 = tmp105 + tmp106; +//│ undefined +//│ = 4 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 4 diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 1cae3bbc4f..fd0f429243 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -169,15 +169,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if silent.isUnset then import Elaborator.Ctx.* - // def definedValues = curCtx.env.iterator.flatMap: - // case (nme, e @ (_: RefElem | SelElem(RefElem(_: InnerSymbol), _, _))) => - // e.symbol match - // case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) - // case S(ts: BlockMemberSymbol) - // if ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) - // case S(vs: VarSymbol) => S((nme, vs, N)) - // case _ => N - // case _ => N val valuesToPrint = List(("", resSym, expect.get)) valuesToPrint.foreach: (nme, sym, expect) => val le = From d029daf194c60753db28d3348b751ee2fd54d268 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 27 Mar 2025 20:04:33 +0800 Subject: [PATCH 145/303] better names for fields of classes --- .../scala/hkmc2/codegen/Deforestation.scala | 6 +- .../test/mlscript/deforest/nestedMatch.mls | 196 +++++++-------- .../deforest/selectionsInNestedMatch.mls | 100 ++++---- .../src/test/mlscript/deforest/simple.mls | 226 +++++++++--------- .../src/test/mlscript/deforest/todos.mls | 98 ++++---- 5 files changed, 313 insertions(+), 313 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6ee5942bdf..970673ecb4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -672,8 +672,8 @@ class Deforest(using TL, Raise, Elaborator.State): fieldNameToSymToBeReplaced.updateWith(fs.field): case Some(v) => Some(v) case None => Some(if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"${c.name}_${fs.field.name}")) - else TempSymbol(N, s"${c.name}_${fs.field.name}")) + then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) + else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) val sym = fieldNameToSymToBeReplaced(fs.field) selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) @@ -949,7 +949,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend // use pre-determined symbols, create temp symbols for un-used fields val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => - f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"${c.name}_${f.id.name}_unused")) + f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"_deforest_${c.name}_${f.id.name}_unused")) // if all vars are temp vars, no need to create more temp vars // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 67dd77cb9a..edab118a15 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -76,10 +76,10 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ tmp7 + tmp10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, AA_x_tmp, match_a_rest, match_a_branch_AA, AA_x_tmp1; -//│ match_a_branch_AA = function match_a_branch_AA(y, AA_x) { +//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, _deforest_AA_x_tmp, match_a_rest, match_a_branch_AA, _deforest_AA_x_tmp1; +//│ match_a_branch_AA = function match_a_branch_AA(y, _deforest_AA_x) { //│ let param0, x, tmp11; -//│ param0 = AA_x; +//│ param0 = _deforest_AA_x; //│ x = param0; //│ tmp11 = x; //│ return match_a_rest(y, tmp11) @@ -114,9 +114,9 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ AA_x_tmp = 3; +//│ _deforest_AA_x_tmp = 3; //│ tmp = (y) => { -//│ return match_a_branch_AA(y, AA_x_tmp) +//│ return match_a_branch_AA(y, _deforest_AA_x_tmp) //│ }; //│ tmp1 = AA1(tmp); //│ a = tmp1; @@ -126,9 +126,9 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ tmp5 = BB1(3); //│ tmp6 = f(tmp5, 2); //│ tmp7 = tmp4 + tmp6; -//│ AA_x_tmp1 = 4; +//│ _deforest_AA_x_tmp1 = 4; //│ tmp8 = (y) => { -//│ return match_a_branch_AA(y, AA_x_tmp1) +//│ return match_a_branch_AA(y, _deforest_AA_x_tmp1) //│ }; //│ tmp9 = AA1(tmp8); //│ tmp10 = f(tmp9, 5); @@ -180,17 +180,17 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ tmp24 + tmp27 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, AA_x_tmp2, match_x_rest, match_a_rest1, match_a_branch_AA1, AA_x_tmp3, match_x_branch_AA, AA_x_tmp4, AA_x_tmp5; -//│ match_a_branch_AA1 = function match_a_branch_AA(y, AA_x) { +//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, _deforest_AA_x_tmp2, match_x_rest, match_a_rest1, match_a_branch_AA1, _deforest_AA_x_tmp3, match_x_branch_AA, _deforest_AA_x_tmp4, _deforest_AA_x_tmp5; +//│ match_a_branch_AA1 = function match_a_branch_AA(y, _deforest_AA_x) { //│ let param0, m, tmp28; -//│ param0 = AA_x; +//│ param0 = _deforest_AA_x; //│ m = param0; //│ tmp28 = m; //│ return match_a_rest1(y, tmp28, match_x_rest) //│ }; -//│ match_x_branch_AA = function match_x_branch_AA(y, AA_x) { +//│ match_x_branch_AA = function match_x_branch_AA(y, _deforest_AA_x) { //│ let param0, a1; -//│ param0 = AA_x; +//│ param0 = _deforest_AA_x; //│ a1 = param0; //│ return runtime.safeCall(a1(y)) //│ }; @@ -208,22 +208,22 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ f1 = function f(x, y) { //│ return runtime.safeCall(x(y)) //│ }; -//│ AA_x_tmp2 = 3; +//│ _deforest_AA_x_tmp2 = 3; //│ tmp22 = (y) => { -//│ return match_a_branch_AA1(y, AA_x_tmp2) +//│ return match_a_branch_AA1(y, _deforest_AA_x_tmp2) //│ }; -//│ AA_x_tmp3 = tmp22; +//│ _deforest_AA_x_tmp3 = tmp22; //│ tmp23 = (y) => { -//│ return match_x_branch_AA(y, AA_x_tmp3) +//│ return match_x_branch_AA(y, _deforest_AA_x_tmp3) //│ }; //│ tmp24 = f1(tmp23, 9); -//│ AA_x_tmp4 = 4; +//│ _deforest_AA_x_tmp4 = 4; //│ tmp25 = (y) => { -//│ return match_a_branch_AA1(y, AA_x_tmp4) +//│ return match_a_branch_AA1(y, _deforest_AA_x_tmp4) //│ }; -//│ AA_x_tmp5 = tmp25; +//│ _deforest_AA_x_tmp5 = tmp25; //│ tmp26 = (y) => { -//│ return match_x_branch_AA(y, AA_x_tmp5) +//│ return match_x_branch_AA(y, _deforest_AA_x_tmp5) //│ }; //│ tmp27 = f1(tmp26, 10); //│ block$res6 = tmp24 + tmp27; @@ -268,24 +268,24 @@ f(AA(AA(3))) //│ f2(tmp35) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f2, tmp34, tmp35, AA_x, AA_x1; +//│ let f2, tmp34, tmp35, _deforest_AA_x, _deforest_AA_x1; //│ f2 = function f(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x = 3; +//│ _deforest_AA_x = 3; //│ tmp34 = () => { //│ let n, param0, m, tmp36, tmp37; -//│ param0 = AA_x; +//│ param0 = _deforest_AA_x; //│ m = param0; //│ tmp36 = m; //│ tmp37 = tmp36; //│ n = tmp37; //│ return n + 2 //│ }; -//│ AA_x1 = tmp34; +//│ _deforest_AA_x1 = tmp34; //│ tmp35 = () => { //│ let param0, a1; -//│ param0 = AA_x1; +//│ param0 = _deforest_AA_x1; //│ a1 = param0; //│ return runtime.safeCall(a1()) //│ }; @@ -331,17 +331,17 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ tmp40 + tmp43 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, AA_x_tmp6, match_x_rest1, match_param0_branch_AA, AA_x_tmp7, match_x_branch_AA1, AA_x_tmp8, AA_x_tmp9; -//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x2) { +//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp6, match_x_rest1, match_param0_branch_AA, _deforest_AA_x_tmp7, match_x_branch_AA1, _deforest_AA_x_tmp8, _deforest_AA_x_tmp9; +//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x2) { //│ let param0, a1, tmp44; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ a1 = param0; //│ tmp44 = a1; //│ return match_x_rest1(tmp44) //│ }; -//│ match_x_branch_AA1 = function match_x_branch_AA(AA_x2) { +//│ match_x_branch_AA1 = function match_x_branch_AA(_deforest_AA_x2) { //│ let param0; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ return runtime.safeCall(param0()) //│ }; //│ match_x_rest1 = function match_x_rest(tmp44) { @@ -352,22 +352,22 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ f3 = function f(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x_tmp6 = A1; +//│ _deforest_AA_x_tmp6 = A1; //│ tmp38 = () => { -//│ return match_param0_branch_AA(AA_x_tmp6) +//│ return match_param0_branch_AA(_deforest_AA_x_tmp6) //│ }; -//│ AA_x_tmp7 = tmp38; +//│ _deforest_AA_x_tmp7 = tmp38; //│ tmp39 = () => { -//│ return match_x_branch_AA1(AA_x_tmp7) +//│ return match_x_branch_AA1(_deforest_AA_x_tmp7) //│ }; //│ tmp40 = f3(tmp39); -//│ AA_x_tmp8 = A1; +//│ _deforest_AA_x_tmp8 = A1; //│ tmp41 = () => { -//│ return match_param0_branch_AA(AA_x_tmp8) +//│ return match_param0_branch_AA(_deforest_AA_x_tmp8) //│ }; -//│ AA_x_tmp9 = tmp41; +//│ _deforest_AA_x_tmp9 = tmp41; //│ tmp42 = () => { -//│ return match_x_branch_AA1(AA_x_tmp9) +//│ return match_x_branch_AA1(_deforest_AA_x_tmp9) //│ }; //│ tmp43 = f3(tmp42); //│ block$res10 = tmp40 + tmp43; @@ -404,21 +404,21 @@ c2(AA(AA(0))) //│ c2(tmp51) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c2, tmp50, tmp51, AA_x2, AA_x3; +//│ let c2, tmp50, tmp51, _deforest_AA_x2, _deforest_AA_x3; //│ c2 = function c2(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x2 = 0; +//│ _deforest_AA_x2 = 0; //│ tmp50 = () => { //│ let param0, a1; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ a1 = param0; //│ return a1 //│ }; -//│ AA_x3 = tmp50; +//│ _deforest_AA_x3 = tmp50; //│ tmp51 = () => { //│ let param0; -//│ param0 = AA_x3; +//│ param0 = _deforest_AA_x3; //│ return runtime.safeCall(param0()) //│ }; //│ block$res12 = c2(tmp51); @@ -458,22 +458,22 @@ f(AA(BB(B))) //│ f4(tmp55) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f4, tmp54, tmp55, BB_x, AA_x4; +//│ let f4, tmp54, tmp55, _deforest_BB_x, _deforest_AA_x4; //│ f4 = function f(a1) { //│ return runtime.safeCall(a1()) //│ }; -//│ BB_x = () => { +//│ _deforest_BB_x = () => { //│ return 3 //│ }; //│ tmp54 = () => { //│ let param0; -//│ param0 = BB_x; +//│ param0 = _deforest_BB_x; //│ return runtime.safeCall(param0()) //│ }; -//│ AA_x4 = tmp54; +//│ _deforest_AA_x4 = tmp54; //│ tmp55 = () => { //│ let param0; -//│ param0 = AA_x4; +//│ param0 = _deforest_AA_x4; //│ return runtime.safeCall(param0()) //│ }; //│ block$res14 = f4(tmp55); @@ -531,23 +531,23 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp61 + tmp65 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, AA_x_tmp10, match_x_branch_AA2, AA_x_tmp11, match_y_rest, match_y_branch_AA, BB_x_tmp, match_z_rest, match_z_branch_BB, AA_x_tmp12, AA_x_tmp13, BB_x_tmp1; -//│ match_x_branch_AA2 = function match_x_branch_AA(y, z, i, AA_x5) { +//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, _deforest_AA_x_tmp10, match_x_branch_AA2, _deforest_AA_x_tmp11, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp12, _deforest_AA_x_tmp13, _deforest_BB_x_tmp1; +//│ match_x_branch_AA2 = function match_x_branch_AA(y, z, i, _deforest_AA_x5) { //│ let param0, a1; -//│ param0 = AA_x5; +//│ param0 = _deforest_AA_x5; //│ a1 = param0; //│ return runtime.safeCall(y(z, i)) //│ }; -//│ match_y_branch_AA = function match_y_branch_AA(z, i, AA_x5) { +//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x5) { //│ let param0, a1, tmp66; -//│ param0 = AA_x5; +//│ param0 = _deforest_AA_x5; //│ a1 = param0; //│ tmp66 = a1 + i; //│ return match_y_rest(z, i, tmp66) //│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, BB_x1) { +//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x1) { //│ let param0, a2, tmp66; -//│ param0 = BB_x1; +//│ param0 = _deforest_BB_x1; //│ a2 = param0; //│ tmp66 = a2 - i; //│ return match_z_rest(m, tmp66) @@ -565,30 +565,30 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) //│ }; -//│ AA_x_tmp10 = 1; +//│ _deforest_AA_x_tmp10 = 1; //│ tmp58 = (y, z, i) => { -//│ return match_x_branch_AA2(y, z, i, AA_x_tmp10) +//│ return match_x_branch_AA2(y, z, i, _deforest_AA_x_tmp10) //│ }; -//│ AA_x_tmp11 = 2; +//│ _deforest_AA_x_tmp11 = 2; //│ tmp59 = (z, i) => { -//│ return match_y_branch_AA(z, i, AA_x_tmp11) +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp11) //│ }; -//│ BB_x_tmp = 3; +//│ _deforest_BB_x_tmp = 3; //│ tmp60 = (i, m) => { -//│ return match_z_branch_BB(i, m, BB_x_tmp) +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) //│ }; //│ tmp61 = test(tmp58, tmp59, tmp60, 4); -//│ AA_x_tmp12 = 1; +//│ _deforest_AA_x_tmp12 = 1; //│ tmp62 = (y, z, i) => { -//│ return match_x_branch_AA2(y, z, i, AA_x_tmp12) +//│ return match_x_branch_AA2(y, z, i, _deforest_AA_x_tmp12) //│ }; -//│ AA_x_tmp13 = 2; +//│ _deforest_AA_x_tmp13 = 2; //│ tmp63 = (z, i) => { -//│ return match_y_branch_AA(z, i, AA_x_tmp13) +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp13) //│ }; -//│ BB_x_tmp1 = 3; +//│ _deforest_BB_x_tmp1 = 3; //│ tmp64 = (i, m) => { -//│ return match_z_branch_BB(i, m, BB_x_tmp1) +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) //│ }; //│ tmp65 = test(tmp62, tmp63, tmp64, 4); //│ block$res16 = tmp61 + tmp65; @@ -664,7 +664,7 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ tmp80 + tmp81 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, BB_x1; +//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, _deforest_BB_x1; //│ f11 = function f1(x) { //│ let param0, x1; //│ if (x instanceof AA1.class) { @@ -694,10 +694,10 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ }; //│ tmp74 = CC1("cc"); //│ cc = tmp74; -//│ BB_x1 = cc; +//│ _deforest_BB_x1 = cc; //│ tmp75 = () => { //│ let param0, x2, param01, x3; -//│ param0 = BB_x1; +//│ param0 = _deforest_BB_x1; //│ x2 = param0; //│ if (x2 instanceof CC1.class) { //│ param01 = x2.x; @@ -751,19 +751,19 @@ c(AA(2), y) //│ c(tmp90, y) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, y, tmp90, AA_x5; +//│ let c, y, tmp90, _deforest_AA_x5; //│ c = function c(x, y1) { //│ return runtime.safeCall(x(y1)) //│ }; -//│ y = (AA_x6) => { +//│ y = (_deforest_AA_x6) => { //│ let t, tmp91; //│ tmp91 = 2; //│ t = tmp91; -//│ return t + AA_x6 +//│ return t + _deforest_AA_x6 //│ }; -//│ AA_x5 = 2; +//│ _deforest_AA_x5 = 2; //│ tmp90 = (y1) => { -//│ return runtime.safeCall(y1(AA_x5)) +//│ return runtime.safeCall(y1(_deforest_AA_x5)) //│ }; //│ block$res20 = c(tmp90, y); //│ undefined @@ -812,7 +812,7 @@ c(AA(2), y) + c2(y) //│ tmp93 + tmp94 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, c21, y1, tmp92, tmp93, tmp94, AA_x6; +//│ let c1, c21, y1, tmp92, tmp93, tmp94, _deforest_AA_x6; //│ c1 = function c(x, y2) { //│ return runtime.safeCall(x(y2)) //│ }; @@ -824,16 +824,16 @@ c(AA(2), y) + c2(y) //│ } //│ }; //│ y1 = A1; -//│ AA_x6 = 2; +//│ _deforest_AA_x6 = 2; //│ tmp92 = (y2) => { //│ let t, tmp95; //│ if (y2 instanceof A1.class) { -//│ tmp95 = 2 + AA_x6; +//│ tmp95 = 2 + _deforest_AA_x6; //│ } else { //│ throw new this.Error("match error"); //│ } //│ t = tmp95; -//│ return t + AA_x6 +//│ return t + _deforest_AA_x6 //│ }; //│ tmp93 = c1(tmp92, y1); //│ tmp94 = c21(y1); @@ -907,7 +907,7 @@ test(p) + f(p) + test(AA("3")) //│ tmp103 + tmp105 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105, AA_x7; +//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105, _deforest_AA_x7; //│ test1 = function test(x) { //│ let t, param0, x1, param01, tmp106; //│ if (x instanceof AA1.class) { @@ -938,10 +938,10 @@ test(p) + f(p) + test(AA("3")) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ AA_x7 = "10"; +//│ _deforest_AA_x7 = "10"; //│ tmp98 = (param0) => { //│ let t, param01, a1, tmp106; -//│ param01 = AA_x7; +//│ param01 = _deforest_AA_x7; //│ a1 = param01; //│ tmp106 = a1; //│ t = tmp106; @@ -1017,35 +1017,35 @@ f(aa, 3) + f(aa, 4) //│ tmp115 + tmp116 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f6, aa1, tmp114, tmp115, tmp116, AA_x_unused; +//│ let f6, aa1, tmp114, tmp115, tmp116, _deforest_AA_x_unused; //│ f6 = function f(x, y2) { -//│ let tmp117, tmp118, tmp119, BB_x2; +//│ let tmp117, tmp118, tmp119, _deforest_BB_x2; //│ tmp118 = y2 + 1; -//│ BB_x2 = tmp118; +//│ _deforest_BB_x2 = tmp118; //│ tmp119 = (x1) => { -//│ return runtime.safeCall(x1(BB_x2)) +//│ return runtime.safeCall(x1(_deforest_BB_x2)) //│ }; //│ tmp117 = tmp119; //│ return runtime.safeCall(tmp117(x)) //│ }; -//│ AA_x_unused = 3; -//│ tmp114 = (BB_x2) => { -//│ let scrut, AA_x8; -//│ AA_x8 = 2; -//│ scrut = (BB_x3) => { -//│ let m, param0, yf, scrut1, tmp117, tmp118, AA_x_unused1; -//│ param0 = AA_x8; +//│ _deforest_AA_x_unused = 3; +//│ tmp114 = (_deforest_BB_x2) => { +//│ let scrut, _deforest_AA_x8; +//│ _deforest_AA_x8 = 2; +//│ scrut = (_deforest_BB_x3) => { +//│ let m, param0, yf, scrut1, tmp117, tmp118, _deforest_AA_x_unused1; +//│ param0 = _deforest_AA_x8; //│ yf = param0; //│ tmp117 = yf; //│ tmp118 = tmp117; //│ m = tmp118; -//│ AA_x_unused1 = 3; -//│ scrut1 = (m1, BB_x4) => { -//│ return m1 + BB_x4 +//│ _deforest_AA_x_unused1 = 3; +//│ scrut1 = (m1, _deforest_BB_x4) => { +//│ return m1 + _deforest_BB_x4 //│ }; -//│ return runtime.safeCall(scrut1(m, BB_x3)) +//│ return runtime.safeCall(scrut1(m, _deforest_BB_x3)) //│ }; -//│ return runtime.safeCall(scrut(BB_x2)) +//│ return runtime.safeCall(scrut(_deforest_BB_x2)) //│ }; //│ aa1 = tmp114; //│ tmp115 = f6(aa1, 3); diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 8875b7f08a..42d5e268b1 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -41,17 +41,17 @@ c(AA(2)) //│ c(tmp) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, tmp, AA_x; +//│ let c, tmp, _deforest_AA_x; //│ c = function c(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x = 2; +//│ _deforest_AA_x = 2; //│ tmp = () => { //│ let scrut; -//│ scrut = (AA_x1) => { -//│ return AA_x1 +//│ scrut = (_deforest_AA_x1) => { +//│ return _deforest_AA_x1 //│ }; -//│ return runtime.safeCall(scrut(AA_x)) +//│ return runtime.safeCall(scrut(_deforest_AA_x)) //│ }; //│ block$res4 = c(tmp); //│ undefined @@ -83,16 +83,16 @@ c(AA(2), A) //│ c1(tmp2, A1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, tmp2, AA_x1; +//│ let c1, tmp2, _deforest_AA_x1; //│ c1 = function c(x, y) { //│ return runtime.safeCall(x(y)) //│ }; -//│ AA_x1 = 2; +//│ _deforest_AA_x1 = 2; //│ tmp2 = (y) => { -//│ return runtime.safeCall(y(AA_x1)) +//│ return runtime.safeCall(y(_deforest_AA_x1)) //│ }; -//│ block$res6 = c1(tmp2, (AA_x2) => { -//│ return AA_x2 +//│ block$res6 = c1(tmp2, (_deforest_AA_x2) => { +//│ return _deforest_AA_x2 //│ }); //│ undefined //│ = 2 @@ -133,15 +133,15 @@ c(p(), A) //│ return runtime.safeCall(x(y)) //│ }; //│ p = function p() { -//│ let AA_x2; -//│ AA_x2 = 2; +//│ let _deforest_AA_x2; +//│ _deforest_AA_x2 = 2; //│ return (y) => { -//│ return runtime.safeCall(y(AA_x2)) +//│ return runtime.safeCall(y(_deforest_AA_x2)) //│ } //│ }; //│ tmp4 = p(); -//│ block$res8 = c2(tmp4, (AA_x2) => { -//│ return AA_x2 +//│ block$res8 = c2(tmp4, (_deforest_AA_x2) => { +//│ return _deforest_AA_x2 //│ }); //│ undefined //│ = 2 @@ -187,18 +187,18 @@ c(p(), A) //│ return runtime.safeCall(x(y)) //│ }; //│ p1 = function p() { -//│ let AA_x2; -//│ AA_x2 = 2; +//│ let _deforest_AA_x2; +//│ _deforest_AA_x2 = 2; //│ return (y) => { -//│ return runtime.safeCall(y(AA_x2)) +//│ return runtime.safeCall(y(_deforest_AA_x2)) //│ } //│ }; //│ tmp6 = p1(); -//│ block$res10 = c3(tmp6, (AA_x2) => { +//│ block$res10 = c3(tmp6, (_deforest_AA_x2) => { //│ let a, tmp7; //│ tmp7 = 1; //│ a = tmp7; -//│ return a + AA_x2 +//│ return a + _deforest_AA_x2 //│ }); //│ undefined //│ = 3 @@ -252,16 +252,16 @@ f(AA(AA(AA(AA(AA(A)))))) //│ f(tmp12) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12, AA_x2, AA_x_tmp, match_param0_branch_AA, AA_x_tmp1, match_x_branch_AA, AA_x_tmp2, AA_x_tmp3; -//│ match_param0_branch_AA = function match_param0_branch_AA(AA_x3) { +//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12, _deforest_AA_x2, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1, match_x_branch_AA, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3; +//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x3) { //│ let param0, a; -//│ param0 = AA_x3; +//│ param0 = _deforest_AA_x3; //│ a = param0; //│ return g(a) //│ }; -//│ match_x_branch_AA = function match_x_branch_AA(AA_x3) { +//│ match_x_branch_AA = function match_x_branch_AA(_deforest_AA_x3) { //│ let param0; -//│ param0 = AA_x3; +//│ param0 = _deforest_AA_x3; //│ return runtime.safeCall(param0()) //│ }; //│ f = function f(x) { @@ -270,30 +270,30 @@ f(AA(AA(AA(AA(AA(A)))))) //│ g = function g(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x_tmp = () => { +//│ _deforest_AA_x_tmp = () => { //│ return 42 //│ }; //│ tmp8 = () => { -//│ return match_param0_branch_AA(AA_x_tmp) +//│ return match_param0_branch_AA(_deforest_AA_x_tmp) //│ }; -//│ AA_x_tmp1 = tmp8; +//│ _deforest_AA_x_tmp1 = tmp8; //│ tmp9 = () => { -//│ return match_x_branch_AA(AA_x_tmp1) +//│ return match_x_branch_AA(_deforest_AA_x_tmp1) //│ }; -//│ AA_x2 = tmp9; +//│ _deforest_AA_x2 = tmp9; //│ tmp10 = () => { //│ let param0, b; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ b = param0; //│ return f(b) //│ }; -//│ AA_x_tmp2 = tmp10; +//│ _deforest_AA_x_tmp2 = tmp10; //│ tmp11 = () => { -//│ return match_param0_branch_AA(AA_x_tmp2) +//│ return match_param0_branch_AA(_deforest_AA_x_tmp2) //│ }; -//│ AA_x_tmp3 = tmp11; +//│ _deforest_AA_x_tmp3 = tmp11; //│ tmp12 = () => { -//│ return match_x_branch_AA(AA_x_tmp3) +//│ return match_x_branch_AA(_deforest_AA_x_tmp3) //│ }; //│ block$res12 = f(tmp12); //│ undefined @@ -327,21 +327,21 @@ f(AA(AA(A))) //│ f1(tmp19) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, tmp18, tmp19, AA_x3, AA_x4; +//│ let f1, tmp18, tmp19, _deforest_AA_x3, _deforest_AA_x4; //│ f1 = function f(x) { //│ return runtime.safeCall(x()) //│ }; -//│ AA_x3 = A1; +//│ _deforest_AA_x3 = A1; //│ tmp18 = () => { //│ let param0, a; -//│ param0 = AA_x3; +//│ param0 = _deforest_AA_x3; //│ a = param0; //│ return a //│ }; -//│ AA_x4 = tmp18; +//│ _deforest_AA_x4 = tmp18; //│ tmp19 = () => { //│ let param0; -//│ param0 = AA_x4; +//│ param0 = _deforest_AA_x4; //│ return runtime.safeCall(param0()) //│ }; //│ block$res14 = f1(tmp19); @@ -386,18 +386,18 @@ f(p()) //│ return runtime.safeCall(x()) //│ }; //│ p2 = function p() { -//│ let tmp23, AA_x5, AA_x6; -//│ AA_x5 = A1; +//│ let tmp23, _deforest_AA_x5, _deforest_AA_x6; +//│ _deforest_AA_x5 = A1; //│ tmp23 = () => { //│ let param0, a; -//│ param0 = AA_x5; +//│ param0 = _deforest_AA_x5; //│ a = param0; //│ return a //│ }; -//│ AA_x6 = tmp23; +//│ _deforest_AA_x6 = tmp23; //│ return () => { //│ let param0; -//│ param0 = AA_x6; +//│ param0 = _deforest_AA_x6; //│ return runtime.safeCall(param0()) //│ } //│ }; @@ -440,21 +440,21 @@ c(AA(2), 10) //│ c4(tmp24, 10) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c4, tmp24, AA_x5; +//│ let c4, tmp24, _deforest_AA_x5; //│ c4 = function c(x, y) { //│ return runtime.safeCall(x(y)) //│ }; -//│ AA_x5 = 2; +//│ _deforest_AA_x5 = 2; //│ tmp24 = (y) => { //│ let scrut; -//│ scrut = (y1, AA_x6) => { +//│ scrut = (y1, _deforest_AA_x6) => { //│ let t, tmp25, tmp26; -//│ tmp25 = AA_x6; +//│ tmp25 = _deforest_AA_x6; //│ tmp26 = tmp25; //│ t = tmp26; //│ return t + y1 //│ }; -//│ return runtime.safeCall(scrut(y, AA_x5)) +//│ return runtime.safeCall(scrut(y, _deforest_AA_x5)) //│ }; //│ block$res18 = c4(tmp24, 10); //│ undefined diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 563dedb4a9..64ee8d8b51 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -99,21 +99,21 @@ test() //│ ==== JS (deforested): ==== //│ let test1; //│ test1 = function test() { -//│ let x, scrut, tmp, AA_aa, BB_bb; +//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ AA_aa = A1; +//│ _deforest_AA_aa = A1; //│ tmp = () => { //│ let param0, x1; -//│ param0 = AA_aa; +//│ param0 = _deforest_AA_aa; //│ x1 = param0; //│ return x1 //│ }; //│ } else { -//│ BB_bb = B1; +//│ _deforest_BB_bb = B1; //│ tmp = () => { //│ let param0, x1; -//│ param0 = BB_bb; +//│ param0 = _deforest_BB_bb; //│ x1 = param0; //│ return x1 //│ }; @@ -178,25 +178,25 @@ test() //│ return runtime.safeCall(a()) //│ }; //│ test2 = function test() { -//│ let x, scrut, tmp, AA_aa, BB_bb; +//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ AA_aa = () => { +//│ _deforest_AA_aa = () => { //│ return 1 //│ }; //│ tmp = () => { //│ let param0, x1; -//│ param0 = AA_aa; +//│ param0 = _deforest_AA_aa; //│ x1 = param0; //│ return f(x1) //│ }; //│ } else { -//│ BB_bb = () => { +//│ _deforest_BB_bb = () => { //│ return 2 //│ }; //│ tmp = () => { //│ let param0, x1; -//│ param0 = BB_bb; +//│ param0 = _deforest_BB_bb; //│ x1 = param0; //│ return f(x1) //│ }; @@ -279,21 +279,21 @@ test() //│ return runtime.safeCall(a()) //│ }; //│ test3 = function test() { -//│ let x, scrut, tmp, AA_aa, BB_bb; +//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ AA_aa = () => { +//│ _deforest_AA_aa = () => { //│ return 1 //│ }; //│ tmp = () => { -//│ return f1(AA_aa) +//│ return f1(_deforest_AA_aa) //│ }; //│ } else { -//│ BB_bb = () => { +//│ _deforest_BB_bb = () => { //│ return 5 //│ }; //│ tmp = () => { -//│ return f2(BB_bb) +//│ return f2(_deforest_BB_bb) //│ }; //│ } //│ x = tmp; @@ -351,21 +351,21 @@ test() //│ test4 = function test() { //│ let c, g, tmp; //│ g = function g(x) { -//│ let scrut, AA_aa, BB_bb; +//│ let scrut, _deforest_AA_aa, _deforest_BB_bb; //│ scrut = true; //│ if (scrut === true) { -//│ AA_aa = 11; +//│ _deforest_AA_aa = 11; //│ return () => { //│ let param0, x1; -//│ param0 = AA_aa; +//│ param0 = _deforest_AA_aa; //│ x1 = param0; //│ return x1 //│ } //│ } else { -//│ BB_bb = 22; +//│ _deforest_BB_bb = 22; //│ return () => { //│ let param0, x1; -//│ param0 = BB_bb; +//│ param0 = _deforest_BB_bb; //│ x1 = param0; //│ return x1 //│ } @@ -434,17 +434,17 @@ map(enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo, map, tmp; //│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2, Cons_t, Cons_h; +//│ let scrut, tmp1, tmp2, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp1 = a + 1; //│ tmp2 = enumFromTo(tmp1, b); -//│ Cons_h = a; -//│ Cons_t = tmp2; +//│ _deforest_Cons_h = a; +//│ _deforest_Cons_t = tmp2; //│ return () => { //│ let param0, param1, h, t, tmp3, tmp4; -//│ param0 = Cons_h; -//│ param1 = Cons_t; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; //│ tmp3 = h + 4; @@ -510,17 +510,17 @@ sum(enumFromTo(1,10)) //│ ==== JS (deforested): ==== //│ let enumFromTo1, sum, tmp2; //│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp3, tmp4, Cons_t, Cons_h; +//│ let scrut, tmp3, tmp4, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp3 = a + 1; //│ tmp4 = enumFromTo1(tmp3, b); -//│ Cons_h = a; -//│ Cons_t = tmp4; +//│ _deforest_Cons_h = a; +//│ _deforest_Cons_t = tmp4; //│ return () => { //│ let param0, param1, h, t, tmp5; -//│ param0 = Cons_h; -//│ param1 = Cons_t; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; //│ tmp5 = sum(t); @@ -754,17 +754,17 @@ map(x => x + 4, enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo2, map1, tmp8, lambda; //│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp9, tmp10, Cons_t, Cons_h; +//│ let scrut, tmp9, tmp10, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp9 = a + 1; //│ tmp10 = enumFromTo2(tmp9, b); -//│ Cons_h = a; -//│ Cons_t = tmp10; +//│ _deforest_Cons_h = a; +//│ _deforest_Cons_t = tmp10; //│ return (f3) => { //│ let param0, param1, h, t, tmp11, tmp12; -//│ param0 = Cons_h; -//│ param1 = Cons_t; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; //│ tmp11 = runtime.safeCall(f3(h)); @@ -848,17 +848,17 @@ map(x => x + 4, enumFromTo(1, 4)) //│ return runtime.safeCall(tmp11(f3)); //│ }; //│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12, Cons_t, Cons_h; +//│ let scrut, tmp11, tmp12, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp11 = a + 1; //│ tmp12 = enumFromTo3(tmp11, b); -//│ Cons_h = a; -//│ Cons_t = tmp12; +//│ _deforest_Cons_h = a; +//│ _deforest_Cons_t = tmp12; //│ return (f3) => { //│ let param0, param1, h, t, tmp13, tmp14, lambda3; -//│ param0 = Cons_h; -//│ param1 = Cons_t; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; //│ lambda3 = (undefined, function (f4) { @@ -937,17 +937,17 @@ sum(enumFromTo(1, 10), 0) //│ ==== JS (deforested): ==== //│ let enumFromTo4, sum1, tmp12; //│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14, Cons_t, Cons_h; +//│ let scrut, tmp13, tmp14, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = a < b; //│ if (scrut === true) { //│ tmp13 = a + 1; //│ tmp14 = enumFromTo4(tmp13, b); -//│ Cons_h = a; -//│ Cons_t = tmp14; +//│ _deforest_Cons_h = a; +//│ _deforest_Cons_t = tmp14; //│ return (a1) => { //│ let param0, param1, h, t, tmp15; -//│ param0 = Cons_h; -//│ param1 = Cons_t; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; //│ tmp15 = h + a1; @@ -1240,21 +1240,21 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ tmp26 + tmp28 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, AAA_x_tmp, AAA_y_tmp, match_x_rest, match_x_branch_AAA, BBB_x_tmp, BBB_y_tmp, match_x_branch_BBB, AAA_x_tmp1, AAA_y_tmp1, BBB_x_tmp1, BBB_y_tmp1; -//│ match_x_branch_AAA = function match_x_branch_AAA(y1, AAA_x, AAA_y) { +//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp, match_x_rest, match_x_branch_AAA, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp, match_x_branch_BBB, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1; +//│ match_x_branch_AAA = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { //│ let param0, param1, n, m, tmp29, tmp30; -//│ param0 = AAA_x; -//│ param1 = AAA_y; +//│ param0 = _deforest_AAA_x; +//│ param1 = _deforest_AAA_y; //│ n = param0; //│ m = param1; //│ tmp29 = y1 + n; //│ tmp30 = tmp29 - m; //│ return match_x_rest(tmp30) //│ }; -//│ match_x_branch_BBB = function match_x_branch_BBB(y1, BBB_x, BBB_y) { +//│ match_x_branch_BBB = function match_x_branch_BBB(y1, _deforest_BBB_x, _deforest_BBB_y) { //│ let param0, param1, n, m, tmp29, tmp30; -//│ param0 = BBB_x; -//│ param1 = BBB_y; +//│ param0 = _deforest_BBB_x; +//│ param1 = _deforest_BBB_y; //│ n = param0; //│ m = param1; //│ tmp30 = m + 1; @@ -1269,30 +1269,30 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ AAA_x_tmp = 1; -//│ AAA_y_tmp = 3; +//│ _deforest_AAA_x_tmp = 1; +//│ _deforest_AAA_y_tmp = 3; //│ tmp19 = (y1) => { -//│ return match_x_branch_AAA(y1, AAA_x_tmp, AAA_y_tmp) +//│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp) //│ }; //│ tmp20 = f5(tmp19, 1); -//│ BBB_x_tmp = 2; -//│ BBB_y_tmp = 3; +//│ _deforest_BBB_x_tmp = 2; +//│ _deforest_BBB_y_tmp = 3; //│ tmp21 = (y1) => { -//│ return match_x_branch_BBB(y1, BBB_x_tmp, BBB_y_tmp) +//│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp) //│ }; //│ tmp22 = f5(tmp21, 2); //│ tmp23 = tmp20 + tmp22; -//│ AAA_x_tmp1 = 3; -//│ AAA_y_tmp1 = 2; +//│ _deforest_AAA_x_tmp1 = 3; +//│ _deforest_AAA_y_tmp1 = 2; //│ tmp24 = (y1) => { -//│ return match_x_branch_AAA(y1, AAA_x_tmp1, AAA_y_tmp1) +//│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1) //│ }; //│ tmp25 = f5(tmp24, 4); //│ tmp26 = tmp23 + tmp25; -//│ BBB_x_tmp1 = 4; -//│ BBB_y_tmp1 = 6; +//│ _deforest_BBB_x_tmp1 = 4; +//│ _deforest_BBB_y_tmp1 = 6; //│ tmp27 = (y1) => { -//│ return match_x_branch_BBB(y1, BBB_x_tmp1, BBB_y_tmp1) +//│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1) //│ }; //│ tmp28 = f5(tmp27, 0); //│ block$res39 = tmp26 + tmp28; @@ -1403,14 +1403,14 @@ c(AA(2), A) //│ c3(tmp41, A1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c3, tmp41, AA_aa; +//│ let c3, tmp41, _deforest_AA_aa; //│ c3 = function c(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ AA_aa = 2; +//│ _deforest_AA_aa = 2; //│ tmp41 = (y1) => { //│ let param0, a; -//│ param0 = AA_aa; +//│ param0 = _deforest_AA_aa; //│ a = param0; //│ return runtime.safeCall(y1(a)) //│ }; @@ -1464,20 +1464,20 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ tmp50 + tmp52 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, AAA_x_tmp2, AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, CCC_c_tmp, match_x_branch_CCC, AAA_x_tmp3, AAA_y_tmp3, CCC_c_tmp1; -//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, AAA_x, AAA_y) { +//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, _deforest_CCC_c_tmp, match_x_branch_CCC, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3, _deforest_CCC_c_tmp1; +//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { //│ let param0, param1, n, m, tmp53, tmp54; -//│ param0 = AAA_x; -//│ param1 = AAA_y; +//│ param0 = _deforest_AAA_x; +//│ param1 = _deforest_AAA_y; //│ n = param0; //│ m = param1; //│ tmp53 = y1 + n; //│ tmp54 = tmp53 - m; //│ return match_x_rest1(tmp54) //│ }; -//│ match_x_branch_CCC = function match_x_branch_CCC(y1, CCC_c) { +//│ match_x_branch_CCC = function match_x_branch_CCC(y1, _deforest_CCC_c) { //│ let param0, n, tmp53; -//│ param0 = CCC_c; +//│ param0 = _deforest_CCC_c; //│ n = param0; //│ tmp53 = n + 1; //│ return match_x_rest1(tmp53) @@ -1490,28 +1490,28 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ f6 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ AAA_x_tmp2 = 1; -//│ AAA_y_tmp2 = 3; +//│ _deforest_AAA_x_tmp2 = 1; +//│ _deforest_AAA_y_tmp2 = 3; //│ tmp43 = (y1) => { -//│ return match_x_branch_AAA1(y1, AAA_x_tmp2, AAA_y_tmp2) +//│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2) //│ }; //│ tmp44 = f6(tmp43, 1); -//│ CCC_c_tmp = 2; +//│ _deforest_CCC_c_tmp = 2; //│ tmp45 = (y1) => { -//│ return match_x_branch_CCC(y1, CCC_c_tmp) +//│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp) //│ }; //│ tmp46 = f6(tmp45, 2); //│ tmp47 = tmp44 + tmp46; -//│ AAA_x_tmp3 = 3; -//│ AAA_y_tmp3 = 2; +//│ _deforest_AAA_x_tmp3 = 3; +//│ _deforest_AAA_y_tmp3 = 2; //│ tmp48 = (y1) => { -//│ return match_x_branch_AAA1(y1, AAA_x_tmp3, AAA_y_tmp3) +//│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3) //│ }; //│ tmp49 = f6(tmp48, 4); //│ tmp50 = tmp47 + tmp49; -//│ CCC_c_tmp1 = 4; +//│ _deforest_CCC_c_tmp1 = 4; //│ tmp51 = (y1) => { -//│ return match_x_branch_CCC(y1, CCC_c_tmp1) +//│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp1) //│ }; //│ tmp52 = f6(tmp51, 0); //│ block$res45 = tmp50 + tmp52; @@ -1556,10 +1556,10 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ tmp67 + tmp69 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, CCC_c, AAA_x_unused, AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, AAA_x_unused1, AAA_y_tmp5; -//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, AAA_y) { +//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, _deforest_CCC_c, _deforest_AAA_x_unused, _deforest_AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, _deforest_AAA_x_unused1, _deforest_AAA_y_tmp5; +//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, _deforest_AAA_y) { //│ let tmp70; -//│ tmp70 = y1 + AAA_y; +//│ tmp70 = y1 + _deforest_AAA_y; //│ return match_x_rest2(tmp70) //│ }; //│ match_x_rest2 = function match_x_rest(tmp70) { @@ -1570,26 +1570,26 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ f7 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; -//│ AAA_x_unused = 1; -//│ AAA_y_tmp4 = 3; +//│ _deforest_AAA_x_unused = 1; +//│ _deforest_AAA_y_tmp4 = 3; //│ tmp63 = (y1) => { -//│ return match_x_branch_AAA2(y1, AAA_y_tmp4) +//│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp4) //│ }; //│ tmp64 = f7(tmp63, 1); -//│ CCC_c = 2; +//│ _deforest_CCC_c = 2; //│ tmp65 = (y1) => { //│ let param0, n, tmp70; -//│ param0 = CCC_c; +//│ param0 = _deforest_CCC_c; //│ n = param0; //│ tmp70 = n + 1; //│ return match_x_rest2(tmp70) //│ }; //│ tmp66 = f7(tmp65, 2); //│ tmp67 = tmp64 + tmp66; -//│ AAA_x_unused1 = 3; -//│ AAA_y_tmp5 = 2; +//│ _deforest_AAA_x_unused1 = 3; +//│ _deforest_AAA_y_tmp5 = 2; //│ tmp68 = (y1) => { -//│ return match_x_branch_AAA2(y1, AAA_y_tmp5) +//│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp5) //│ }; //│ tmp69 = f7(tmp68, 4); //│ block$res47 = tmp67 + tmp69; @@ -1620,11 +1620,11 @@ c(BB(3), 0) //│ c4(tmp77, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c4, tmp77, BB_bb_unused; +//│ let c4, tmp77, _deforest_BB_bb_unused; //│ c4 = function c(x1, m) { //│ return runtime.safeCall(x1(m)) //│ }; -//│ BB_bb_unused = 3; +//│ _deforest_BB_bb_unused = 3; //│ tmp77 = (m) => { //│ return m //│ }; @@ -1670,24 +1670,24 @@ f(AA(AA(0))) //│ f8(tmp80) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f8, g, tmp79, tmp80, AA_aa1, AA_aa2; +//│ let f8, g, tmp79, tmp80, _deforest_AA_aa1, _deforest_AA_aa2; //│ f8 = function f(x1) { //│ return runtime.safeCall(x1()) //│ }; //│ g = function g(b) { //│ return runtime.safeCall(b()) //│ }; -//│ AA_aa1 = 0; +//│ _deforest_AA_aa1 = 0; //│ tmp79 = () => { //│ let param0, a; -//│ param0 = AA_aa1; +//│ param0 = _deforest_AA_aa1; //│ a = param0; //│ return a + 1 //│ }; -//│ AA_aa2 = tmp79; +//│ _deforest_AA_aa2 = tmp79; //│ tmp80 = () => { //│ let param0, b; -//│ param0 = AA_aa2; +//│ param0 = _deforest_AA_aa2; //│ b = param0; //│ return g(b) //│ }; @@ -1797,10 +1797,10 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ tmp91 + tmp93 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, AA_aa_tmp, match_x_rest3, match_x_branch_AA, AA_aa_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y1, AA_aa3) { +//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, _deforest_AA_aa_tmp, match_x_rest3, match_x_branch_AA, _deforest_AA_aa_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y1, _deforest_AA_aa3) { //│ let param0, a, tmp94; -//│ param0 = AA_aa3; +//│ param0 = _deforest_AA_aa3; //│ a = param0; //│ tmp94 = a + y1; //│ return match_x_rest3(y1, tmp94) @@ -1845,18 +1845,18 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ t = tmp94; //│ return t + y1 //│ }; -//│ AA_aa_tmp = 3; +//│ _deforest_AA_aa_tmp = 3; //│ tmp87 = (y1) => { -//│ return match_x_branch_AA(y1, AA_aa_tmp) +//│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp) //│ }; //│ tmp88 = AA1(tmp87); //│ p1 = tmp88; //│ tmp89 = outer1(p1, 1, 2); //│ tmp90 = outer2(p1, 3, 4); //│ tmp91 = tmp89 + tmp90; -//│ AA_aa_tmp1 = 5; +//│ _deforest_AA_aa_tmp1 = 5; //│ tmp92 = (y1) => { -//│ return match_x_branch_AA(y1, AA_aa_tmp1) +//│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp1) //│ }; //│ tmp93 = inner(tmp92, 6); //│ block$res55 = tmp91 + tmp93; @@ -1893,17 +1893,17 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ mapHead(lambda4, tmp102) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let mapHead, tmp101, tmp102, lambda4, AAA_x, AAA_y; +//│ let mapHead, tmp101, tmp102, lambda4, _deforest_AAA_x, _deforest_AAA_y; //│ mapHead = function mapHead(f10, x1) { //│ return runtime.safeCall(x1(f10)) //│ }; //│ tmp101 = AAA1(2, None1); -//│ AAA_x = 1; -//│ AAA_y = tmp101; +//│ _deforest_AAA_x = 1; +//│ _deforest_AAA_y = tmp101; //│ tmp102 = (f10) => { //│ let param0, param1, h, t; -//│ param0 = AAA_x; -//│ param1 = AAA_y; +//│ param0 = _deforest_AAA_x; +//│ param1 = _deforest_AAA_y; //│ h = param0; //│ t = param1; //│ return runtime.safeCall(f10(h)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 5a20124701..f6393f2fa5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -243,17 +243,17 @@ f(AA(AA(3))) //│ f(tmp1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, tmp, tmp1, AA_x; +//│ let f, tmp, tmp1, _deforest_AA_x; //│ f = function f(x) { //│ return runtime.safeCall(x()) //│ }; //│ tmp = AA1(3); -//│ AA_x = tmp; +//│ _deforest_AA_x = tmp; //│ tmp1 = () => { //│ let scrut; -//│ scrut = AA_x; +//│ scrut = _deforest_AA_x; //│ if (scrut instanceof AA1.class) { -//│ return AA_x.x +//│ return _deforest_AA_x.x //│ } else { //│ throw new this.Error("match error"); //│ } @@ -321,23 +321,23 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp7 + tmp11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, AA_x_tmp, match_x_rest, match_x_branch_AA, AA_x_tmp1, match_y_rest, match_y_branch_AA, BB_x_tmp, match_z_rest, match_z_branch_BB, AA_x_tmp2, AA_x_tmp3, BB_x_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, AA_x1) { +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_rest, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x1) { //│ let param0, a; -//│ param0 = AA_x1; +//│ param0 = _deforest_AA_x1; //│ a = param0; //│ return runtime.safeCall(y(z, i)) //│ }; -//│ match_y_branch_AA = function match_y_branch_AA(z, i, AA_x1) { +//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x1) { //│ let param0, a1, tmp12; -//│ param0 = AA_x1; +//│ param0 = _deforest_AA_x1; //│ a1 = param0; //│ tmp12 = a1 + i; //│ return match_y_rest(z, i, tmp12) //│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, BB_x) { +//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x) { //│ let param0, a2, tmp12; -//│ param0 = BB_x; +//│ param0 = _deforest_BB_x; //│ a2 = param0; //│ tmp12 = a2 - i; //│ return match_z_rest(i, m, tmp12, match_x_rest) @@ -362,30 +362,30 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) //│ }; -//│ AA_x_tmp = 1; +//│ _deforest_AA_x_tmp = 1; //│ tmp4 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, AA_x_tmp) +//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp) //│ }; -//│ AA_x_tmp1 = 2; +//│ _deforest_AA_x_tmp1 = 2; //│ tmp5 = (z, i) => { -//│ return match_y_branch_AA(z, i, AA_x_tmp1) +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp1) //│ }; -//│ BB_x_tmp = 3; +//│ _deforest_BB_x_tmp = 3; //│ tmp6 = (i, m) => { -//│ return match_z_branch_BB(i, m, BB_x_tmp) +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) //│ }; //│ tmp7 = test(tmp4, tmp5, tmp6, 4); -//│ AA_x_tmp2 = 1; +//│ _deforest_AA_x_tmp2 = 1; //│ tmp8 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, AA_x_tmp2) +//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp2) //│ }; -//│ AA_x_tmp3 = 2; +//│ _deforest_AA_x_tmp3 = 2; //│ tmp9 = (z, i) => { -//│ return match_y_branch_AA(z, i, AA_x_tmp3) +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp3) //│ }; -//│ BB_x_tmp1 = 3; +//│ _deforest_BB_x_tmp1 = 3; //│ tmp10 = (i, m) => { -//│ return match_z_branch_BB(i, m, BB_x_tmp1) +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) //│ }; //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ block$res10 = tmp7 + tmp11; @@ -456,7 +456,7 @@ test(p) + f(p) //│ tmp23 + tmp24 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, AA_x1; +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x1; //│ test1 = function test(x) { //│ let t, param0, param01; //│ if (x instanceof AA1.class) { @@ -486,10 +486,10 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ AA_x1 = 10; +//│ _deforest_AA_x1 = 10; //│ tmp20 = () => { //│ let t, param0, a, tmp25; -//│ param0 = AA_x1; +//│ param0 = _deforest_AA_x1; //│ a = param0; //│ tmp25 = a; //│ t = tmp25; @@ -570,13 +570,13 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ tmp31 + tmp33 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f2, tmp30, tmp31, tmp32, tmp33, AA_x_unused, match_x_rest1, match_x_branch_AA1, AA_x_unused1; +//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_rest1, match_x_branch_AA1, _deforest_AA_x_unused1; //│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { -//│ let scrut, AA_x2; -//│ AA_x2 = 2; +//│ let scrut, _deforest_AA_x2; +//│ _deforest_AA_x2 = 2; //│ scrut = (k1, tmp35) => { //│ let param0, yf, tmp36, tmp37; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ yf = param0; //│ tmp36 = yf; //│ tmp37 = tmp36; @@ -585,9 +585,9 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ return runtime.safeCall(scrut(k, tmp34)) //│ }; //│ match_x_rest1 = function match_x_rest(k, tmp34, tmp35) { -//│ let m, scrut, AA_x_unused2; +//│ let m, scrut, _deforest_AA_x_unused2; //│ m = tmp35; -//│ AA_x_unused2 = 3; +//│ _deforest_AA_x_unused2 = 3; //│ scrut = (k1, tmp36, m1) => { //│ let tmp37, tmp38; //│ tmp37 = m1 + k1; @@ -614,12 +614,12 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ } //│ return tmp34.x + tmp_not_in_scope //│ }; -//│ AA_x_unused = 3; +//│ _deforest_AA_x_unused = 3; //│ tmp30 = (k, tmp34) => { //│ return match_x_branch_AA1(k, tmp34) //│ }; //│ tmp31 = f2(tmp30, 3, true, 10); -//│ AA_x_unused1 = 5; +//│ _deforest_AA_x_unused1 = 5; //│ tmp32 = (k, tmp34) => { //│ return match_x_branch_AA1(k, tmp34) //│ }; @@ -678,17 +678,17 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ tmp40 + tmp43 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, AA_x_tmp4, match_p_rest, match_p_branch_AA, AA_x_tmp5, match_q_rest, match_q_branch_AA, AA_x_tmp6, AA_x_tmp7; -//│ match_p_branch_AA = function match_p_branch_AA(n, q, AA_x2) { +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_rest, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_rest, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; +//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x2) { //│ let param0, x, tmp44; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ x = param0; //│ tmp44 = x; //│ return match_p_rest(n, q, tmp44) //│ }; -//│ match_q_branch_AA = function match_q_branch_AA(n, k, AA_x2) { +//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x2) { //│ let param0, y, tmp44; -//│ param0 = AA_x2; +//│ param0 = _deforest_AA_x2; //│ y = param0; //│ tmp44 = k + y; //│ return match_q_rest(n, tmp44) @@ -705,29 +705,29 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ return n + tmp45; //│ }; //│ test2 = function test(n, p1, q) { -//│ let scrut, AA_x_unused2; -//│ AA_x_unused2 = 0; +//│ let scrut, _deforest_AA_x_unused2; +//│ _deforest_AA_x_unused2 = 0; //│ scrut = (n1, p2, q1) => { //│ return runtime.safeCall(p2(n1, q1)) //│ }; //│ return runtime.safeCall(scrut(n, p1, q)) //│ }; -//│ AA_x_tmp4 = 2; +//│ _deforest_AA_x_tmp4 = 2; //│ tmp38 = (n, q) => { -//│ return match_p_branch_AA(n, q, AA_x_tmp4) +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp4) //│ }; -//│ AA_x_tmp5 = 3; +//│ _deforest_AA_x_tmp5 = 3; //│ tmp39 = (n, k) => { -//│ return match_q_branch_AA(n, k, AA_x_tmp5) +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp5) //│ }; //│ tmp40 = test2(3, tmp38, tmp39); -//│ AA_x_tmp6 = 2; +//│ _deforest_AA_x_tmp6 = 2; //│ tmp41 = (n, q) => { -//│ return match_p_branch_AA(n, q, AA_x_tmp6) +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp6) //│ }; -//│ AA_x_tmp7 = 3; +//│ _deforest_AA_x_tmp7 = 3; //│ tmp42 = (n, k) => { -//│ return match_q_branch_AA(n, k, AA_x_tmp7) +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp7) //│ }; //│ tmp43 = test2(3, tmp41, tmp42); //│ block$res16 = tmp40 + tmp43; From 4ba1ea9cba0a4ee87696849f6867181278a9b69c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 28 Mar 2025 20:53:01 +0800 Subject: [PATCH 146/303] update fixme tests --- .../src/test/mlscript/deforest/todos.mls | 408 +++++------------- 1 file changed, 119 insertions(+), 289 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index f6393f2fa5..3c7a341c9a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -320,80 +320,7 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ tmp7 + tmp11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_rest, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x1) { -//│ let param0, a; -//│ param0 = _deforest_AA_x1; -//│ a = param0; -//│ return runtime.safeCall(y(z, i)) -//│ }; -//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x1) { -//│ let param0, a1, tmp12; -//│ param0 = _deforest_AA_x1; -//│ a1 = param0; -//│ tmp12 = a1 + i; -//│ return match_y_rest(z, i, tmp12) -//│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x) { -//│ let param0, a2, tmp12; -//│ param0 = _deforest_BB_x; -//│ a2 = param0; -//│ tmp12 = a2 - i; -//│ return match_z_rest(i, m, tmp12, match_x_rest) -//│ }; -//│ match_x_rest = function match_x_rest(i, tmp12) { -//│ let k; -//│ k = tmp12; -//│ return k + i; -//│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp12) { -//│ let m; -//│ m = tmp12; -//│ return runtime.safeCall(z(i, m)); -//│ return match_x_rest(i, tmp_not_in_scope) -//│ }; -//│ match_z_rest = function match_z_rest(i, m, tmp12) { -//│ let n, tmp13; -//│ n = tmp12; -//│ tmp13 = m + n; -//│ return match_x_rest(i, tmp13) -//│ }; -//│ test = function test(x, y, z, i) { -//│ return runtime.safeCall(x(y, z, i)) -//│ }; -//│ _deforest_AA_x_tmp = 1; -//│ tmp4 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp) -//│ }; -//│ _deforest_AA_x_tmp1 = 2; -//│ tmp5 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp1) -//│ }; -//│ _deforest_BB_x_tmp = 3; -//│ tmp6 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) -//│ }; -//│ tmp7 = test(tmp4, tmp5, tmp6, 4); -//│ _deforest_AA_x_tmp2 = 1; -//│ tmp8 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp2) -//│ }; -//│ _deforest_AA_x_tmp3 = 2; -//│ tmp9 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp3) -//│ }; -//│ _deforest_BB_x_tmp1 = 3; -//│ tmp10 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) -//│ }; -//│ tmp11 = test(tmp8, tmp9, tmp10, 4); -//│ block$res10 = tmp7 + tmp11; -//│ undefined -//│ = 18 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 18 -//│ FAILURE: Unexpected lack of error to fix +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) // FIXME: @@ -411,9 +338,9 @@ fun f(a) = if a is let p = AA(AA(AA(10))) test(p) + f(p) //│ JS (unsanitized): -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24; +//│ let test1, f1, p, tmp12, tmp13, tmp14, tmp15, tmp16; //│ test1 = function test(x) { -//│ let t, param0, param01, param02, a, tmp25; +//│ let t, param0, param01, param02, a, tmp17; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -421,7 +348,7 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp25 = a; +//│ tmp17 = a; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -431,7 +358,7 @@ test(p) + f(p) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp25; +//│ t = tmp17; //│ return t + 5 //│ }; //│ f1 = function f(a) { @@ -447,70 +374,19 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp20 = AA1(10); -//│ tmp21 = AA1(tmp20); -//│ tmp22 = AA1(tmp21); -//│ p = tmp22; -//│ tmp23 = test1(p); -//│ tmp24 = f1(p); -//│ tmp23 + tmp24 +//│ tmp12 = AA1(10); +//│ tmp13 = AA1(tmp12); +//│ tmp14 = AA1(tmp13); +//│ p = tmp14; +//│ tmp15 = test1(p); +//│ tmp16 = f1(p); +//│ tmp15 + tmp16 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x1; -//│ test1 = function test(x) { -//│ let t, param0, param01; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ return runtime.safeCall(param01()) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp_not_in_scope; -//│ return t + 5 -//│ }; -//│ f1 = function f(a) { -//│ let param0; -//│ if (a instanceof AA1.class) { -//│ param0 = a.x; -//│ if (param0 instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x1 = 10; -//│ tmp20 = () => { -//│ let t, param0, a, tmp25; -//│ param0 = _deforest_AA_x1; -//│ a = param0; -//│ tmp25 = a; -//│ t = tmp25; -//│ return t + 5 -//│ }; -//│ tmp21 = AA1(tmp20); -//│ tmp22 = AA1(tmp21); -//│ p = tmp22; -//│ tmp23 = test1(p); -//│ tmp24 = f1(p); -//│ block$res12 = tmp23 + tmp24; -//│ undefined -//│ = 15 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 15 -//│ p = AA(AA(AA(10))) -//│ FAILURE: Unexpected lack of error to fix - +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +:fixme :sjs fun f(x, y, z, k) = let tmp = if z then BB(y + 1) else BB(y - 1) @@ -525,113 +401,54 @@ fun f(x, y, z, k) = AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ JS (unsanitized): -//│ let f2, tmp30, tmp31, tmp32, tmp33; +//│ let f2, tmp17, tmp18, tmp19, tmp20; //│ f2 = function f(x, y, z, k) { -//│ let tmp34, m, scrut, param0, yf, scrut1, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41; +//│ let tmp21, m, scrut, param0, yf, scrut1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; //│ if (z === true) { -//│ tmp35 = y + 1; -//│ tmp36 = BB1(tmp35); +//│ tmp22 = y + 1; +//│ tmp23 = BB1(tmp22); //│ } else { -//│ tmp37 = y - 1; -//│ tmp36 = BB1(tmp37); +//│ tmp24 = y - 1; +//│ tmp23 = BB1(tmp24); //│ } -//│ tmp34 = tmp36; -//│ if (tmp34 instanceof BB1.class) { +//│ tmp21 = tmp23; +//│ if (tmp21 instanceof BB1.class) { //│ if (x instanceof AA1.class) { //│ scrut = AA1(2); //│ if (scrut instanceof AA1.class) { //│ param0 = scrut.x; //│ yf = param0; -//│ tmp38 = yf; +//│ tmp25 = yf; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp39 = tmp38; +//│ tmp26 = tmp25; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp39; +//│ m = tmp26; //│ scrut1 = AA1(3); //│ if (scrut1 instanceof AA1.class) { -//│ tmp40 = m + k; +//│ tmp27 = m + k; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp41 = tmp40; +//│ tmp28 = tmp27; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return tmp34.x + tmp41 +//│ return tmp21.x + tmp28 //│ }; -//│ tmp30 = AA1(3); -//│ tmp31 = f2(tmp30, 3, true, 10); -//│ tmp32 = AA1(5); -//│ tmp33 = f2(tmp32, 4, false, 20); -//│ tmp31 + tmp33 +//│ tmp17 = AA1(3); +//│ tmp18 = f2(tmp17, 3, true, 10); +//│ tmp19 = AA1(5); +//│ tmp20 = f2(tmp19, 4, false, 20); +//│ tmp18 + tmp20 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_rest1, match_x_branch_AA1, _deforest_AA_x_unused1; -//│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { -//│ let scrut, _deforest_AA_x2; -//│ _deforest_AA_x2 = 2; -//│ scrut = (k1, tmp35) => { -//│ let param0, yf, tmp36, tmp37; -//│ param0 = _deforest_AA_x2; -//│ yf = param0; -//│ tmp36 = yf; -//│ tmp37 = tmp36; -//│ return match_x_rest1(k1, tmp35, tmp37) -//│ }; -//│ return runtime.safeCall(scrut(k, tmp34)) -//│ }; -//│ match_x_rest1 = function match_x_rest(k, tmp34, tmp35) { -//│ let m, scrut, _deforest_AA_x_unused2; -//│ m = tmp35; -//│ _deforest_AA_x_unused2 = 3; -//│ scrut = (k1, tmp36, m1) => { -//│ let tmp37, tmp38; -//│ tmp37 = m1 + k1; -//│ tmp38 = tmp37; -//│ return tmp36.x + tmp38 -//│ }; -//│ return runtime.safeCall(scrut(k, tmp34, m)); -//│ return tmp34.x + tmp_not_in_scope; -//│ }; -//│ f2 = function f(x, y, z, k) { -//│ let tmp34, tmp35, tmp36, tmp37; -//│ if (z === true) { -//│ tmp35 = y + 1; -//│ tmp36 = BB1(tmp35); -//│ } else { -//│ tmp37 = y - 1; -//│ tmp36 = BB1(tmp37); -//│ } -//│ tmp34 = tmp36; -//│ if (tmp34 instanceof BB1.class) { -//│ return runtime.safeCall(x(k, tmp34)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return tmp34.x + tmp_not_in_scope -//│ }; -//│ _deforest_AA_x_unused = 3; -//│ tmp30 = (k, tmp34) => { -//│ return match_x_branch_AA1(k, tmp34) -//│ }; -//│ tmp31 = f2(tmp30, 3, true, 10); -//│ _deforest_AA_x_unused1 = 5; -//│ tmp32 = (k, tmp34) => { -//│ return match_x_branch_AA1(k, tmp34) -//│ }; -//│ tmp33 = f2(tmp32, 4, false, 20); -//│ block$res14 = tmp31 + tmp33; -//│ undefined -//│ = 41 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 41 - +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +:fixme :sjs fun test(n, p, q) = n + @@ -643,95 +460,108 @@ fun test(n, p, q) = AA(y) then k + y test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ JS (unsanitized): -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; +//│ let test2, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; //│ test2 = function test(n, p1, q) { -//│ let scrut, k, param0, x, param01, y, tmp44, tmp45, tmp46; +//│ let scrut, k, param0, x, param01, y, tmp27, tmp28, tmp29; //│ scrut = AA1(0); //│ if (scrut instanceof AA1.class) { //│ if (p1 instanceof AA1.class) { //│ param0 = p1.x; //│ x = param0; -//│ tmp44 = x; +//│ tmp27 = x; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ k = tmp44; +//│ k = tmp27; //│ if (q instanceof AA1.class) { //│ param01 = q.x; //│ y = param01; -//│ tmp45 = k + y; +//│ tmp28 = k + y; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp46 = tmp45; +//│ tmp29 = tmp28; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return n + tmp46 -//│ }; -//│ tmp38 = AA1(2); -//│ tmp39 = AA1(3); -//│ tmp40 = test2(3, tmp38, tmp39); -//│ tmp41 = AA1(2); -//│ tmp42 = AA1(3); -//│ tmp43 = test2(3, tmp41, tmp42); -//│ tmp40 + tmp43 +//│ return n + tmp29 +//│ }; +//│ tmp21 = AA1(2); +//│ tmp22 = AA1(3); +//│ tmp23 = test2(3, tmp21, tmp22); +//│ tmp24 = AA1(2); +//│ tmp25 = AA1(3); +//│ tmp26 = test2(3, tmp24, tmp25); +//│ tmp23 + tmp26 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_rest, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_rest, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; -//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x2) { -//│ let param0, x, tmp44; -//│ param0 = _deforest_AA_x2; -//│ x = param0; -//│ tmp44 = x; -//│ return match_p_rest(n, q, tmp44) -//│ }; -//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x2) { -//│ let param0, y, tmp44; -//│ param0 = _deforest_AA_x2; -//│ y = param0; -//│ tmp44 = k + y; -//│ return match_q_rest(n, tmp44) -//│ }; -//│ match_p_rest = function match_p_rest(n, q, tmp44) { -//│ let k; -//│ k = tmp44; -//│ return runtime.safeCall(q(n, k)); -//│ return n + tmp_not_in_scope; -//│ }; -//│ match_q_rest = function match_q_rest(n, tmp44) { -//│ let tmp45; -//│ tmp45 = tmp44; -//│ return n + tmp45; -//│ }; -//│ test2 = function test(n, p1, q) { -//│ let scrut, _deforest_AA_x_unused2; -//│ _deforest_AA_x_unused2 = 0; -//│ scrut = (n1, p2, q1) => { -//│ return runtime.safeCall(p2(n1, q1)) -//│ }; -//│ return runtime.safeCall(scrut(n, p1, q)) -//│ }; -//│ _deforest_AA_x_tmp4 = 2; -//│ tmp38 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp4) -//│ }; -//│ _deforest_AA_x_tmp5 = 3; -//│ tmp39 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp5) -//│ }; -//│ tmp40 = test2(3, tmp38, tmp39); -//│ _deforest_AA_x_tmp6 = 2; -//│ tmp41 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp6) +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) + + + + +:fixme +:sjs +fun test(n, p, q, a) = + let o + if a is + A then + let k + if p is + AA(x) then + k = x + if q is + AA(y) then + o = k + y + o + n +let a = A +fun c(a) = if a is A then 0 +test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) +//│ JS (unsanitized): +//│ let c, test3, a, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ test3 = function test(n, p1, q, a1) { +//│ let o, k, param0, x, param01, y, tmp35, tmp36, tmp37, tmp38; +//│ o = undefined; +//│ if (a1 instanceof A1.class) { +//│ k = undefined; +//│ if (p1 instanceof AA1.class) { +//│ param0 = p1.x; +//│ x = param0; +//│ k = x; +//│ tmp35 = runtime.Unit; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ if (q instanceof AA1.class) { +//│ param01 = q.x; +//│ y = param01; +//│ tmp36 = k + y; +//│ o = tmp36; +//│ tmp37 = runtime.Unit; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp38 = tmp37; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return o + n //│ }; -//│ _deforest_AA_x_tmp7 = 3; -//│ tmp42 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp7) +//│ c = function c(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ }; -//│ tmp43 = test2(3, tmp41, tmp42); -//│ block$res16 = tmp40 + tmp43; -//│ undefined -//│ = 16 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 16 +//│ a = A1; +//│ tmp27 = AA1(2); +//│ tmp28 = AA1(3); +//│ tmp29 = test3(1, tmp27, tmp28, a); +//│ tmp30 = AA1(2); +//│ tmp31 = AA1(3); +//│ tmp32 = test3(2, tmp30, tmp31, a); +//│ tmp33 = tmp29 + tmp32; +//│ tmp34 = c(a); +//│ tmp33 + tmp34 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: o (class hkmc2.semantics.VarSymbol) From a855c0fb4450fbe265599311b40dde7480f0c201 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 29 Mar 2025 12:34:28 +0800 Subject: [PATCH 147/303] tests output program despite of undefined var --- .../src/test/mlscript/deforest/todos.mls | 448 +++++++++++++++--- 1 file changed, 382 insertions(+), 66 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 3c7a341c9a..cb29e453a9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -320,13 +320,87 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ tmp7 + tmp11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_rest, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x1) { +//│ let param0, a; +//│ param0 = _deforest_AA_x1; +//│ a = param0; +//│ return runtime.safeCall(y(z, i)) +//│ }; +//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x1) { +//│ let param0, a1, tmp12; +//│ param0 = _deforest_AA_x1; +//│ a1 = param0; +//│ tmp12 = a1 + i; +//│ return match_y_rest(z, i, tmp12) +//│ }; +//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x) { +//│ let param0, a2, tmp12; +//│ param0 = _deforest_BB_x; +//│ a2 = param0; +//│ tmp12 = a2 - i; +//│ return match_z_rest(i, m, tmp12, match_x_rest) +//│ }; +//│ match_x_rest = function match_x_rest(i, tmp12) { +//│ let k; +//│ k = tmp12; +//│ return k + i; +//│ }; +//│ match_y_rest = function match_y_rest(z, i, tmp12) { +//│ let m; +//│ m = tmp12; +//│ return runtime.safeCall(z(i, m)); +//│ return match_x_rest(i, tmp_not_in_scope) +//│ }; +//│ match_z_rest = function match_z_rest(i, m, tmp12) { +//│ let n, tmp13; +//│ n = tmp12; +//│ tmp13 = m + n; +//│ return match_x_rest(i, tmp13) +//│ }; +//│ test = function test(x, y, z, i) { +//│ return runtime.safeCall(x(y, z, i)) +//│ }; +//│ _deforest_AA_x_tmp = 1; +//│ tmp4 = (y, z, i) => { +//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp) +//│ }; +//│ _deforest_AA_x_tmp1 = 2; +//│ tmp5 = (z, i) => { +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp1) +//│ }; +//│ _deforest_BB_x_tmp = 3; +//│ tmp6 = (i, m) => { +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) +//│ }; +//│ tmp7 = test(tmp4, tmp5, tmp6, 4); +//│ _deforest_AA_x_tmp2 = 1; +//│ tmp8 = (y, z, i) => { +//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp2) +//│ }; +//│ _deforest_AA_x_tmp3 = 2; +//│ tmp9 = (z, i) => { +//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp3) +//│ }; +//│ _deforest_BB_x_tmp1 = 3; +//│ tmp10 = (i, m) => { +//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) +//│ }; +//│ tmp11 = test(tmp8, tmp9, tmp10, 4); +//│ block$res10 = tmp7 + tmp11; +//│ undefined +//│ = 18 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 18 +//│ FAILURE: Unexpected lack of error to fix // FIXME: // - need to include the computation in the `rest` from // matches multiple levels up // - how to deal with the original `rest` from matches multiple levels up? +// - (after rewriting it becomes dead code) :fixme :sjs fun test(x) = @@ -338,9 +412,9 @@ fun f(a) = if a is let p = AA(AA(AA(10))) test(p) + f(p) //│ JS (unsanitized): -//│ let test1, f1, p, tmp12, tmp13, tmp14, tmp15, tmp16; +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24; //│ test1 = function test(x) { -//│ let t, param0, param01, param02, a, tmp17; +//│ let t, param0, param01, param02, a, tmp25; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -348,7 +422,7 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp17 = a; +//│ tmp25 = a; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -358,7 +432,7 @@ test(p) + f(p) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp17; +//│ t = tmp25; //│ return t + 5 //│ }; //│ f1 = function f(a) { @@ -374,15 +448,66 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp12 = AA1(10); -//│ tmp13 = AA1(tmp12); -//│ tmp14 = AA1(tmp13); -//│ p = tmp14; -//│ tmp15 = test1(p); -//│ tmp16 = f1(p); -//│ tmp15 + tmp16 +//│ tmp20 = AA1(10); +//│ tmp21 = AA1(tmp20); +//│ tmp22 = AA1(tmp21); +//│ p = tmp22; +//│ tmp23 = test1(p); +//│ tmp24 = f1(p); +//│ tmp23 + tmp24 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x1; +//│ test1 = function test(x) { +//│ let t, param0, param01; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ return runtime.safeCall(param01()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp_not_in_scope; +//│ return t + 5 +//│ }; +//│ f1 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ _deforest_AA_x1 = 10; +//│ tmp20 = () => { +//│ let t, param0, a, tmp25; +//│ param0 = _deforest_AA_x1; +//│ a = param0; +//│ tmp25 = a; +//│ t = tmp25; +//│ return t + 5 +//│ }; +//│ tmp21 = AA1(tmp20); +//│ tmp22 = AA1(tmp21); +//│ p = tmp22; +//│ tmp23 = test1(p); +//│ tmp24 = f1(p); +//│ block$res12 = tmp23 + tmp24; +//│ undefined +//│ = 15 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 15 +//│ p = AA(AA(AA(10))) +//│ FAILURE: Unexpected lack of error to fix @@ -401,51 +526,111 @@ fun f(x, y, z, k) = AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ JS (unsanitized): -//│ let f2, tmp17, tmp18, tmp19, tmp20; +//│ let f2, tmp30, tmp31, tmp32, tmp33; //│ f2 = function f(x, y, z, k) { -//│ let tmp21, m, scrut, param0, yf, scrut1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; +//│ let tmp34, m, scrut, param0, yf, scrut1, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41; //│ if (z === true) { -//│ tmp22 = y + 1; -//│ tmp23 = BB1(tmp22); +//│ tmp35 = y + 1; +//│ tmp36 = BB1(tmp35); //│ } else { -//│ tmp24 = y - 1; -//│ tmp23 = BB1(tmp24); +//│ tmp37 = y - 1; +//│ tmp36 = BB1(tmp37); //│ } -//│ tmp21 = tmp23; -//│ if (tmp21 instanceof BB1.class) { +//│ tmp34 = tmp36; +//│ if (tmp34 instanceof BB1.class) { //│ if (x instanceof AA1.class) { //│ scrut = AA1(2); //│ if (scrut instanceof AA1.class) { //│ param0 = scrut.x; //│ yf = param0; -//│ tmp25 = yf; +//│ tmp38 = yf; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp26 = tmp25; +//│ tmp39 = tmp38; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp26; +//│ m = tmp39; //│ scrut1 = AA1(3); //│ if (scrut1 instanceof AA1.class) { -//│ tmp27 = m + k; +//│ tmp40 = m + k; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp28 = tmp27; +//│ tmp41 = tmp40; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return tmp21.x + tmp28 +//│ return tmp34.x + tmp41 //│ }; -//│ tmp17 = AA1(3); -//│ tmp18 = f2(tmp17, 3, true, 10); -//│ tmp19 = AA1(5); -//│ tmp20 = f2(tmp19, 4, false, 20); -//│ tmp18 + tmp20 +//│ tmp30 = AA1(3); +//│ tmp31 = f2(tmp30, 3, true, 10); +//│ tmp32 = AA1(5); +//│ tmp33 = f2(tmp32, 4, false, 20); +//│ tmp31 + tmp33 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_rest1, match_x_branch_AA1, _deforest_AA_x_unused1; +//│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { +//│ let scrut, _deforest_AA_x2; +//│ _deforest_AA_x2 = 2; +//│ scrut = (k1, tmp35) => { +//│ let param0, yf, tmp36, tmp37; +//│ param0 = _deforest_AA_x2; +//│ yf = param0; +//│ tmp36 = yf; +//│ tmp37 = tmp36; +//│ return match_x_rest1(k1, tmp35, tmp37) +//│ }; +//│ return runtime.safeCall(scrut(k, tmp34)) +//│ }; +//│ match_x_rest1 = function match_x_rest(k, tmp34, tmp35) { +//│ let m, scrut, _deforest_AA_x_unused2; +//│ m = tmp35; +//│ _deforest_AA_x_unused2 = 3; +//│ scrut = (k1, tmp36, m1) => { +//│ let tmp37, tmp38; +//│ tmp37 = m1 + k1; +//│ tmp38 = tmp37; +//│ return tmp36.x + tmp38 +//│ }; +//│ return runtime.safeCall(scrut(k, tmp34, m)); +//│ return tmp34.x + tmp_not_in_scope; +//│ }; +//│ f2 = function f(x, y, z, k) { +//│ let tmp34, tmp35, tmp36, tmp37; +//│ if (z === true) { +//│ tmp35 = y + 1; +//│ tmp36 = BB1(tmp35); +//│ } else { +//│ tmp37 = y - 1; +//│ tmp36 = BB1(tmp37); +//│ } +//│ tmp34 = tmp36; +//│ if (tmp34 instanceof BB1.class) { +//│ return runtime.safeCall(x(k, tmp34)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return tmp34.x + tmp_not_in_scope +//│ }; +//│ _deforest_AA_x_unused = 3; +//│ tmp30 = (k, tmp34) => { +//│ return match_x_branch_AA1(k, tmp34) +//│ }; +//│ tmp31 = f2(tmp30, 3, true, 10); +//│ _deforest_AA_x_unused1 = 5; +//│ tmp32 = (k, tmp34) => { +//│ return match_x_branch_AA1(k, tmp34) +//│ }; +//│ tmp33 = f2(tmp32, 4, false, 20); +//│ block$res14 = tmp31 + tmp33; +//│ undefined +//│ = 41 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 41 +//│ FAILURE: Unexpected lack of error to fix :fixme @@ -460,41 +645,99 @@ fun test(n, p, q) = AA(y) then k + y test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ JS (unsanitized): -//│ let test2, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; //│ test2 = function test(n, p1, q) { -//│ let scrut, k, param0, x, param01, y, tmp27, tmp28, tmp29; +//│ let scrut, k, param0, x, param01, y, tmp44, tmp45, tmp46; //│ scrut = AA1(0); //│ if (scrut instanceof AA1.class) { //│ if (p1 instanceof AA1.class) { //│ param0 = p1.x; //│ x = param0; -//│ tmp27 = x; +//│ tmp44 = x; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ k = tmp27; +//│ k = tmp44; //│ if (q instanceof AA1.class) { //│ param01 = q.x; //│ y = param01; -//│ tmp28 = k + y; +//│ tmp45 = k + y; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp29 = tmp28; +//│ tmp46 = tmp45; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return n + tmp29 -//│ }; -//│ tmp21 = AA1(2); -//│ tmp22 = AA1(3); -//│ tmp23 = test2(3, tmp21, tmp22); -//│ tmp24 = AA1(2); -//│ tmp25 = AA1(3); -//│ tmp26 = test2(3, tmp24, tmp25); -//│ tmp23 + tmp26 +//│ return n + tmp46 +//│ }; +//│ tmp38 = AA1(2); +//│ tmp39 = AA1(3); +//│ tmp40 = test2(3, tmp38, tmp39); +//│ tmp41 = AA1(2); +//│ tmp42 = AA1(3); +//│ tmp43 = test2(3, tmp41, tmp42); +//│ tmp40 + tmp43 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: $tmp (class hkmc2.semantics.TempSymbol) +//│ ==== JS (deforested): ==== +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_rest, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_rest, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; +//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x2) { +//│ let param0, x, tmp44; +//│ param0 = _deforest_AA_x2; +//│ x = param0; +//│ tmp44 = x; +//│ return match_p_rest(n, q, tmp44) +//│ }; +//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x2) { +//│ let param0, y, tmp44; +//│ param0 = _deforest_AA_x2; +//│ y = param0; +//│ tmp44 = k + y; +//│ return match_q_rest(n, tmp44) +//│ }; +//│ match_p_rest = function match_p_rest(n, q, tmp44) { +//│ let k; +//│ k = tmp44; +//│ return runtime.safeCall(q(n, k)); +//│ return n + tmp_not_in_scope; +//│ }; +//│ match_q_rest = function match_q_rest(n, tmp44) { +//│ let tmp45; +//│ tmp45 = tmp44; +//│ return n + tmp45; +//│ }; +//│ test2 = function test(n, p1, q) { +//│ let scrut, _deforest_AA_x_unused2; +//│ _deforest_AA_x_unused2 = 0; +//│ scrut = (n1, p2, q1) => { +//│ return runtime.safeCall(p2(n1, q1)) +//│ }; +//│ return runtime.safeCall(scrut(n, p1, q)) +//│ }; +//│ _deforest_AA_x_tmp4 = 2; +//│ tmp38 = (n, q) => { +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp4) +//│ }; +//│ _deforest_AA_x_tmp5 = 3; +//│ tmp39 = (n, k) => { +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp5) +//│ }; +//│ tmp40 = test2(3, tmp38, tmp39); +//│ _deforest_AA_x_tmp6 = 2; +//│ tmp41 = (n, q) => { +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp6) +//│ }; +//│ _deforest_AA_x_tmp7 = 3; +//│ tmp42 = (n, k) => { +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp7) +//│ }; +//│ tmp43 = test2(3, tmp41, tmp42); +//│ block$res16 = tmp40 + tmp43; +//│ undefined +//│ = 16 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 16 +//│ FAILURE: Unexpected lack of error to fix @@ -517,9 +760,9 @@ let a = A fun c(a) = if a is A then 0 test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ JS (unsanitized): -//│ let c, test3, a, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34; +//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57; //│ test3 = function test(n, p1, q, a1) { -//│ let o, k, param0, x, param01, y, tmp35, tmp36, tmp37, tmp38; +//│ let o, k, param0, x, param01, y, tmp58, tmp59, tmp60, tmp61; //│ o = undefined; //│ if (a1 instanceof A1.class) { //│ k = undefined; @@ -527,20 +770,20 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ param0 = p1.x; //│ x = param0; //│ k = x; -//│ tmp35 = runtime.Unit; +//│ tmp58 = runtime.Unit; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ if (q instanceof AA1.class) { //│ param01 = q.x; //│ y = param01; -//│ tmp36 = k + y; -//│ o = tmp36; -//│ tmp37 = runtime.Unit; +//│ tmp59 = k + y; +//│ o = tmp59; +//│ tmp60 = runtime.Unit; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp38 = tmp37; +//│ tmp61 = tmp60; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -554,14 +797,87 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ } //│ }; //│ a = A1; -//│ tmp27 = AA1(2); -//│ tmp28 = AA1(3); -//│ tmp29 = test3(1, tmp27, tmp28, a); -//│ tmp30 = AA1(2); -//│ tmp31 = AA1(3); -//│ tmp32 = test3(2, tmp30, tmp31, a); -//│ tmp33 = tmp29 + tmp32; -//│ tmp34 = c(a); -//│ tmp33 + tmp34 +//│ tmp50 = AA1(2); +//│ tmp51 = AA1(3); +//│ tmp52 = test3(1, tmp50, tmp51, a); +//│ tmp53 = AA1(2); +//│ tmp54 = AA1(3); +//│ tmp55 = test3(2, tmp53, tmp54, a); +//│ tmp56 = tmp52 + tmp55; +//│ tmp57 = c(a); +//│ tmp56 + tmp57 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: o (class hkmc2.semantics.VarSymbol) +//│ ==== JS (deforested): ==== +//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, _deforest_AA_x_tmp8, match_p_rest1, match_p_branch_AA1, _deforest_AA_x_tmp9, match_q_rest1, match_q_branch_AA1, _deforest_AA_x_tmp10, _deforest_AA_x_tmp11; +//│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x2) { +//│ let k, param0, x, tmp58; +//│ param0 = _deforest_AA_x2; +//│ x = param0; +//│ k = x; +//│ tmp58 = runtime.Unit; +//│ return match_p_rest1(n, q, k) +//│ }; +//│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x2) { +//│ let o, param0, y, tmp58, tmp59; +//│ param0 = _deforest_AA_x2; +//│ y = param0; +//│ tmp58 = k + y; +//│ o = tmp58; +//│ tmp59 = runtime.Unit; +//│ return match_q_rest1(n, o, tmp59) +//│ }; +//│ match_p_rest1 = function match_p_rest(n, q, k) { +//│ return runtime.safeCall(q(n, k)); +//│ return o_not_in_scope + n; +//│ }; +//│ match_q_rest1 = function match_q_rest(n, o, tmp58) { +//│ let tmp59; +//│ tmp59 = tmp58; +//│ return o + n; +//│ }; +//│ test3 = function test(n, p1, q, a1) { +//│ let o, k; +//│ o = undefined; +//│ if (a1 instanceof A1.class) { +//│ k = undefined; +//│ return runtime.safeCall(p1(n, q)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return o + n +//│ }; +//│ c = function c(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ a = A1; +//│ _deforest_AA_x_tmp8 = 2; +//│ tmp50 = (n, q) => { +//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp8) +//│ }; +//│ _deforest_AA_x_tmp9 = 3; +//│ tmp51 = (n, k) => { +//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp9) +//│ }; +//│ tmp52 = test3(1, tmp50, tmp51, a); +//│ _deforest_AA_x_tmp10 = 2; +//│ tmp53 = (n, q) => { +//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp10) +//│ }; +//│ _deforest_AA_x_tmp11 = 3; +//│ tmp54 = (n, k) => { +//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp11) +//│ }; +//│ tmp55 = test3(2, tmp53, tmp54, a); +//│ tmp56 = tmp52 + tmp55; +//│ tmp57 = c(a); +//│ block$res18 = tmp56 + tmp57; +//│ undefined +//│ = 13 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 13 +//│ a = A +//│ FAILURE: Unexpected lack of error to fix From a666b60294df024c328831caa0ecea6832562f75 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:01:55 +0800 Subject: [PATCH 148/303] avoid creating needless `match_rest` functions --- .../scala/hkmc2/codegen/Deforestation.scala | 52 +++++------ .../test/mlscript/deforest/nestedMatch.mls | 62 ++++--------- .../src/test/mlscript/deforest/simple.mls | 10 +- .../src/test/mlscript/deforest/todos.mls | 93 ++++++------------- 4 files changed, 75 insertions(+), 142 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 970673ecb4..7d0a059a69 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -37,6 +37,23 @@ object StratVarState: type CtorExpr = ResultId +extension (i: ResultId) + def getResult = ResultUid(i) + def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A) = + i.getResult match + case Call(fun, args) => fun match + case s: Select if s.symbol.flatMap(_.asCls).isDefined => + Some(k(i, s, s.symbol.get.asCls.get, args)) + case v: Value.Ref if v.l.asCls.isDefined => + Some(k(i, v, v.l.asCls.get, args)) + case _ => None + case s: Select if s.symbol.flatMap(_.asObj).isDefined => + Some(k(i, s, s.symbol.get.asObj.get, Nil)) + case v: Value.Ref if v.l.asObj.isDefined => + Some(k(i, v, v.l.asObj.get, Nil)) + case _ => None + def getClsSymOfUid = i.handleCtorIds((_, _, s, _) => s).get + case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) @@ -191,7 +208,6 @@ extension (b: Block) var flag = false override def applyBlock(b: Block): Unit = b match case Return(_, imp) => flag = !imp - case Define(defn, rest) => applyBlock(rest) case _ => super.applyBlock(b) HasExplicitRetTraverser.applyBlock(b) @@ -522,25 +538,7 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== - - def handleCtors[A](c: ResultId, k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A): Opt[A] = - ResultUid(c) match - case Call(fun, args) => fun match - case s: Select if s.symbol.flatMap(_.asCls).isDefined => - Some(k(c, s, s.symbol.get.asCls.get, args)) - case v: Value.Ref if v.l.asCls.isDefined => - Some(k(c, v, v.l.asCls.get, args)) - case _ => None - case s: Select if s.symbol.flatMap(_.asObj).isDefined => - Some(k(c, s, s.symbol.get.asObj.get, Nil)) - case v: Value.Ref if v.l.asObj.isDefined => - Some(k(c, v, v.l.asObj.get, Nil)) - case _ => None - - def getClsSymOfUid(c: CtorExpr): ClsOrModSymbol = - handleCtors(c, (_, _, s, _) => s).get - lazy val resolveClashes = type CtorToDtor = Map[CtorExpr, CtorDest] type DtorToCtor = Map[DtorExpr, Set[CtorExpr]] @@ -586,12 +584,10 @@ class Deforest(using TL, Raise, Elaborator.State): object GetCtorsTraverser extends BlockTraverser: val ctors = mutable.Set.empty[ResultId] override def applyResult(r: Result): Unit = - handleCtors( - r.uid, - (id, f, clsOrMod, args) => - ctors += id - args.foreach { case Arg(_, value) => applyResult(value) } - ) match + r.uid.handleCtorIds{ (id, f, clsOrMod, args) => + ctors += id + args.foreach { case Arg(_, value) => applyResult(value) } + } match case Some(_) => () case None => r match case Call(_, args) => @@ -806,8 +802,8 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case t => t if d.resolveClashes._2(DtorExpr.Match(scrut)).count{c => - if !isDflt then d.getClsSymOfUid(c) === cls - else m.arms.find{ case (Case.Cls(c1, _), _) => c1 === d.getClsSymOfUid(c) }.isEmpty + if !isDflt then c.getClsSymOfUid === cls + else m.arms.find{ case (Case.Cls(c1, _), _) => c1 === c.getClsSymOfUid }.isEmpty } > 1 then // make a function, and register, and return a lambda calling that function with correct arguments // arguments for lambda: free vars @@ -896,7 +892,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) // no need to build a new function for empty rest, or if the rest is only going to be used once - if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).size == 1) then + if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then val res = N -> restRewritten store += s -> res res diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index edab118a15..1d23943ac2 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -76,20 +76,16 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ tmp7 + tmp10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, _deforest_AA_x_tmp, match_a_rest, match_a_branch_AA, _deforest_AA_x_tmp1; +//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, _deforest_AA_x_tmp, match_a_branch_AA, _deforest_AA_x_tmp1; //│ match_a_branch_AA = function match_a_branch_AA(y, _deforest_AA_x) { -//│ let param0, x, tmp11; +//│ let t, m, param0, x, tmp11, tmp12; //│ param0 = _deforest_AA_x; //│ x = param0; //│ tmp11 = x; -//│ return match_a_rest(y, tmp11) -//│ }; -//│ match_a_rest = function match_a_rest(y, tmp11) { -//│ let t, m, tmp12; //│ m = tmp11; //│ tmp12 = m + 9; //│ t = tmp12; -//│ return t + y; +//│ return t + y //│ }; //│ f = function f(x, y) { //│ let t, param0, b, param01, a1, tmp11; @@ -180,13 +176,16 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ tmp24 + tmp27 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, _deforest_AA_x_tmp2, match_x_rest, match_a_rest1, match_a_branch_AA1, _deforest_AA_x_tmp3, match_x_branch_AA, _deforest_AA_x_tmp4, _deforest_AA_x_tmp5; +//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, _deforest_AA_x_tmp2, match_a_branch_AA1, _deforest_AA_x_tmp3, match_x_branch_AA, _deforest_AA_x_tmp4, _deforest_AA_x_tmp5; //│ match_a_branch_AA1 = function match_a_branch_AA(y, _deforest_AA_x) { -//│ let param0, m, tmp28; +//│ let n, param0, m, tmp28, tmp29, tmp30; //│ param0 = _deforest_AA_x; //│ m = param0; //│ tmp28 = m; -//│ return match_a_rest1(y, tmp28, match_x_rest) +//│ tmp29 = tmp28; +//│ n = tmp29; +//│ tmp30 = n + 2; +//│ return tmp30 + y //│ }; //│ match_x_branch_AA = function match_x_branch_AA(y, _deforest_AA_x) { //│ let param0, a1; @@ -194,17 +193,6 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ a1 = param0; //│ return runtime.safeCall(a1(y)) //│ }; -//│ match_x_rest = function match_x_rest(y, tmp28) { -//│ let n, tmp29; -//│ n = tmp28; -//│ tmp29 = n + 2; -//│ return tmp29 + y; -//│ }; -//│ match_a_rest1 = function match_a_rest(y, tmp28) { -//│ let tmp29; -//│ tmp29 = tmp28; -//│ return match_x_rest(y, tmp29) -//│ }; //│ f1 = function f(x, y) { //│ return runtime.safeCall(x(y)) //│ }; @@ -331,24 +319,20 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ tmp40 + tmp43 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp6, match_x_rest1, match_param0_branch_AA, _deforest_AA_x_tmp7, match_x_branch_AA1, _deforest_AA_x_tmp8, _deforest_AA_x_tmp9; +//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp6, match_param0_branch_AA, _deforest_AA_x_tmp7, match_x_branch_AA1, _deforest_AA_x_tmp8, _deforest_AA_x_tmp9; //│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x2) { -//│ let param0, a1, tmp44; +//│ let t, param0, a1, tmp44; //│ param0 = _deforest_AA_x2; //│ a1 = param0; //│ tmp44 = a1; -//│ return match_x_rest1(tmp44) +//│ t = tmp44; +//│ return t + 3 //│ }; //│ match_x_branch_AA1 = function match_x_branch_AA(_deforest_AA_x2) { //│ let param0; //│ param0 = _deforest_AA_x2; //│ return runtime.safeCall(param0()) //│ }; -//│ match_x_rest1 = function match_x_rest(tmp44) { -//│ let t; -//│ t = tmp44; -//│ return t + 3; -//│ }; //│ f3 = function f(x) { //│ return runtime.safeCall(x()) //│ }; @@ -531,7 +515,7 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp61 + tmp65 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, _deforest_AA_x_tmp10, match_x_branch_AA2, _deforest_AA_x_tmp11, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp12, _deforest_AA_x_tmp13, _deforest_BB_x_tmp1; +//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, _deforest_AA_x_tmp10, match_x_branch_AA2, _deforest_AA_x_tmp11, match_y_branch_AA, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp12, _deforest_AA_x_tmp13, _deforest_BB_x_tmp1; //│ match_x_branch_AA2 = function match_x_branch_AA(y, z, i, _deforest_AA_x5) { //│ let param0, a1; //│ param0 = _deforest_AA_x5; @@ -539,28 +523,20 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ return runtime.safeCall(y(z, i)) //│ }; //│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x5) { -//│ let param0, a1, tmp66; +//│ let m, param0, a1, tmp66; //│ param0 = _deforest_AA_x5; //│ a1 = param0; //│ tmp66 = a1 + i; -//│ return match_y_rest(z, i, tmp66) +//│ m = tmp66; +//│ return runtime.safeCall(z(i, m)) //│ }; //│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x1) { -//│ let param0, a2, tmp66; +//│ let n, param0, a2, tmp66; //│ param0 = _deforest_BB_x1; //│ a2 = param0; //│ tmp66 = a2 - i; -//│ return match_z_rest(m, tmp66) -//│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp66) { -//│ let m; -//│ m = tmp66; -//│ return runtime.safeCall(z(i, m)); -//│ }; -//│ match_z_rest = function match_z_rest(m, tmp66) { -//│ let n; //│ n = tmp66; -//│ return m + n; +//│ return m + n //│ }; //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 64ee8d8b51..d09e72aff1 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1797,18 +1797,14 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ tmp91 + tmp93 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, _deforest_AA_aa_tmp, match_x_rest3, match_x_branch_AA, _deforest_AA_aa_tmp1; +//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, _deforest_AA_aa_tmp, match_x_branch_AA, _deforest_AA_aa_tmp1; //│ match_x_branch_AA = function match_x_branch_AA(y1, _deforest_AA_aa3) { -//│ let param0, a, tmp94; +//│ let t, param0, a, tmp94; //│ param0 = _deforest_AA_aa3; //│ a = param0; //│ tmp94 = a + y1; -//│ return match_x_rest3(y1, tmp94) -//│ }; -//│ match_x_rest3 = function match_x_rest(y1, tmp94) { -//│ let t; //│ t = tmp94; -//│ return t * y1; +//│ return t * y1 //│ }; //│ inner = function inner(x1, y1) { //│ return runtime.safeCall(x1(y1)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index cb29e453a9..371ee80e50 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -321,7 +321,7 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp7 + tmp11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_rest, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_rest, match_y_branch_AA, _deforest_BB_x_tmp, match_z_rest, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_branch_AA, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; //│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x1) { //│ let param0, a; //│ param0 = _deforest_AA_x1; @@ -329,35 +329,22 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ return runtime.safeCall(y(z, i)) //│ }; //│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x1) { -//│ let param0, a1, tmp12; +//│ let m, param0, a1, tmp12; //│ param0 = _deforest_AA_x1; //│ a1 = param0; //│ tmp12 = a1 + i; -//│ return match_y_rest(z, i, tmp12) +//│ m = tmp12; +//│ return runtime.safeCall(z(i, m)) //│ }; //│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x) { -//│ let param0, a2, tmp12; +//│ let k, n, param0, a2, tmp12, tmp13; //│ param0 = _deforest_BB_x; //│ a2 = param0; //│ tmp12 = a2 - i; -//│ return match_z_rest(i, m, tmp12, match_x_rest) -//│ }; -//│ match_x_rest = function match_x_rest(i, tmp12) { -//│ let k; -//│ k = tmp12; -//│ return k + i; -//│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp12) { -//│ let m; -//│ m = tmp12; -//│ return runtime.safeCall(z(i, m)); -//│ return match_x_rest(i, tmp_not_in_scope) -//│ }; -//│ match_z_rest = function match_z_rest(i, m, tmp12) { -//│ let n, tmp13; //│ n = tmp12; //│ tmp13 = m + n; -//│ return match_x_rest(i, tmp13) +//│ k = tmp13; +//│ return k + i //│ }; //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) @@ -571,33 +558,28 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ tmp31 + tmp33 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_rest1, match_x_branch_AA1, _deforest_AA_x_unused1; +//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_branch_AA1, _deforest_AA_x_unused1; //│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { //│ let scrut, _deforest_AA_x2; //│ _deforest_AA_x2 = 2; //│ scrut = (k1, tmp35) => { -//│ let param0, yf, tmp36, tmp37; +//│ let m, param0, yf, scrut1, tmp36, tmp37, _deforest_AA_x_unused2; //│ param0 = _deforest_AA_x2; //│ yf = param0; //│ tmp36 = yf; //│ tmp37 = tmp36; -//│ return match_x_rest1(k1, tmp35, tmp37) +//│ m = tmp37; +//│ _deforest_AA_x_unused2 = 3; +//│ scrut1 = (k2, tmp38, m1) => { +//│ let tmp39, tmp40; +//│ tmp39 = m1 + k2; +//│ tmp40 = tmp39; +//│ return tmp38.x + tmp40 +//│ }; +//│ return runtime.safeCall(scrut1(k1, tmp35, m)) //│ }; //│ return runtime.safeCall(scrut(k, tmp34)) //│ }; -//│ match_x_rest1 = function match_x_rest(k, tmp34, tmp35) { -//│ let m, scrut, _deforest_AA_x_unused2; -//│ m = tmp35; -//│ _deforest_AA_x_unused2 = 3; -//│ scrut = (k1, tmp36, m1) => { -//│ let tmp37, tmp38; -//│ tmp37 = m1 + k1; -//│ tmp38 = tmp37; -//│ return tmp36.x + tmp38 -//│ }; -//│ return runtime.safeCall(scrut(k, tmp34, m)); -//│ return tmp34.x + tmp_not_in_scope; -//│ }; //│ f2 = function f(x, y, z, k) { //│ let tmp34, tmp35, tmp36, tmp37; //│ if (z === true) { @@ -680,31 +662,22 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ tmp40 + tmp43 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_rest, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_rest, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; +//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; //│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x2) { -//│ let param0, x, tmp44; +//│ let k, param0, x, tmp44; //│ param0 = _deforest_AA_x2; //│ x = param0; //│ tmp44 = x; -//│ return match_p_rest(n, q, tmp44) +//│ k = tmp44; +//│ return runtime.safeCall(q(n, k)) //│ }; //│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x2) { -//│ let param0, y, tmp44; +//│ let param0, y, tmp44, tmp45; //│ param0 = _deforest_AA_x2; //│ y = param0; //│ tmp44 = k + y; -//│ return match_q_rest(n, tmp44) -//│ }; -//│ match_p_rest = function match_p_rest(n, q, tmp44) { -//│ let k; -//│ k = tmp44; -//│ return runtime.safeCall(q(n, k)); -//│ return n + tmp_not_in_scope; -//│ }; -//│ match_q_rest = function match_q_rest(n, tmp44) { -//│ let tmp45; //│ tmp45 = tmp44; -//│ return n + tmp45; +//│ return n + tmp45 //│ }; //│ test2 = function test(n, p1, q) { //│ let scrut, _deforest_AA_x_unused2; @@ -808,32 +781,24 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ tmp56 + tmp57 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, _deforest_AA_x_tmp8, match_p_rest1, match_p_branch_AA1, _deforest_AA_x_tmp9, match_q_rest1, match_q_branch_AA1, _deforest_AA_x_tmp10, _deforest_AA_x_tmp11; +//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, _deforest_AA_x_tmp8, match_p_branch_AA1, _deforest_AA_x_tmp9, match_q_branch_AA1, _deforest_AA_x_tmp10, _deforest_AA_x_tmp11; //│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x2) { //│ let k, param0, x, tmp58; //│ param0 = _deforest_AA_x2; //│ x = param0; //│ k = x; //│ tmp58 = runtime.Unit; -//│ return match_p_rest1(n, q, k) +//│ return runtime.safeCall(q(n, k)) //│ }; //│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x2) { -//│ let o, param0, y, tmp58, tmp59; +//│ let o, param0, y, tmp58, tmp59, tmp60; //│ param0 = _deforest_AA_x2; //│ y = param0; //│ tmp58 = k + y; //│ o = tmp58; //│ tmp59 = runtime.Unit; -//│ return match_q_rest1(n, o, tmp59) -//│ }; -//│ match_p_rest1 = function match_p_rest(n, q, k) { -//│ return runtime.safeCall(q(n, k)); -//│ return o_not_in_scope + n; -//│ }; -//│ match_q_rest1 = function match_q_rest(n, o, tmp58) { -//│ let tmp59; -//│ tmp59 = tmp58; -//│ return o + n; +//│ tmp60 = tmp59; +//│ return o + n //│ }; //│ test3 = function test(n, p1, q, a1) { //│ let o, k; From 8d8a83a791e1a88beb1240176fd3bede491f4fa5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 29 Mar 2025 14:47:05 +0800 Subject: [PATCH 149/303] use extension method for resultId --- .../main/scala/hkmc2/codegen/Deforestation.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7d0a059a69..6278214a2d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -466,7 +466,7 @@ class Deforest(using TL, Raise, Elaborator.State): object dtorSources: val dtorSources = mutable.Map.empty[DtorExpr, Set[ResultId]].withDefaultValue(Set.empty) - private def getDtorExprOfResultId(i: ResultId) = ResultUid(i) match + private def getDtorExprOfResultId(i: ResultId) = i.getResult match case s: Select => DtorExpr.Sel(i) case r: Value.Ref => DtorExpr.Match(i) case _ => ??? // unreachable @@ -568,8 +568,8 @@ class Deforest(using TL, Raise, Elaborator.State): ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels) => (dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { - val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) - sels.forall { s => ResultUid(s.expr) match + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult + sels.forall { s => s.expr.getResult match case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee case _ => false } }) @@ -645,10 +645,10 @@ class Deforest(using TL, Raise, Elaborator.State): None else if dtors.size == 1 then val currentCtorCls = getClsSymOfUid(ctor) - val scrutRef@Value.Ref(scrut) = ResultUid(dtors.head._1) + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult handledMatches.getOrElseUpdate(scrutRef.uid -> currentCtorCls, { - if sels.forall{ s => ResultUid(s.expr) match + if sels.forall{ s => s.expr.getResult match case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) case _ => false } then @@ -813,7 +813,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) val funBody = makeBody(bodyReplaceSel) - val funSym = BlockMemberSymbol(s"match_${ResultUid(scrut).asInstanceOf[Value.Ref].l.nme}_branch_${if isDflt then "dflt" else cls.nme}", Nil) + val funSym = BlockMemberSymbol(s"match_${scrut.getResult.asInstanceOf[Value.Ref].l.nme}_branch_${if isDflt then "dflt" else cls.nme}", Nil) val newDef = FunDefn( N, funSym, @@ -897,7 +897,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store += s -> res res else // build a new function and update the store - val scrutName = ResultUid(s).asInstanceOf[Value.Ref].l.nme + val scrutName = s.getResult.asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap val newFunDef = FunDefn( @@ -972,7 +972,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } case CtorFinalDest.Sel(s) => - val selFieldName = ResultUid(s) match { case Select(p, nme) => nme } + val selFieldName = s.getResult match { case Select(p, nme) => nme } val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) k(args(idx).value) f match From b61ea2e3ca0ecd95a30d698e44bf2a018e36ad4d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:57:41 +0800 Subject: [PATCH 150/303] update test: found code duplication; some problems are masked by changes to avoid creating needless matchRest functions --- .../test/mlscript/deforest/nestedMatch.mls | 343 +++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 405 +++++++----------- 2 files changed, 503 insertions(+), 245 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 1d23943ac2..fc305c6112 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1032,3 +1032,346 @@ f(aa, 3) + f(aa, 4) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 13 //│ aa = AA(3) + + + +:sjs +fun test(n, p, q) = + n + + if AA(0) is + AA then + let k = if p is + AA(x) then x + if q is + AA(y) then k + y +test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) +//│ JS (unsanitized): +//│ let test2, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125; +//│ test2 = function test(n, p1, q) { +//│ let scrut, k, param0, x, param01, y2, tmp126, tmp127, tmp128; +//│ scrut = AA1(0); +//│ if (scrut instanceof AA1.class) { +//│ if (p1 instanceof AA1.class) { +//│ param0 = p1.x; +//│ x = param0; +//│ tmp126 = x; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ k = tmp126; +//│ if (q instanceof AA1.class) { +//│ param01 = q.x; +//│ y2 = param01; +//│ tmp127 = k + y2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp128 = tmp127; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return n + tmp128 +//│ }; +//│ tmp120 = AA1(2); +//│ tmp121 = AA1(3); +//│ tmp122 = test2(3, tmp120, tmp121); +//│ tmp123 = AA1(2); +//│ tmp124 = AA1(3); +//│ tmp125 = test2(3, tmp123, tmp124); +//│ tmp122 + tmp125 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test2, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125, _deforest_AA_x_tmp14, match_p_branch_AA, _deforest_AA_x_tmp15, match_q_branch_AA, _deforest_AA_x_tmp16, _deforest_AA_x_tmp17; +//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x8) { +//│ let k, param0, x, tmp126; +//│ param0 = _deforest_AA_x8; +//│ x = param0; +//│ tmp126 = x; +//│ k = tmp126; +//│ return runtime.safeCall(q(n, k)) +//│ }; +//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x8) { +//│ let param0, y2, tmp126, tmp127; +//│ param0 = _deforest_AA_x8; +//│ y2 = param0; +//│ tmp126 = k + y2; +//│ tmp127 = tmp126; +//│ return n + tmp127 +//│ }; +//│ test2 = function test(n, p1, q) { +//│ let scrut, _deforest_AA_x_unused1; +//│ _deforest_AA_x_unused1 = 0; +//│ scrut = (n1, p2, q1) => { +//│ return runtime.safeCall(p2(n1, q1)) +//│ }; +//│ return runtime.safeCall(scrut(n, p1, q)) +//│ }; +//│ _deforest_AA_x_tmp14 = 2; +//│ tmp120 = (n, q) => { +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp14) +//│ }; +//│ _deforest_AA_x_tmp15 = 3; +//│ tmp121 = (n, k) => { +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp15) +//│ }; +//│ tmp122 = test2(3, tmp120, tmp121); +//│ _deforest_AA_x_tmp16 = 2; +//│ tmp123 = (n, q) => { +//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp16) +//│ }; +//│ _deforest_AA_x_tmp17 = 3; +//│ tmp124 = (n, k) => { +//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp17) +//│ }; +//│ tmp125 = test2(3, tmp123, tmp124); +//│ block$res28 = tmp122 + tmp125; +//│ undefined +//│ = 16 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 16 + + + +:sjs +fun test(n, p, q, a) = + let o + if a is + A then + let k + if p is + AA(x) then + k = x + if q is + AA(y) then + o = k + y + o + n +let a = A +fun c(a) = if a is A then 0 +test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) +//│ JS (unsanitized): +//│ let c3, test3, a1, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139; +//│ test3 = function test(n, p1, q, a2) { +//│ let o, k, param0, x, param01, y2, tmp140, tmp141, tmp142, tmp143; +//│ o = undefined; +//│ if (a2 instanceof A1.class) { +//│ k = undefined; +//│ if (p1 instanceof AA1.class) { +//│ param0 = p1.x; +//│ x = param0; +//│ k = x; +//│ tmp140 = runtime.Unit; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ if (q instanceof AA1.class) { +//│ param01 = q.x; +//│ y2 = param01; +//│ tmp141 = k + y2; +//│ o = tmp141; +//│ tmp142 = runtime.Unit; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp143 = tmp142; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return o + n +//│ }; +//│ c3 = function c(a2) { +//│ if (a2 instanceof A1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ a1 = A1; +//│ tmp132 = AA1(2); +//│ tmp133 = AA1(3); +//│ tmp134 = test3(1, tmp132, tmp133, a1); +//│ tmp135 = AA1(2); +//│ tmp136 = AA1(3); +//│ tmp137 = test3(2, tmp135, tmp136, a1); +//│ tmp138 = tmp134 + tmp137; +//│ tmp139 = c3(a1); +//│ tmp138 + tmp139 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c3, test3, a1, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139, _deforest_AA_x_tmp18, match_p_branch_AA1, _deforest_AA_x_tmp19, match_q_branch_AA1, _deforest_AA_x_tmp20, _deforest_AA_x_tmp21; +//│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x8) { +//│ let k, param0, x, tmp140; +//│ param0 = _deforest_AA_x8; +//│ x = param0; +//│ k = x; +//│ tmp140 = runtime.Unit; +//│ return runtime.safeCall(q(n, k)) +//│ }; +//│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x8) { +//│ let o, param0, y2, tmp140, tmp141, tmp142; +//│ param0 = _deforest_AA_x8; +//│ y2 = param0; +//│ tmp140 = k + y2; +//│ o = tmp140; +//│ tmp141 = runtime.Unit; +//│ tmp142 = tmp141; +//│ return o + n +//│ }; +//│ test3 = function test(n, p1, q, a2) { +//│ let o, k; +//│ o = undefined; +//│ if (a2 instanceof A1.class) { +//│ k = undefined; +//│ return runtime.safeCall(p1(n, q)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return o + n +//│ }; +//│ c3 = function c(a2) { +//│ if (a2 instanceof A1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ a1 = A1; +//│ _deforest_AA_x_tmp18 = 2; +//│ tmp132 = (n, q) => { +//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp18) +//│ }; +//│ _deforest_AA_x_tmp19 = 3; +//│ tmp133 = (n, k) => { +//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp19) +//│ }; +//│ tmp134 = test3(1, tmp132, tmp133, a1); +//│ _deforest_AA_x_tmp20 = 2; +//│ tmp135 = (n, q) => { +//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp20) +//│ }; +//│ _deforest_AA_x_tmp21 = 3; +//│ tmp136 = (n, k) => { +//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp21) +//│ }; +//│ tmp137 = test3(2, tmp135, tmp136, a1); +//│ tmp138 = tmp134 + tmp137; +//│ tmp139 = c3(a1); +//│ block$res30 = tmp138 + tmp139; +//│ undefined +//│ = 13 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 13 +//│ a = A + + + +:sjs +fun test(x, y, z, i) = + let k = if x is + AA(a) then + let m = if y is + AA(a1) then a1 + i + let n = if z is + BB(a2) then a2 - i + m + n + k + i +test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) +//│ JS (unsanitized): +//│ let test4, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155; +//│ test4 = function test(x, y2, z, i) { +//│ let k, param0, a2, m, param01, a11, n, param02, a21, tmp156, tmp157, tmp158; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a2 = param0; +//│ if (y2 instanceof AA1.class) { +//│ param01 = y2.x; +//│ a11 = param01; +//│ tmp156 = a11 + i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp156; +//│ if (z instanceof BB1.class) { +//│ param02 = z.x; +//│ a21 = param02; +//│ tmp157 = a21 - i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp157; +//│ tmp158 = m + n; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ k = tmp158; +//│ return k + i +//│ }; +//│ tmp148 = AA1(1); +//│ tmp149 = AA1(2); +//│ tmp150 = BB1(3); +//│ tmp151 = test4(tmp148, tmp149, tmp150, 4); +//│ tmp152 = AA1(1); +//│ tmp153 = AA1(2); +//│ tmp154 = BB1(3); +//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); +//│ tmp151 + tmp155 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test4, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155, _deforest_AA_x_tmp22, match_x_branch_AA3, _deforest_AA_x_tmp23, match_y_branch_AA1, _deforest_BB_x_tmp2, match_z_branch_BB1, _deforest_AA_x_tmp24, _deforest_AA_x_tmp25, _deforest_BB_x_tmp3; +//│ match_x_branch_AA3 = function match_x_branch_AA(y2, z, i, _deforest_AA_x8) { +//│ let param0, a2; +//│ param0 = _deforest_AA_x8; +//│ a2 = param0; +//│ return runtime.safeCall(y2(z, i)) +//│ }; +//│ match_y_branch_AA1 = function match_y_branch_AA(z, i, _deforest_AA_x8) { +//│ let m, param0, a11, tmp156; +//│ param0 = _deforest_AA_x8; +//│ a11 = param0; +//│ tmp156 = a11 + i; +//│ m = tmp156; +//│ return runtime.safeCall(z(i, m)) +//│ }; +//│ match_z_branch_BB1 = function match_z_branch_BB(i, m, _deforest_BB_x2) { +//│ let k, n, param0, a2, tmp156, tmp157; +//│ param0 = _deforest_BB_x2; +//│ a2 = param0; +//│ tmp156 = a2 - i; +//│ n = tmp156; +//│ tmp157 = m + n; +//│ k = tmp157; +//│ return k + i +//│ }; +//│ test4 = function test(x, y2, z, i) { +//│ return runtime.safeCall(x(y2, z, i)) +//│ }; +//│ _deforest_AA_x_tmp22 = 1; +//│ tmp148 = (y2, z, i) => { +//│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp22) +//│ }; +//│ _deforest_AA_x_tmp23 = 2; +//│ tmp149 = (z, i) => { +//│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp23) +//│ }; +//│ _deforest_BB_x_tmp2 = 3; +//│ tmp150 = (i, m) => { +//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp2) +//│ }; +//│ tmp151 = test4(tmp148, tmp149, tmp150, 4); +//│ _deforest_AA_x_tmp24 = 1; +//│ tmp152 = (y2, z, i) => { +//│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp24) +//│ }; +//│ _deforest_AA_x_tmp25 = 2; +//│ tmp153 = (z, i) => { +//│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp25) +//│ }; +//│ _deforest_BB_x_tmp3 = 3; +//│ tmp154 = (i, m) => { +//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp3) +//│ }; +//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); +//│ block$res32 = tmp151 + tmp155; +//│ undefined +//│ = 18 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 18 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 371ee80e50..8bace463c4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -275,29 +275,34 @@ fun test(x, y, z, i) = AA(a) then let m = if y is AA(a1) then a1 + i + BB(b2) then b2 - i let n = if z is BB(a2) then a2 - i m + n k + i -test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) +test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ JS (unsanitized): //│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; //│ test = function test(x, y, z, i) { -//│ let k, param0, a, m, param01, a1, n, param02, a2, tmp12, tmp13, tmp14; +//│ let k, param0, a, m, param01, b2, param02, a1, n, param03, a2, tmp12, tmp13, tmp14; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ a = param0; //│ if (y instanceof AA1.class) { -//│ param01 = y.x; -//│ a1 = param01; +//│ param02 = y.x; +//│ a1 = param02; //│ tmp12 = a1 + i; +//│ } else if (y instanceof BB1.class) { +//│ param01 = y.x; +//│ b2 = param01; +//│ tmp12 = b2 - i; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ m = tmp12; //│ if (z instanceof BB1.class) { -//│ param02 = z.x; -//│ a2 = param02; +//│ param03 = z.x; +//│ a2 = param03; //│ tmp13 = a2 - i; //│ } else { //│ throw new globalThis.Error("match error"); @@ -315,30 +320,22 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp6 = BB1(3); //│ tmp7 = test(tmp4, tmp5, tmp6, 4); //│ tmp8 = AA1(1); -//│ tmp9 = AA1(2); +//│ tmp9 = BB1(2); //│ tmp10 = BB1(3); //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ tmp7 + tmp11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x_tmp, match_x_branch_AA, _deforest_AA_x_tmp1, match_y_branch_AA, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3, _deforest_BB_x_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x1) { +//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x1, _deforest_BB_x, _deforest_AA_x_tmp, match_x_branch_AA, match_y_rest, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp1, _deforest_BB_x_tmp1; +//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x2) { //│ let param0, a; -//│ param0 = _deforest_AA_x1; +//│ param0 = _deforest_AA_x2; //│ a = param0; //│ return runtime.safeCall(y(z, i)) //│ }; -//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x1) { -//│ let m, param0, a1, tmp12; -//│ param0 = _deforest_AA_x1; -//│ a1 = param0; -//│ tmp12 = a1 + i; -//│ m = tmp12; -//│ return runtime.safeCall(z(i, m)) -//│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x) { +//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x1) { //│ let k, n, param0, a2, tmp12, tmp13; -//│ param0 = _deforest_BB_x; +//│ param0 = _deforest_BB_x1; //│ a2 = param0; //│ tmp12 = a2 - i; //│ n = tmp12; @@ -346,6 +343,13 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ k = tmp13; //│ return k + i //│ }; +//│ match_y_rest = function match_y_rest(z, i, tmp12) { +//│ let k, m; +//│ m = tmp12; +//│ return runtime.safeCall(z(i, m)); +//│ k = tmp_not_in_scope; +//│ return k + i; +//│ }; //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) //│ }; @@ -353,22 +357,30 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp4 = (y, z, i) => { //│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp) //│ }; -//│ _deforest_AA_x_tmp1 = 2; +//│ _deforest_AA_x1 = 2; //│ tmp5 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp1) +//│ let param0, a1, tmp12; +//│ param0 = _deforest_AA_x1; +//│ a1 = param0; +//│ tmp12 = a1 + i; +//│ return match_y_rest(z, i, tmp12) //│ }; //│ _deforest_BB_x_tmp = 3; //│ tmp6 = (i, m) => { //│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) //│ }; //│ tmp7 = test(tmp4, tmp5, tmp6, 4); -//│ _deforest_AA_x_tmp2 = 1; +//│ _deforest_AA_x_tmp1 = 1; //│ tmp8 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp2) +//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp1) //│ }; -//│ _deforest_AA_x_tmp3 = 2; +//│ _deforest_BB_x = 2; //│ tmp9 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp3) +//│ let param0, b2, tmp12; +//│ param0 = _deforest_BB_x; +//│ b2 = param0; +//│ tmp12 = b2 - i; +//│ return match_y_rest(z, i, tmp12) //│ }; //│ _deforest_BB_x_tmp1 = 3; //│ tmp10 = (i, m) => { @@ -377,17 +389,15 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ tmp11 = test(tmp8, tmp9, tmp10, 4); //│ block$res10 = tmp7 + tmp11; //│ undefined -//│ = 18 +//│ = 10 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 18 +//│ = 10 //│ FAILURE: Unexpected lack of error to fix // FIXME: -// - need to include the computation in the `rest` from -// matches multiple levels up // - how to deal with the original `rest` from matches multiple levels up? -// - (after rewriting it becomes dead code) +// (after rewriting it becomes dead code and contains undefined variable) :fixme :sjs fun test(x) = @@ -444,7 +454,7 @@ test(p) + f(p) //│ tmp23 + tmp24 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x1; +//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x2; //│ test1 = function test(x) { //│ let t, param0, param01; //│ if (x instanceof AA1.class) { @@ -474,10 +484,10 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ _deforest_AA_x1 = 10; +//│ _deforest_AA_x2 = 10; //│ tmp20 = () => { //│ let t, param0, a, tmp25; -//│ param0 = _deforest_AA_x1; +//│ param0 = _deforest_AA_x2; //│ a = param0; //│ tmp25 = a; //│ t = tmp25; @@ -560,11 +570,11 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ ==== JS (deforested): ==== //│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_branch_AA1, _deforest_AA_x_unused1; //│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { -//│ let scrut, _deforest_AA_x2; -//│ _deforest_AA_x2 = 2; +//│ let scrut, _deforest_AA_x3; +//│ _deforest_AA_x3 = 2; //│ scrut = (k1, tmp35) => { //│ let m, param0, yf, scrut1, tmp36, tmp37, _deforest_AA_x_unused2; -//│ param0 = _deforest_AA_x2; +//│ param0 = _deforest_AA_x3; //│ yf = param0; //│ tmp36 = yf; //│ tmp37 = tmp36; @@ -615,234 +625,139 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ FAILURE: Unexpected lack of error to fix -:fixme + +// TODO: the computation of `t + 5 * 4 - 3 + 2 - 1` is still duplicated... :sjs -fun test(n, p, q) = - n + - if AA(0) is - AA then - let k = if p is - AA(x) then x - if q is - AA(y) then k + y -test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) +fun test(x) = + let t = if x is + AA(AA(AA(a))) then a + else 4 + t + 5 * 4 - 3 + 2 - 1 +fun f(a) = if a is + AA(AA) then 0 +let p = AA(AA(AA(10))) +test(p) + f(p) + test(AA(AA(AA(10)))) //│ JS (unsanitized): -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; -//│ test2 = function test(n, p1, q) { -//│ let scrut, k, param0, x, param01, y, tmp44, tmp45, tmp46; -//│ scrut = AA1(0); -//│ if (scrut instanceof AA1.class) { -//│ if (p1 instanceof AA1.class) { -//│ param0 = p1.x; -//│ x = param0; -//│ tmp44 = x; +//│ let test2, f3, p1, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47; +//│ test2 = function test(x) { +//│ let t, param0, param01, param02, a, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a = param02; +//│ tmp48 = a; +//│ } else { +//│ tmp48 = 4; +//│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp48 = 4; //│ } -//│ k = tmp44; -//│ if (q instanceof AA1.class) { -//│ param01 = q.x; -//│ y = param01; -//│ tmp45 = k + y; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp46 = tmp45; //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp48 = 4; //│ } -//│ return n + tmp46 -//│ }; -//│ tmp38 = AA1(2); -//│ tmp39 = AA1(3); -//│ tmp40 = test2(3, tmp38, tmp39); -//│ tmp41 = AA1(2); -//│ tmp42 = AA1(3); -//│ tmp43 = test2(3, tmp41, tmp42); -//│ tmp40 + tmp43 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test2, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp4, match_p_branch_AA, _deforest_AA_x_tmp5, match_q_branch_AA, _deforest_AA_x_tmp6, _deforest_AA_x_tmp7; -//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x2) { -//│ let k, param0, x, tmp44; -//│ param0 = _deforest_AA_x2; -//│ x = param0; -//│ tmp44 = x; -//│ k = tmp44; -//│ return runtime.safeCall(q(n, k)) -//│ }; -//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x2) { -//│ let param0, y, tmp44, tmp45; -//│ param0 = _deforest_AA_x2; -//│ y = param0; -//│ tmp44 = k + y; -//│ tmp45 = tmp44; -//│ return n + tmp45 -//│ }; -//│ test2 = function test(n, p1, q) { -//│ let scrut, _deforest_AA_x_unused2; -//│ _deforest_AA_x_unused2 = 0; -//│ scrut = (n1, p2, q1) => { -//│ return runtime.safeCall(p2(n1, q1)) -//│ }; -//│ return runtime.safeCall(scrut(n, p1, q)) -//│ }; -//│ _deforest_AA_x_tmp4 = 2; -//│ tmp38 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp4) -//│ }; -//│ _deforest_AA_x_tmp5 = 3; -//│ tmp39 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp5) -//│ }; -//│ tmp40 = test2(3, tmp38, tmp39); -//│ _deforest_AA_x_tmp6 = 2; -//│ tmp41 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp6) -//│ }; -//│ _deforest_AA_x_tmp7 = 3; -//│ tmp42 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp7) -//│ }; -//│ tmp43 = test2(3, tmp41, tmp42); -//│ block$res16 = tmp40 + tmp43; -//│ undefined -//│ = 16 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 16 -//│ FAILURE: Unexpected lack of error to fix - - - - -:fixme -:sjs -fun test(n, p, q, a) = - let o - if a is - A then - let k - if p is - AA(x) then - k = x - if q is - AA(y) then - o = k + y - o + n -let a = A -fun c(a) = if a is A then 0 -test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) -//│ JS (unsanitized): -//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57; -//│ test3 = function test(n, p1, q, a1) { -//│ let o, k, param0, x, param01, y, tmp58, tmp59, tmp60, tmp61; -//│ o = undefined; -//│ if (a1 instanceof A1.class) { -//│ k = undefined; -//│ if (p1 instanceof AA1.class) { -//│ param0 = p1.x; -//│ x = param0; -//│ k = x; -//│ tmp58 = runtime.Unit; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (q instanceof AA1.class) { -//│ param01 = q.x; -//│ y = param01; -//│ tmp59 = k + y; -//│ o = tmp59; -//│ tmp60 = runtime.Unit; +//│ t = tmp48; +//│ tmp49 = 5 * 4; +//│ tmp50 = t + tmp49; +//│ tmp51 = tmp50 - 3; +//│ tmp52 = tmp51 + 2; +//│ return tmp52 - 1 +//│ }; +//│ f3 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp61 = tmp60; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return o + n //│ }; -//│ c = function c(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ a = A1; -//│ tmp50 = AA1(2); -//│ tmp51 = AA1(3); -//│ tmp52 = test3(1, tmp50, tmp51, a); -//│ tmp53 = AA1(2); -//│ tmp54 = AA1(3); -//│ tmp55 = test3(2, tmp53, tmp54, a); -//│ tmp56 = tmp52 + tmp55; -//│ tmp57 = c(a); -//│ tmp56 + tmp57 +//│ tmp38 = AA1(10); +//│ tmp39 = AA1(tmp38); +//│ tmp40 = AA1(tmp39); +//│ p1 = tmp40; +//│ tmp41 = test2(p1); +//│ tmp42 = f3(p1); +//│ tmp43 = tmp41 + tmp42; +//│ tmp44 = AA1(10); +//│ tmp45 = AA1(tmp44); +//│ tmp46 = AA1(tmp45); +//│ tmp47 = test2(tmp46); +//│ tmp43 + tmp47 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, test3, a, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, _deforest_AA_x_tmp8, match_p_branch_AA1, _deforest_AA_x_tmp9, match_q_branch_AA1, _deforest_AA_x_tmp10, _deforest_AA_x_tmp11; -//│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x2) { -//│ let k, param0, x, tmp58; -//│ param0 = _deforest_AA_x2; -//│ x = param0; -//│ k = x; -//│ tmp58 = runtime.Unit; -//│ return runtime.safeCall(q(n, k)) -//│ }; -//│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x2) { -//│ let o, param0, y, tmp58, tmp59, tmp60; -//│ param0 = _deforest_AA_x2; -//│ y = param0; -//│ tmp58 = k + y; -//│ o = tmp58; -//│ tmp59 = runtime.Unit; -//│ tmp60 = tmp59; -//│ return o + n -//│ }; -//│ test3 = function test(n, p1, q, a1) { -//│ let o, k; -//│ o = undefined; -//│ if (a1 instanceof A1.class) { -//│ k = undefined; -//│ return runtime.safeCall(p1(n, q)) +//│ let test2, f3, p1, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, _deforest_AA_x_tmp2, match_param0_branch_AA, _deforest_AA_x_tmp3; +//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x3) { +//│ let t, param0, a, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ param0 = _deforest_AA_x3; +//│ a = param0; +//│ tmp48 = a; +//│ t = tmp48; +//│ tmp49 = 5 * 4; +//│ tmp50 = t + tmp49; +//│ tmp51 = tmp50 - 3; +//│ tmp52 = tmp51 + 2; +//│ return tmp52 - 1 +//│ }; +//│ test2 = function test(x) { +//│ let t, param0, param01, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ return runtime.safeCall(param01()) +//│ } else { +//│ tmp48 = 4; +//│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp48 = 4; //│ } -//│ return o + n -//│ }; -//│ c = function c(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 0 +//│ t = tmp48; +//│ tmp49 = 5 * 4; +//│ tmp50 = t + tmp49; +//│ tmp51 = tmp50 - 3; +//│ tmp52 = tmp51 + 2; +//│ return tmp52 - 1 +//│ }; +//│ f3 = function f(a) { +//│ let param0; +//│ if (a instanceof AA1.class) { +//│ param0 = a.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ a = A1; -//│ _deforest_AA_x_tmp8 = 2; -//│ tmp50 = (n, q) => { -//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp8) -//│ }; -//│ _deforest_AA_x_tmp9 = 3; -//│ tmp51 = (n, k) => { -//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp9) -//│ }; -//│ tmp52 = test3(1, tmp50, tmp51, a); -//│ _deforest_AA_x_tmp10 = 2; -//│ tmp53 = (n, q) => { -//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp10) -//│ }; -//│ _deforest_AA_x_tmp11 = 3; -//│ tmp54 = (n, k) => { -//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp11) -//│ }; -//│ tmp55 = test3(2, tmp53, tmp54, a); -//│ tmp56 = tmp52 + tmp55; -//│ tmp57 = c(a); -//│ block$res18 = tmp56 + tmp57; +//│ _deforest_AA_x_tmp2 = 10; +//│ tmp38 = () => { +//│ return match_param0_branch_AA(_deforest_AA_x_tmp2) +//│ }; +//│ tmp39 = AA1(tmp38); +//│ tmp40 = AA1(tmp39); +//│ p1 = tmp40; +//│ tmp41 = test2(p1); +//│ tmp42 = f3(p1); +//│ tmp43 = tmp41 + tmp42; +//│ _deforest_AA_x_tmp3 = 10; +//│ tmp44 = () => { +//│ return match_param0_branch_AA(_deforest_AA_x_tmp3) +//│ }; +//│ tmp45 = AA1(tmp44); +//│ tmp46 = AA1(tmp45); +//│ tmp47 = test2(tmp46); +//│ block$res16 = tmp43 + tmp47; //│ undefined -//│ = 13 +//│ = 56 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 13 -//│ a = A -//│ FAILURE: Unexpected lack of error to fix +//│ = 56 +//│ p = AA(AA(AA(10))) From 7a3cb55f93a676f60b8d8fe389a2c2b270c59761 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:33:44 +0800 Subject: [PATCH 151/303] another duplication --- .../src/test/mlscript/deforest/todos.mls | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 8bace463c4..00d317faaf 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -761,3 +761,113 @@ test(p) + f(p) + test(AA(AA(AA(10)))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 56 //│ p = AA(AA(AA(10))) + + + +// TODO: `n = tmp67; return n + 3` is duplicated +:sjs +fun test(x) = + let n = if x is + AA(BB(b)) then b + AA(CC(c)) then c + n + 3 +fun c(x) = if x is AA then 0 +fun p(x) = AA(x) +test(p(BB(3))) + test(p(CC(3))) + c(p(0)) +//│ JS (unsanitized): +//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66; +//│ test3 = function test(x) { +//│ let n, param0, param01, c1, param02, b, tmp67; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof BB1.class) { +//│ param02 = param0.x; +//│ b = param02; +//│ tmp67 = b; +//│ } else if (param0 instanceof CC1.class) { +//│ param01 = param0.x; +//│ c1 = param01; +//│ tmp67 = c1; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp67; +//│ return n + 3 +//│ }; +//│ c = function c(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p2 = function p(x) { +//│ return AA1(x) +//│ }; +//│ tmp58 = BB1(3); +//│ tmp59 = p2(tmp58); +//│ tmp60 = test3(tmp59); +//│ tmp61 = CC1(3); +//│ tmp62 = p2(tmp61); +//│ tmp63 = test3(tmp62); +//│ tmp64 = tmp60 + tmp63; +//│ tmp65 = p2(0); +//│ tmp66 = c(tmp65); +//│ tmp64 + tmp66 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, _deforest_BB_x1, _deforest_CC_x; +//│ test3 = function test(x) { +//│ let n, param0; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ return runtime.safeCall(param0()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp_not_in_scope; +//│ return n + 3 +//│ }; +//│ c = function c(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p2 = function p(x) { +//│ return AA1(x) +//│ }; +//│ _deforest_BB_x1 = 3; +//│ tmp58 = () => { +//│ let n, param0, b, tmp67; +//│ param0 = _deforest_BB_x1; +//│ b = param0; +//│ tmp67 = b; +//│ n = tmp67; +//│ return n + 3 +//│ }; +//│ tmp59 = p2(tmp58); +//│ tmp60 = test3(tmp59); +//│ _deforest_CC_x = 3; +//│ tmp61 = () => { +//│ let n, param0, c1, tmp67; +//│ param0 = _deforest_CC_x; +//│ c1 = param0; +//│ tmp67 = c1; +//│ n = tmp67; +//│ return n + 3 +//│ }; +//│ tmp62 = p2(tmp61); +//│ tmp63 = test3(tmp62); +//│ tmp64 = tmp60 + tmp63; +//│ tmp65 = p2(0); +//│ tmp66 = c(tmp65); +//│ block$res18 = tmp64 + tmp66; +//│ undefined +//│ = 12 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 12 From f4c693f9764cd261f1d7ea4222b0aa7f67030ac0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:13:14 +0800 Subject: [PATCH 152/303] fix for avoiding the previous duplication --- .../scala/hkmc2/codegen/Deforestation.scala | 21 +++++++++++++------ .../src/test/mlscript/deforest/simple.mls | 10 ++++----- .../src/test/mlscript/deforest/todos.mls | 19 ++++++++++------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6278214a2d..cba4836a7d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -868,31 +868,40 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend store.get(s) match case Some(f, b) => f.map(_.sym) -> b case _ => + // return all blocks concat together using `Begin`, and return if all of them are `End` blocks + def concatAllRestBlocksOfMatches(ps: List[ResultId]) = + ps.foldRight[Block -> Bool](End("") -> true){ (pid, acc) => + val b = d.matchScrutToMatchBlock(pid).rest + Begin(b, acc._1) -> (acc._2 && b.isInstanceOf[End]) + } val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => // return the original rests from unfused parent matches, // and the function symbol for the `rest` of the fusing parent match (if any) // and the rewritten `rest` block of that fusing parent match - ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> + concatAllRestBlocksOfMatches(ps) -> getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) case ps -> None => // return the original rests from unfused parent matches, and none (meaning that there is no fusing parent match) - ps.foldRight[Block](End("")){ (pid, acc) => Begin(d.matchScrutToMatchBlock(pid).rest, acc) } -> None + concatAllRestBlocksOfMatches(ps) -> None // bd: original `rest`s of non-fusing parent matches val restRewritten = parentRestInfo match // None: there is no fusing parent match - case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + case (bd, false) -> None => applyBlock(Begin(restBeforeRewriting, bd)) + case (bd, true) -> None => applyBlock(restBeforeRewriting) // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, // and the transformed `rest` is b - case bd -> (Some(s), b) => Begin( + case (bd, _) -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function - case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) + case (bd, false) -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) + case (bd, true) -> (None, b) => Begin(applyBlock(restBeforeRewriting), b) // no need to build a new function for empty rest, or if the rest is only going to be used once - if restBeforeRewriting.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then + if (restBeforeRewriting.isInstanceOf[End] && parentRestInfo._1._2) + || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then val res = N -> restRewritten store += s -> res res diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index d09e72aff1..556037e4c0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -681,7 +681,7 @@ c(A) + c(B) //│ let x, tmp7; //│ x = tmp6; //│ tmp7 = Predef.print(x); -//│ return x; +//│ return x //│ }; //│ c = function c(a) { //│ return runtime.safeCall(a()) @@ -845,7 +845,7 @@ map(x => x + 4, enumFromTo(1, 4)) //│ ==== JS (deforested): ==== //│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; //│ match_ls_rest = function match_ls_rest(f3, tmp11) { -//│ return runtime.safeCall(tmp11(f3)); +//│ return runtime.safeCall(tmp11(f3)) //│ }; //│ enumFromTo3 = function enumFromTo(a, b) { //│ let scrut, tmp11, tmp12, _deforest_Cons_t, _deforest_Cons_h; @@ -1264,7 +1264,7 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ match_x_rest = function match_x_rest(tmp29) { //│ let a; //│ a = tmp29; -//│ return a + 3; +//│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) @@ -1485,7 +1485,7 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ match_x_rest1 = function match_x_rest(tmp53) { //│ let a; //│ a = tmp53; -//│ return a + 3; +//│ return a + 3 //│ }; //│ f6 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) @@ -1565,7 +1565,7 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ match_x_rest2 = function match_x_rest(tmp70) { //│ let a; //│ a = tmp70; -//│ return a + 3; +//│ return a + 3 //│ }; //│ f7 = function f(x1, y1) { //│ return runtime.safeCall(x1(y1)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 00d317faaf..ab7444f9f3 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -348,7 +348,7 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ m = tmp12; //│ return runtime.safeCall(z(i, m)); //│ k = tmp_not_in_scope; -//│ return k + i; +//│ return k + i //│ }; //│ test = function test(x, y, z, i) { //│ return runtime.safeCall(x(y, z, i)) @@ -819,7 +819,12 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ tmp64 + tmp66 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, _deforest_BB_x1, _deforest_CC_x; +//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, _deforest_BB_x1, _deforest_CC_x, match_param0_rest; +//│ match_param0_rest = function match_param0_rest(tmp67) { +//│ let n; +//│ n = tmp67; +//│ return n + 3; +//│ }; //│ test3 = function test(x) { //│ let n, param0; //│ if (x instanceof AA1.class) { @@ -843,23 +848,21 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ }; //│ _deforest_BB_x1 = 3; //│ tmp58 = () => { -//│ let n, param0, b, tmp67; +//│ let param0, b, tmp67; //│ param0 = _deforest_BB_x1; //│ b = param0; //│ tmp67 = b; -//│ n = tmp67; -//│ return n + 3 +//│ return match_param0_rest(tmp67) //│ }; //│ tmp59 = p2(tmp58); //│ tmp60 = test3(tmp59); //│ _deforest_CC_x = 3; //│ tmp61 = () => { -//│ let n, param0, c1, tmp67; +//│ let param0, c1, tmp67; //│ param0 = _deforest_CC_x; //│ c1 = param0; //│ tmp67 = c1; -//│ n = tmp67; -//│ return n + 3 +//│ return match_param0_rest(tmp67) //│ }; //│ tmp62 = p2(tmp61); //│ tmp63 = test3(tmp62); From f86dc2ced8159039b838ac45ee17741f594ab364 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:30:01 +0800 Subject: [PATCH 153/303] minor --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cba4836a7d..6e9340a6cc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -872,7 +872,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend def concatAllRestBlocksOfMatches(ps: List[ResultId]) = ps.foldRight[Block -> Bool](End("") -> true){ (pid, acc) => val b = d.matchScrutToMatchBlock(pid).rest - Begin(b, acc._1) -> (acc._2 && b.isInstanceOf[End]) + val isEnd = b.isInstanceOf[End] + if isEnd then acc._1 -> (acc._2 && isEnd) + else Begin(b, acc._1) -> (acc._2 && isEnd) } val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => From f8c2fc510c5f684e87493b2290391b14c080551d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:57:23 +0800 Subject: [PATCH 154/303] use flatten to remove some dead code which contains free vars --- .../scala/hkmc2/codegen/Deforestation.scala | 32 +- .../test/mlscript/deforest/nestedMatch.mls | 123 +++++ .../src/test/mlscript/deforest/todos.mls | 494 +++++++----------- 3 files changed, 320 insertions(+), 329 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6e9340a6cc..430bde306a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -870,11 +870,10 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case _ => // return all blocks concat together using `Begin`, and return if all of them are `End` blocks def concatAllRestBlocksOfMatches(ps: List[ResultId]) = - ps.foldRight[Block -> Bool](End("") -> true){ (pid, acc) => + ps.foldRight[Block](End("")){ (pid, acc) => val b = d.matchScrutToMatchBlock(pid).rest val isEnd = b.isInstanceOf[End] - if isEnd then acc._1 -> (acc._2 && isEnd) - else Begin(b, acc._1) -> (acc._2 && isEnd) + if isEnd then acc else Begin(b, acc) } val parentRestInfo = parentMatchesUptoAFusingOne(s) match case ps -> Some(theFusingOne) => @@ -888,22 +887,21 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend concatAllRestBlocksOfMatches(ps) -> None // bd: original `rest`s of non-fusing parent matches - val restRewritten = parentRestInfo match - // None: there is no fusing parent match - case (bd, false) -> None => applyBlock(Begin(restBeforeRewriting, bd)) - case (bd, true) -> None => applyBlock(restBeforeRewriting) - // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, - // and the transformed `rest` is b - case (bd, _) -> (Some(s), b) => Begin( - applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function - case (bd, false) -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) - case (bd, true) -> (None, b) => Begin(applyBlock(restBeforeRewriting), b) + val restRewritten = + val nonFlatten = parentRestInfo match + // None: there is no fusing parent match + case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) + // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, + // and the transformed `rest` is b + case bd -> (Some(s), b) => Begin( + applyBlock(restBeforeRewriting), + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function + case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) + nonFlatten.flattened // no need to build a new function for empty rest, or if the rest is only going to be used once - if (restBeforeRewriting.isInstanceOf[End] && parentRestInfo._1._2) - || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then + if restRewritten.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then val res = N -> restRewritten store += s -> res res diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index fc305c6112..6e7c7073f5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1375,3 +1375,126 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ = 18 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 18 + + +:sjs +fun test(x, y, z, i) = + let k = if x is + AA(a) then + let m = if y is + AA(a1) then a1 + i + BB(b2) then b2 - i + let n = if z is + BB(a2) then a2 - i + m + n + k + i +test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) +//│ JS (unsanitized): +//│ let test5, tmp164, tmp165, tmp166, tmp167, tmp168, tmp169, tmp170, tmp171; +//│ test5 = function test(x, y2, z, i) { +//│ let k, param0, a2, m, param01, b2, param02, a11, n, param03, a21, tmp172, tmp173, tmp174; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ a2 = param0; +//│ if (y2 instanceof AA1.class) { +//│ param02 = y2.x; +//│ a11 = param02; +//│ tmp172 = a11 + i; +//│ } else if (y2 instanceof BB1.class) { +//│ param01 = y2.x; +//│ b2 = param01; +//│ tmp172 = b2 - i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp172; +//│ if (z instanceof BB1.class) { +//│ param03 = z.x; +//│ a21 = param03; +//│ tmp173 = a21 - i; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp173; +//│ tmp174 = m + n; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ k = tmp174; +//│ return k + i +//│ }; +//│ tmp164 = AA1(1); +//│ tmp165 = AA1(2); +//│ tmp166 = BB1(3); +//│ tmp167 = test5(tmp164, tmp165, tmp166, 4); +//│ tmp168 = AA1(1); +//│ tmp169 = BB1(2); +//│ tmp170 = BB1(3); +//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); +//│ tmp167 + tmp171 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test5, tmp164, tmp165, tmp166, tmp167, tmp168, tmp169, tmp170, tmp171, _deforest_AA_x8, _deforest_BB_x2, _deforest_AA_x_tmp26, match_x_branch_AA4, match_y_rest, _deforest_BB_x_tmp4, match_z_branch_BB2, _deforest_AA_x_tmp27, _deforest_BB_x_tmp5; +//│ match_x_branch_AA4 = function match_x_branch_AA(y2, z, i, _deforest_AA_x9) { +//│ let param0, a2; +//│ param0 = _deforest_AA_x9; +//│ a2 = param0; +//│ return runtime.safeCall(y2(z, i)) +//│ }; +//│ match_z_branch_BB2 = function match_z_branch_BB(i, m, _deforest_BB_x3) { +//│ let k, n, param0, a2, tmp172, tmp173; +//│ param0 = _deforest_BB_x3; +//│ a2 = param0; +//│ tmp172 = a2 - i; +//│ n = tmp172; +//│ tmp173 = m + n; +//│ k = tmp173; +//│ return k + i +//│ }; +//│ match_y_rest = function match_y_rest(z, i, tmp172) { +//│ let m; +//│ m = tmp172; +//│ return runtime.safeCall(z(i, m)) +//│ }; +//│ test5 = function test(x, y2, z, i) { +//│ return runtime.safeCall(x(y2, z, i)) +//│ }; +//│ _deforest_AA_x_tmp26 = 1; +//│ tmp164 = (y2, z, i) => { +//│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp26) +//│ }; +//│ _deforest_AA_x8 = 2; +//│ tmp165 = (z, i) => { +//│ let param0, a11, tmp172; +//│ param0 = _deforest_AA_x8; +//│ a11 = param0; +//│ tmp172 = a11 + i; +//│ return match_y_rest(z, i, tmp172) +//│ }; +//│ _deforest_BB_x_tmp4 = 3; +//│ tmp166 = (i, m) => { +//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp4) +//│ }; +//│ tmp167 = test5(tmp164, tmp165, tmp166, 4); +//│ _deforest_AA_x_tmp27 = 1; +//│ tmp168 = (y2, z, i) => { +//│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp27) +//│ }; +//│ _deforest_BB_x2 = 2; +//│ tmp169 = (z, i) => { +//│ let param0, b2, tmp172; +//│ param0 = _deforest_BB_x2; +//│ b2 = param0; +//│ tmp172 = b2 - i; +//│ return match_y_rest(z, i, tmp172) +//│ }; +//│ _deforest_BB_x_tmp5 = 3; +//│ tmp170 = (i, m) => { +//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp5) +//│ }; +//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); +//│ block$res34 = tmp167 + tmp171; +//│ undefined +//│ = 10 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 10 diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index ab7444f9f3..1ff5e23106 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -265,136 +265,6 @@ f(AA(AA(3))) //│ = 3 - - -// FIXME: fused matches should always be considered as in tail positions -:fixme -:sjs -fun test(x, y, z, i) = - let k = if x is - AA(a) then - let m = if y is - AA(a1) then a1 + i - BB(b2) then b2 - i - let n = if z is - BB(a2) then a2 - i - m + n - k + i -test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) -//│ JS (unsanitized): -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; -//│ test = function test(x, y, z, i) { -//│ let k, param0, a, m, param01, b2, param02, a1, n, param03, a2, tmp12, tmp13, tmp14; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a = param0; -//│ if (y instanceof AA1.class) { -//│ param02 = y.x; -//│ a1 = param02; -//│ tmp12 = a1 + i; -//│ } else if (y instanceof BB1.class) { -//│ param01 = y.x; -//│ b2 = param01; -//│ tmp12 = b2 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp12; -//│ if (z instanceof BB1.class) { -//│ param03 = z.x; -//│ a2 = param03; -//│ tmp13 = a2 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp13; -//│ tmp14 = m + n; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ k = tmp14; -//│ return k + i -//│ }; -//│ tmp4 = AA1(1); -//│ tmp5 = AA1(2); -//│ tmp6 = BB1(3); -//│ tmp7 = test(tmp4, tmp5, tmp6, 4); -//│ tmp8 = AA1(1); -//│ tmp9 = BB1(2); -//│ tmp10 = BB1(3); -//│ tmp11 = test(tmp8, tmp9, tmp10, 4); -//│ tmp7 + tmp11 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, _deforest_AA_x1, _deforest_BB_x, _deforest_AA_x_tmp, match_x_branch_AA, match_y_rest, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp1, _deforest_BB_x_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y, z, i, _deforest_AA_x2) { -//│ let param0, a; -//│ param0 = _deforest_AA_x2; -//│ a = param0; -//│ return runtime.safeCall(y(z, i)) -//│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x1) { -//│ let k, n, param0, a2, tmp12, tmp13; -//│ param0 = _deforest_BB_x1; -//│ a2 = param0; -//│ tmp12 = a2 - i; -//│ n = tmp12; -//│ tmp13 = m + n; -//│ k = tmp13; -//│ return k + i -//│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp12) { -//│ let k, m; -//│ m = tmp12; -//│ return runtime.safeCall(z(i, m)); -//│ k = tmp_not_in_scope; -//│ return k + i -//│ }; -//│ test = function test(x, y, z, i) { -//│ return runtime.safeCall(x(y, z, i)) -//│ }; -//│ _deforest_AA_x_tmp = 1; -//│ tmp4 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp) -//│ }; -//│ _deforest_AA_x1 = 2; -//│ tmp5 = (z, i) => { -//│ let param0, a1, tmp12; -//│ param0 = _deforest_AA_x1; -//│ a1 = param0; -//│ tmp12 = a1 + i; -//│ return match_y_rest(z, i, tmp12) -//│ }; -//│ _deforest_BB_x_tmp = 3; -//│ tmp6 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) -//│ }; -//│ tmp7 = test(tmp4, tmp5, tmp6, 4); -//│ _deforest_AA_x_tmp1 = 1; -//│ tmp8 = (y, z, i) => { -//│ return match_x_branch_AA(y, z, i, _deforest_AA_x_tmp1) -//│ }; -//│ _deforest_BB_x = 2; -//│ tmp9 = (z, i) => { -//│ let param0, b2, tmp12; -//│ param0 = _deforest_BB_x; -//│ b2 = param0; -//│ tmp12 = b2 - i; -//│ return match_y_rest(z, i, tmp12) -//│ }; -//│ _deforest_BB_x_tmp1 = 3; -//│ tmp10 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) -//│ }; -//│ tmp11 = test(tmp8, tmp9, tmp10, 4); -//│ block$res10 = tmp7 + tmp11; -//│ undefined -//│ = 10 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 10 -//│ FAILURE: Unexpected lack of error to fix - - // FIXME: // - how to deal with the original `rest` from matches multiple levels up? // (after rewriting it becomes dead code and contains undefined variable) @@ -409,9 +279,9 @@ fun f(a) = if a is let p = AA(AA(AA(10))) test(p) + f(p) //│ JS (unsanitized): -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24; -//│ test1 = function test(x) { -//│ let t, param0, param01, param02, a, tmp25; +//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8; +//│ test = function test(x) { +//│ let t, param0, param01, param02, a, tmp9; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -419,7 +289,7 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp25 = a; +//│ tmp9 = a; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -429,7 +299,7 @@ test(p) + f(p) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp25; +//│ t = tmp9; //│ return t + 5 //│ }; //│ f1 = function f(a) { @@ -445,17 +315,17 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp20 = AA1(10); -//│ tmp21 = AA1(tmp20); -//│ tmp22 = AA1(tmp21); -//│ p = tmp22; -//│ tmp23 = test1(p); -//│ tmp24 = f1(p); -//│ tmp23 + tmp24 +//│ tmp4 = AA1(10); +//│ tmp5 = AA1(tmp4); +//│ tmp6 = AA1(tmp5); +//│ p = tmp6; +//│ tmp7 = test(p); +//│ tmp8 = f1(p); +//│ tmp7 + tmp8 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1, f1, p, tmp20, tmp21, tmp22, tmp23, tmp24, _deforest_AA_x2; -//│ test1 = function test(x) { +//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, _deforest_AA_x1; +//│ test = function test(x) { //│ let t, param0, param01; //│ if (x instanceof AA1.class) { //│ param0 = x.x; @@ -484,21 +354,21 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ _deforest_AA_x2 = 10; -//│ tmp20 = () => { -//│ let t, param0, a, tmp25; -//│ param0 = _deforest_AA_x2; +//│ _deforest_AA_x1 = 10; +//│ tmp4 = () => { +//│ let t, param0, a, tmp9; +//│ param0 = _deforest_AA_x1; //│ a = param0; -//│ tmp25 = a; -//│ t = tmp25; +//│ tmp9 = a; +//│ t = tmp9; //│ return t + 5 //│ }; -//│ tmp21 = AA1(tmp20); -//│ tmp22 = AA1(tmp21); -//│ p = tmp22; -//│ tmp23 = test1(p); -//│ tmp24 = f1(p); -//│ block$res12 = tmp23 + tmp24; +//│ tmp5 = AA1(tmp4); +//│ tmp6 = AA1(tmp5); +//│ p = tmp6; +//│ tmp7 = test(p); +//│ tmp8 = f1(p); +//│ block$res10 = tmp7 + tmp8; //│ undefined //│ = 15 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -523,101 +393,101 @@ fun f(x, y, z, k) = AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ JS (unsanitized): -//│ let f2, tmp30, tmp31, tmp32, tmp33; +//│ let f2, tmp14, tmp15, tmp16, tmp17; //│ f2 = function f(x, y, z, k) { -//│ let tmp34, m, scrut, param0, yf, scrut1, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41; +//│ let tmp18, m, scrut, param0, yf, scrut1, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; //│ if (z === true) { -//│ tmp35 = y + 1; -//│ tmp36 = BB1(tmp35); +//│ tmp19 = y + 1; +//│ tmp20 = BB1(tmp19); //│ } else { -//│ tmp37 = y - 1; -//│ tmp36 = BB1(tmp37); +//│ tmp21 = y - 1; +//│ tmp20 = BB1(tmp21); //│ } -//│ tmp34 = tmp36; -//│ if (tmp34 instanceof BB1.class) { +//│ tmp18 = tmp20; +//│ if (tmp18 instanceof BB1.class) { //│ if (x instanceof AA1.class) { //│ scrut = AA1(2); //│ if (scrut instanceof AA1.class) { //│ param0 = scrut.x; //│ yf = param0; -//│ tmp38 = yf; +//│ tmp22 = yf; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp39 = tmp38; +//│ tmp23 = tmp22; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp39; +//│ m = tmp23; //│ scrut1 = AA1(3); //│ if (scrut1 instanceof AA1.class) { -//│ tmp40 = m + k; +//│ tmp24 = m + k; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp41 = tmp40; +//│ tmp25 = tmp24; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return tmp34.x + tmp41 +//│ return tmp18.x + tmp25 //│ }; -//│ tmp30 = AA1(3); -//│ tmp31 = f2(tmp30, 3, true, 10); -//│ tmp32 = AA1(5); -//│ tmp33 = f2(tmp32, 4, false, 20); -//│ tmp31 + tmp33 +//│ tmp14 = AA1(3); +//│ tmp15 = f2(tmp14, 3, true, 10); +//│ tmp16 = AA1(5); +//│ tmp17 = f2(tmp16, 4, false, 20); +//│ tmp15 + tmp17 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f2, tmp30, tmp31, tmp32, tmp33, _deforest_AA_x_unused, match_x_branch_AA1, _deforest_AA_x_unused1; -//│ match_x_branch_AA1 = function match_x_branch_AA(k, tmp34) { -//│ let scrut, _deforest_AA_x3; -//│ _deforest_AA_x3 = 2; -//│ scrut = (k1, tmp35) => { -//│ let m, param0, yf, scrut1, tmp36, tmp37, _deforest_AA_x_unused2; -//│ param0 = _deforest_AA_x3; +//│ let f2, tmp14, tmp15, tmp16, tmp17, _deforest_AA_x_unused, match_x_branch_AA, _deforest_AA_x_unused1; +//│ match_x_branch_AA = function match_x_branch_AA(k, tmp18) { +//│ let scrut, _deforest_AA_x2; +//│ _deforest_AA_x2 = 2; +//│ scrut = (k1, tmp19) => { +//│ let m, param0, yf, scrut1, tmp20, tmp21, _deforest_AA_x_unused2; +//│ param0 = _deforest_AA_x2; //│ yf = param0; -//│ tmp36 = yf; -//│ tmp37 = tmp36; -//│ m = tmp37; +//│ tmp20 = yf; +//│ tmp21 = tmp20; +//│ m = tmp21; //│ _deforest_AA_x_unused2 = 3; -//│ scrut1 = (k2, tmp38, m1) => { -//│ let tmp39, tmp40; -//│ tmp39 = m1 + k2; -//│ tmp40 = tmp39; -//│ return tmp38.x + tmp40 +//│ scrut1 = (k2, tmp22, m1) => { +//│ let tmp23, tmp24; +//│ tmp23 = m1 + k2; +//│ tmp24 = tmp23; +//│ return tmp22.x + tmp24 //│ }; -//│ return runtime.safeCall(scrut1(k1, tmp35, m)) +//│ return runtime.safeCall(scrut1(k1, tmp19, m)) //│ }; -//│ return runtime.safeCall(scrut(k, tmp34)) +//│ return runtime.safeCall(scrut(k, tmp18)) //│ }; //│ f2 = function f(x, y, z, k) { -//│ let tmp34, tmp35, tmp36, tmp37; +//│ let tmp18, tmp19, tmp20, tmp21; //│ if (z === true) { -//│ tmp35 = y + 1; -//│ tmp36 = BB1(tmp35); +//│ tmp19 = y + 1; +//│ tmp20 = BB1(tmp19); //│ } else { -//│ tmp37 = y - 1; -//│ tmp36 = BB1(tmp37); +//│ tmp21 = y - 1; +//│ tmp20 = BB1(tmp21); //│ } -//│ tmp34 = tmp36; -//│ if (tmp34 instanceof BB1.class) { -//│ return runtime.safeCall(x(k, tmp34)) +//│ tmp18 = tmp20; +//│ if (tmp18 instanceof BB1.class) { +//│ return runtime.safeCall(x(k, tmp18)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return tmp34.x + tmp_not_in_scope +//│ return tmp18.x + tmp_not_in_scope //│ }; //│ _deforest_AA_x_unused = 3; -//│ tmp30 = (k, tmp34) => { -//│ return match_x_branch_AA1(k, tmp34) +//│ tmp14 = (k, tmp18) => { +//│ return match_x_branch_AA(k, tmp18) //│ }; -//│ tmp31 = f2(tmp30, 3, true, 10); +//│ tmp15 = f2(tmp14, 3, true, 10); //│ _deforest_AA_x_unused1 = 5; -//│ tmp32 = (k, tmp34) => { -//│ return match_x_branch_AA1(k, tmp34) +//│ tmp16 = (k, tmp18) => { +//│ return match_x_branch_AA(k, tmp18) //│ }; -//│ tmp33 = f2(tmp32, 4, false, 20); -//│ block$res14 = tmp31 + tmp33; +//│ tmp17 = f2(tmp16, 4, false, 20); +//│ block$res12 = tmp15 + tmp17; //│ undefined //│ = 41 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -638,9 +508,9 @@ fun f(a) = if a is let p = AA(AA(AA(10))) test(p) + f(p) + test(AA(AA(AA(10)))) //│ JS (unsanitized): -//│ let test2, f3, p1, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47; -//│ test2 = function test(x) { -//│ let t, param0, param01, param02, a, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ let test1, f3, p1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31; +//│ test1 = function test(x) { +//│ let t, param0, param01, param02, a, tmp32, tmp33, tmp34, tmp35, tmp36; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -648,22 +518,22 @@ test(p) + f(p) + test(AA(AA(AA(10)))) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp48 = a; +//│ tmp32 = a; //│ } else { -//│ tmp48 = 4; +//│ tmp32 = 4; //│ } //│ } else { -//│ tmp48 = 4; +//│ tmp32 = 4; //│ } //│ } else { -//│ tmp48 = 4; +//│ tmp32 = 4; //│ } -//│ t = tmp48; -//│ tmp49 = 5 * 4; -//│ tmp50 = t + tmp49; -//│ tmp51 = tmp50 - 3; -//│ tmp52 = tmp51 + 2; -//│ return tmp52 - 1 +//│ t = tmp32; +//│ tmp33 = 5 * 4; +//│ tmp34 = t + tmp33; +//│ tmp35 = tmp34 - 3; +//│ tmp36 = tmp35 + 2; +//│ return tmp36 - 1 //│ }; //│ f3 = function f(a) { //│ let param0; @@ -678,52 +548,52 @@ test(p) + f(p) + test(AA(AA(AA(10)))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp38 = AA1(10); -//│ tmp39 = AA1(tmp38); -//│ tmp40 = AA1(tmp39); -//│ p1 = tmp40; -//│ tmp41 = test2(p1); -//│ tmp42 = f3(p1); -//│ tmp43 = tmp41 + tmp42; -//│ tmp44 = AA1(10); -//│ tmp45 = AA1(tmp44); -//│ tmp46 = AA1(tmp45); -//│ tmp47 = test2(tmp46); -//│ tmp43 + tmp47 +//│ tmp22 = AA1(10); +//│ tmp23 = AA1(tmp22); +//│ tmp24 = AA1(tmp23); +//│ p1 = tmp24; +//│ tmp25 = test1(p1); +//│ tmp26 = f3(p1); +//│ tmp27 = tmp25 + tmp26; +//│ tmp28 = AA1(10); +//│ tmp29 = AA1(tmp28); +//│ tmp30 = AA1(tmp29); +//│ tmp31 = test1(tmp30); +//│ tmp27 + tmp31 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test2, f3, p1, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, _deforest_AA_x_tmp2, match_param0_branch_AA, _deforest_AA_x_tmp3; -//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x3) { -//│ let t, param0, a, tmp48, tmp49, tmp50, tmp51, tmp52; -//│ param0 = _deforest_AA_x3; +//│ let test1, f3, p1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1; +//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x2) { +//│ let t, param0, a, tmp32, tmp33, tmp34, tmp35, tmp36; +//│ param0 = _deforest_AA_x2; //│ a = param0; -//│ tmp48 = a; -//│ t = tmp48; -//│ tmp49 = 5 * 4; -//│ tmp50 = t + tmp49; -//│ tmp51 = tmp50 - 3; -//│ tmp52 = tmp51 + 2; -//│ return tmp52 - 1 +//│ tmp32 = a; +//│ t = tmp32; +//│ tmp33 = 5 * 4; +//│ tmp34 = t + tmp33; +//│ tmp35 = tmp34 - 3; +//│ tmp36 = tmp35 + 2; +//│ return tmp36 - 1 //│ }; -//│ test2 = function test(x) { -//│ let t, param0, param01, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ test1 = function test(x) { +//│ let t, param0, param01, tmp32, tmp33, tmp34, tmp35, tmp36; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; //│ return runtime.safeCall(param01()) //│ } else { -//│ tmp48 = 4; +//│ tmp32 = 4; //│ } //│ } else { -//│ tmp48 = 4; +//│ tmp32 = 4; //│ } -//│ t = tmp48; -//│ tmp49 = 5 * 4; -//│ tmp50 = t + tmp49; -//│ tmp51 = tmp50 - 3; -//│ tmp52 = tmp51 + 2; -//│ return tmp52 - 1 +//│ t = tmp32; +//│ tmp33 = 5 * 4; +//│ tmp34 = t + tmp33; +//│ tmp35 = tmp34 - 3; +//│ tmp36 = tmp35 + 2; +//│ return tmp36 - 1 //│ }; //│ f3 = function f(a) { //│ let param0; @@ -738,24 +608,24 @@ test(p) + f(p) + test(AA(AA(AA(10)))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ _deforest_AA_x_tmp2 = 10; -//│ tmp38 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp2) -//│ }; -//│ tmp39 = AA1(tmp38); -//│ tmp40 = AA1(tmp39); -//│ p1 = tmp40; -//│ tmp41 = test2(p1); -//│ tmp42 = f3(p1); -//│ tmp43 = tmp41 + tmp42; -//│ _deforest_AA_x_tmp3 = 10; -//│ tmp44 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp3) -//│ }; -//│ tmp45 = AA1(tmp44); -//│ tmp46 = AA1(tmp45); -//│ tmp47 = test2(tmp46); -//│ block$res16 = tmp43 + tmp47; +//│ _deforest_AA_x_tmp = 10; +//│ tmp22 = () => { +//│ return match_param0_branch_AA(_deforest_AA_x_tmp) +//│ }; +//│ tmp23 = AA1(tmp22); +//│ tmp24 = AA1(tmp23); +//│ p1 = tmp24; +//│ tmp25 = test1(p1); +//│ tmp26 = f3(p1); +//│ tmp27 = tmp25 + tmp26; +//│ _deforest_AA_x_tmp1 = 10; +//│ tmp28 = () => { +//│ return match_param0_branch_AA(_deforest_AA_x_tmp1) +//│ }; +//│ tmp29 = AA1(tmp28); +//│ tmp30 = AA1(tmp29); +//│ tmp31 = test1(tmp30); +//│ block$res14 = tmp27 + tmp31; //│ undefined //│ = 56 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -775,26 +645,26 @@ fun c(x) = if x is AA then 0 fun p(x) = AA(x) test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ JS (unsanitized): -//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66; -//│ test3 = function test(x) { -//│ let n, param0, param01, c1, param02, b, tmp67; +//│ let p2, c, test2, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50; +//│ test2 = function test(x) { +//│ let n, param0, param01, c1, param02, b, tmp51; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof BB1.class) { //│ param02 = param0.x; //│ b = param02; -//│ tmp67 = b; +//│ tmp51 = b; //│ } else if (param0 instanceof CC1.class) { //│ param01 = param0.x; //│ c1 = param01; -//│ tmp67 = c1; +//│ tmp51 = c1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp67; +//│ n = tmp51; //│ return n + 3 //│ }; //│ c = function c(x) { @@ -807,25 +677,25 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ p2 = function p(x) { //│ return AA1(x) //│ }; -//│ tmp58 = BB1(3); -//│ tmp59 = p2(tmp58); -//│ tmp60 = test3(tmp59); -//│ tmp61 = CC1(3); -//│ tmp62 = p2(tmp61); -//│ tmp63 = test3(tmp62); -//│ tmp64 = tmp60 + tmp63; -//│ tmp65 = p2(0); -//│ tmp66 = c(tmp65); -//│ tmp64 + tmp66 +//│ tmp42 = BB1(3); +//│ tmp43 = p2(tmp42); +//│ tmp44 = test2(tmp43); +//│ tmp45 = CC1(3); +//│ tmp46 = p2(tmp45); +//│ tmp47 = test2(tmp46); +//│ tmp48 = tmp44 + tmp47; +//│ tmp49 = p2(0); +//│ tmp50 = c(tmp49); +//│ tmp48 + tmp50 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p2, c, test3, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, _deforest_BB_x1, _deforest_CC_x, match_param0_rest; -//│ match_param0_rest = function match_param0_rest(tmp67) { +//│ let p2, c, test2, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, _deforest_BB_x, _deforest_CC_x, match_param0_rest; +//│ match_param0_rest = function match_param0_rest(tmp51) { //│ let n; -//│ n = tmp67; -//│ return n + 3; +//│ n = tmp51; +//│ return n + 3 //│ }; -//│ test3 = function test(x) { +//│ test2 = function test(x) { //│ let n, param0; //│ if (x instanceof AA1.class) { //│ param0 = x.x; @@ -846,30 +716,30 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ p2 = function p(x) { //│ return AA1(x) //│ }; -//│ _deforest_BB_x1 = 3; -//│ tmp58 = () => { -//│ let param0, b, tmp67; -//│ param0 = _deforest_BB_x1; +//│ _deforest_BB_x = 3; +//│ tmp42 = () => { +//│ let param0, b, tmp51; +//│ param0 = _deforest_BB_x; //│ b = param0; -//│ tmp67 = b; -//│ return match_param0_rest(tmp67) +//│ tmp51 = b; +//│ return match_param0_rest(tmp51) //│ }; -//│ tmp59 = p2(tmp58); -//│ tmp60 = test3(tmp59); +//│ tmp43 = p2(tmp42); +//│ tmp44 = test2(tmp43); //│ _deforest_CC_x = 3; -//│ tmp61 = () => { -//│ let param0, c1, tmp67; +//│ tmp45 = () => { +//│ let param0, c1, tmp51; //│ param0 = _deforest_CC_x; //│ c1 = param0; -//│ tmp67 = c1; -//│ return match_param0_rest(tmp67) -//│ }; -//│ tmp62 = p2(tmp61); -//│ tmp63 = test3(tmp62); -//│ tmp64 = tmp60 + tmp63; -//│ tmp65 = p2(0); -//│ tmp66 = c(tmp65); -//│ block$res18 = tmp64 + tmp66; +//│ tmp51 = c1; +//│ return match_param0_rest(tmp51) +//│ }; +//│ tmp46 = p2(tmp45); +//│ tmp47 = test2(tmp46); +//│ tmp48 = tmp44 + tmp47; +//│ tmp49 = p2(0); +//│ tmp50 = c(tmp49); +//│ block$res16 = tmp48 + tmp50; //│ undefined //│ = 12 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From b6489368ea41e1527a4ac7b70c49d2dfd0a2dd18 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:23:01 +0800 Subject: [PATCH 155/303] remove dead code which contains f.v.s in match `rest` --- .../scala/hkmc2/codegen/Deforestation.scala | 18 + .../test/mlscript/deforest/nestedMatch.mls | 333 ++++++++++++- .../src/test/mlscript/deforest/todos.mls | 455 +++--------------- 3 files changed, 408 insertions(+), 398 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 430bde306a..40346e391e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -212,6 +212,22 @@ extension (b: Block) HasExplicitRetTraverser.applyBlock(b) HasExplicitRetTraverser.flag + + def willBeNonEndTailBlock(using d: Deforest): Bool = + object WillBeNonEndTailBlockTraverser extends BlockTraverserShallow: + var flag = false + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + flag = + d.filteredDtors(scrut.uid) || + (arms.forall { case (_, b) => b.willBeNonEndTailBlock } && dflt.fold(true)(_.willBeNonEndTailBlock)) || + rest.willBeNonEndTailBlock + case _: End => () + case _: BlockTail => flag = true + case _ => super.applyBlock(b) + WillBeNonEndTailBlockTraverser.applyBlock(b) + WillBeNonEndTailBlockTraverser.flag + class Deforest(using TL, Raise, Elaborator.State): @@ -934,6 +950,8 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) + case Match(scrut, arms, dflt, rest) if dflt.fold(true)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } => + super.applyBlock(Match(scrut, arms, dflt, End(""))) case _ => super.applyBlock(b) override def applyResult(r: Result): Result = r match diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 6e7c7073f5..825eaea4fe 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1225,7 +1225,6 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return o + n //│ }; //│ c3 = function c(a2) { //│ if (a2 instanceof A1.class) { @@ -1498,3 +1497,335 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ = 10 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 + + +:sjs +fun test(x) = + let n = if x is + AA(BB(b)) then b + AA(CC(c)) then c + n + 3 +fun c(x) = if x is AA then 0 +fun p(x) = AA(x) +test(p(BB(3))) + test(p(CC(3))) + c(p(0)) +//│ JS (unsanitized): +//│ let p1, c4, test6, tmp180, tmp181, tmp182, tmp183, tmp184, tmp185, tmp186, tmp187, tmp188; +//│ test6 = function test(x) { +//│ let n, param0, param01, c5, param02, b, tmp189; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof BB1.class) { +//│ param02 = param0.x; +//│ b = param02; +//│ tmp189 = b; +//│ } else if (param0 instanceof CC1.class) { +//│ param01 = param0.x; +//│ c5 = param01; +//│ tmp189 = c5; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp189; +//│ return n + 3 +//│ }; +//│ c4 = function c(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p1 = function p(x) { +//│ return AA1(x) +//│ }; +//│ tmp180 = BB1(3); +//│ tmp181 = p1(tmp180); +//│ tmp182 = test6(tmp181); +//│ tmp183 = CC1(3); +//│ tmp184 = p1(tmp183); +//│ tmp185 = test6(tmp184); +//│ tmp186 = tmp182 + tmp185; +//│ tmp187 = p1(0); +//│ tmp188 = c4(tmp187); +//│ tmp186 + tmp188 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p1, c4, test6, tmp180, tmp181, tmp182, tmp183, tmp184, tmp185, tmp186, tmp187, tmp188, _deforest_BB_x3, _deforest_CC_x, match_param0_rest; +//│ match_param0_rest = function match_param0_rest(tmp189) { +//│ let n; +//│ n = tmp189; +//│ return n + 3 +//│ }; +//│ test6 = function test(x) { +//│ let param0; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ return runtime.safeCall(param0()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c4 = function c(x) { +//│ if (x instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ p1 = function p(x) { +//│ return AA1(x) +//│ }; +//│ _deforest_BB_x3 = 3; +//│ tmp180 = () => { +//│ let param0, b, tmp189; +//│ param0 = _deforest_BB_x3; +//│ b = param0; +//│ tmp189 = b; +//│ return match_param0_rest(tmp189) +//│ }; +//│ tmp181 = p1(tmp180); +//│ tmp182 = test6(tmp181); +//│ _deforest_CC_x = 3; +//│ tmp183 = () => { +//│ let param0, c5, tmp189; +//│ param0 = _deforest_CC_x; +//│ c5 = param0; +//│ tmp189 = c5; +//│ return match_param0_rest(tmp189) +//│ }; +//│ tmp184 = p1(tmp183); +//│ tmp185 = test6(tmp184); +//│ tmp186 = tmp182 + tmp185; +//│ tmp187 = p1(0); +//│ tmp188 = c4(tmp187); +//│ block$res36 = tmp186 + tmp188; +//│ undefined +//│ = 12 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 12 + + + + + +:sjs +fun f(x, y, z, k) = + let tmp = if z then BB(y + 1) else BB(y - 1) + tmp.x + + if tmp is + BB then + let m = if x is + AA then + if AA(2) is + AA(yf) then yf + if AA(3) is + AA then m + k +f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) +//│ JS (unsanitized): +//│ let f7, tmp198, tmp199, tmp200, tmp201; +//│ f7 = function f(x, y2, z, k) { +//│ let tmp202, m, scrut, param0, yf, scrut1, tmp203, tmp204, tmp205, tmp206, tmp207, tmp208, tmp209; +//│ if (z === true) { +//│ tmp203 = y2 + 1; +//│ tmp204 = BB1(tmp203); +//│ } else { +//│ tmp205 = y2 - 1; +//│ tmp204 = BB1(tmp205); +//│ } +//│ tmp202 = tmp204; +//│ if (tmp202 instanceof BB1.class) { +//│ if (x instanceof AA1.class) { +//│ scrut = AA1(2); +//│ if (scrut instanceof AA1.class) { +//│ param0 = scrut.x; +//│ yf = param0; +//│ tmp206 = yf; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp207 = tmp206; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp207; +//│ scrut1 = AA1(3); +//│ if (scrut1 instanceof AA1.class) { +//│ tmp208 = m + k; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ tmp209 = tmp208; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ return tmp202.x + tmp209 +//│ }; +//│ tmp198 = AA1(3); +//│ tmp199 = f7(tmp198, 3, true, 10); +//│ tmp200 = AA1(5); +//│ tmp201 = f7(tmp200, 4, false, 20); +//│ tmp199 + tmp201 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f7, tmp198, tmp199, tmp200, tmp201, _deforest_AA_x_unused1, match_x_branch_AA5, _deforest_AA_x_unused2; +//│ match_x_branch_AA5 = function match_x_branch_AA(k, tmp202) { +//│ let scrut, _deforest_AA_x9; +//│ _deforest_AA_x9 = 2; +//│ scrut = (k1, tmp203) => { +//│ let m, param0, yf, scrut1, tmp204, tmp205, _deforest_AA_x_unused3; +//│ param0 = _deforest_AA_x9; +//│ yf = param0; +//│ tmp204 = yf; +//│ tmp205 = tmp204; +//│ m = tmp205; +//│ _deforest_AA_x_unused3 = 3; +//│ scrut1 = (k2, tmp206, m1) => { +//│ let tmp207, tmp208; +//│ tmp207 = m1 + k2; +//│ tmp208 = tmp207; +//│ return tmp206.x + tmp208 +//│ }; +//│ return runtime.safeCall(scrut1(k1, tmp203, m)) +//│ }; +//│ return runtime.safeCall(scrut(k, tmp202)) +//│ }; +//│ f7 = function f(x, y2, z, k) { +//│ let tmp202, tmp203, tmp204, tmp205; +//│ if (z === true) { +//│ tmp203 = y2 + 1; +//│ tmp204 = BB1(tmp203); +//│ } else { +//│ tmp205 = y2 - 1; +//│ tmp204 = BB1(tmp205); +//│ } +//│ tmp202 = tmp204; +//│ if (tmp202 instanceof BB1.class) { +//│ return runtime.safeCall(x(k, tmp202)) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ _deforest_AA_x_unused1 = 3; +//│ tmp198 = (k, tmp202) => { +//│ return match_x_branch_AA5(k, tmp202) +//│ }; +//│ tmp199 = f7(tmp198, 3, true, 10); +//│ _deforest_AA_x_unused2 = 5; +//│ tmp200 = (k, tmp202) => { +//│ return match_x_branch_AA5(k, tmp202) +//│ }; +//│ tmp201 = f7(tmp200, 4, false, 20); +//│ block$res38 = tmp199 + tmp201; +//│ undefined +//│ = 41 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 41 + + + +:sjs +fun test(x) = + let t = if x is + AA(AA(AA(a))) then a + t + 5 +fun f(a) = if a is + AA(AA) then 0 +let p = AA(AA(AA(10))) +test(p) + f(p) +//│ JS (unsanitized): +//│ let test7, f8, p2, tmp206, tmp207, tmp208, tmp209, tmp210; +//│ test7 = function test(x) { +//│ let t, param0, param01, param02, a2, tmp211; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a2 = param02; +//│ tmp211 = a2; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ t = tmp211; +//│ return t + 5 +//│ }; +//│ f8 = function f(a2) { +//│ let param0; +//│ if (a2 instanceof AA1.class) { +//│ param0 = a2.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp206 = AA1(10); +//│ tmp207 = AA1(tmp206); +//│ tmp208 = AA1(tmp207); +//│ p2 = tmp208; +//│ tmp209 = test7(p2); +//│ tmp210 = f8(p2); +//│ tmp209 + tmp210 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test7, f8, p2, tmp206, tmp207, tmp208, tmp209, tmp210, _deforest_AA_x9; +//│ test7 = function test(x) { +//│ let param0, param01; +//│ if (x instanceof AA1.class) { +//│ param0 = x.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ return runtime.safeCall(param01()) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f8 = function f(a2) { +//│ let param0; +//│ if (a2 instanceof AA1.class) { +//│ param0 = a2.x; +//│ if (param0 instanceof AA1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ _deforest_AA_x9 = 10; +//│ tmp206 = () => { +//│ let t, param0, a2, tmp211; +//│ param0 = _deforest_AA_x9; +//│ a2 = param0; +//│ tmp211 = a2; +//│ t = tmp211; +//│ return t + 5 +//│ }; +//│ tmp207 = AA1(tmp206); +//│ tmp208 = AA1(tmp207); +//│ p2 = tmp208; +//│ tmp209 = test7(p2); +//│ tmp210 = f8(p2); +//│ block$res40 = tmp209 + tmp210; +//│ undefined +//│ = 15 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 15 +//│ p = AA(AA(AA(10))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 1ff5e23106..4ca22a1fac 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -265,23 +265,21 @@ f(AA(AA(3))) //│ = 3 -// FIXME: -// - how to deal with the original `rest` from matches multiple levels up? -// (after rewriting it becomes dead code and contains undefined variable) -:fixme +// TODO: the computation of `t + 5 * 4 - 3 + 2 - 1` is still duplicated... :sjs fun test(x) = let t = if x is AA(AA(AA(a))) then a - t + 5 + else 4 + t + 5 * 4 - 3 + 2 - 1 fun f(a) = if a is AA(AA) then 0 let p = AA(AA(AA(10))) -test(p) + f(p) +test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ JS (unsanitized): -//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8; +//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; //│ test = function test(x) { -//│ let t, param0, param01, param02, a, tmp9; +//│ let t, param0, param01, param02, a, tmp16, tmp17, tmp18, tmp19, tmp20; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -289,18 +287,22 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a = param02; -//│ tmp9 = a; +//│ tmp16 = a; //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp16 = 4; //│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp16 = 4; //│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp16 = 4; //│ } -//│ t = tmp9; -//│ return t + 5 +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 //│ }; //│ f1 = function f(a) { //│ let param0; @@ -321,25 +323,48 @@ test(p) + f(p) //│ p = tmp6; //│ tmp7 = test(p); //│ tmp8 = f1(p); -//│ tmp7 + tmp8 +//│ tmp9 = tmp7 + tmp8; +//│ tmp10 = AA1(10); +//│ tmp11 = AA1(tmp10); +//│ tmp12 = AA1(tmp11); +//│ tmp13 = test(tmp12); +//│ tmp14 = tmp9 + tmp13; +//│ tmp15 = test(B1); +//│ tmp14 + tmp15 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, _deforest_AA_x1; +//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1; +//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x1) { +//│ let t, param0, a, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ param0 = _deforest_AA_x1; +//│ a = param0; +//│ tmp16 = a; +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 +//│ }; //│ test = function test(x) { -//│ let t, param0, param01; +//│ let t, param0, param01, tmp16, tmp17, tmp18, tmp19, tmp20; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; //│ return runtime.safeCall(param01()) //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp16 = 4; //│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp16 = 4; //│ } -//│ t = tmp_not_in_scope; -//│ return t + 5 +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 //│ }; //│ f1 = function f(a) { //│ let param0; @@ -354,393 +379,29 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ _deforest_AA_x1 = 10; +//│ _deforest_AA_x_tmp = 10; //│ tmp4 = () => { -//│ let t, param0, a, tmp9; -//│ param0 = _deforest_AA_x1; -//│ a = param0; -//│ tmp9 = a; -//│ t = tmp9; -//│ return t + 5 +//│ return match_param0_branch_AA(_deforest_AA_x_tmp) //│ }; //│ tmp5 = AA1(tmp4); //│ tmp6 = AA1(tmp5); //│ p = tmp6; //│ tmp7 = test(p); //│ tmp8 = f1(p); -//│ block$res10 = tmp7 + tmp8; -//│ undefined -//│ = 15 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 15 -//│ p = AA(AA(AA(10))) -//│ FAILURE: Unexpected lack of error to fix - - - -:fixme -:sjs -fun f(x, y, z, k) = - let tmp = if z then BB(y + 1) else BB(y - 1) - tmp.x + - if tmp is - BB then - let m = if x is - AA then - if AA(2) is - AA(yf) then yf - if AA(3) is - AA then m + k -f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) -//│ JS (unsanitized): -//│ let f2, tmp14, tmp15, tmp16, tmp17; -//│ f2 = function f(x, y, z, k) { -//│ let tmp18, m, scrut, param0, yf, scrut1, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; -//│ if (z === true) { -//│ tmp19 = y + 1; -//│ tmp20 = BB1(tmp19); -//│ } else { -//│ tmp21 = y - 1; -//│ tmp20 = BB1(tmp21); -//│ } -//│ tmp18 = tmp20; -//│ if (tmp18 instanceof BB1.class) { -//│ if (x instanceof AA1.class) { -//│ scrut = AA1(2); -//│ if (scrut instanceof AA1.class) { -//│ param0 = scrut.x; -//│ yf = param0; -//│ tmp22 = yf; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp23 = tmp22; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp23; -//│ scrut1 = AA1(3); -//│ if (scrut1 instanceof AA1.class) { -//│ tmp24 = m + k; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp25 = tmp24; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return tmp18.x + tmp25 -//│ }; -//│ tmp14 = AA1(3); -//│ tmp15 = f2(tmp14, 3, true, 10); -//│ tmp16 = AA1(5); -//│ tmp17 = f2(tmp16, 4, false, 20); -//│ tmp15 + tmp17 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f2, tmp14, tmp15, tmp16, tmp17, _deforest_AA_x_unused, match_x_branch_AA, _deforest_AA_x_unused1; -//│ match_x_branch_AA = function match_x_branch_AA(k, tmp18) { -//│ let scrut, _deforest_AA_x2; -//│ _deforest_AA_x2 = 2; -//│ scrut = (k1, tmp19) => { -//│ let m, param0, yf, scrut1, tmp20, tmp21, _deforest_AA_x_unused2; -//│ param0 = _deforest_AA_x2; -//│ yf = param0; -//│ tmp20 = yf; -//│ tmp21 = tmp20; -//│ m = tmp21; -//│ _deforest_AA_x_unused2 = 3; -//│ scrut1 = (k2, tmp22, m1) => { -//│ let tmp23, tmp24; -//│ tmp23 = m1 + k2; -//│ tmp24 = tmp23; -//│ return tmp22.x + tmp24 -//│ }; -//│ return runtime.safeCall(scrut1(k1, tmp19, m)) -//│ }; -//│ return runtime.safeCall(scrut(k, tmp18)) -//│ }; -//│ f2 = function f(x, y, z, k) { -//│ let tmp18, tmp19, tmp20, tmp21; -//│ if (z === true) { -//│ tmp19 = y + 1; -//│ tmp20 = BB1(tmp19); -//│ } else { -//│ tmp21 = y - 1; -//│ tmp20 = BB1(tmp21); -//│ } -//│ tmp18 = tmp20; -//│ if (tmp18 instanceof BB1.class) { -//│ return runtime.safeCall(x(k, tmp18)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return tmp18.x + tmp_not_in_scope -//│ }; -//│ _deforest_AA_x_unused = 3; -//│ tmp14 = (k, tmp18) => { -//│ return match_x_branch_AA(k, tmp18) -//│ }; -//│ tmp15 = f2(tmp14, 3, true, 10); -//│ _deforest_AA_x_unused1 = 5; -//│ tmp16 = (k, tmp18) => { -//│ return match_x_branch_AA(k, tmp18) -//│ }; -//│ tmp17 = f2(tmp16, 4, false, 20); -//│ block$res12 = tmp15 + tmp17; -//│ undefined -//│ = 41 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 41 -//│ FAILURE: Unexpected lack of error to fix - - - -// TODO: the computation of `t + 5 * 4 - 3 + 2 - 1` is still duplicated... -:sjs -fun test(x) = - let t = if x is - AA(AA(AA(a))) then a - else 4 - t + 5 * 4 - 3 + 2 - 1 -fun f(a) = if a is - AA(AA) then 0 -let p = AA(AA(AA(10))) -test(p) + f(p) + test(AA(AA(AA(10)))) -//│ JS (unsanitized): -//│ let test1, f3, p1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31; -//│ test1 = function test(x) { -//│ let t, param0, param01, param02, a, tmp32, tmp33, tmp34, tmp35, tmp36; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ if (param01 instanceof AA1.class) { -//│ param02 = param01.x; -//│ a = param02; -//│ tmp32 = a; -//│ } else { -//│ tmp32 = 4; -//│ } -//│ } else { -//│ tmp32 = 4; -//│ } -//│ } else { -//│ tmp32 = 4; -//│ } -//│ t = tmp32; -//│ tmp33 = 5 * 4; -//│ tmp34 = t + tmp33; -//│ tmp35 = tmp34 - 3; -//│ tmp36 = tmp35 + 2; -//│ return tmp36 - 1 -//│ }; -//│ f3 = function f(a) { -//│ let param0; -//│ if (a instanceof AA1.class) { -//│ param0 = a.x; -//│ if (param0 instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp22 = AA1(10); -//│ tmp23 = AA1(tmp22); -//│ tmp24 = AA1(tmp23); -//│ p1 = tmp24; -//│ tmp25 = test1(p1); -//│ tmp26 = f3(p1); -//│ tmp27 = tmp25 + tmp26; -//│ tmp28 = AA1(10); -//│ tmp29 = AA1(tmp28); -//│ tmp30 = AA1(tmp29); -//│ tmp31 = test1(tmp30); -//│ tmp27 + tmp31 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test1, f3, p1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1; -//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x2) { -//│ let t, param0, a, tmp32, tmp33, tmp34, tmp35, tmp36; -//│ param0 = _deforest_AA_x2; -//│ a = param0; -//│ tmp32 = a; -//│ t = tmp32; -//│ tmp33 = 5 * 4; -//│ tmp34 = t + tmp33; -//│ tmp35 = tmp34 - 3; -//│ tmp36 = tmp35 + 2; -//│ return tmp36 - 1 -//│ }; -//│ test1 = function test(x) { -//│ let t, param0, param01, tmp32, tmp33, tmp34, tmp35, tmp36; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ return runtime.safeCall(param01()) -//│ } else { -//│ tmp32 = 4; -//│ } -//│ } else { -//│ tmp32 = 4; -//│ } -//│ t = tmp32; -//│ tmp33 = 5 * 4; -//│ tmp34 = t + tmp33; -//│ tmp35 = tmp34 - 3; -//│ tmp36 = tmp35 + 2; -//│ return tmp36 - 1 -//│ }; -//│ f3 = function f(a) { -//│ let param0; -//│ if (a instanceof AA1.class) { -//│ param0 = a.x; -//│ if (param0 instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x_tmp = 10; -//│ tmp22 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp) -//│ }; -//│ tmp23 = AA1(tmp22); -//│ tmp24 = AA1(tmp23); -//│ p1 = tmp24; -//│ tmp25 = test1(p1); -//│ tmp26 = f3(p1); -//│ tmp27 = tmp25 + tmp26; +//│ tmp9 = tmp7 + tmp8; //│ _deforest_AA_x_tmp1 = 10; -//│ tmp28 = () => { +//│ tmp10 = () => { //│ return match_param0_branch_AA(_deforest_AA_x_tmp1) //│ }; -//│ tmp29 = AA1(tmp28); -//│ tmp30 = AA1(tmp29); -//│ tmp31 = test1(tmp30); -//│ block$res14 = tmp27 + tmp31; +//│ tmp11 = AA1(tmp10); +//│ tmp12 = AA1(tmp11); +//│ tmp13 = test(tmp12); +//│ tmp14 = tmp9 + tmp13; +//│ tmp15 = test(B1); +//│ block$res10 = tmp14 + tmp15; //│ undefined -//│ = 56 +//│ = 78 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 56 +//│ = 78 //│ p = AA(AA(AA(10))) - - -// TODO: `n = tmp67; return n + 3` is duplicated -:sjs -fun test(x) = - let n = if x is - AA(BB(b)) then b - AA(CC(c)) then c - n + 3 -fun c(x) = if x is AA then 0 -fun p(x) = AA(x) -test(p(BB(3))) + test(p(CC(3))) + c(p(0)) -//│ JS (unsanitized): -//│ let p2, c, test2, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50; -//│ test2 = function test(x) { -//│ let n, param0, param01, c1, param02, b, tmp51; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof BB1.class) { -//│ param02 = param0.x; -//│ b = param02; -//│ tmp51 = b; -//│ } else if (param0 instanceof CC1.class) { -//│ param01 = param0.x; -//│ c1 = param01; -//│ tmp51 = c1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp51; -//│ return n + 3 -//│ }; -//│ c = function c(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p2 = function p(x) { -//│ return AA1(x) -//│ }; -//│ tmp42 = BB1(3); -//│ tmp43 = p2(tmp42); -//│ tmp44 = test2(tmp43); -//│ tmp45 = CC1(3); -//│ tmp46 = p2(tmp45); -//│ tmp47 = test2(tmp46); -//│ tmp48 = tmp44 + tmp47; -//│ tmp49 = p2(0); -//│ tmp50 = c(tmp49); -//│ tmp48 + tmp50 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p2, c, test2, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, _deforest_BB_x, _deforest_CC_x, match_param0_rest; -//│ match_param0_rest = function match_param0_rest(tmp51) { -//│ let n; -//│ n = tmp51; -//│ return n + 3 -//│ }; -//│ test2 = function test(x) { -//│ let n, param0; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ return runtime.safeCall(param0()) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp_not_in_scope; -//│ return n + 3 -//│ }; -//│ c = function c(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p2 = function p(x) { -//│ return AA1(x) -//│ }; -//│ _deforest_BB_x = 3; -//│ tmp42 = () => { -//│ let param0, b, tmp51; -//│ param0 = _deforest_BB_x; -//│ b = param0; -//│ tmp51 = b; -//│ return match_param0_rest(tmp51) -//│ }; -//│ tmp43 = p2(tmp42); -//│ tmp44 = test2(tmp43); -//│ _deforest_CC_x = 3; -//│ tmp45 = () => { -//│ let param0, c1, tmp51; -//│ param0 = _deforest_CC_x; -//│ c1 = param0; -//│ tmp51 = c1; -//│ return match_param0_rest(tmp51) -//│ }; -//│ tmp46 = p2(tmp45); -//│ tmp47 = test2(tmp46); -//│ tmp48 = tmp44 + tmp47; -//│ tmp49 = p2(0); -//│ tmp50 = c(tmp49); -//│ block$res16 = tmp48 + tmp50; -//│ undefined -//│ = 12 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 12 From 5194522549c2c5a9681021f8c235c84ce21b4fd2 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:01:24 +0800 Subject: [PATCH 156/303] return block unchanged instead of panic for unsupported cases --- .../scala/hkmc2/codegen/Deforestation.scala | 37 ++-- .../src/test/mlscript/deforest/todos.mls | 205 +----------------- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 6 +- 3 files changed, 22 insertions(+), 226 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 40346e391e..cdbdea3786 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -98,6 +98,7 @@ trait StratVarTrait(stratState: StratVarState): lazy val asConsStrat = stratState.asConsStrat lazy val uid = stratState.uid +final case class NotDeforestableException(msg: String) extends Exception(msg) // Compute free vars for a block, without considering deforestation, used on transformed blocks // This means that for matches we don't need to consider the extra @@ -236,7 +237,7 @@ class Deforest(using TL, Raise, Elaborator.State): given Uid.Handler[StratVar]#State = StratVarUidHandler.State() import StratVarState.freshVar - def apply(p: Program) = + def apply(p: Program): Program = val mainBlk = p.main globallyDefinedVars.init(mainBlk) @@ -244,8 +245,15 @@ class Deforest(using TL, Raise, Elaborator.State): // allocate type vars for defined symbols in the blocks symToStrat.init(mainBlk) - processBlock(mainBlk) + try + processBlock(mainBlk) + catch + case NotDeforestableException(msg) => + // return the original program if deforestation is not applicable + return p + resolveConstraints + tl.log("upper:") upperBounds.foreach(u => tl.log("\t" + u)) @@ -359,21 +367,10 @@ class Deforest(using TL, Raise, Elaborator.State): val funStrat = constrFun(param, body) // TODO: handle mutiple param list constrain(funStrat, funSymStratVar.asConsStrat) funSymStratVar - case ValDefn(owner, k, sym, rhs) => ??? + case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") // only handle code inside module for now to show the // todo case of if scrut being not the same as what the user writes - case c: ClsLikeDefn if c.sym.asMod.isDefined => - c.methods.foreach: - case FunDefn(_, sym, params, body) => - val funSymStratVar = freshVar(sym.nme) - symToStrat += sym -> funSymStratVar._1 - val param = params.head match - case ParamList(flags, params, restParam) => params - val funStrat = constrFun(param, body) // TODO: handle mutiple param list - constrain(funStrat, funSymStratVar._2) - funSymStratVar._1 - processBlock(c.ctor) - case _ => ??? + case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") processBlock(rest) case End(msg) => NoProd case Throw(exc) => NoProd @@ -430,12 +427,12 @@ class Deforest(using TL, Raise, Elaborator.State): val appRes = freshVar() constrain(funTpe, ConsFun(argsTpe, appRes._2)) appRes._1 - - case Value.This(sym) => ??? + + case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") case Value.Lit(lit) => ??? case Value.Arr(elems) => ??? - case Instantiate(cls, args) => ??? + case Instantiate(cls, args) => throw NotDeforestableException("No support for `instantiate` yet") case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => @@ -458,11 +455,11 @@ class Deforest(using TL, Raise, Elaborator.State): case None => symToStrat.getStratOfSym(l) case Some(m) => Ctor(m, Map.empty, v.uid) - case Value.This(sym) => ??? + case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") case Value.Lit(lit) => NoProd case Value.Lam(ParamList(_, params, N), body) => constrFun(params, body) - case Value.Arr(elems) => ??? + case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 4ca22a1fac..d701ace06c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -15,207 +15,6 @@ object None class Some(val x) - -// TODO: keep track of the equality between `Test.#s` and scrut. -// The `s` in `if s is ...` is actually `Test.#s`, -// and `s.x`s in branches are actually `Test.#s.x`. -// This causes that `Test.#s` has 3 consumption sites, -// and the `A` or `B` in `Test.#s.x` cannot get the branch-specific information -// to be considered as having only 1 consumption site. -:sjs -module Test with - fun f1(a) = if a is - A then 1 - B then 2 - C then 3 - fun f2(a) = if a is - A then 4 - B then 5 - C then 6 - let s = if true then AA(A) else BB(B) - if s is - AA then f1(s.x) - BB then f2(s.x) -//│ JS (unsanitized): -//│ let Test1; -//│ Test1 = class Test { -//│ static #s; -//│ static { -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ Test.#s = tmp; -//│ scrut1 = Test.#s; -//│ if (scrut1 instanceof AA1.class) { -//│ Test.f1(Test.#s.x) -//│ } else if (scrut1 instanceof BB1.class) { -//│ Test.f2(Test.#s.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f1(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else if (a instanceof B1.class) { -//│ return 2 -//│ } else if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f2(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 4 -//│ } else if (a1 instanceof B1.class) { -//│ return 5 -//│ } else if (a1 instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let Test1; -//│ Test1 = class Test { -//│ static #s1; -//│ static { -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ Test.#s1 = tmp; -//│ scrut1 = Test.#s1; -//│ if (scrut1 instanceof AA1.class) { -//│ Test.f1(Test.#s1.x) -//│ } else if (scrut1 instanceof BB1.class) { -//│ Test.f2(Test.#s1.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f1(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else if (a instanceof B1.class) { -//│ return 2 -//│ } else if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f2(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 4 -//│ } else if (a1 instanceof B1.class) { -//│ return 5 -//│ } else if (a1 instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ block$res4 = undefined; -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// if `Test.#s.x` is always consumed by `f` -// the `A` and `B` can be fused, but not the outer `AA` or `BB` -:sjs -module Test with - fun f(a) = if a is - A then 1 - B then 2 - C then 3 - let s = if true then AA(A) else BB(B) - if s is - AA then f(s.x) - BB then f(s.x) -//│ JS (unsanitized): -//│ let Test3; -//│ Test3 = class Test { -//│ static #s; -//│ static { -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ Test.#s = tmp; -//│ scrut1 = Test.#s; -//│ if (scrut1 instanceof AA1.class) { -//│ Test.f(Test.#s.x) -//│ } else if (scrut1 instanceof BB1.class) { -//│ Test.f(Test.#s.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else if (a instanceof B1.class) { -//│ return 2 -//│ } else if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let Test3; -//│ Test3 = class Test { -//│ static #s1; -//│ static { -//│ let scrut, scrut1, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(() => { -//│ return 1 -//│ }); -//│ } else { -//│ tmp = BB1(() => { -//│ return 2 -//│ }); -//│ } -//│ Test.#s1 = tmp; -//│ scrut1 = Test.#s1; -//│ if (scrut1 instanceof AA1.class) { -//│ Test.f(Test.#s1.x) -//│ } else if (scrut1 instanceof BB1.class) { -//│ Test.f(Test.#s1.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } -//│ static f(a) { -//│ return runtime.safeCall(a()) -//│ } -//│ static toString() { return "Test"; } -//│ }; -//│ block$res6 = undefined; -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - // similar as above, since `x.x` is considered as being consumed // at two places,the inner match is not fused :sjs @@ -258,7 +57,7 @@ f(AA(AA(3))) //│ throw new this.Error("match error"); //│ } //│ }; -//│ block$res8 = f(tmp1); +//│ block$res4 = f(tmp1); //│ undefined //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -398,7 +197,7 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ tmp13 = test(tmp12); //│ tmp14 = tmp9 + tmp13; //│ tmp15 = test(B1); -//│ block$res10 = tmp14 + tmp15; +//│ block$res6 = tmp14 + tmp15; //│ undefined //│ = 78 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index fd0f429243..30c1d68c74 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -252,9 +252,9 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: host.execute(s"$resNme = undefined") mkQuery(preStr, jsStr): stdout => - stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) - .foreach: line => - output(s"> ${line}") + stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) + .foreach: line => + output(s"> ${line}") if deforestFlag.isSet && showJS.isUnset then mkQuery(preStr, jsStr)(using hostDeforest)(_ => ()) From 4b27f0585889916f8f8474e01823c86a9495b9d7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:05:28 +0800 Subject: [PATCH 157/303] wip: no need for two repl hosts --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 30c1d68c74..ee17436b7f 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -58,14 +58,15 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: lazy val hostDeforest = hostDeforestCreated = true - given TL = replTL - val h = ReplHost(rootPath) - h.execute(s"const $runtimeNme = (await import(\"${runtimeFile}\")).default;") match - case ReplHost.Result(msg) => - if msg.startsWith("Uncaught") then output(s"Failed to load runtime: $msg") - case r => output(s"Failed to load runtime: $r") - h - + // given TL = replTL + // val h = ReplHost(rootPath) + // h.execute(s"const $runtimeNme = (await import(\"${runtimeFile}\")).default;") match + // case ReplHost.Result(msg) => + // if msg.startsWith("Uncaught") then output(s"Failed to load runtime: $msg") + // case r => output(s"Failed to load runtime: $r") + // h + host + private var hostCreated = false private var hostDeforestCreated = false @@ -256,8 +257,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: .foreach: line => output(s"> ${line}") - if deforestFlag.isSet && showJS.isUnset then - mkQuery(preStr, jsStr)(using hostDeforest)(_ => ()) + // if deforestFlag.isSet && showJS.isUnset then + // mkQuery(preStr, jsStr)(using hostDeforest)(_ => ()) From 0739bf60f495dfc3decfff4432daf763ca01334d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:42:09 +0800 Subject: [PATCH 158/303] better handling for noprod --- .../scala/hkmc2/codegen/Deforestation.scala | 98 ++- .../test/mlscript/deforest/nestedMatch.mls | 800 +++++++++--------- 2 files changed, 466 insertions(+), 432 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cdbdea3786..1b3afcc38c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -465,27 +465,37 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel]) - + case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool) + case class DtorSource(ctors: Set[CtorExpr], noProd: Bool) object ctorDests: - val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil)) + val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false)) def update(ctor: CtorExpr, m: Match) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels)) - case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil)) + case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) + case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false)) def update(ctor: CtorExpr, s: FieldSel) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels)) => Some(CtorDest(matches, s :: sels)) - case None => Some(CtorDest(Map.empty, s :: Nil)) + case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, s :: sels, noCons)) + case None => Some(CtorDest(Map.empty, s :: Nil, false)) + def update(ctor: CtorExpr, n: NoCons.type) = ctorDests.updateWith(ctor): + case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels, true)) + case None => Some(CtorDest(Map.empty, Nil, true)) def get(ctor: CtorExpr) = ctorDests.get(ctor) object dtorSources: - val dtorSources = mutable.Map.empty[DtorExpr, Set[ResultId]].withDefaultValue(Set.empty) + val dtorSources = mutable.Map.empty[DtorExpr, DtorSource].withDefaultValue(DtorSource(Set.empty, false)) private def getDtorExprOfResultId(i: ResultId) = i.getResult match case s: Select => DtorExpr.Sel(i) case r: Value.Ref => DtorExpr.Match(i) case _ => ??? // unreachable def update(dtor: ResultId, ctor: ResultId) = val dtorExpr = getDtorExprOfResultId(dtor) - dtorSources += dtorExpr -> (dtorSources(dtorExpr) + ctor) + dtorSources.updateWith(dtorExpr): + case None => Some(DtorSource(Set(ctor), false)) + case Some(DtorSource(ctors, noProd)) => Some(DtorSource(ctors + ctor, noProd)) + def update(dtor: ResultId, noProd: NoProd.type) = + val dtorExpr = getDtorExprOfResultId(dtor) + dtorSources.updateWith(dtorExpr): + case None => Some(DtorSource(Set.empty, true)) + case Some(DtorSource(ctors, noProd)) => Some(DtorSource(ctors, true)) def get(dtor: ResultId) = dtorSources.get(getDtorExprOfResultId(dtor)) @@ -509,7 +519,7 @@ class Deforest(using TL, Raise, Elaborator.State): dtorSources.update(selDtor.expr, expr) args.find(a => a._1.id == field).map: p => handle(p._2 -> consVar) - case (Ctor(ctor, args, _), ConsFun(l, r)) => ??? + case (Ctor(ctor, args, _), ConsFun(l, r)) => () // ignore case (p: ProdVar, _) => upperBounds += p.uid -> (cons :: upperBounds(p.uid)) @@ -535,16 +545,22 @@ class Deforest(using TL, Raise, Elaborator.State): () case (_: ProdVar, _) => ??? // unreachable, should be handled above case _ => handle(prod -> u) - case (Ctor(ctor, args, _), NoCons) => () - case (ProdFun(l, r), Dtor(cls)) => ??? - case (ProdFun(l, r), FieldSel(field, consVar)) => ??? + case (Ctor(ctor, args, expr), NoCons) => + ctorDests.update(expr, NoCons) + args.valuesIterator.foreach(a => handle(a, NoCons)) + case (ProdFun(l, r), Dtor(cls)) => () // ignore + case (ProdFun(l, r), FieldSel(field, consVar)) => () // ignore case (ProdFun(lp, rp), ConsFun(lc, rc)) => lc.zip(lp).foreach(handle) handle(rp, rc) - case (ProdFun(l, r), NoCons) => () - case (NoProd, Dtor(cls)) => () - case (NoProd, FieldSel(field, consVar)) => () - case (NoProd, ConsFun(l, r)) => () + case (ProdFun(l, r), NoCons) => + l.foreach(a => handle(NoProd, a)) + handle(r, NoCons) + case (NoProd, Dtor(scrut)) => dtorSources.update(scrut, NoProd) + case (NoProd, fSel@FieldSel(field, consVar)) => dtorSources.update(fSel.expr, NoProd) + case (NoProd, ConsFun(l, r)) => + l.foreach(a => handle(a, NoCons)) + handle(NoProd, r) case (NoProd, NoCons) => () constraints.foreach(c => handle(c)(using mutable.Set.empty)) @@ -554,14 +570,14 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val resolveClashes = type CtorToDtor = Map[CtorExpr, CtorDest] - type DtorToCtor = Map[DtorExpr, Set[CtorExpr]] + type DtorToCtor = Map[DtorExpr, DtorSource] def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = if rm.isEmpty then ctorDests -> dtorSources else val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) - removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels) => + removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) }.toSet) @@ -570,24 +586,28 @@ class Deforest(using TL, Raise, Elaborator.State): ctorDests -> dtorSources else val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) - removeCtor(ctorDests, newDtorSources, toDelete.values.flatten.toSet) + removeCtor(ctorDests, newDtorSources, toDelete.values.map(_.ctors).flatten.toSet) val ctorToDtor = ctorDests.ctorDests.toMap val dtorToCtor = dtorSources.dtorSources.toMap - val removeClashes = removeCtor( - ctorToDtor, - dtorToCtor, - ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels) => - (dtors.size == 0 && sels.size == 1) - || (dtors.size == 1 && { - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - sels.forall { s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee - case _ => false } - }) - }.keySet - ) + val removeClashes = + val tmpRes = removeCtor( + ctorToDtor, + dtorToCtor, + ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => + ((dtors.size == 0 && sels.size == 1) + || (dtors.size == 1 && { + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult + sels.forall { s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee + case _ => false } + })) + && !noCons + }.keySet + ) + removeDtor(tmpRes._1, tmpRes._2, tmpRes._2.filter(_._2.noProd).keySet) + val removeCycle = { def getCtorInArm(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = @@ -618,7 +638,7 @@ class Deforest(using TL, Raise, Elaborator.State): val newCtorsAndNewMatches = ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => removeClashes._1.get(c).flatMap: - case CtorDest(matches, sels) => matches.values.headOption.map(m => c -> m) + case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) if newCtorsAndNewMatches.isEmpty then Set.empty @@ -629,7 +649,7 @@ class Deforest(using TL, Raise, Elaborator.State): go(Set(ctor -> dtor)) val toRmCtor = removeClashes._1.flatMap: - case (c, CtorDest(matches, sels)) => + case (c, CtorDest(matches, sels, false)) => assert(matches.size <= 1) matches.values.flatMap(m => findCycle(c, m)) @@ -647,7 +667,7 @@ class Deforest(using TL, Raise, Elaborator.State): // we need only one CtorFinalDest per arm for each pat mat expr val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, Opt[CtorFinalDest]] - resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels)) => + resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels, false)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head.expr)) else if dtors.size == 0 && sels.size > 1 then @@ -673,7 +693,7 @@ class Deforest(using TL, Raise, Elaborator.State): val c = cOrMod.asCls.get // if this arm is used more than once, should be var symbol because the arm body will be // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).count(getClsSymOfUid(_) === c) > 1 + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } selsInArms.foreach: fs => @@ -814,7 +834,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend case Return(res, implct) => Return(res, false) case t => t - if d.resolveClashes._2(DtorExpr.Match(scrut)).count{c => + if d.resolveClashes._2(DtorExpr.Match(scrut)).ctors.count{c => if !isDflt then c.getClsSymOfUid === cls else m.arms.find{ case (Case.Cls(c1, _), _) => c1 === c.getClsSymOfUid }.isEmpty } > 1 then @@ -914,7 +934,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend nonFlatten.flattened // no need to build a new function for empty rest, or if the rest is only going to be used once - if restRewritten.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).map(c => c.getClsSymOfUid).size == 1) then + if restRewritten.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).ctors.map(c => c.getClsSymOfUid).size == 1) then val res = N -> restRewritten store += s -> res res diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 825eaea4fe..cb671e72a8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -582,13 +582,13 @@ fun f1(x) = if x is BB(x2) then if x2 is CC(x3) then x3 -fun f2(x) = if x is AA then "f2" + x.x +fun f2(x) = if x is AA then "f2" fun f3(x) = if x is CC then "f3" + x.x fun aa(x) = AA(x) let cc = CC("cc") -f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) +f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) //│ JS (unsanitized): -//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81; +//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83; //│ f11 = function f1(x) { //│ let param0, x1, param01, x2, param02, x3; //│ if (x instanceof AA1.class) { @@ -613,7 +613,7 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ }; //│ f21 = function f2(x) { //│ if (x instanceof AA1.class) { -//│ return "f2" + x.x +//│ return "f2" //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -633,14 +633,28 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ tmp75 = BB1(cc); //│ tmp76 = aa(tmp75); //│ tmp77 = f11(tmp76); -//│ tmp78 = aa("0"); -//│ tmp79 = f21(tmp78); -//│ tmp80 = tmp77 + tmp79; -//│ tmp81 = f31(cc); -//│ tmp80 + tmp81 +//│ tmp78 = CC1(0); +//│ tmp79 = BB1(tmp78); +//│ tmp80 = aa(tmp79); +//│ tmp81 = f21(tmp80); +//│ tmp82 = tmp77 + tmp81; +//│ tmp83 = f31(cc); +//│ tmp82 + tmp83 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, _deforest_BB_x1; +//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, _deforest_BB_x_tmp2, match_x1_branch_BB, _deforest_BB_x_tmp3; +//│ match_x1_branch_BB = function match_x1_branch_BB(_deforest_BB_x1) { +//│ let param0, x2, param01, x3; +//│ param0 = _deforest_BB_x1; +//│ x2 = param0; +//│ if (x2 instanceof CC1.class) { +//│ param01 = x2.x; +//│ x3 = param01; +//│ return x3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; //│ f11 = function f1(x) { //│ let param0, x1; //│ if (x instanceof AA1.class) { @@ -653,7 +667,7 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ }; //│ f21 = function f2(x) { //│ if (x instanceof AA1.class) { -//│ return "f2" + x.x +//│ return "f2" //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -670,30 +684,26 @@ f1(aa(BB(cc))) + f2(aa("0")) + f3(cc) //│ }; //│ tmp74 = CC1("cc"); //│ cc = tmp74; -//│ _deforest_BB_x1 = cc; +//│ _deforest_BB_x_tmp2 = cc; //│ tmp75 = () => { -//│ let param0, x2, param01, x3; -//│ param0 = _deforest_BB_x1; -//│ x2 = param0; -//│ if (x2 instanceof CC1.class) { -//│ param01 = x2.x; -//│ x3 = param01; -//│ return x3 -//│ } else { -//│ throw new this.Error("match error"); -//│ } +//│ return match_x1_branch_BB(_deforest_BB_x_tmp2) //│ }; //│ tmp76 = aa(tmp75); //│ tmp77 = f11(tmp76); -//│ tmp78 = aa("0"); -//│ tmp79 = f21(tmp78); -//│ tmp80 = tmp77 + tmp79; -//│ tmp81 = f31(cc); -//│ block$res18 = tmp80 + tmp81; +//│ tmp78 = CC1(0); +//│ _deforest_BB_x_tmp3 = tmp78; +//│ tmp79 = () => { +//│ return match_x1_branch_BB(_deforest_BB_x_tmp3) +//│ }; +//│ tmp80 = aa(tmp79); +//│ tmp81 = f21(tmp80); +//│ tmp82 = tmp77 + tmp81; +//│ tmp83 = f31(cc); +//│ block$res18 = tmp82 + tmp83; //│ undefined -//│ = "ccf20f3cc" +//│ = "ccf2f3cc" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "ccf20f3cc" +//│ = "ccf2f3cc" //│ cc = CC("cc") @@ -707,41 +717,41 @@ fun c(x, y) = if x is let y = A c(AA(2), y) //│ JS (unsanitized): -//│ let c, y, tmp90; +//│ let c, y, tmp94; //│ c = function c(x, y1) { -//│ let t, tmp91; +//│ let t, tmp95; //│ if (x instanceof AA1.class) { //│ if (y1 instanceof A1.class) { -//│ tmp91 = 2; +//│ tmp95 = 2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp91; +//│ t = tmp95; //│ return t + x.x //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; //│ y = A1; -//│ tmp90 = AA1(2); -//│ c(tmp90, y) +//│ tmp94 = AA1(2); +//│ c(tmp94, y) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c, y, tmp90, _deforest_AA_x5; +//│ let c, y, tmp94, _deforest_AA_x5; //│ c = function c(x, y1) { //│ return runtime.safeCall(x(y1)) //│ }; //│ y = (_deforest_AA_x6) => { -//│ let t, tmp91; -//│ tmp91 = 2; -//│ t = tmp91; +//│ let t, tmp95; +//│ tmp95 = 2; +//│ t = tmp95; //│ return t + _deforest_AA_x6 //│ }; //│ _deforest_AA_x5 = 2; -//│ tmp90 = (y1) => { +//│ tmp94 = (y1) => { //│ return runtime.safeCall(y1(_deforest_AA_x5)) //│ }; -//│ block$res20 = c(tmp90, y); +//│ block$res20 = c(tmp94, y); //│ undefined //│ = 4 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -759,16 +769,16 @@ fun c2(y) = if y is A then 3 let y = A c(AA(2), y) + c2(y) //│ JS (unsanitized): -//│ let c1, c21, y1, tmp92, tmp93, tmp94; +//│ let c1, c21, y1, tmp96, tmp97, tmp98; //│ c1 = function c(x, y2) { -//│ let t, tmp95; +//│ let t, tmp99; //│ if (x instanceof AA1.class) { //│ if (y2 instanceof A1.class) { -//│ tmp95 = 2 + x.x; +//│ tmp99 = 2 + x.x; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp95; +//│ t = tmp99; //│ return t + x.x //│ } else { //│ throw new globalThis.Error("match error"); @@ -782,13 +792,13 @@ c(AA(2), y) + c2(y) //│ } //│ }; //│ y1 = A1; -//│ tmp92 = AA1(2); -//│ tmp93 = c1(tmp92, y1); -//│ tmp94 = c21(y1); -//│ tmp93 + tmp94 +//│ tmp96 = AA1(2); +//│ tmp97 = c1(tmp96, y1); +//│ tmp98 = c21(y1); +//│ tmp97 + tmp98 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c1, c21, y1, tmp92, tmp93, tmp94, _deforest_AA_x6; +//│ let c1, c21, y1, tmp96, tmp97, tmp98, _deforest_AA_x6; //│ c1 = function c(x, y2) { //│ return runtime.safeCall(x(y2)) //│ }; @@ -801,19 +811,19 @@ c(AA(2), y) + c2(y) //│ }; //│ y1 = A1; //│ _deforest_AA_x6 = 2; -//│ tmp92 = (y2) => { -//│ let t, tmp95; +//│ tmp96 = (y2) => { +//│ let t, tmp99; //│ if (y2 instanceof A1.class) { -//│ tmp95 = 2 + _deforest_AA_x6; +//│ tmp99 = 2 + _deforest_AA_x6; //│ } else { //│ throw new this.Error("match error"); //│ } -//│ t = tmp95; +//│ t = tmp99; //│ return t + _deforest_AA_x6 //│ }; -//│ tmp93 = c1(tmp92, y1); -//│ tmp94 = c21(y1); -//│ block$res22 = tmp93 + tmp94; +//│ tmp97 = c1(tmp96, y1); +//│ tmp98 = c21(y1); +//│ block$res22 = tmp97 + tmp98; //│ undefined //│ = 9 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -826,16 +836,16 @@ c(AA(2), y) + c2(y) fun test(x) = let t = if x is AA(AA(AA(a))) then a - AA(x) then x + AA then "3" t + "5" fun f(a) = if a is AA(AA) then "0" let p = AA(AA(AA("10"))) -test(p) + f(p) + test(AA("3")) +test(p) + f(p) + test(AA(A)) //│ JS (unsanitized): -//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105; +//│ let test1, f5, p, tmp102, tmp103, tmp104, tmp105, tmp106, tmp107, tmp108, tmp109; //│ test1 = function test(x) { -//│ let t, param0, x1, param01, param02, a1, tmp106; +//│ let t, param0, param01, param02, a1, tmp110; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -843,19 +853,17 @@ test(p) + f(p) + test(AA("3")) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a1 = param02; -//│ tmp106 = a1; +//│ tmp110 = a1; //│ } else { -//│ x1 = param0; -//│ tmp106 = x1; +//│ tmp110 = "3"; //│ } //│ } else { -//│ x1 = param0; -//│ tmp106 = x1; +//│ tmp110 = "3"; //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp106; +//│ t = tmp110; //│ return t + "5" //│ }; //│ f5 = function f(a1) { @@ -871,34 +879,33 @@ test(p) + f(p) + test(AA("3")) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp98 = AA1("10"); -//│ tmp99 = AA1(tmp98); -//│ tmp100 = AA1(tmp99); -//│ p = tmp100; -//│ tmp101 = test1(p); -//│ tmp102 = f5(p); -//│ tmp103 = tmp101 + tmp102; -//│ tmp104 = AA1("3"); -//│ tmp105 = test1(tmp104); -//│ tmp103 + tmp105 +//│ tmp102 = AA1("10"); +//│ tmp103 = AA1(tmp102); +//│ tmp104 = AA1(tmp103); +//│ p = tmp104; +//│ tmp105 = test1(p); +//│ tmp106 = f5(p); +//│ tmp107 = tmp105 + tmp106; +//│ tmp108 = AA1(A1); +//│ tmp109 = test1(tmp108); +//│ tmp107 + tmp109 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test1, f5, p, tmp98, tmp99, tmp100, tmp101, tmp102, tmp103, tmp104, tmp105, _deforest_AA_x7; +//│ let test1, f5, p, tmp102, tmp103, tmp104, tmp105, tmp106, tmp107, tmp108, tmp109, _deforest_AA_x7; //│ test1 = function test(x) { -//│ let t, param0, x1, param01, tmp106; +//│ let t, param0, param01, tmp110; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; -//│ return runtime.safeCall(param01(param0)) +//│ return runtime.safeCall(param01()) //│ } else { -//│ x1 = param0; -//│ tmp106 = x1; +//│ tmp110 = "3"; //│ } //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp106; +//│ t = tmp110; //│ return t + "5" //│ }; //│ f5 = function f(a1) { @@ -915,23 +922,23 @@ test(p) + f(p) + test(AA("3")) //│ } //│ }; //│ _deforest_AA_x7 = "10"; -//│ tmp98 = (param0) => { -//│ let t, param01, a1, tmp106; -//│ param01 = _deforest_AA_x7; -//│ a1 = param01; -//│ tmp106 = a1; -//│ t = tmp106; +//│ tmp102 = () => { +//│ let t, param0, a1, tmp110; +//│ param0 = _deforest_AA_x7; +//│ a1 = param0; +//│ tmp110 = a1; +//│ t = tmp110; //│ return t + "5" //│ }; -//│ tmp99 = AA1(tmp98); -//│ tmp100 = AA1(tmp99); -//│ p = tmp100; -//│ tmp101 = test1(p); -//│ tmp102 = f5(p); -//│ tmp103 = tmp101 + tmp102; -//│ tmp104 = AA1("3"); -//│ tmp105 = test1(tmp104); -//│ block$res24 = tmp103 + tmp105; +//│ tmp103 = AA1(tmp102); +//│ tmp104 = AA1(tmp103); +//│ p = tmp104; +//│ tmp105 = test1(p); +//│ tmp106 = f5(p); +//│ tmp107 = tmp105 + tmp106; +//│ tmp108 = AA1(A1); +//│ tmp109 = test1(tmp108); +//│ block$res24 = tmp107 + tmp109; //│ undefined //│ = "105035" //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -955,30 +962,30 @@ fun f(x, y) = let aa = AA(3) f(aa, 3) + f(aa, 4) //│ JS (unsanitized): -//│ let f6, aa1, tmp114, tmp115, tmp116; +//│ let f6, aa1, tmp118, tmp119, tmp120; //│ f6 = function f(x, y2) { -//│ let tmp117, m, scrut, param0, yf, scrut1, tmp118, tmp119, tmp120, tmp121; -//│ tmp118 = y2 + 1; -//│ tmp119 = BB1(tmp118); -//│ tmp117 = tmp119; -//│ if (tmp117 instanceof BB1.class) { +//│ let tmp121, m, scrut, param0, yf, scrut1, tmp122, tmp123, tmp124, tmp125; +//│ tmp122 = y2 + 1; +//│ tmp123 = BB1(tmp122); +//│ tmp121 = tmp123; +//│ if (tmp121 instanceof BB1.class) { //│ if (x instanceof AA1.class) { //│ scrut = AA1(2); //│ if (scrut instanceof AA1.class) { //│ param0 = scrut.x; //│ yf = param0; -//│ tmp120 = yf; +//│ tmp124 = yf; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp121 = tmp120; +//│ tmp125 = tmp124; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp121; +//│ m = tmp125; //│ scrut1 = AA1(3); //│ if (scrut1 instanceof AA1.class) { -//│ return m + tmp117.x +//│ return m + tmp121.x //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -986,47 +993,47 @@ f(aa, 3) + f(aa, 4) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp114 = AA1(3); -//│ aa1 = tmp114; -//│ tmp115 = f6(aa1, 3); -//│ tmp116 = f6(aa1, 4); -//│ tmp115 + tmp116 +//│ tmp118 = AA1(3); +//│ aa1 = tmp118; +//│ tmp119 = f6(aa1, 3); +//│ tmp120 = f6(aa1, 4); +//│ tmp119 + tmp120 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f6, aa1, tmp114, tmp115, tmp116, _deforest_AA_x_unused; +//│ let f6, aa1, tmp118, tmp119, tmp120, _deforest_AA_x_unused; //│ f6 = function f(x, y2) { -//│ let tmp117, tmp118, tmp119, _deforest_BB_x2; -//│ tmp118 = y2 + 1; -//│ _deforest_BB_x2 = tmp118; -//│ tmp119 = (x1) => { -//│ return runtime.safeCall(x1(_deforest_BB_x2)) +//│ let tmp121, tmp122, tmp123, _deforest_BB_x1; +//│ tmp122 = y2 + 1; +//│ _deforest_BB_x1 = tmp122; +//│ tmp123 = (x1) => { +//│ return runtime.safeCall(x1(_deforest_BB_x1)) //│ }; -//│ tmp117 = tmp119; -//│ return runtime.safeCall(tmp117(x)) +//│ tmp121 = tmp123; +//│ return runtime.safeCall(tmp121(x)) //│ }; //│ _deforest_AA_x_unused = 3; -//│ tmp114 = (_deforest_BB_x2) => { +//│ tmp118 = (_deforest_BB_x1) => { //│ let scrut, _deforest_AA_x8; //│ _deforest_AA_x8 = 2; -//│ scrut = (_deforest_BB_x3) => { -//│ let m, param0, yf, scrut1, tmp117, tmp118, _deforest_AA_x_unused1; +//│ scrut = (_deforest_BB_x2) => { +//│ let m, param0, yf, scrut1, tmp121, tmp122, _deforest_AA_x_unused1; //│ param0 = _deforest_AA_x8; //│ yf = param0; -//│ tmp117 = yf; -//│ tmp118 = tmp117; -//│ m = tmp118; +//│ tmp121 = yf; +//│ tmp122 = tmp121; +//│ m = tmp122; //│ _deforest_AA_x_unused1 = 3; -//│ scrut1 = (m1, _deforest_BB_x4) => { -//│ return m1 + _deforest_BB_x4 +//│ scrut1 = (m1, _deforest_BB_x3) => { +//│ return m1 + _deforest_BB_x3 //│ }; -//│ return runtime.safeCall(scrut1(m, _deforest_BB_x3)) +//│ return runtime.safeCall(scrut1(m, _deforest_BB_x2)) //│ }; -//│ return runtime.safeCall(scrut(_deforest_BB_x2)) +//│ return runtime.safeCall(scrut(_deforest_BB_x1)) //│ }; -//│ aa1 = tmp114; -//│ tmp115 = f6(aa1, 3); -//│ tmp116 = f6(aa1, 4); -//│ block$res26 = tmp115 + tmp116; +//│ aa1 = tmp118; +//│ tmp119 = f6(aa1, 3); +//│ tmp120 = f6(aa1, 4); +//│ block$res26 = tmp119 + tmp120; //│ undefined //│ = 13 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1046,57 +1053,57 @@ fun test(n, p, q) = AA(y) then k + y test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ JS (unsanitized): -//│ let test2, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125; +//│ let test2, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129; //│ test2 = function test(n, p1, q) { -//│ let scrut, k, param0, x, param01, y2, tmp126, tmp127, tmp128; +//│ let scrut, k, param0, x, param01, y2, tmp130, tmp131, tmp132; //│ scrut = AA1(0); //│ if (scrut instanceof AA1.class) { //│ if (p1 instanceof AA1.class) { //│ param0 = p1.x; //│ x = param0; -//│ tmp126 = x; +//│ tmp130 = x; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ k = tmp126; +//│ k = tmp130; //│ if (q instanceof AA1.class) { //│ param01 = q.x; //│ y2 = param01; -//│ tmp127 = k + y2; +//│ tmp131 = k + y2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp128 = tmp127; +//│ tmp132 = tmp131; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return n + tmp128 -//│ }; -//│ tmp120 = AA1(2); -//│ tmp121 = AA1(3); -//│ tmp122 = test2(3, tmp120, tmp121); -//│ tmp123 = AA1(2); -//│ tmp124 = AA1(3); -//│ tmp125 = test2(3, tmp123, tmp124); -//│ tmp122 + tmp125 +//│ return n + tmp132 +//│ }; +//│ tmp124 = AA1(2); +//│ tmp125 = AA1(3); +//│ tmp126 = test2(3, tmp124, tmp125); +//│ tmp127 = AA1(2); +//│ tmp128 = AA1(3); +//│ tmp129 = test2(3, tmp127, tmp128); +//│ tmp126 + tmp129 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test2, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125, _deforest_AA_x_tmp14, match_p_branch_AA, _deforest_AA_x_tmp15, match_q_branch_AA, _deforest_AA_x_tmp16, _deforest_AA_x_tmp17; +//│ let test2, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, _deforest_AA_x_tmp14, match_p_branch_AA, _deforest_AA_x_tmp15, match_q_branch_AA, _deforest_AA_x_tmp16, _deforest_AA_x_tmp17; //│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x8) { -//│ let k, param0, x, tmp126; +//│ let k, param0, x, tmp130; //│ param0 = _deforest_AA_x8; //│ x = param0; -//│ tmp126 = x; -//│ k = tmp126; +//│ tmp130 = x; +//│ k = tmp130; //│ return runtime.safeCall(q(n, k)) //│ }; //│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x8) { -//│ let param0, y2, tmp126, tmp127; +//│ let param0, y2, tmp130, tmp131; //│ param0 = _deforest_AA_x8; //│ y2 = param0; -//│ tmp126 = k + y2; -//│ tmp127 = tmp126; -//│ return n + tmp127 +//│ tmp130 = k + y2; +//│ tmp131 = tmp130; +//│ return n + tmp131 //│ }; //│ test2 = function test(n, p1, q) { //│ let scrut, _deforest_AA_x_unused1; @@ -1107,24 +1114,24 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ return runtime.safeCall(scrut(n, p1, q)) //│ }; //│ _deforest_AA_x_tmp14 = 2; -//│ tmp120 = (n, q) => { +//│ tmp124 = (n, q) => { //│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp14) //│ }; //│ _deforest_AA_x_tmp15 = 3; -//│ tmp121 = (n, k) => { +//│ tmp125 = (n, k) => { //│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp15) //│ }; -//│ tmp122 = test2(3, tmp120, tmp121); +//│ tmp126 = test2(3, tmp124, tmp125); //│ _deforest_AA_x_tmp16 = 2; -//│ tmp123 = (n, q) => { +//│ tmp127 = (n, q) => { //│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp16) //│ }; //│ _deforest_AA_x_tmp17 = 3; -//│ tmp124 = (n, k) => { +//│ tmp128 = (n, k) => { //│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp17) //│ }; -//│ tmp125 = test2(3, tmp123, tmp124); -//│ block$res28 = tmp122 + tmp125; +//│ tmp129 = test2(3, tmp127, tmp128); +//│ block$res28 = tmp126 + tmp129; //│ undefined //│ = 16 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1149,9 +1156,9 @@ let a = A fun c(a) = if a is A then 0 test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ JS (unsanitized): -//│ let c3, test3, a1, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139; +//│ let c3, test3, a1, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143; //│ test3 = function test(n, p1, q, a2) { -//│ let o, k, param0, x, param01, y2, tmp140, tmp141, tmp142, tmp143; +//│ let o, k, param0, x, param01, y2, tmp144, tmp145, tmp146, tmp147; //│ o = undefined; //│ if (a2 instanceof A1.class) { //│ k = undefined; @@ -1159,20 +1166,20 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ param0 = p1.x; //│ x = param0; //│ k = x; -//│ tmp140 = runtime.Unit; +//│ tmp144 = runtime.Unit; //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ if (q instanceof AA1.class) { //│ param01 = q.x; //│ y2 = param01; -//│ tmp141 = k + y2; -//│ o = tmp141; -//│ tmp142 = runtime.Unit; +//│ tmp145 = k + y2; +//│ o = tmp145; +//│ tmp146 = runtime.Unit; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp143 = tmp142; +//│ tmp147 = tmp146; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1186,34 +1193,34 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ } //│ }; //│ a1 = A1; -//│ tmp132 = AA1(2); -//│ tmp133 = AA1(3); -//│ tmp134 = test3(1, tmp132, tmp133, a1); -//│ tmp135 = AA1(2); -//│ tmp136 = AA1(3); -//│ tmp137 = test3(2, tmp135, tmp136, a1); -//│ tmp138 = tmp134 + tmp137; -//│ tmp139 = c3(a1); -//│ tmp138 + tmp139 +//│ tmp136 = AA1(2); +//│ tmp137 = AA1(3); +//│ tmp138 = test3(1, tmp136, tmp137, a1); +//│ tmp139 = AA1(2); +//│ tmp140 = AA1(3); +//│ tmp141 = test3(2, tmp139, tmp140, a1); +//│ tmp142 = tmp138 + tmp141; +//│ tmp143 = c3(a1); +//│ tmp142 + tmp143 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c3, test3, a1, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139, _deforest_AA_x_tmp18, match_p_branch_AA1, _deforest_AA_x_tmp19, match_q_branch_AA1, _deforest_AA_x_tmp20, _deforest_AA_x_tmp21; +//│ let c3, test3, a1, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143, _deforest_AA_x_tmp18, match_p_branch_AA1, _deforest_AA_x_tmp19, match_q_branch_AA1, _deforest_AA_x_tmp20, _deforest_AA_x_tmp21; //│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x8) { -//│ let k, param0, x, tmp140; +//│ let k, param0, x, tmp144; //│ param0 = _deforest_AA_x8; //│ x = param0; //│ k = x; -//│ tmp140 = runtime.Unit; +//│ tmp144 = runtime.Unit; //│ return runtime.safeCall(q(n, k)) //│ }; //│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x8) { -//│ let o, param0, y2, tmp140, tmp141, tmp142; +//│ let o, param0, y2, tmp144, tmp145, tmp146; //│ param0 = _deforest_AA_x8; //│ y2 = param0; -//│ tmp140 = k + y2; -//│ o = tmp140; -//│ tmp141 = runtime.Unit; -//│ tmp142 = tmp141; +//│ tmp144 = k + y2; +//│ o = tmp144; +//│ tmp145 = runtime.Unit; +//│ tmp146 = tmp145; //│ return o + n //│ }; //│ test3 = function test(n, p1, q, a2) { @@ -1235,26 +1242,26 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ }; //│ a1 = A1; //│ _deforest_AA_x_tmp18 = 2; -//│ tmp132 = (n, q) => { +//│ tmp136 = (n, q) => { //│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp18) //│ }; //│ _deforest_AA_x_tmp19 = 3; -//│ tmp133 = (n, k) => { +//│ tmp137 = (n, k) => { //│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp19) //│ }; -//│ tmp134 = test3(1, tmp132, tmp133, a1); +//│ tmp138 = test3(1, tmp136, tmp137, a1); //│ _deforest_AA_x_tmp20 = 2; -//│ tmp135 = (n, q) => { +//│ tmp139 = (n, q) => { //│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp20) //│ }; //│ _deforest_AA_x_tmp21 = 3; -//│ tmp136 = (n, k) => { +//│ tmp140 = (n, k) => { //│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp21) //│ }; -//│ tmp137 = test3(2, tmp135, tmp136, a1); -//│ tmp138 = tmp134 + tmp137; -//│ tmp139 = c3(a1); -//│ block$res30 = tmp138 + tmp139; +//│ tmp141 = test3(2, tmp139, tmp140, a1); +//│ tmp142 = tmp138 + tmp141; +//│ tmp143 = c3(a1); +//│ block$res30 = tmp142 + tmp143; //│ undefined //│ = 13 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1275,47 +1282,47 @@ fun test(x, y, z, i) = k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ JS (unsanitized): -//│ let test4, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155; +//│ let test4, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159; //│ test4 = function test(x, y2, z, i) { -//│ let k, param0, a2, m, param01, a11, n, param02, a21, tmp156, tmp157, tmp158; +//│ let k, param0, a2, m, param01, a11, n, param02, a21, tmp160, tmp161, tmp162; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ a2 = param0; //│ if (y2 instanceof AA1.class) { //│ param01 = y2.x; //│ a11 = param01; -//│ tmp156 = a11 + i; +//│ tmp160 = a11 + i; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp156; +//│ m = tmp160; //│ if (z instanceof BB1.class) { //│ param02 = z.x; //│ a21 = param02; -//│ tmp157 = a21 - i; +//│ tmp161 = a21 - i; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp157; -//│ tmp158 = m + n; +//│ n = tmp161; +//│ tmp162 = m + n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ k = tmp158; +//│ k = tmp162; //│ return k + i //│ }; -//│ tmp148 = AA1(1); -//│ tmp149 = AA1(2); -//│ tmp150 = BB1(3); -//│ tmp151 = test4(tmp148, tmp149, tmp150, 4); //│ tmp152 = AA1(1); //│ tmp153 = AA1(2); //│ tmp154 = BB1(3); //│ tmp155 = test4(tmp152, tmp153, tmp154, 4); -//│ tmp151 + tmp155 +//│ tmp156 = AA1(1); +//│ tmp157 = AA1(2); +//│ tmp158 = BB1(3); +//│ tmp159 = test4(tmp156, tmp157, tmp158, 4); +//│ tmp155 + tmp159 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test4, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155, _deforest_AA_x_tmp22, match_x_branch_AA3, _deforest_AA_x_tmp23, match_y_branch_AA1, _deforest_BB_x_tmp2, match_z_branch_BB1, _deforest_AA_x_tmp24, _deforest_AA_x_tmp25, _deforest_BB_x_tmp3; +//│ let test4, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159, _deforest_AA_x_tmp22, match_x_branch_AA3, _deforest_AA_x_tmp23, match_y_branch_AA1, _deforest_BB_x_tmp4, match_z_branch_BB1, _deforest_AA_x_tmp24, _deforest_AA_x_tmp25, _deforest_BB_x_tmp5; //│ match_x_branch_AA3 = function match_x_branch_AA(y2, z, i, _deforest_AA_x8) { //│ let param0, a2; //│ param0 = _deforest_AA_x8; @@ -1323,53 +1330,53 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ return runtime.safeCall(y2(z, i)) //│ }; //│ match_y_branch_AA1 = function match_y_branch_AA(z, i, _deforest_AA_x8) { -//│ let m, param0, a11, tmp156; +//│ let m, param0, a11, tmp160; //│ param0 = _deforest_AA_x8; //│ a11 = param0; -//│ tmp156 = a11 + i; -//│ m = tmp156; +//│ tmp160 = a11 + i; +//│ m = tmp160; //│ return runtime.safeCall(z(i, m)) //│ }; -//│ match_z_branch_BB1 = function match_z_branch_BB(i, m, _deforest_BB_x2) { -//│ let k, n, param0, a2, tmp156, tmp157; -//│ param0 = _deforest_BB_x2; +//│ match_z_branch_BB1 = function match_z_branch_BB(i, m, _deforest_BB_x1) { +//│ let k, n, param0, a2, tmp160, tmp161; +//│ param0 = _deforest_BB_x1; //│ a2 = param0; -//│ tmp156 = a2 - i; -//│ n = tmp156; -//│ tmp157 = m + n; -//│ k = tmp157; +//│ tmp160 = a2 - i; +//│ n = tmp160; +//│ tmp161 = m + n; +//│ k = tmp161; //│ return k + i //│ }; //│ test4 = function test(x, y2, z, i) { //│ return runtime.safeCall(x(y2, z, i)) //│ }; //│ _deforest_AA_x_tmp22 = 1; -//│ tmp148 = (y2, z, i) => { +//│ tmp152 = (y2, z, i) => { //│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp22) //│ }; //│ _deforest_AA_x_tmp23 = 2; -//│ tmp149 = (z, i) => { +//│ tmp153 = (z, i) => { //│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp23) //│ }; -//│ _deforest_BB_x_tmp2 = 3; -//│ tmp150 = (i, m) => { -//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp2) +//│ _deforest_BB_x_tmp4 = 3; +//│ tmp154 = (i, m) => { +//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp4) //│ }; -//│ tmp151 = test4(tmp148, tmp149, tmp150, 4); +//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); //│ _deforest_AA_x_tmp24 = 1; -//│ tmp152 = (y2, z, i) => { +//│ tmp156 = (y2, z, i) => { //│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp24) //│ }; //│ _deforest_AA_x_tmp25 = 2; -//│ tmp153 = (z, i) => { +//│ tmp157 = (z, i) => { //│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp25) //│ }; -//│ _deforest_BB_x_tmp3 = 3; -//│ tmp154 = (i, m) => { -//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp3) +//│ _deforest_BB_x_tmp5 = 3; +//│ tmp158 = (i, m) => { +//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp5) //│ }; -//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); -//│ block$res32 = tmp151 + tmp155; +//│ tmp159 = test4(tmp156, tmp157, tmp158, 4); +//│ block$res32 = tmp155 + tmp159; //│ undefined //│ = 18 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1389,110 +1396,110 @@ fun test(x, y, z, i) = k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ JS (unsanitized): -//│ let test5, tmp164, tmp165, tmp166, tmp167, tmp168, tmp169, tmp170, tmp171; +//│ let test5, tmp168, tmp169, tmp170, tmp171, tmp172, tmp173, tmp174, tmp175; //│ test5 = function test(x, y2, z, i) { -//│ let k, param0, a2, m, param01, b2, param02, a11, n, param03, a21, tmp172, tmp173, tmp174; +//│ let k, param0, a2, m, param01, b2, param02, a11, n, param03, a21, tmp176, tmp177, tmp178; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ a2 = param0; //│ if (y2 instanceof AA1.class) { //│ param02 = y2.x; //│ a11 = param02; -//│ tmp172 = a11 + i; +//│ tmp176 = a11 + i; //│ } else if (y2 instanceof BB1.class) { //│ param01 = y2.x; //│ b2 = param01; -//│ tmp172 = b2 - i; +//│ tmp176 = b2 - i; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp172; +//│ m = tmp176; //│ if (z instanceof BB1.class) { //│ param03 = z.x; //│ a21 = param03; -//│ tmp173 = a21 - i; +//│ tmp177 = a21 - i; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp173; -//│ tmp174 = m + n; +//│ n = tmp177; +//│ tmp178 = m + n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ k = tmp174; +//│ k = tmp178; //│ return k + i //│ }; -//│ tmp164 = AA1(1); -//│ tmp165 = AA1(2); -//│ tmp166 = BB1(3); -//│ tmp167 = test5(tmp164, tmp165, tmp166, 4); //│ tmp168 = AA1(1); -//│ tmp169 = BB1(2); +//│ tmp169 = AA1(2); //│ tmp170 = BB1(3); //│ tmp171 = test5(tmp168, tmp169, tmp170, 4); -//│ tmp167 + tmp171 +//│ tmp172 = AA1(1); +//│ tmp173 = BB1(2); +//│ tmp174 = BB1(3); +//│ tmp175 = test5(tmp172, tmp173, tmp174, 4); +//│ tmp171 + tmp175 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test5, tmp164, tmp165, tmp166, tmp167, tmp168, tmp169, tmp170, tmp171, _deforest_AA_x8, _deforest_BB_x2, _deforest_AA_x_tmp26, match_x_branch_AA4, match_y_rest, _deforest_BB_x_tmp4, match_z_branch_BB2, _deforest_AA_x_tmp27, _deforest_BB_x_tmp5; +//│ let test5, tmp168, tmp169, tmp170, tmp171, tmp172, tmp173, tmp174, tmp175, _deforest_AA_x8, _deforest_BB_x1, _deforest_AA_x_tmp26, match_x_branch_AA4, match_y_rest, _deforest_BB_x_tmp6, match_z_branch_BB2, _deforest_AA_x_tmp27, _deforest_BB_x_tmp7; //│ match_x_branch_AA4 = function match_x_branch_AA(y2, z, i, _deforest_AA_x9) { //│ let param0, a2; //│ param0 = _deforest_AA_x9; //│ a2 = param0; //│ return runtime.safeCall(y2(z, i)) //│ }; -//│ match_z_branch_BB2 = function match_z_branch_BB(i, m, _deforest_BB_x3) { -//│ let k, n, param0, a2, tmp172, tmp173; -//│ param0 = _deforest_BB_x3; +//│ match_z_branch_BB2 = function match_z_branch_BB(i, m, _deforest_BB_x2) { +//│ let k, n, param0, a2, tmp176, tmp177; +//│ param0 = _deforest_BB_x2; //│ a2 = param0; -//│ tmp172 = a2 - i; -//│ n = tmp172; -//│ tmp173 = m + n; -//│ k = tmp173; +//│ tmp176 = a2 - i; +//│ n = tmp176; +//│ tmp177 = m + n; +//│ k = tmp177; //│ return k + i //│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp172) { +//│ match_y_rest = function match_y_rest(z, i, tmp176) { //│ let m; -//│ m = tmp172; +//│ m = tmp176; //│ return runtime.safeCall(z(i, m)) //│ }; //│ test5 = function test(x, y2, z, i) { //│ return runtime.safeCall(x(y2, z, i)) //│ }; //│ _deforest_AA_x_tmp26 = 1; -//│ tmp164 = (y2, z, i) => { +//│ tmp168 = (y2, z, i) => { //│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp26) //│ }; //│ _deforest_AA_x8 = 2; -//│ tmp165 = (z, i) => { -//│ let param0, a11, tmp172; +//│ tmp169 = (z, i) => { +//│ let param0, a11, tmp176; //│ param0 = _deforest_AA_x8; //│ a11 = param0; -//│ tmp172 = a11 + i; -//│ return match_y_rest(z, i, tmp172) +//│ tmp176 = a11 + i; +//│ return match_y_rest(z, i, tmp176) //│ }; -//│ _deforest_BB_x_tmp4 = 3; -//│ tmp166 = (i, m) => { -//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp4) +//│ _deforest_BB_x_tmp6 = 3; +//│ tmp170 = (i, m) => { +//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp6) //│ }; -//│ tmp167 = test5(tmp164, tmp165, tmp166, 4); +//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); //│ _deforest_AA_x_tmp27 = 1; -//│ tmp168 = (y2, z, i) => { +//│ tmp172 = (y2, z, i) => { //│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp27) //│ }; -//│ _deforest_BB_x2 = 2; -//│ tmp169 = (z, i) => { -//│ let param0, b2, tmp172; -//│ param0 = _deforest_BB_x2; +//│ _deforest_BB_x1 = 2; +//│ tmp173 = (z, i) => { +//│ let param0, b2, tmp176; +//│ param0 = _deforest_BB_x1; //│ b2 = param0; -//│ tmp172 = b2 - i; -//│ return match_y_rest(z, i, tmp172) +//│ tmp176 = b2 - i; +//│ return match_y_rest(z, i, tmp176) //│ }; -//│ _deforest_BB_x_tmp5 = 3; -//│ tmp170 = (i, m) => { -//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp5) +//│ _deforest_BB_x_tmp7 = 3; +//│ tmp174 = (i, m) => { +//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp7) //│ }; -//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); -//│ block$res34 = tmp167 + tmp171; +//│ tmp175 = test5(tmp172, tmp173, tmp174, 4); +//│ block$res34 = tmp171 + tmp175; //│ undefined //│ = 10 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1504,31 +1511,32 @@ fun test(x) = let n = if x is AA(BB(b)) then b AA(CC(c)) then c + else 0 n + 3 fun c(x) = if x is AA then 0 fun p(x) = AA(x) -test(p(BB(3))) + test(p(CC(3))) + c(p(0)) +test(p(BB(3))) + test(p(CC(3))) + c(p(A)) //│ JS (unsanitized): -//│ let p1, c4, test6, tmp180, tmp181, tmp182, tmp183, tmp184, tmp185, tmp186, tmp187, tmp188; +//│ let p1, c4, test6, tmp184, tmp185, tmp186, tmp187, tmp188, tmp189, tmp190, tmp191, tmp192; //│ test6 = function test(x) { -//│ let n, param0, param01, c5, param02, b, tmp189; +//│ let n, param0, param01, c5, param02, b, tmp193; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof BB1.class) { //│ param02 = param0.x; //│ b = param02; -//│ tmp189 = b; +//│ tmp193 = b; //│ } else if (param0 instanceof CC1.class) { //│ param01 = param0.x; //│ c5 = param01; -//│ tmp189 = c5; +//│ tmp193 = c5; //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp193 = 0; //│ } //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp193 = 0; //│ } -//│ n = tmp189; +//│ n = tmp193; //│ return n + 3 //│ }; //│ c4 = function c(x) { @@ -1541,32 +1549,34 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ p1 = function p(x) { //│ return AA1(x) //│ }; -//│ tmp180 = BB1(3); -//│ tmp181 = p1(tmp180); -//│ tmp182 = test6(tmp181); -//│ tmp183 = CC1(3); -//│ tmp184 = p1(tmp183); -//│ tmp185 = test6(tmp184); -//│ tmp186 = tmp182 + tmp185; -//│ tmp187 = p1(0); -//│ tmp188 = c4(tmp187); -//│ tmp186 + tmp188 +//│ tmp184 = BB1(3); +//│ tmp185 = p1(tmp184); +//│ tmp186 = test6(tmp185); +//│ tmp187 = CC1(3); +//│ tmp188 = p1(tmp187); +//│ tmp189 = test6(tmp188); +//│ tmp190 = tmp186 + tmp189; +//│ tmp191 = p1(A1); +//│ tmp192 = c4(tmp191); +//│ tmp190 + tmp192 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p1, c4, test6, tmp180, tmp181, tmp182, tmp183, tmp184, tmp185, tmp186, tmp187, tmp188, _deforest_BB_x3, _deforest_CC_x, match_param0_rest; -//│ match_param0_rest = function match_param0_rest(tmp189) { +//│ let p1, c4, test6, tmp184, tmp185, tmp186, tmp187, tmp188, tmp189, tmp190, tmp191, tmp192, _deforest_BB_x2, _deforest_CC_x, match_param0_rest; +//│ match_param0_rest = function match_param0_rest(tmp193) { //│ let n; -//│ n = tmp189; +//│ n = tmp193; //│ return n + 3 //│ }; //│ test6 = function test(x) { -//│ let param0; +//│ let n, param0, tmp193; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ return runtime.safeCall(param0()) //│ } else { -//│ throw new globalThis.Error("match error"); +//│ tmp193 = 0; //│ } +//│ n = tmp193; +//│ return n + 3 //│ }; //│ c4 = function c(x) { //│ if (x instanceof AA1.class) { @@ -1578,30 +1588,34 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(0)) //│ p1 = function p(x) { //│ return AA1(x) //│ }; -//│ _deforest_BB_x3 = 3; -//│ tmp180 = () => { -//│ let param0, b, tmp189; -//│ param0 = _deforest_BB_x3; +//│ _deforest_BB_x2 = 3; +//│ tmp184 = () => { +//│ let param0, b, tmp193; +//│ param0 = _deforest_BB_x2; //│ b = param0; -//│ tmp189 = b; -//│ return match_param0_rest(tmp189) +//│ tmp193 = b; +//│ return match_param0_rest(tmp193) //│ }; -//│ tmp181 = p1(tmp180); -//│ tmp182 = test6(tmp181); +//│ tmp185 = p1(tmp184); +//│ tmp186 = test6(tmp185); //│ _deforest_CC_x = 3; -//│ tmp183 = () => { -//│ let param0, c5, tmp189; +//│ tmp187 = () => { +//│ let param0, c5, tmp193; //│ param0 = _deforest_CC_x; //│ c5 = param0; -//│ tmp189 = c5; -//│ return match_param0_rest(tmp189) -//│ }; -//│ tmp184 = p1(tmp183); -//│ tmp185 = test6(tmp184); -//│ tmp186 = tmp182 + tmp185; -//│ tmp187 = p1(0); -//│ tmp188 = c4(tmp187); -//│ block$res36 = tmp186 + tmp188; +//│ tmp193 = c5; +//│ return match_param0_rest(tmp193) +//│ }; +//│ tmp188 = p1(tmp187); +//│ tmp189 = test6(tmp188); +//│ tmp190 = tmp186 + tmp189; +//│ tmp191 = p1(() => { +//│ let tmp193; +//│ tmp193 = 0; +//│ return match_param0_rest(tmp193) +//│ }); +//│ tmp192 = c4(tmp191); +//│ block$res36 = tmp190 + tmp192; //│ undefined //│ = 12 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1625,100 +1639,100 @@ fun f(x, y, z, k) = AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ JS (unsanitized): -//│ let f7, tmp198, tmp199, tmp200, tmp201; +//│ let f7, tmp202, tmp203, tmp204, tmp205; //│ f7 = function f(x, y2, z, k) { -//│ let tmp202, m, scrut, param0, yf, scrut1, tmp203, tmp204, tmp205, tmp206, tmp207, tmp208, tmp209; +//│ let tmp206, m, scrut, param0, yf, scrut1, tmp207, tmp208, tmp209, tmp210, tmp211, tmp212, tmp213; //│ if (z === true) { -//│ tmp203 = y2 + 1; -//│ tmp204 = BB1(tmp203); +//│ tmp207 = y2 + 1; +//│ tmp208 = BB1(tmp207); //│ } else { -//│ tmp205 = y2 - 1; -//│ tmp204 = BB1(tmp205); +//│ tmp209 = y2 - 1; +//│ tmp208 = BB1(tmp209); //│ } -//│ tmp202 = tmp204; -//│ if (tmp202 instanceof BB1.class) { +//│ tmp206 = tmp208; +//│ if (tmp206 instanceof BB1.class) { //│ if (x instanceof AA1.class) { //│ scrut = AA1(2); //│ if (scrut instanceof AA1.class) { //│ param0 = scrut.x; //│ yf = param0; -//│ tmp206 = yf; +//│ tmp210 = yf; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp207 = tmp206; +//│ tmp211 = tmp210; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp207; +//│ m = tmp211; //│ scrut1 = AA1(3); //│ if (scrut1 instanceof AA1.class) { -//│ tmp208 = m + k; +//│ tmp212 = m + k; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ tmp209 = tmp208; +//│ tmp213 = tmp212; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ return tmp202.x + tmp209 +//│ return tmp206.x + tmp213 //│ }; -//│ tmp198 = AA1(3); -//│ tmp199 = f7(tmp198, 3, true, 10); -//│ tmp200 = AA1(5); -//│ tmp201 = f7(tmp200, 4, false, 20); -//│ tmp199 + tmp201 +//│ tmp202 = AA1(3); +//│ tmp203 = f7(tmp202, 3, true, 10); +//│ tmp204 = AA1(5); +//│ tmp205 = f7(tmp204, 4, false, 20); +//│ tmp203 + tmp205 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f7, tmp198, tmp199, tmp200, tmp201, _deforest_AA_x_unused1, match_x_branch_AA5, _deforest_AA_x_unused2; -//│ match_x_branch_AA5 = function match_x_branch_AA(k, tmp202) { +//│ let f7, tmp202, tmp203, tmp204, tmp205, _deforest_AA_x_unused1, match_x_branch_AA5, _deforest_AA_x_unused2; +//│ match_x_branch_AA5 = function match_x_branch_AA(k, tmp206) { //│ let scrut, _deforest_AA_x9; //│ _deforest_AA_x9 = 2; -//│ scrut = (k1, tmp203) => { -//│ let m, param0, yf, scrut1, tmp204, tmp205, _deforest_AA_x_unused3; +//│ scrut = (k1, tmp207) => { +//│ let m, param0, yf, scrut1, tmp208, tmp209, _deforest_AA_x_unused3; //│ param0 = _deforest_AA_x9; //│ yf = param0; -//│ tmp204 = yf; -//│ tmp205 = tmp204; -//│ m = tmp205; +//│ tmp208 = yf; +//│ tmp209 = tmp208; +//│ m = tmp209; //│ _deforest_AA_x_unused3 = 3; -//│ scrut1 = (k2, tmp206, m1) => { -//│ let tmp207, tmp208; -//│ tmp207 = m1 + k2; -//│ tmp208 = tmp207; -//│ return tmp206.x + tmp208 +//│ scrut1 = (k2, tmp210, m1) => { +//│ let tmp211, tmp212; +//│ tmp211 = m1 + k2; +//│ tmp212 = tmp211; +//│ return tmp210.x + tmp212 //│ }; -//│ return runtime.safeCall(scrut1(k1, tmp203, m)) +//│ return runtime.safeCall(scrut1(k1, tmp207, m)) //│ }; -//│ return runtime.safeCall(scrut(k, tmp202)) +//│ return runtime.safeCall(scrut(k, tmp206)) //│ }; //│ f7 = function f(x, y2, z, k) { -//│ let tmp202, tmp203, tmp204, tmp205; +//│ let tmp206, tmp207, tmp208, tmp209; //│ if (z === true) { -//│ tmp203 = y2 + 1; -//│ tmp204 = BB1(tmp203); +//│ tmp207 = y2 + 1; +//│ tmp208 = BB1(tmp207); //│ } else { -//│ tmp205 = y2 - 1; -//│ tmp204 = BB1(tmp205); +//│ tmp209 = y2 - 1; +//│ tmp208 = BB1(tmp209); //│ } -//│ tmp202 = tmp204; -//│ if (tmp202 instanceof BB1.class) { -//│ return runtime.safeCall(x(k, tmp202)) +//│ tmp206 = tmp208; +//│ if (tmp206 instanceof BB1.class) { +//│ return runtime.safeCall(x(k, tmp206)) //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; //│ _deforest_AA_x_unused1 = 3; -//│ tmp198 = (k, tmp202) => { -//│ return match_x_branch_AA5(k, tmp202) +//│ tmp202 = (k, tmp206) => { +//│ return match_x_branch_AA5(k, tmp206) //│ }; -//│ tmp199 = f7(tmp198, 3, true, 10); +//│ tmp203 = f7(tmp202, 3, true, 10); //│ _deforest_AA_x_unused2 = 5; -//│ tmp200 = (k, tmp202) => { -//│ return match_x_branch_AA5(k, tmp202) +//│ tmp204 = (k, tmp206) => { +//│ return match_x_branch_AA5(k, tmp206) //│ }; -//│ tmp201 = f7(tmp200, 4, false, 20); -//│ block$res38 = tmp199 + tmp201; +//│ tmp205 = f7(tmp204, 4, false, 20); +//│ block$res38 = tmp203 + tmp205; //│ undefined //│ = 41 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1736,9 +1750,9 @@ fun f(a) = if a is let p = AA(AA(AA(10))) test(p) + f(p) //│ JS (unsanitized): -//│ let test7, f8, p2, tmp206, tmp207, tmp208, tmp209, tmp210; +//│ let test7, f8, p2, tmp210, tmp211, tmp212, tmp213, tmp214; //│ test7 = function test(x) { -//│ let t, param0, param01, param02, a2, tmp211; +//│ let t, param0, param01, param02, a2, tmp215; //│ if (x instanceof AA1.class) { //│ param0 = x.x; //│ if (param0 instanceof AA1.class) { @@ -1746,7 +1760,7 @@ test(p) + f(p) //│ if (param01 instanceof AA1.class) { //│ param02 = param01.x; //│ a2 = param02; -//│ tmp211 = a2; +//│ tmp215 = a2; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1756,7 +1770,7 @@ test(p) + f(p) //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp211; +//│ t = tmp215; //│ return t + 5 //│ }; //│ f8 = function f(a2) { @@ -1772,16 +1786,16 @@ test(p) + f(p) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp206 = AA1(10); -//│ tmp207 = AA1(tmp206); -//│ tmp208 = AA1(tmp207); -//│ p2 = tmp208; -//│ tmp209 = test7(p2); -//│ tmp210 = f8(p2); -//│ tmp209 + tmp210 +//│ tmp210 = AA1(10); +//│ tmp211 = AA1(tmp210); +//│ tmp212 = AA1(tmp211); +//│ p2 = tmp212; +//│ tmp213 = test7(p2); +//│ tmp214 = f8(p2); +//│ tmp213 + tmp214 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test7, f8, p2, tmp206, tmp207, tmp208, tmp209, tmp210, _deforest_AA_x9; +//│ let test7, f8, p2, tmp210, tmp211, tmp212, tmp213, tmp214, _deforest_AA_x9; //│ test7 = function test(x) { //│ let param0, param01; //│ if (x instanceof AA1.class) { @@ -1810,20 +1824,20 @@ test(p) + f(p) //│ } //│ }; //│ _deforest_AA_x9 = 10; -//│ tmp206 = () => { -//│ let t, param0, a2, tmp211; +//│ tmp210 = () => { +//│ let t, param0, a2, tmp215; //│ param0 = _deforest_AA_x9; //│ a2 = param0; -//│ tmp211 = a2; -//│ t = tmp211; +//│ tmp215 = a2; +//│ t = tmp215; //│ return t + 5 //│ }; -//│ tmp207 = AA1(tmp206); -//│ tmp208 = AA1(tmp207); -//│ p2 = tmp208; -//│ tmp209 = test7(p2); -//│ tmp210 = f8(p2); -//│ block$res40 = tmp209 + tmp210; +//│ tmp211 = AA1(tmp210); +//│ tmp212 = AA1(tmp211); +//│ p2 = tmp212; +//│ tmp213 = test7(p2); +//│ tmp214 = f8(p2); +//│ block$res40 = tmp213 + tmp214; //│ undefined //│ = 15 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 8bcb75322295f12ddea7fd669ad1acd174db8c73 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:50:23 +0800 Subject: [PATCH 159/303] update test --- .../src/test/mlscript/deforest/simple.mls | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 556037e4c0..28e16ca134 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1994,3 +1994,84 @@ test(B) + test(C) //│ = 4 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 4 + + + +// technically ill-typed, so `AA(3)` (which flows to `z`) is not fused +:sjs +fun test(x, y, z) = if x is + AA(a) then + let m = if y is + AA(a1) then a1 + z + let n = if z is + AA(a2) then a2 - z + m + n +test(AA(1), AA(2), AA(3)) +//│ JS (unsanitized): +//│ let test10, tmp109, tmp110, tmp111; +//│ test10 = function test(x1, y1, z) { +//│ let param0, a, m, param01, a1, n, param02, a2, tmp112, tmp113; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.aa; +//│ a = param0; +//│ if (y1 instanceof AA1.class) { +//│ param01 = y1.aa; +//│ a1 = param01; +//│ tmp112 = a1 + z; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ m = tmp112; +//│ if (z instanceof AA1.class) { +//│ param02 = z.aa; +//│ a2 = param02; +//│ tmp113 = a2 - z; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ n = tmp113; +//│ return m + n +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp109 = AA1(1); +//│ tmp110 = AA1(2); +//│ tmp111 = AA1(3); +//│ test10(tmp109, tmp110, tmp111) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let test10, tmp109, tmp110, tmp111, _deforest_AA_aa3, _deforest_AA_aa4; +//│ test10 = function test(x1, y1, z) { +//│ return runtime.safeCall(x1(y1, z)) +//│ }; +//│ _deforest_AA_aa3 = 1; +//│ tmp109 = (y1, z) => { +//│ let param0, a; +//│ param0 = _deforest_AA_aa3; +//│ a = param0; +//│ return runtime.safeCall(y1(z)) +//│ }; +//│ _deforest_AA_aa4 = 2; +//│ tmp110 = (z) => { +//│ let m, param0, a1, n, param01, a2, tmp112, tmp113; +//│ param0 = _deforest_AA_aa4; +//│ a1 = param0; +//│ tmp112 = a1 + z; +//│ m = tmp112; +//│ if (z instanceof AA1.class) { +//│ param01 = z.aa; +//│ a2 = param01; +//│ tmp113 = a2 - z; +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ n = tmp113; +//│ return m + n +//│ }; +//│ tmp111 = AA1(3); +//│ block$res63 = test10(tmp109, tmp110, tmp111); +//│ undefined +//│ = "2AA(3)NaN" +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "2AA(3)NaN" From a16a2b7923f2a4ce191b80812cb3327ae991685c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:03:14 +0800 Subject: [PATCH 160/303] minor fix --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 1b3afcc38c..360a947011 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -967,7 +967,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - case Match(scrut, arms, dflt, rest) if dflt.fold(true)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } => + case Match(scrut, arms, dflt, rest) if dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } => super.applyBlock(Match(scrut, arms, dflt, End(""))) case _ => super.applyBlock(b) From 31bc090dc1d3d69d728cd732daf643e6a3b320a3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:49:03 +0800 Subject: [PATCH 161/303] fusion stats --- .../scala/hkmc2/codegen/Deforestation.scala | 17 +++- .../src/test/mlscript/deforest/imperative.mls | 6 ++ .../test/mlscript/deforest/nestedMatch.mls | 81 ++++++++++++++++ .../deforest/selectionsInNestedMatch.mls | 28 ++++++ .../src/test/mlscript/deforest/simple.mls | 92 +++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 5 + .../test/scala/hkmc2/JSBackendDiffMaker.scala | 4 +- 7 files changed, 229 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 360a947011..19a74965e9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -237,7 +237,7 @@ class Deforest(using TL, Raise, Elaborator.State): given Uid.Handler[StratVar]#State = StratVarUidHandler.State() import StratVarState.freshVar - def apply(p: Program): Program = + def apply(p: Program): Program -> String = val mainBlk = p.main globallyDefinedVars.init(mainBlk) @@ -250,7 +250,7 @@ class Deforest(using TL, Raise, Elaborator.State): catch case NotDeforestableException(msg) => // return the original program if deforestation is not applicable - return p + return p -> "" resolveConstraints @@ -269,10 +269,21 @@ class Deforest(using TL, Raise, Elaborator.State): case (ctorUid, CtorFinalDest.Sel(s)) => tl.log("\t" + ctorUid + " --sel--> " + s) case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log("\t" + ctorUid + " --mat-->" + scrut ) + val fusionStat = filteredCtorDests.map: + case (ctorUid, CtorFinalDest.Sel(s)) => + "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${ResultUid(s).asInstanceOf[Select].name}`" + case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => + "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" + Program( p.imports, rewrite(mainBlk) - ) + ) -> + locally: + if filteredCtorDests.nonEmpty then + s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" + else + s"0 fusion opportunity" // these are never considered as free vars (because of their symbol type) // consider TopLevelSym, BlockMemberSymbols and BuiltInSyms as globally defined... diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index cba18b3705..a74229dd8d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -68,6 +68,7 @@ fun foo(x) = //│ } //│ x = tmp; //│ block$res2 = undefined; +//│ 0 fusion opportunity //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ x = A @@ -123,6 +124,7 @@ fun foo(x) = //│ } //│ x1 = tmp2; //│ block$res4 = undefined; +//│ 0 fusion opportunity //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ x = A @@ -198,6 +200,10 @@ foo(bar, 123) //│ block$res6 = foo2(bar, 123); //│ undefined //│ = 1 +//│ 3 fusion opportunities: +//│ A --match--> `if v is ...` +//│ A --match--> `if v is ...` +//│ B --match--> `if v is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index cb671e72a8..04e81ad5b9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -131,6 +131,9 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ block$res4 = tmp7 + tmp10; //│ undefined //│ = 37 +//│ 2 fusion opportunities: +//│ AA --match--> `if a is ...` +//│ AA --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 37 //│ a = AA(AA(3)) @@ -217,6 +220,11 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ block$res6 = tmp24 + tmp27; //│ undefined //│ = 30 +//│ 4 fusion opportunities: +//│ AA --match--> `if a is ...` +//│ AA --match--> `if a is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 30 @@ -280,6 +288,9 @@ f(AA(AA(3))) //│ block$res8 = f2(tmp35); //│ undefined //│ = 5 +//│ 2 fusion opportunities: +//│ AA --match--> `if a is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 @@ -357,6 +368,11 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ block$res10 = tmp40 + tmp43; //│ undefined //│ = "A3A3" +//│ 4 fusion opportunities: +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "A3A3" @@ -408,6 +424,9 @@ c2(AA(AA(0))) //│ block$res12 = c2(tmp51); //│ undefined //│ = 0 +//│ 2 fusion opportunities: +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 @@ -463,6 +482,10 @@ f(AA(BB(B))) //│ block$res14 = f4(tmp55); //│ undefined //│ = 3 +//│ 3 fusion opportunities: +//│ AA --match--> `if a is ...` +//│ B --match--> `if param0 is ...` +//│ BB --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -570,6 +593,13 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ block$res16 = tmp61 + tmp65; //│ undefined //│ = 10 +//│ 6 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if y is ...` +//│ AA --match--> `if y is ...` +//│ BB --match--> `if z is ...` +//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 @@ -702,6 +732,9 @@ f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) //│ block$res18 = tmp82 + tmp83; //│ undefined //│ = "ccf2f3cc" +//│ 2 fusion opportunities: +//│ BB --match--> `if x1 is ...` +//│ BB --match--> `if x1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "ccf2f3cc" //│ cc = CC("cc") @@ -754,6 +787,9 @@ c(AA(2), y) //│ block$res20 = c(tmp94, y); //│ undefined //│ = 4 +//│ 2 fusion opportunities: +//│ A --match--> `if y is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 4 //│ y = A @@ -826,6 +862,8 @@ c(AA(2), y) + c2(y) //│ block$res22 = tmp97 + tmp98; //│ undefined //│ = 9 +//│ 1 fusion opportunities: +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 9 //│ y = A @@ -941,6 +979,8 @@ test(p) + f(p) + test(AA(A)) //│ block$res24 = tmp107 + tmp109; //│ undefined //│ = "105035" +//│ 1 fusion opportunities: +//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "105035" //│ p = AA(AA(AA("10"))) @@ -1036,6 +1076,11 @@ f(aa, 3) + f(aa, 4) //│ block$res26 = tmp119 + tmp120; //│ undefined //│ = 13 +//│ 4 fusion opportunities: +//│ AA --match--> `if scrut is ...` +//│ AA --match--> `if scrut is ...` +//│ AA --match--> `if x is ...` +//│ BB --match--> `if tmp is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 13 //│ aa = AA(3) @@ -1134,6 +1179,12 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ block$res28 = tmp126 + tmp129; //│ undefined //│ = 16 +//│ 5 fusion opportunities: +//│ AA --match--> `if p is ...` +//│ AA --match--> `if p is ...` +//│ AA --match--> `if q is ...` +//│ AA --match--> `if q is ...` +//│ AA --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 16 @@ -1264,6 +1315,11 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ block$res30 = tmp142 + tmp143; //│ undefined //│ = 13 +//│ 4 fusion opportunities: +//│ AA --match--> `if p is ...` +//│ AA --match--> `if p is ...` +//│ AA --match--> `if q is ...` +//│ AA --match--> `if q is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 13 //│ a = A @@ -1379,6 +1435,13 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ block$res32 = tmp155 + tmp159; //│ undefined //│ = 18 +//│ 6 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if y is ...` +//│ AA --match--> `if y is ...` +//│ BB --match--> `if z is ...` +//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 18 @@ -1502,6 +1565,13 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ block$res34 = tmp171 + tmp175; //│ undefined //│ = 10 +//│ 6 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if y is ...` +//│ BB --match--> `if y is ...` +//│ BB --match--> `if z is ...` +//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 @@ -1618,6 +1688,10 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(A)) //│ block$res36 = tmp190 + tmp192; //│ undefined //│ = 12 +//│ 3 fusion opportunities: +//│ A --match--> `if param0 is ...` +//│ BB --match--> `if param0 is ...` +//│ CC --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 12 @@ -1735,6 +1809,11 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ block$res38 = tmp203 + tmp205; //│ undefined //│ = 41 +//│ 4 fusion opportunities: +//│ AA --match--> `if scrut is ...` +//│ AA --match--> `if scrut is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 41 @@ -1840,6 +1919,8 @@ test(p) + f(p) //│ block$res40 = tmp213 + tmp214; //│ undefined //│ = 15 +//│ 1 fusion opportunities: +//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ p = AA(AA(AA(10))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 42d5e268b1..a818aae3ca 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -56,6 +56,9 @@ c(AA(2)) //│ block$res4 = c(tmp); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if scrut is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -96,6 +99,9 @@ c(AA(2), A) //│ }); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if y is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -145,6 +151,9 @@ c(p(), A) //│ }); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if y is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -202,6 +211,9 @@ c(p(), A) //│ }); //│ undefined //│ = 3 +//│ 2 fusion opportunities: +//│ A --match--> `if y is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -298,6 +310,13 @@ f(AA(AA(AA(AA(AA(A)))))) //│ block$res12 = f(tmp12); //│ undefined //│ = 42 +//│ 6 fusion opportunities: +//│ A --match--> `if x is ...` +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 42 @@ -347,6 +366,9 @@ f(AA(AA(A))) //│ block$res14 = f1(tmp19); //│ undefined //│ = A +//│ 2 fusion opportunities: +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A @@ -405,6 +427,9 @@ f(p()) //│ block$res16 = f2(tmp22); //│ undefined //│ = A +//│ 2 fusion opportunities: +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A @@ -459,5 +484,8 @@ c(AA(2), 10) //│ block$res18 = c4(tmp24, 10); //│ undefined //│ = 12 +//│ 2 fusion opportunities: +//│ A --match--> `if scrut is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 12 diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 28e16ca134..258b04c237 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -61,6 +61,9 @@ test() //│ block$res2 = test(); //│ undefined //│ = 1 +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -124,6 +127,9 @@ test() //│ block$res4 = test1(); //│ undefined //│ = A +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A @@ -207,6 +213,11 @@ test() //│ block$res6 = test2(); //│ undefined //│ = 1 +//│ 4 fusion opportunities: +//│ A --match--> `if a is ...` +//│ AA --match--> `if x is ...` +//│ B --match--> `if a is ...` +//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -302,6 +313,11 @@ test() //│ block$res8 = test3(); //│ undefined //│ = 1 +//│ 4 fusion opportunities: +//│ A --match--> `if a is ...` +//│ AA --match--> `if x is ...` +//│ B --match--> `if a is ...` +//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -380,6 +396,9 @@ test() //│ block$res10 = test4(); //│ undefined //│ = 11 +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 @@ -464,6 +483,9 @@ map(enumFromTo(1, 4)) //│ block$res13 = map(tmp); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -539,6 +561,9 @@ sum(enumFromTo(1,10)) //│ block$res15 = sum(tmp2); //│ undefined //│ = 45 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 @@ -594,6 +619,7 @@ test() //│ block$res17 = test5(); //│ undefined //│ = 2 +//│ 0 fusion opportunity //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -644,6 +670,9 @@ test() //│ block$res19 = test6(); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -701,6 +730,9 @@ c(A) + c(B) //│ > 1 //│ > 2 //│ = 3 +//│ 2 fusion opportunities: +//│ A --match--> `if a is ...` +//│ B --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ > 1 //│ > 2 @@ -787,6 +819,9 @@ map(x => x + 4, enumFromTo(1, 4)) //│ block$res23 = map1(lambda, tmp8); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -892,6 +927,9 @@ map(x => x + 4, enumFromTo(1, 4)) //│ block$res25 = map2(lambda2, tmp10); //│ undefined //│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -966,6 +1004,9 @@ sum(enumFromTo(1, 10), 0) //│ block$res27 = sum1(tmp12, 0); //│ undefined //│ = 45 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 @@ -1013,6 +1054,7 @@ c(AA(3)) //│ block$res29 = c1(tmp14); //│ undefined //│ = 5 +//│ 0 fusion opportunity //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 @@ -1050,6 +1092,9 @@ f(A, B) //│ }); //│ undefined //│ = 3 +//│ 2 fusion opportunities: +//│ A --match--> `if a is ...` +//│ B --match--> `if b is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -1099,6 +1144,7 @@ f(Some(2)) //│ block$res33 = f4(tmp16); //│ undefined //│ = 0 +//│ 0 fusion opportunity //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 @@ -1139,6 +1185,9 @@ if y is //│ block$res35 = runtime.safeCall(x(y)); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ x = A @@ -1191,6 +1240,9 @@ test() //│ block$res37 = test7(); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -1298,6 +1350,11 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ block$res39 = tmp26 + tmp28; //│ undefined //│ = 21 +//│ 4 fusion opportunities: +//│ AAA --match--> `if x is ...` +//│ AAA --match--> `if x is ...` +//│ BBB --match--> `if x is ...` +//│ BBB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 21 @@ -1372,6 +1429,8 @@ p(AA(1)) //│ block$res41 = p(tmp39); //│ undefined //│ = 2 +//│ 1 fusion opportunities: +//│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -1419,6 +1478,9 @@ c(AA(2), A) //│ }); //│ undefined //│ = 2 +//│ 2 fusion opportunities: +//│ A --match--> `if y is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 @@ -1517,6 +1579,11 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ block$res45 = tmp50 + tmp52; //│ undefined //│ = 24 +//│ 4 fusion opportunities: +//│ AAA --match--> `if x is ...` +//│ AAA --match--> `if x is ...` +//│ CCC --match--> `if x is ...` +//│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 24 @@ -1595,6 +1662,10 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ block$res47 = tmp67 + tmp69; //│ undefined //│ = 22 +//│ 3 fusion opportunities: +//│ AAA --match--> `if x is ...` +//│ AAA --match--> `if x is ...` +//│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 22 @@ -1631,6 +1702,8 @@ c(BB(3), 0) //│ block$res49 = c4(tmp77, 0); //│ undefined //│ = 0 +//│ 1 fusion opportunities: +//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 @@ -1694,6 +1767,9 @@ f(AA(AA(0))) //│ block$res51 = f8(tmp80); //│ undefined //│ = 1 +//│ 2 fusion opportunities: +//│ AA --match--> `if b is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -1715,6 +1791,9 @@ f(AA(AA(3))) //│ let f9, tmp83, tmp84; //│ f9 = function f(x1) { return x1 }; tmp83 = 3; tmp84 = tmp83; block$res53 = f9(tmp84); undefined //│ = 3 +//│ 2 fusion opportunities: +//│ AA --sel--> `.Ident(aa)` +//│ AA --sel--> `.Ident(aa)` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -1858,6 +1937,9 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ block$res55 = tmp91 + tmp93; //│ undefined //│ = 103 +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 103 //│ p = AA(AA(3)) @@ -1910,6 +1992,8 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ block$res57 = mapHead(lambda4, tmp102); //│ undefined //│ = 1 +//│ 1 fusion opportunities: +//│ AAA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 @@ -1954,6 +2038,8 @@ test() //│ block$res59 = test8(); //│ undefined //│ = 5 +//│ 1 fusion opportunities: +//│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 @@ -1992,6 +2078,9 @@ test(B) + test(C) //│ block$res61 = tmp105 + tmp106; //│ undefined //│ = 4 +//│ 2 fusion opportunities: +//│ B --match--> `if a is ...` +//│ C --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 4 @@ -2073,5 +2162,8 @@ test(AA(1), AA(2), AA(3)) //│ block$res63 = test10(tmp109, tmp110, tmp111); //│ undefined //│ = "2AA(3)NaN" +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ AA --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "2AA(3)NaN" diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index d701ace06c..d06eb901fb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -60,6 +60,8 @@ f(AA(AA(3))) //│ block$res4 = f(tmp1); //│ undefined //│ = 3 +//│ 1 fusion opportunities: +//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 @@ -200,6 +202,9 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ block$res6 = tmp14 + tmp15; //│ undefined //│ = 78 +//│ 2 fusion opportunities: +//│ AA --match--> `if param0 is ...` +//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 78 //│ p = AA(AA(AA(10))) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index ee17436b7f..f4695157a6 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -132,7 +132,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output("\n==== Non-inserted lowered tree ====") output(le.showAsTree) - val deforestRes = deforest(le) + val (deforestRes, deforestStat) = deforest(le) if showLoweredTree.isSet then output("\n==== deforested tree ====") @@ -195,6 +195,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case "()" => case _ => output(s"${if nme.isEmpty then "" else s"$nme "}= ${result.indentNewLines("| ")}") + + output(deforestStat) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet then From 0e68eecf4692d23818cd49c01280b777fa543144 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:31:57 +0800 Subject: [PATCH 162/303] improve jsbackenddiffmaker --- .../scala/hkmc2/codegen/Deforestation.scala | 19 +- .../src/test/mlscript/deforest/imperative.mls | 104 +-- .../test/mlscript/deforest/nestedMatch.mls | 150 ++-- .../deforest/selectionsInNestedMatch.mls | 63 +- .../src/test/mlscript/deforest/simple.mls | 757 +++++++++--------- .../src/test/mlscript/deforest/todos.mls | 17 +- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 183 +++-- 7 files changed, 618 insertions(+), 675 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 19a74965e9..0c6ab93d6e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -237,7 +237,7 @@ class Deforest(using TL, Raise, Elaborator.State): given Uid.Handler[StratVar]#State = StratVarUidHandler.State() import StratVarState.freshVar - def apply(p: Program): Program -> String = + def apply(p: Program): Opt[Program] -> String -> Int = val mainBlk = p.main globallyDefinedVars.init(mainBlk) @@ -249,8 +249,8 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(mainBlk) catch case NotDeforestableException(msg) => - // return the original program if deforestation is not applicable - return p -> "" + // return None if deforestation is not applicable + return N -> "" -> 0 resolveConstraints @@ -275,15 +275,10 @@ class Deforest(using TL, Raise, Elaborator.State): case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" - Program( - p.imports, - rewrite(mainBlk) - ) -> - locally: - if filteredCtorDests.nonEmpty then - s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" - else - s"0 fusion opportunity" + if filteredCtorDests.nonEmpty then + S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size + else + S(p) -> s"0 fusion opportunity" -> 0 // these are never considered as free vars (because of their symbol type) // consider TopLevelSym, BlockMemberSymbols and BuiltInSyms as globally defined... diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index a74229dd8d..035d286a2c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -1,6 +1,7 @@ :js :deforest +//│ No fusion opportunity object A object B @@ -42,35 +43,9 @@ fun foo(x) = //│ tmp = B1; //│ } //│ x = tmp; -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let foo, x, scrut, tmp; -//│ foo = function foo(x1) { -//│ let tmp1; -//│ if (x1 instanceof A1.class) { -//│ tmp1 = Predef.print(123); -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ if (x1 instanceof A1.class) { -//│ return 1 -//│ } else if (x1 instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = A1; -//│ } else { -//│ tmp = B1; -//│ } -//│ x = tmp; -//│ block$res2 = undefined; -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity //│ x = A +//│ No fusion opportunity // * We could make it work. But it's a special case that's probably not very important :sjs @@ -79,36 +54,13 @@ fun foo(x) = if x is A do print(123) if x is B do print(456) //│ JS (unsanitized): -//│ let foo1, x1, scrut1, tmp2; -//│ foo1 = function foo(x2) { -//│ let tmp3; -//│ if (x2 instanceof A1.class) { -//│ tmp3 = Predef.print(123); -//│ } else { -//│ tmp3 = runtime.Unit; -//│ } -//│ if (x2 instanceof B1.class) { -//│ return Predef.print(456) -//│ } else { -//│ return runtime.Unit -//│ } -//│ }; -//│ scrut1 = true; -//│ if (scrut1 === true) { -//│ tmp2 = A1; -//│ } else { -//│ tmp2 = B1; -//│ } -//│ x1 = tmp2; -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let foo1, x1, scrut1, tmp2; +//│ let foo1, x1, scrut1, tmp1; //│ foo1 = function foo(x2) { -//│ let tmp3; +//│ let tmp2; //│ if (x2 instanceof A1.class) { -//│ tmp3 = Predef.print(123); +//│ tmp2 = Predef.print(123); //│ } else { -//│ tmp3 = runtime.Unit; +//│ tmp2 = runtime.Unit; //│ } //│ if (x2 instanceof B1.class) { //│ return Predef.print(456) @@ -118,15 +70,14 @@ fun foo(x) = //│ }; //│ scrut1 = true; //│ if (scrut1 === true) { -//│ tmp2 = A1; +//│ tmp1 = A1; //│ } else { -//│ tmp2 = B1; +//│ tmp1 = B1; //│ } -//│ x1 = tmp2; -//│ block$res4 = undefined; -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ x1 = tmp1; +//│ No fusion opportunity //│ x = A +//│ No fusion opportunity :sjs fun foo(k, x) = @@ -141,20 +92,20 @@ foo(bar, 123) //│ JS (unsanitized): //│ let bar, foo2; //│ foo2 = function foo(k, x2) { -//│ let scrut2, scrut3, tmp4, tmp5; +//│ let scrut2, scrut3, tmp2, tmp3; //│ scrut2 = x2 === 0; //│ if (scrut2 === true) { -//│ tmp4 = runtime.safeCall(k(A1)); +//│ tmp2 = runtime.safeCall(k(A1)); //│ } else { -//│ tmp4 = runtime.Unit; +//│ tmp2 = runtime.Unit; //│ } //│ scrut3 = x2 > 0; //│ if (scrut3 === true) { -//│ tmp5 = A1; +//│ tmp3 = A1; //│ } else { -//│ tmp5 = B1; +//│ tmp3 = B1; //│ } -//│ return runtime.safeCall(k(tmp5)) +//│ return runtime.safeCall(k(tmp3)) //│ }; //│ bar = function bar(v) { //│ if (v instanceof A1.class) { @@ -173,38 +124,39 @@ foo(bar, 123) //│ return 1 //│ }; //│ foo2 = function foo(k, x2) { -//│ let scrut2, scrut3, tmp4, tmp5; +//│ let scrut2, scrut3, tmp2, tmp3; //│ scrut2 = x2 === 0; //│ if (scrut2 === true) { -//│ tmp4 = runtime.safeCall(k(() => { +//│ tmp2 = runtime.safeCall(k(() => { //│ return match_v_branch_A() //│ })); //│ } else { -//│ tmp4 = runtime.Unit; +//│ tmp2 = runtime.Unit; //│ } //│ scrut3 = x2 > 0; //│ if (scrut3 === true) { -//│ tmp5 = () => { +//│ tmp3 = () => { //│ return match_v_branch_A() //│ }; //│ } else { -//│ tmp5 = () => { +//│ tmp3 = () => { //│ return 2 //│ }; //│ } -//│ return runtime.safeCall(k(tmp5)) +//│ return runtime.safeCall(k(tmp3)) //│ }; //│ bar = function bar(v) { //│ return runtime.safeCall(v()) //│ }; -//│ block$res6 = foo2(bar, 123); -//│ undefined +//│ foo2(bar, 123) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 3 fusion opportunities: //│ A --match--> `if v is ...` //│ A --match--> `if v is ...` //│ B --match--> `if v is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 04e81ad5b9..80665d8040 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1,6 +1,7 @@ :js :deforest +//│ No fusion opportunity object A object B object C @@ -128,15 +129,16 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ }; //│ tmp9 = AA1(tmp8); //│ tmp10 = f(tmp9, 5); -//│ block$res4 = tmp7 + tmp10; -//│ undefined +//│ tmp7 + tmp10 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 37 +//│ a = AA(AA(3)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 37 //│ 2 fusion opportunities: //│ AA --match--> `if a is ...` //│ AA --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 37 -//│ a = AA(AA(3)) @@ -217,8 +219,10 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ return match_x_branch_AA(y, _deforest_AA_x_tmp5) //│ }; //│ tmp27 = f1(tmp26, 10); -//│ block$res6 = tmp24 + tmp27; -//│ undefined +//│ tmp24 + tmp27 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 30 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 30 //│ 4 fusion opportunities: //│ AA --match--> `if a is ...` @@ -226,7 +230,6 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ AA --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 30 @@ -285,14 +288,15 @@ f(AA(AA(3))) //│ a1 = param0; //│ return runtime.safeCall(a1()) //│ }; -//│ block$res8 = f2(tmp35); -//│ undefined +//│ f2(tmp35) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 //│ 2 fusion opportunities: //│ AA --match--> `if a is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 5 @@ -365,8 +369,10 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ return match_x_branch_AA1(_deforest_AA_x_tmp9) //│ }; //│ tmp43 = f3(tmp42); -//│ block$res10 = tmp40 + tmp43; -//│ undefined +//│ tmp40 + tmp43 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "A3A3" +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "A3A3" //│ 4 fusion opportunities: //│ AA --match--> `if param0 is ...` @@ -374,7 +380,6 @@ f(AA(AA(A))) + f(AA(AA(A))) //│ AA --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "A3A3" @@ -421,14 +426,15 @@ c2(AA(AA(0))) //│ param0 = _deforest_AA_x3; //│ return runtime.safeCall(param0()) //│ }; -//│ block$res12 = c2(tmp51); -//│ undefined +//│ c2(tmp51) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 0 //│ 2 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 0 @@ -479,15 +485,16 @@ f(AA(BB(B))) //│ param0 = _deforest_AA_x4; //│ return runtime.safeCall(param0()) //│ }; -//│ block$res14 = f4(tmp55); -//│ undefined +//│ f4(tmp55) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 //│ 3 fusion opportunities: //│ AA --match--> `if a is ...` //│ B --match--> `if param0 is ...` //│ BB --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 :sjs @@ -590,8 +597,10 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) //│ }; //│ tmp65 = test(tmp62, tmp63, tmp64, 4); -//│ block$res16 = tmp61 + tmp65; -//│ undefined +//│ tmp61 + tmp65 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 10 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 //│ 6 fusion opportunities: //│ AA --match--> `if x is ...` @@ -601,7 +610,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ BB --match--> `if z is ...` //│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 10 // only the match in the middle is fused @@ -729,15 +737,16 @@ f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) //│ tmp81 = f21(tmp80); //│ tmp82 = tmp77 + tmp81; //│ tmp83 = f31(cc); -//│ block$res18 = tmp82 + tmp83; -//│ undefined +//│ tmp82 + tmp83 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "ccf2f3cc" +//│ cc = CC("cc") +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "ccf2f3cc" //│ 2 fusion opportunities: //│ BB --match--> `if x1 is ...` //│ BB --match--> `if x1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "ccf2f3cc" -//│ cc = CC("cc") @@ -784,15 +793,16 @@ c(AA(2), y) //│ tmp94 = (y1) => { //│ return runtime.safeCall(y1(_deforest_AA_x5)) //│ }; -//│ block$res20 = c(tmp94, y); -//│ undefined +//│ c(tmp94, y) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 4 +//│ y = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 //│ 2 fusion opportunities: //│ A --match--> `if y is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 4 -//│ y = A :sjs @@ -859,14 +869,15 @@ c(AA(2), y) + c2(y) //│ }; //│ tmp97 = c1(tmp96, y1); //│ tmp98 = c21(y1); -//│ block$res22 = tmp97 + tmp98; -//│ undefined +//│ tmp97 + tmp98 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 9 +//│ y = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 9 //│ 1 fusion opportunities: //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 9 -//│ y = A // need to include computations of `rest` from more than one levels of parent matches @@ -976,14 +987,15 @@ test(p) + f(p) + test(AA(A)) //│ tmp107 = tmp105 + tmp106; //│ tmp108 = AA1(A1); //│ tmp109 = test1(tmp108); -//│ block$res24 = tmp107 + tmp109; -//│ undefined +//│ tmp107 + tmp109 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "105035" +//│ p = AA(AA(AA("10"))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "105035" //│ 1 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "105035" -//│ p = AA(AA(AA("10"))) @@ -1073,8 +1085,11 @@ f(aa, 3) + f(aa, 4) //│ aa1 = tmp118; //│ tmp119 = f6(aa1, 3); //│ tmp120 = f6(aa1, 4); -//│ block$res26 = tmp119 + tmp120; -//│ undefined +//│ tmp119 + tmp120 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 13 +//│ aa = AA(3) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 13 //│ 4 fusion opportunities: //│ AA --match--> `if scrut is ...` @@ -1082,8 +1097,6 @@ f(aa, 3) + f(aa, 4) //│ AA --match--> `if x is ...` //│ BB --match--> `if tmp is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 13 -//│ aa = AA(3) @@ -1176,8 +1189,10 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp17) //│ }; //│ tmp129 = test2(3, tmp127, tmp128); -//│ block$res28 = tmp126 + tmp129; -//│ undefined +//│ tmp126 + tmp129 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 16 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 16 //│ 5 fusion opportunities: //│ AA --match--> `if p is ...` @@ -1186,7 +1201,6 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ AA --match--> `if q is ...` //│ AA --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 16 @@ -1312,8 +1326,11 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ tmp141 = test3(2, tmp139, tmp140, a1); //│ tmp142 = tmp138 + tmp141; //│ tmp143 = c3(a1); -//│ block$res30 = tmp142 + tmp143; -//│ undefined +//│ tmp142 + tmp143 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 13 +//│ a = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 13 //│ 4 fusion opportunities: //│ AA --match--> `if p is ...` @@ -1321,8 +1338,6 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ AA --match--> `if q is ...` //│ AA --match--> `if q is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 13 -//│ a = A @@ -1432,8 +1447,10 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp5) //│ }; //│ tmp159 = test4(tmp156, tmp157, tmp158, 4); -//│ block$res32 = tmp155 + tmp159; -//│ undefined +//│ tmp155 + tmp159 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 18 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 18 //│ 6 fusion opportunities: //│ AA --match--> `if x is ...` @@ -1443,7 +1460,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ BB --match--> `if z is ...` //│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 18 :sjs @@ -1562,8 +1578,10 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp7) //│ }; //│ tmp175 = test5(tmp172, tmp173, tmp174, 4); -//│ block$res34 = tmp171 + tmp175; -//│ undefined +//│ tmp171 + tmp175 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 10 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 //│ 6 fusion opportunities: //│ AA --match--> `if x is ...` @@ -1573,7 +1591,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ BB --match--> `if z is ...` //│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 10 :sjs @@ -1685,15 +1702,16 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(A)) //│ return match_param0_rest(tmp193) //│ }); //│ tmp192 = c4(tmp191); -//│ block$res36 = tmp190 + tmp192; -//│ undefined +//│ tmp190 + tmp192 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 12 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 12 //│ 3 fusion opportunities: //│ A --match--> `if param0 is ...` //│ BB --match--> `if param0 is ...` //│ CC --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 12 @@ -1806,8 +1824,10 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ return match_x_branch_AA5(k, tmp206) //│ }; //│ tmp205 = f7(tmp204, 4, false, 20); -//│ block$res38 = tmp203 + tmp205; -//│ undefined +//│ tmp203 + tmp205 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 41 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 41 //│ 4 fusion opportunities: //│ AA --match--> `if scrut is ...` @@ -1815,7 +1835,6 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ AA --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 41 @@ -1916,11 +1935,12 @@ test(p) + f(p) //│ p2 = tmp212; //│ tmp213 = test7(p2); //│ tmp214 = f8(p2); -//│ block$res40 = tmp213 + tmp214; -//│ undefined +//│ tmp213 + tmp214 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 15 +//│ p = AA(AA(AA(10))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 15 //│ 1 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 15 -//│ p = AA(AA(AA(10))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index a818aae3ca..ebbee50c09 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -1,6 +1,7 @@ :js :deforest +//│ No fusion opportunity object A object B object C @@ -53,14 +54,15 @@ c(AA(2)) //│ }; //│ return runtime.safeCall(scrut(_deforest_AA_x)) //│ }; -//│ block$res4 = c(tmp); -//│ undefined +//│ c(tmp) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if scrut is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 :sjs @@ -94,16 +96,17 @@ c(AA(2), A) //│ tmp2 = (y) => { //│ return runtime.safeCall(y(_deforest_AA_x1)) //│ }; -//│ block$res6 = c1(tmp2, (_deforest_AA_x2) => { +//│ c1(tmp2, (_deforest_AA_x2) => { //│ return _deforest_AA_x2 -//│ }); -//│ undefined +//│ }) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if y is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 @@ -146,16 +149,17 @@ c(p(), A) //│ } //│ }; //│ tmp4 = p(); -//│ block$res8 = c2(tmp4, (_deforest_AA_x2) => { +//│ c2(tmp4, (_deforest_AA_x2) => { //│ return _deforest_AA_x2 -//│ }); -//│ undefined +//│ }) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if y is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 @@ -203,19 +207,20 @@ c(p(), A) //│ } //│ }; //│ tmp6 = p1(); -//│ block$res10 = c3(tmp6, (_deforest_AA_x2) => { +//│ c3(tmp6, (_deforest_AA_x2) => { //│ let a, tmp7; //│ tmp7 = 1; //│ a = tmp7; //│ return a + _deforest_AA_x2 -//│ }); -//│ undefined +//│ }) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 //│ 2 fusion opportunities: //│ A --match--> `if y is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 @@ -307,8 +312,10 @@ f(AA(AA(AA(AA(AA(A)))))) //│ tmp12 = () => { //│ return match_x_branch_AA(_deforest_AA_x_tmp3) //│ }; -//│ block$res12 = f(tmp12); -//│ undefined +//│ f(tmp12) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 42 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 42 //│ 6 fusion opportunities: //│ A --match--> `if x is ...` @@ -318,7 +325,6 @@ f(AA(AA(AA(AA(AA(A)))))) //│ AA --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 42 :sjs fun f(x) = if x is @@ -363,14 +369,15 @@ f(AA(AA(A))) //│ param0 = _deforest_AA_x4; //│ return runtime.safeCall(param0()) //│ }; -//│ block$res14 = f1(tmp19); -//│ undefined +//│ f1(tmp19) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A //│ 2 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A :sjs fun f(x) = if x is @@ -424,14 +431,15 @@ f(p()) //│ } //│ }; //│ tmp22 = p2(); -//│ block$res16 = f2(tmp22); -//│ undefined +//│ f2(tmp22) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A //│ 2 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A @@ -481,11 +489,12 @@ c(AA(2), 10) //│ }; //│ return runtime.safeCall(scrut(y, _deforest_AA_x5)) //│ }; -//│ block$res18 = c4(tmp24, 10); -//│ undefined +//│ c4(tmp24, 10) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 12 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 12 //│ 2 fusion opportunities: //│ A --match--> `if scrut is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 12 diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 258b04c237..579131f5ba 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1,6 +1,7 @@ :js :deforest +//│ No fusion opportunity object A object B @@ -58,14 +59,15 @@ test() //│ x = tmp; //│ return runtime.safeCall(x()) //│ }; -//│ block$res2 = test(); -//│ undefined +//│ test() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 2 fusion opportunities: //│ A --match--> `if x is ...` //│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 :sjs fun test() = @@ -124,14 +126,15 @@ test() //│ x = tmp; //│ return runtime.safeCall(x()) //│ }; -//│ block$res4 = test1(); -//│ undefined +//│ test1() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = A +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A //│ 2 fusion opportunities: //│ AA --match--> `if x is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = A :sjs @@ -210,8 +213,10 @@ test() //│ x = tmp; //│ return runtime.safeCall(x()) //│ }; -//│ block$res6 = test2(); -//│ undefined +//│ test2() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 4 fusion opportunities: //│ A --match--> `if a is ...` @@ -219,7 +224,6 @@ test() //│ B --match--> `if a is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 // `x.x` is successfully fused @@ -310,8 +314,10 @@ test() //│ x = tmp; //│ return runtime.safeCall(x()) //│ }; -//│ block$res8 = test3(); -//│ undefined +//│ test3() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 4 fusion opportunities: //│ A --match--> `if a is ...` @@ -319,7 +325,6 @@ test() //│ B --match--> `if a is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 :sjs @@ -393,14 +398,15 @@ test() //│ tmp = g(true); //│ return c(tmp) //│ }; -//│ block$res10 = test4(); -//│ undefined +//│ test4() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 11 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 11 //│ 2 fusion opportunities: //│ AA --match--> `if x is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 11 @@ -480,14 +486,15 @@ map(enumFromTo(1, 4)) //│ return runtime.safeCall(ls()) //│ }; //│ tmp = enumFromTo(1, 4); -//│ block$res13 = map(tmp); -//│ undefined +//│ map(tmp) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ 2 fusion opportunities: //│ Cons --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -558,14 +565,15 @@ sum(enumFromTo(1,10)) //│ return runtime.safeCall(ls()) //│ }; //│ tmp2 = enumFromTo1(1, 10); -//│ block$res15 = sum(tmp2); -//│ undefined +//│ sum(tmp2) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 45 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 45 //│ 2 fusion opportunities: //│ Cons --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 45 // multiple match, no fusion @@ -597,31 +605,9 @@ test() //│ } //│ }; //│ test5() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test5; -//│ test5 = function test() { -//│ let x, tmp4; -//│ x = B1; -//│ if (x instanceof A1.class) { -//│ tmp4 = 1; -//│ } else if (x instanceof B1.class) { -//│ tmp4 = 3; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (x instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ block$res17 = test5(); -//│ undefined -//│ = 2 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity //│ = 2 +//│ No fusion opportunity @@ -667,14 +653,15 @@ test() //│ }; //│ return runtime.safeCall(x(y)) //│ }; -//│ block$res19 = test6(); -//│ undefined +//│ test6() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if x is ...` //│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 :sjs @@ -725,8 +712,12 @@ c(A) + c(B) //│ tmp6 = 2; //│ return match_a_rest(tmp6) //│ }); -//│ block$res21 = tmp4 + tmp5; -//│ undefined +//│ tmp4 + tmp5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ > 1 +//│ > 2 +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ > 1 //│ > 2 //│ = 3 @@ -734,9 +725,6 @@ c(A) + c(B) //│ A --match--> `if a is ...` //│ B --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ > 1 -//│ > 2 -//│ = 3 @@ -816,14 +804,15 @@ map(x => x + 4, enumFromTo(1, 4)) //│ lambda = (undefined, function (x) { //│ return x + 4 //│ }); -//│ block$res23 = map1(lambda, tmp8); -//│ undefined +//│ map1(lambda, tmp8) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ 2 fusion opportunities: //│ Cons --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) :sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil @@ -924,14 +913,15 @@ map(x => x + 4, enumFromTo(1, 4)) //│ lambda2 = (undefined, function (x) { //│ return x + 4 //│ }); -//│ block$res25 = map2(lambda2, tmp10); -//│ undefined +//│ map2(lambda2, tmp10) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ 2 fusion opportunities: //│ Cons --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -1001,14 +991,15 @@ sum(enumFromTo(1, 10), 0) //│ return runtime.safeCall(ls(a)) //│ }; //│ tmp12 = enumFromTo4(1, 10); -//│ block$res27 = sum1(tmp12, 0); -//│ undefined +//│ sum1(tmp12, 0) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 45 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 45 //│ 2 fusion opportunities: //│ Cons --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 45 @@ -1036,27 +1027,9 @@ c(AA(3)) //│ }; //│ tmp14 = AA1(3); //│ c1(tmp14) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c1, tmp14; -//│ c1 = function c(x) { -//│ let t, n, tmp15; -//│ t = x.aa; -//│ if (x instanceof AA1.class) { -//│ tmp15 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp15; -//│ return n + t -//│ }; -//│ tmp14 = AA1(3); -//│ block$res29 = c1(tmp14); -//│ undefined -//│ = 5 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity //│ = 5 +//│ No fusion opportunity @@ -1085,18 +1058,19 @@ f(A, B) //│ f3 = function f(a, b) { //│ return runtime.safeCall(a(b)) //│ }; -//│ block$res31 = f3((b) => { +//│ f3((b) => { //│ return runtime.safeCall(b()) //│ }, () => { //│ return 3 -//│ }); -//│ undefined +//│ }) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 //│ 2 fusion opportunities: //│ A --match--> `if a is ...` //│ B --match--> `if b is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 :sjs @@ -1104,15 +1078,15 @@ fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) //│ JS (unsanitized): -//│ let f4, tmp16; +//│ let f4, tmp15; //│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; +//│ let scrut, tmp16, tmp17; //│ if (x instanceof Some1.class) { //│ scrut = x.value > 1; //│ if (scrut === true) { -//│ tmp17 = x.value - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) +//│ tmp16 = x.value - 1; +//│ tmp17 = Some1(tmp16); +//│ return f4(tmp17) //│ } else { //│ return 0 //│ } @@ -1120,33 +1094,11 @@ f(Some(2)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp16 = Some1(2); -//│ f4(tmp16) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f4, tmp16; -//│ f4 = function f(x) { -//│ let scrut, tmp17, tmp18; -//│ if (x instanceof Some1.class) { -//│ scrut = x.value > 1; -//│ if (scrut === true) { -//│ tmp17 = x.value - 1; -//│ tmp18 = Some1(tmp17); -//│ return f4(tmp18) -//│ } else { -//│ return 0 -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp16 = Some1(2); -//│ block$res33 = f4(tmp16); -//│ undefined -//│ = 0 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ tmp15 = Some1(2); +//│ f4(tmp15) +//│ No fusion opportunity //│ = 0 +//│ No fusion opportunity @@ -1158,11 +1110,11 @@ if x is if y is B then 2 //│ JS (unsanitized): -//│ let x, y, tmp18; +//│ let x, y, tmp16; //│ x = A1; //│ y = B1; //│ if (x instanceof A1.class) { -//│ tmp18 = 1; +//│ tmp16 = 1; //│ } else { //│ throw new this.Error("match error"); //│ } @@ -1175,23 +1127,24 @@ if y is //│ ==== JS (deforested): ==== //│ let x, y; //│ x = (y1) => { -//│ let tmp18; -//│ tmp18 = 1; +//│ let tmp16; +//│ tmp16 = 1; //│ return runtime.safeCall(y1()) //│ }; //│ y = () => { //│ return 2 //│ }; -//│ block$res35 = runtime.safeCall(x(y)); -//│ undefined +//│ runtime.safeCall(x(y)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ x = A +//│ y = B +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if x is ...` //│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 -//│ x = A -//│ y = B @@ -1207,11 +1160,11 @@ test() //│ JS (unsanitized): //│ let test7; //│ test7 = function test() { -//│ let x1, y1, tmp19; +//│ let x1, y1, tmp17; //│ x1 = A1; //│ y1 = B1; //│ if (x1 instanceof A1.class) { -//│ tmp19 = 1; +//│ tmp17 = 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -1228,8 +1181,8 @@ test() //│ test7 = function test() { //│ let x1, y1; //│ x1 = (y2) => { -//│ let tmp19; -//│ tmp19 = 1; +//│ let tmp17; +//│ tmp17 = 1; //│ return runtime.safeCall(y2()) //│ }; //│ y1 = () => { @@ -1237,14 +1190,15 @@ test() //│ }; //│ return runtime.safeCall(x1(y1)) //│ }; -//│ block$res37 = test7(); -//│ undefined +//│ test7() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if x is ...` //│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 @@ -1256,66 +1210,66 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ JS (unsanitized): -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28; +//│ let f5, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; //│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp29, tmp30, tmp31; +//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp27, tmp28, tmp29; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param11 = x1.y; //│ n1 = param01; //│ m1 = param11; -//│ tmp29 = y1 + n1; -//│ tmp30 = tmp29 - m1; +//│ tmp27 = y1 + n1; +//│ tmp28 = tmp27 - m1; //│ } else if (x1 instanceof BBB1.class) { //│ param0 = x1.x; //│ param1 = x1.y; //│ n = param0; //│ m = param1; -//│ tmp31 = m + 1; -//│ tmp30 = tmp31 - n; +//│ tmp29 = m + 1; +//│ tmp28 = tmp29 - n; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp30; +//│ a = tmp28; //│ return a + 3 //│ }; -//│ tmp19 = AAA1(1, 3); -//│ tmp20 = f5(tmp19, 1); -//│ tmp21 = BBB1(2, 3); -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; -//│ tmp24 = AAA1(3, 2); -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; -//│ tmp27 = BBB1(4, 6); -//│ tmp28 = f5(tmp27, 0); -//│ tmp26 + tmp28 +//│ tmp17 = AAA1(1, 3); +//│ tmp18 = f5(tmp17, 1); +//│ tmp19 = BBB1(2, 3); +//│ tmp20 = f5(tmp19, 2); +//│ tmp21 = tmp18 + tmp20; +//│ tmp22 = AAA1(3, 2); +//│ tmp23 = f5(tmp22, 4); +//│ tmp24 = tmp21 + tmp23; +//│ tmp25 = BBB1(4, 6); +//│ tmp26 = f5(tmp25, 0); +//│ tmp24 + tmp26 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f5, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp, match_x_rest, match_x_branch_AAA, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp, match_x_branch_BBB, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1; +//│ let f5, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp, match_x_rest, match_x_branch_AAA, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp, match_x_branch_BBB, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1; //│ match_x_branch_AAA = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { -//│ let param0, param1, n, m, tmp29, tmp30; +//│ let param0, param1, n, m, tmp27, tmp28; //│ param0 = _deforest_AAA_x; //│ param1 = _deforest_AAA_y; //│ n = param0; //│ m = param1; -//│ tmp29 = y1 + n; -//│ tmp30 = tmp29 - m; -//│ return match_x_rest(tmp30) +//│ tmp27 = y1 + n; +//│ tmp28 = tmp27 - m; +//│ return match_x_rest(tmp28) //│ }; //│ match_x_branch_BBB = function match_x_branch_BBB(y1, _deforest_BBB_x, _deforest_BBB_y) { -//│ let param0, param1, n, m, tmp29, tmp30; +//│ let param0, param1, n, m, tmp27, tmp28; //│ param0 = _deforest_BBB_x; //│ param1 = _deforest_BBB_y; //│ n = param0; //│ m = param1; -//│ tmp30 = m + 1; -//│ tmp29 = tmp30 - n; -//│ return match_x_rest(tmp29) +//│ tmp28 = m + 1; +//│ tmp27 = tmp28 - n; +//│ return match_x_rest(tmp27) //│ }; -//│ match_x_rest = function match_x_rest(tmp29) { +//│ match_x_rest = function match_x_rest(tmp27) { //│ let a; -//│ a = tmp29; +//│ a = tmp27; //│ return a + 3 //│ }; //│ f5 = function f(x1, y1) { @@ -1323,32 +1277,34 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ }; //│ _deforest_AAA_x_tmp = 1; //│ _deforest_AAA_y_tmp = 3; -//│ tmp19 = (y1) => { +//│ tmp17 = (y1) => { //│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp) //│ }; -//│ tmp20 = f5(tmp19, 1); +//│ tmp18 = f5(tmp17, 1); //│ _deforest_BBB_x_tmp = 2; //│ _deforest_BBB_y_tmp = 3; -//│ tmp21 = (y1) => { +//│ tmp19 = (y1) => { //│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp) //│ }; -//│ tmp22 = f5(tmp21, 2); -//│ tmp23 = tmp20 + tmp22; +//│ tmp20 = f5(tmp19, 2); +//│ tmp21 = tmp18 + tmp20; //│ _deforest_AAA_x_tmp1 = 3; //│ _deforest_AAA_y_tmp1 = 2; -//│ tmp24 = (y1) => { +//│ tmp22 = (y1) => { //│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1) //│ }; -//│ tmp25 = f5(tmp24, 4); -//│ tmp26 = tmp23 + tmp25; +//│ tmp23 = f5(tmp22, 4); +//│ tmp24 = tmp21 + tmp23; //│ _deforest_BBB_x_tmp1 = 4; //│ _deforest_BBB_y_tmp1 = 6; -//│ tmp27 = (y1) => { +//│ tmp25 = (y1) => { //│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1) //│ }; -//│ tmp28 = f5(tmp27, 0); -//│ block$res39 = tmp26 + tmp28; -//│ undefined +//│ tmp26 = f5(tmp25, 0); +//│ tmp24 + tmp26 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 21 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 21 //│ 4 fusion opportunities: //│ AAA --match--> `if x is ...` @@ -1356,7 +1312,6 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ BBB --match--> `if x is ...` //│ BBB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 21 :sjs @@ -1369,7 +1324,7 @@ fun c2(x) = if x is fun p(a) = c1(a) + c2(a) p(AA(1)) //│ JS (unsanitized): -//│ let p, c11, c2, tmp39; +//│ let p, c11, c2, tmp37; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1391,16 +1346,16 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp40, tmp41; -//│ tmp40 = c11(a); -//│ tmp41 = c2(a); -//│ return tmp40 + tmp41 +//│ let tmp38, tmp39; +//│ tmp38 = c11(a); +//│ tmp39 = c2(a); +//│ return tmp38 + tmp39 //│ }; -//│ tmp39 = AA1(1); -//│ p(tmp39) +//│ tmp37 = AA1(1); +//│ p(tmp37) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp39; +//│ let p, c11, c2, tmp37; //│ c11 = function c1(x1) { //│ let scrut; //│ if (x1 instanceof AA1.class) { @@ -1420,19 +1375,20 @@ p(AA(1)) //│ } //│ }; //│ p = function p(a) { -//│ let tmp40, tmp41; -//│ tmp40 = c11(a); -//│ tmp41 = c2(a); -//│ return tmp40 + tmp41 -//│ }; -//│ tmp39 = AA1(1); -//│ block$res41 = p(tmp39); -//│ undefined +//│ let tmp38, tmp39; +//│ tmp38 = c11(a); +//│ tmp39 = c2(a); +//│ return tmp38 + tmp39 +//│ }; +//│ tmp37 = AA1(1); +//│ p(tmp37) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 1 fusion opportunities: //│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 @@ -1443,7 +1399,7 @@ fun c(x, y) = if x is A then a c(AA(2), A) //│ JS (unsanitized): -//│ let c3, tmp41; +//│ let c3, tmp39; //│ c3 = function c(x1, y1) { //│ let param0, a; //│ if (x1 instanceof AA1.class) { @@ -1458,31 +1414,32 @@ c(AA(2), A) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp41 = AA1(2); -//│ c3(tmp41, A1) +//│ tmp39 = AA1(2); +//│ c3(tmp39, A1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c3, tmp41, _deforest_AA_aa; +//│ let c3, tmp39, _deforest_AA_aa; //│ c3 = function c(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; //│ _deforest_AA_aa = 2; -//│ tmp41 = (y1) => { +//│ tmp39 = (y1) => { //│ let param0, a; //│ param0 = _deforest_AA_aa; //│ a = param0; //│ return runtime.safeCall(y1(a)) //│ }; -//│ block$res43 = c3(tmp41, (a) => { +//│ c3(tmp39, (a) => { //│ return a -//│ }); -//│ undefined +//│ }) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 //│ 2 fusion opportunities: //│ A --match--> `if y is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 :sjs @@ -1493,60 +1450,60 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ JS (unsanitized): -//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52; +//│ let f6, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50; //│ f6 = function f(x1, y1) { -//│ let a, param0, n, param01, param1, n1, m, tmp53, tmp54; +//│ let a, param0, n, param01, param1, n1, m, tmp51, tmp52; //│ if (x1 instanceof AAA1.class) { //│ param01 = x1.x; //│ param1 = x1.y; //│ n1 = param01; //│ m = param1; -//│ tmp53 = y1 + n1; -//│ tmp54 = tmp53 - m; +//│ tmp51 = y1 + n1; +//│ tmp52 = tmp51 - m; //│ } else if (x1 instanceof CCC1.class) { //│ param0 = x1.c; //│ n = param0; -//│ tmp54 = n + 1; +//│ tmp52 = n + 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp54; +//│ a = tmp52; //│ return a + 3 //│ }; -//│ tmp43 = AAA1(1, 3); -//│ tmp44 = f6(tmp43, 1); -//│ tmp45 = CCC1(2); -//│ tmp46 = f6(tmp45, 2); -//│ tmp47 = tmp44 + tmp46; -//│ tmp48 = AAA1(3, 2); -//│ tmp49 = f6(tmp48, 4); -//│ tmp50 = tmp47 + tmp49; -//│ tmp51 = CCC1(4); -//│ tmp52 = f6(tmp51, 0); -//│ tmp50 + tmp52 +//│ tmp41 = AAA1(1, 3); +//│ tmp42 = f6(tmp41, 1); +//│ tmp43 = CCC1(2); +//│ tmp44 = f6(tmp43, 2); +//│ tmp45 = tmp42 + tmp44; +//│ tmp46 = AAA1(3, 2); +//│ tmp47 = f6(tmp46, 4); +//│ tmp48 = tmp45 + tmp47; +//│ tmp49 = CCC1(4); +//│ tmp50 = f6(tmp49, 0); +//│ tmp48 + tmp50 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f6, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, _deforest_CCC_c_tmp, match_x_branch_CCC, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3, _deforest_CCC_c_tmp1; +//│ let f6, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, _deforest_CCC_c_tmp, match_x_branch_CCC, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3, _deforest_CCC_c_tmp1; //│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { -//│ let param0, param1, n, m, tmp53, tmp54; +//│ let param0, param1, n, m, tmp51, tmp52; //│ param0 = _deforest_AAA_x; //│ param1 = _deforest_AAA_y; //│ n = param0; //│ m = param1; -//│ tmp53 = y1 + n; -//│ tmp54 = tmp53 - m; -//│ return match_x_rest1(tmp54) +//│ tmp51 = y1 + n; +//│ tmp52 = tmp51 - m; +//│ return match_x_rest1(tmp52) //│ }; //│ match_x_branch_CCC = function match_x_branch_CCC(y1, _deforest_CCC_c) { -//│ let param0, n, tmp53; +//│ let param0, n, tmp51; //│ param0 = _deforest_CCC_c; //│ n = param0; -//│ tmp53 = n + 1; -//│ return match_x_rest1(tmp53) +//│ tmp51 = n + 1; +//│ return match_x_rest1(tmp51) //│ }; -//│ match_x_rest1 = function match_x_rest(tmp53) { +//│ match_x_rest1 = function match_x_rest(tmp51) { //│ let a; -//│ a = tmp53; +//│ a = tmp51; //│ return a + 3 //│ }; //│ f6 = function f(x1, y1) { @@ -1554,30 +1511,32 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ }; //│ _deforest_AAA_x_tmp2 = 1; //│ _deforest_AAA_y_tmp2 = 3; -//│ tmp43 = (y1) => { +//│ tmp41 = (y1) => { //│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2) //│ }; -//│ tmp44 = f6(tmp43, 1); +//│ tmp42 = f6(tmp41, 1); //│ _deforest_CCC_c_tmp = 2; -//│ tmp45 = (y1) => { +//│ tmp43 = (y1) => { //│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp) //│ }; -//│ tmp46 = f6(tmp45, 2); -//│ tmp47 = tmp44 + tmp46; +//│ tmp44 = f6(tmp43, 2); +//│ tmp45 = tmp42 + tmp44; //│ _deforest_AAA_x_tmp3 = 3; //│ _deforest_AAA_y_tmp3 = 2; -//│ tmp48 = (y1) => { +//│ tmp46 = (y1) => { //│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3) //│ }; -//│ tmp49 = f6(tmp48, 4); -//│ tmp50 = tmp47 + tmp49; +//│ tmp47 = f6(tmp46, 4); +//│ tmp48 = tmp45 + tmp47; //│ _deforest_CCC_c_tmp1 = 4; -//│ tmp51 = (y1) => { +//│ tmp49 = (y1) => { //│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp1) //│ }; -//│ tmp52 = f6(tmp51, 0); -//│ block$res45 = tmp50 + tmp52; -//│ undefined +//│ tmp50 = f6(tmp49, 0); +//│ tmp48 + tmp50 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 24 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 24 //│ 4 fusion opportunities: //│ AAA --match--> `if x is ...` @@ -1585,7 +1544,6 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ CCC --match--> `if x is ...` //│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 24 @@ -1598,40 +1556,40 @@ fun f(x, y) = a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ JS (unsanitized): -//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69; +//│ let f7, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67; //│ f7 = function f(x1, y1) { -//│ let a, param0, n, tmp70; +//│ let a, param0, n, tmp68; //│ if (x1 instanceof AAA1.class) { -//│ tmp70 = y1 + x1.y; +//│ tmp68 = y1 + x1.y; //│ } else if (x1 instanceof CCC1.class) { //│ param0 = x1.c; //│ n = param0; -//│ tmp70 = n + 1; +//│ tmp68 = n + 1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ a = tmp70; +//│ a = tmp68; //│ return a + 3 //│ }; -//│ tmp63 = AAA1(1, 3); -//│ tmp64 = f7(tmp63, 1); -//│ tmp65 = CCC1(2); -//│ tmp66 = f7(tmp65, 2); -//│ tmp67 = tmp64 + tmp66; -//│ tmp68 = AAA1(3, 2); -//│ tmp69 = f7(tmp68, 4); -//│ tmp67 + tmp69 +//│ tmp61 = AAA1(1, 3); +//│ tmp62 = f7(tmp61, 1); +//│ tmp63 = CCC1(2); +//│ tmp64 = f7(tmp63, 2); +//│ tmp65 = tmp62 + tmp64; +//│ tmp66 = AAA1(3, 2); +//│ tmp67 = f7(tmp66, 4); +//│ tmp65 + tmp67 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f7, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, _deforest_CCC_c, _deforest_AAA_x_unused, _deforest_AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, _deforest_AAA_x_unused1, _deforest_AAA_y_tmp5; +//│ let f7, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67, _deforest_CCC_c, _deforest_AAA_x_unused, _deforest_AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, _deforest_AAA_x_unused1, _deforest_AAA_y_tmp5; //│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, _deforest_AAA_y) { -//│ let tmp70; -//│ tmp70 = y1 + _deforest_AAA_y; -//│ return match_x_rest2(tmp70) +//│ let tmp68; +//│ tmp68 = y1 + _deforest_AAA_y; +//│ return match_x_rest2(tmp68) //│ }; -//│ match_x_rest2 = function match_x_rest(tmp70) { +//│ match_x_rest2 = function match_x_rest(tmp68) { //│ let a; -//│ a = tmp70; +//│ a = tmp68; //│ return a + 3 //│ }; //│ f7 = function f(x1, y1) { @@ -1639,35 +1597,36 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ }; //│ _deforest_AAA_x_unused = 1; //│ _deforest_AAA_y_tmp4 = 3; -//│ tmp63 = (y1) => { +//│ tmp61 = (y1) => { //│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp4) //│ }; -//│ tmp64 = f7(tmp63, 1); +//│ tmp62 = f7(tmp61, 1); //│ _deforest_CCC_c = 2; -//│ tmp65 = (y1) => { -//│ let param0, n, tmp70; +//│ tmp63 = (y1) => { +//│ let param0, n, tmp68; //│ param0 = _deforest_CCC_c; //│ n = param0; -//│ tmp70 = n + 1; -//│ return match_x_rest2(tmp70) +//│ tmp68 = n + 1; +//│ return match_x_rest2(tmp68) //│ }; -//│ tmp66 = f7(tmp65, 2); -//│ tmp67 = tmp64 + tmp66; +//│ tmp64 = f7(tmp63, 2); +//│ tmp65 = tmp62 + tmp64; //│ _deforest_AAA_x_unused1 = 3; //│ _deforest_AAA_y_tmp5 = 2; -//│ tmp68 = (y1) => { +//│ tmp66 = (y1) => { //│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp5) //│ }; -//│ tmp69 = f7(tmp68, 4); -//│ block$res47 = tmp67 + tmp69; -//│ undefined +//│ tmp67 = f7(tmp66, 4); +//│ tmp65 + tmp67 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 22 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 22 //│ 3 fusion opportunities: //│ AAA --match--> `if x is ...` //│ AAA --match--> `if x is ...` //│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 22 :sjs @@ -1676,7 +1635,7 @@ fun c(x, m) = if x is else m c(BB(3), 0) //│ JS (unsanitized): -//│ let c4, tmp77; +//│ let c4, tmp75; //│ c4 = function c(x1, m) { //│ let param0, n; //│ if (x1 instanceof AA1.class) { @@ -1687,25 +1646,26 @@ c(BB(3), 0) //│ return m //│ } //│ }; -//│ tmp77 = BB1(3); -//│ c4(tmp77, 0) +//│ tmp75 = BB1(3); +//│ c4(tmp75, 0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let c4, tmp77, _deforest_BB_bb_unused; +//│ let c4, tmp75, _deforest_BB_bb_unused; //│ c4 = function c(x1, m) { //│ return runtime.safeCall(x1(m)) //│ }; //│ _deforest_BB_bb_unused = 3; -//│ tmp77 = (m) => { +//│ tmp75 = (m) => { //│ return m //│ }; -//│ block$res49 = c4(tmp77, 0); -//│ undefined +//│ c4(tmp75, 0) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 0 //│ 1 fusion opportunities: //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 0 @@ -1717,7 +1677,7 @@ fun g(b) = if b is AA(a) then a + 1 f(AA(AA(0))) //│ JS (unsanitized): -//│ let f8, g, tmp79, tmp80; +//│ let f8, g, tmp77, tmp78; //│ f8 = function f(x1) { //│ let param0, b; //│ if (x1 instanceof AA1.class) { @@ -1738,12 +1698,12 @@ f(AA(AA(0))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp79 = AA1(0); -//│ tmp80 = AA1(tmp79); -//│ f8(tmp80) +//│ tmp77 = AA1(0); +//│ tmp78 = AA1(tmp77); +//│ f8(tmp78) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f8, g, tmp79, tmp80, _deforest_AA_aa1, _deforest_AA_aa2; +//│ let f8, g, tmp77, tmp78, _deforest_AA_aa1, _deforest_AA_aa2; //│ f8 = function f(x1) { //│ return runtime.safeCall(x1()) //│ }; @@ -1751,27 +1711,28 @@ f(AA(AA(0))) //│ return runtime.safeCall(b()) //│ }; //│ _deforest_AA_aa1 = 0; -//│ tmp79 = () => { +//│ tmp77 = () => { //│ let param0, a; //│ param0 = _deforest_AA_aa1; //│ a = param0; //│ return a + 1 //│ }; -//│ _deforest_AA_aa2 = tmp79; -//│ tmp80 = () => { +//│ _deforest_AA_aa2 = tmp77; +//│ tmp78 = () => { //│ let param0, b; //│ param0 = _deforest_AA_aa2; //│ b = param0; //│ return g(b) //│ }; -//│ block$res51 = f8(tmp80); -//│ undefined +//│ f8(tmp78) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 2 fusion opportunities: //│ AA --match--> `if b is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 @@ -1779,23 +1740,24 @@ f(AA(AA(0))) fun f(x) = x.aa.aa f(AA(AA(3))) //│ JS (unsanitized): -//│ let f9, tmp83, tmp84; +//│ let f9, tmp81, tmp82; //│ f9 = function f(x1) { //│ return x1.aa.aa //│ }; -//│ tmp83 = AA1(3); -//│ tmp84 = AA1(tmp83); -//│ f9(tmp84) +//│ tmp81 = AA1(3); +//│ tmp82 = AA1(tmp81); +//│ f9(tmp82) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f9, tmp83, tmp84; -//│ f9 = function f(x1) { return x1 }; tmp83 = 3; tmp84 = tmp83; block$res53 = f9(tmp84); undefined +//│ let f9, tmp81, tmp82; f9 = function f(x1) { return x1 }; tmp81 = 3; tmp82 = tmp81; f9(tmp82) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 //│ 2 fusion opportunities: //│ AA --sel--> `.Ident(aa)` //│ AA --sel--> `.Ident(aa)` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 :sjs fun inner(x, y) = @@ -1816,133 +1778,134 @@ fun outer2(x, y, z) = let p = AA(AA(3)) outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ JS (unsanitized): -//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93; +//│ let outer1, outer2, inner, p1, tmp85, tmp86, tmp87, tmp88, tmp89, tmp90, tmp91; //│ inner = function inner(x1, y1) { -//│ let t, param0, b, param01, a, tmp94; +//│ let t, param0, b, param01, a, tmp92; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp94 = a + y1; +//│ tmp92 = a + y1; //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp94 = b - y1; +//│ tmp92 = b - y1; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp94; +//│ t = tmp92; //│ return t * y1 //│ }; //│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp94; +//│ let t, param0, b, param01, a, tmp92; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp94 = inner(a, y1); +//│ tmp92 = inner(a, y1); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp94 = inner(b, y1); +//│ tmp92 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp94; +//│ t = tmp92; //│ return t + z //│ }; //│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp94; +//│ let t, param0, b, param01, a, tmp92; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp94 = inner(a, z); +//│ tmp92 = inner(a, z); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp94 = inner(b, y1); +//│ tmp92 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp94; +//│ t = tmp92; //│ return t + y1 //│ }; -//│ tmp87 = AA1(3); -//│ tmp88 = AA1(tmp87); -//│ p1 = tmp88; -//│ tmp89 = outer1(p1, 1, 2); -//│ tmp90 = outer2(p1, 3, 4); -//│ tmp91 = tmp89 + tmp90; -//│ tmp92 = AA1(5); -//│ tmp93 = inner(tmp92, 6); -//│ tmp91 + tmp93 +//│ tmp85 = AA1(3); +//│ tmp86 = AA1(tmp85); +//│ p1 = tmp86; +//│ tmp87 = outer1(p1, 1, 2); +//│ tmp88 = outer2(p1, 3, 4); +//│ tmp89 = tmp87 + tmp88; +//│ tmp90 = AA1(5); +//│ tmp91 = inner(tmp90, 6); +//│ tmp89 + tmp91 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let outer1, outer2, inner, p1, tmp87, tmp88, tmp89, tmp90, tmp91, tmp92, tmp93, _deforest_AA_aa_tmp, match_x_branch_AA, _deforest_AA_aa_tmp1; +//│ let outer1, outer2, inner, p1, tmp85, tmp86, tmp87, tmp88, tmp89, tmp90, tmp91, _deforest_AA_aa_tmp, match_x_branch_AA, _deforest_AA_aa_tmp1; //│ match_x_branch_AA = function match_x_branch_AA(y1, _deforest_AA_aa3) { -//│ let t, param0, a, tmp94; +//│ let t, param0, a, tmp92; //│ param0 = _deforest_AA_aa3; //│ a = param0; -//│ tmp94 = a + y1; -//│ t = tmp94; +//│ tmp92 = a + y1; +//│ t = tmp92; //│ return t * y1 //│ }; //│ inner = function inner(x1, y1) { //│ return runtime.safeCall(x1(y1)) //│ }; //│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp94; +//│ let t, param0, b, param01, a, tmp92; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp94 = inner(a, y1); +//│ tmp92 = inner(a, y1); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp94 = inner(b, y1); +//│ tmp92 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp94; +//│ t = tmp92; //│ return t + z //│ }; //│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp94; +//│ let t, param0, b, param01, a, tmp92; //│ if (x1 instanceof AA1.class) { //│ param01 = x1.aa; //│ a = param01; -//│ tmp94 = inner(a, z); +//│ tmp92 = inner(a, z); //│ } else if (x1 instanceof BB1.class) { //│ param0 = x1.bb; //│ b = param0; -//│ tmp94 = inner(b, y1); +//│ tmp92 = inner(b, y1); //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ t = tmp94; +//│ t = tmp92; //│ return t + y1 //│ }; //│ _deforest_AA_aa_tmp = 3; -//│ tmp87 = (y1) => { +//│ tmp85 = (y1) => { //│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp) //│ }; -//│ tmp88 = AA1(tmp87); -//│ p1 = tmp88; -//│ tmp89 = outer1(p1, 1, 2); -//│ tmp90 = outer2(p1, 3, 4); -//│ tmp91 = tmp89 + tmp90; +//│ tmp86 = AA1(tmp85); +//│ p1 = tmp86; +//│ tmp87 = outer1(p1, 1, 2); +//│ tmp88 = outer2(p1, 3, 4); +//│ tmp89 = tmp87 + tmp88; //│ _deforest_AA_aa_tmp1 = 5; -//│ tmp92 = (y1) => { +//│ tmp90 = (y1) => { //│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp1) //│ }; -//│ tmp93 = inner(tmp92, 6); -//│ block$res55 = tmp91 + tmp93; -//│ undefined +//│ tmp91 = inner(tmp90, 6); +//│ tmp89 + tmp91 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 103 +//│ p = AA(AA(3)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 103 //│ 2 fusion opportunities: //│ AA --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 103 -//│ p = AA(AA(3)) :sjs @@ -1950,7 +1913,7 @@ fun mapHead(f, x) = if x is AAA(h, t) then f(h) mapHead(x => x, AAA(1, AAA(2, None))) //│ JS (unsanitized): -//│ let mapHead, tmp101, tmp102, lambda4; +//│ let mapHead, tmp99, tmp100, lambda4; //│ mapHead = function mapHead(f10, x1) { //│ let param0, param1, h, t; //│ if (x1 instanceof AAA1.class) { @@ -1963,22 +1926,22 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp101 = AAA1(2, None1); -//│ tmp102 = AAA1(1, tmp101); +//│ tmp99 = AAA1(2, None1); +//│ tmp100 = AAA1(1, tmp99); //│ lambda4 = (undefined, function (x1) { //│ return x1 //│ }); -//│ mapHead(lambda4, tmp102) +//│ mapHead(lambda4, tmp100) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let mapHead, tmp101, tmp102, lambda4, _deforest_AAA_x, _deforest_AAA_y; +//│ let mapHead, tmp99, tmp100, lambda4, _deforest_AAA_x, _deforest_AAA_y; //│ mapHead = function mapHead(f10, x1) { //│ return runtime.safeCall(x1(f10)) //│ }; -//│ tmp101 = AAA1(2, None1); +//│ tmp99 = AAA1(2, None1); //│ _deforest_AAA_x = 1; -//│ _deforest_AAA_y = tmp101; -//│ tmp102 = (f10) => { +//│ _deforest_AAA_y = tmp99; +//│ tmp100 = (f10) => { //│ let param0, param1, h, t; //│ param0 = _deforest_AAA_x; //│ param1 = _deforest_AAA_y; @@ -1989,13 +1952,14 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ lambda4 = (undefined, function (x1) { //│ return x1 //│ }); -//│ block$res57 = mapHead(lambda4, tmp102); -//│ undefined +//│ mapHead(lambda4, tmp100) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 //│ 1 fusion opportunities: //│ AAA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 1 :sjs @@ -2009,12 +1973,12 @@ test() //│ JS (unsanitized): //│ let test8; //│ test8 = function test() { -//│ let k, scrut, tmp105; +//│ let k, scrut, tmp103; //│ k = undefined; //│ scrut = A1; //│ if (scrut instanceof A1.class) { //│ k = 3; -//│ tmp105 = runtime.Unit; +//│ tmp103 = runtime.Unit; //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -2028,20 +1992,21 @@ test() //│ let k, scrut; //│ k = undefined; //│ scrut = () => { -//│ let tmp105; +//│ let tmp103; //│ k = 3; -//│ tmp105 = runtime.Unit; +//│ tmp103 = runtime.Unit; //│ return k + 2 //│ }; //│ return runtime.safeCall(scrut()) //│ }; -//│ block$res59 = test8(); -//│ undefined +//│ test8() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 5 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 //│ 1 fusion opportunities: //│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 5 :sjs fun test(a) = if a is @@ -2049,7 +2014,7 @@ fun test(a) = if a is else 2 test(B) + test(C) //│ JS (unsanitized): -//│ let test9, tmp105, tmp106; +//│ let test9, tmp103, tmp104; //│ test9 = function test(a) { //│ if (a instanceof AA1.class) { //│ return 1 @@ -2057,32 +2022,33 @@ test(B) + test(C) //│ return 2 //│ } //│ }; -//│ tmp105 = test9(B1); -//│ tmp106 = test9(C1); -//│ tmp105 + tmp106 +//│ tmp103 = test9(B1); +//│ tmp104 = test9(C1); +//│ tmp103 + tmp104 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test9, tmp105, tmp106, match_a_branch_dflt; +//│ let test9, tmp103, tmp104, match_a_branch_dflt; //│ match_a_branch_dflt = function match_a_branch_dflt() { //│ return 2 //│ }; //│ test9 = function test(a) { //│ return runtime.safeCall(a()) //│ }; -//│ tmp105 = test9(() => { +//│ tmp103 = test9(() => { //│ return match_a_branch_dflt() //│ }); -//│ tmp106 = test9(() => { +//│ tmp104 = test9(() => { //│ return match_a_branch_dflt() //│ }); -//│ block$res61 = tmp105 + tmp106; -//│ undefined +//│ tmp103 + tmp104 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 4 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 //│ 2 fusion opportunities: //│ B --match--> `if a is ...` //│ C --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 4 @@ -2097,73 +2063,74 @@ fun test(x, y, z) = if x is m + n test(AA(1), AA(2), AA(3)) //│ JS (unsanitized): -//│ let test10, tmp109, tmp110, tmp111; +//│ let test10, tmp107, tmp108, tmp109; //│ test10 = function test(x1, y1, z) { -//│ let param0, a, m, param01, a1, n, param02, a2, tmp112, tmp113; +//│ let param0, a, m, param01, a1, n, param02, a2, tmp110, tmp111; //│ if (x1 instanceof AA1.class) { //│ param0 = x1.aa; //│ a = param0; //│ if (y1 instanceof AA1.class) { //│ param01 = y1.aa; //│ a1 = param01; -//│ tmp112 = a1 + z; +//│ tmp110 = a1 + z; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ m = tmp112; +//│ m = tmp110; //│ if (z instanceof AA1.class) { //│ param02 = z.aa; //│ a2 = param02; -//│ tmp113 = a2 - z; +//│ tmp111 = a2 - z; //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ n = tmp113; +//│ n = tmp111; //│ return m + n //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp109 = AA1(1); -//│ tmp110 = AA1(2); -//│ tmp111 = AA1(3); -//│ test10(tmp109, tmp110, tmp111) +//│ tmp107 = AA1(1); +//│ tmp108 = AA1(2); +//│ tmp109 = AA1(3); +//│ test10(tmp107, tmp108, tmp109) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let test10, tmp109, tmp110, tmp111, _deforest_AA_aa3, _deforest_AA_aa4; +//│ let test10, tmp107, tmp108, tmp109, _deforest_AA_aa3, _deforest_AA_aa4; //│ test10 = function test(x1, y1, z) { //│ return runtime.safeCall(x1(y1, z)) //│ }; //│ _deforest_AA_aa3 = 1; -//│ tmp109 = (y1, z) => { +//│ tmp107 = (y1, z) => { //│ let param0, a; //│ param0 = _deforest_AA_aa3; //│ a = param0; //│ return runtime.safeCall(y1(z)) //│ }; //│ _deforest_AA_aa4 = 2; -//│ tmp110 = (z) => { -//│ let m, param0, a1, n, param01, a2, tmp112, tmp113; +//│ tmp108 = (z) => { +//│ let m, param0, a1, n, param01, a2, tmp110, tmp111; //│ param0 = _deforest_AA_aa4; //│ a1 = param0; -//│ tmp112 = a1 + z; -//│ m = tmp112; +//│ tmp110 = a1 + z; +//│ m = tmp110; //│ if (z instanceof AA1.class) { //│ param01 = z.aa; //│ a2 = param01; -//│ tmp113 = a2 - z; +//│ tmp111 = a2 - z; //│ } else { //│ throw new this.Error("match error"); //│ } -//│ n = tmp113; +//│ n = tmp111; //│ return m + n //│ }; -//│ tmp111 = AA1(3); -//│ block$res63 = test10(tmp109, tmp110, tmp111); -//│ undefined +//│ tmp109 = AA1(3); +//│ test10(tmp107, tmp108, tmp109) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = "2AA(3)NaN" +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "2AA(3)NaN" //│ 2 fusion opportunities: //│ AA --match--> `if x is ...` //│ AA --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = "2AA(3)NaN" diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index d06eb901fb..7b8a4bc770 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -1,6 +1,7 @@ :js :deforest +//│ No fusion opportunity object A object B object C @@ -57,13 +58,14 @@ f(AA(AA(3))) //│ throw new this.Error("match error"); //│ } //│ }; -//│ block$res4 = f(tmp1); -//│ undefined +//│ f(tmp1) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 //│ 1 fusion opportunities: //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 3 // TODO: the computation of `t + 5 * 4 - 3 + 2 - 1` is still duplicated... @@ -199,13 +201,14 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ tmp13 = test(tmp12); //│ tmp14 = tmp9 + tmp13; //│ tmp15 = test(B1); -//│ block$res6 = tmp14 + tmp15; -//│ undefined +//│ tmp14 + tmp15 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 78 +//│ p = AA(AA(AA(10))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 78 //│ 2 fusion opportunities: //│ AA --match--> `if param0 is ...` //│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 78 -//│ p = AA(AA(AA(10))) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index f4695157a6..3211289b1f 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -56,24 +56,11 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case r => output(s"Failed to load runtime: $r") h - lazy val hostDeforest = - hostDeforestCreated = true - // given TL = replTL - // val h = ReplHost(rootPath) - // h.execute(s"const $runtimeNme = (await import(\"${runtimeFile}\")).default;") match - // case ReplHost.Result(msg) => - // if msg.startsWith("Uncaught") then output(s"Failed to load runtime: $msg") - // case r => output(s"Failed to load runtime: $r") - // h - host - private var hostCreated = false - private var hostDeforestCreated = false override def run(): Unit = try super.run() finally if hostCreated then host.terminate() - if hostDeforestCreated then hostDeforest.terminate() @@ -104,7 +91,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val outerRaise: Raise = summon val reportedMessages = mutable.Set.empty[Str] - var deforestResult: Opt[Str] = None + var correctResult: Opt[Str] = None if showJS.isSet then given Raise = @@ -127,78 +114,24 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if deforestFlag.isSet then val deforest = new Deforest(using deforestTL) - output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") - if showLoweredTree.isSet then - output("\n==== Non-inserted lowered tree ====") - output(le.showAsTree) - - val (deforestRes, deforestStat) = deforest(le) - - if showLoweredTree.isSet then - output("\n==== deforested tree ====") - output(deforestRes.showAsTree) - output("\n") - - val resSym = new TempSymbol(S(blk), "block$res") - - val resNme = baseScp.allocateName(resSym) - - val le0 = deforestRes.copy(main = deforestRes.main.mapTail: - case e: End => - Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) - case Return(res, implct) => - assert(implct) - Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) - case tl: (Throw | Break | Continue) => tl - ) - - val (pre, je) = baseScp.givenIn: - jsb.worksheet(le0) - output("==== JS (deforested): ====") - - val jsStr = je.stripBreaks.mkString(100) - val preStr = pre.stripBreaks.mkString(100) - output(preStr) - output(jsStr) - - - hostDeforest.execute(s"$resNme = undefined") - mkQuery(preStr, jsStr)(using hostDeforest): stdout => - stdout.splitSane('\n').init - .foreach: line => - output(s"> ${line}") - - if silent.isUnset then - import Elaborator.Ctx.* - val valuesToPrint = List(("", resSym, expect.get)) - valuesToPrint.foreach: (nme, sym, expect) => - val le = - import codegen.* - Return( - Call( - Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), - Arg(false, Value.Ref(sym)) :: Nil)(true, false), - implct = true) - val je = baseScp.givenIn: - jsb.block(le, endSemi = false) + val deforestRes -> _ -> num = deforest(le) + deforestRes match + case None => () + case Some(_) if num == 0 => output("No fusion opportunity") + case Some(deforestRes) => + output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") + if showLoweredTree.isSet then + output("\n==== deforested tree ====") + output(deforestRes.showAsTree) + output("\n") + + val je = baseScp.nest.givenIn: + jsb.program(deforestRes, N, wd) + output("==== JS (deforested): ====") val jsStr = je.stripBreaks.mkString(100) - mkQuery("", jsStr)(using hostDeforest): out => - val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) - expect match - case S(expected) if result =/= expected => raise: - ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, - source = Diagnostic.Source.Runtime) - case _ => () - if sym === resSym then deforestResult = S(result) - result match - case "undefined" => - case "()" => - case _ => - output(s"${if nme.isEmpty then "" else s"$nme "}= ${result.indentNewLines("| ")}") + output(jsStr) + output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") - output(deforestStat) - output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") - if js.isSet then given Elaborator.Ctx = curCtx given Raise = @@ -259,10 +192,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: .foreach: line => output(s"> ${line}") - // if deforestFlag.isSet && showJS.isUnset then - // mkQuery(preStr, jsStr)(using hostDeforest)(_ => ()) - - if traceJS.isSet then host.execute("globalThis.Predef.TraceLogger.enabled = false") @@ -297,15 +226,83 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, source = Diagnostic.Source.Runtime) case _ => () - if sym === resSym && deforestFlag.isSet && deforestResult.fold(false)(_ != result) then raise: - ErrorReport( - msg"The result from deforestated program (\"${deforestResult.get}\") is different from the one computed by the original prorgam (\"${result}\")" -> N :: Nil, - source = Diagnostic.Source.Runtime) val anon = nme.isEmpty + if sym === resSym then correctResult = S(result) result match case "undefined" if anon => case "()" if anon => case _ => output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") - + if deforestFlag.isSet then + + val deforestLow = ltl.givenIn: + codegen.Lowering() + val lowered0 = deforestLow.program(blk) + val deforest = new Deforest(using deforestTL) + val maybeDeforestRes -> deforestStat -> num = deforest(lowered0) + maybeDeforestRes match + case None => () + case Some(_) if num == 0 => output("No fusion opportunity") + case Some(deforestRes) => + output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") + val resSym = new TempSymbol(S(blk), "block$res_deforest") + val resNme = nestedScp.allocateName(resSym) + val le = deforestRes.copy(main = deforestRes.main.mapTail: + case e: End => + Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) + case Return(res, implct) => + assert(implct) + Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) + case tl: (Throw | Break | Continue) => tl + ) + val (pre, js) = nestedScp.givenIn: + jsb.worksheet(le) + val preStr = pre.stripBreaks.mkString(100) + val jsStr = js.stripBreaks.mkString(100) + if showSanitizedJS.isSet then + output(s"JS:") + output(jsStr) + + host.execute(s"$resNme = undefined") + + mkQuery(preStr, jsStr): stdout => + stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) + .foreach: line => + output(s"> ${line}") + + if silent.isUnset then + import Elaborator.Ctx.* + val valuesToPrint = ("", resSym, expect.get) :: Nil + valuesToPrint.foreach: (nme, sym, expect) => + val le = + import codegen.* + Return( + Call( + Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), + Arg(false, Value.Ref(sym)) :: Nil)(true, false), + implct = true) + val je = nestedScp.givenIn: + jsb.block(le, endSemi = false) + val jsStr = je.stripBreaks.mkString(100) + mkQuery("", jsStr): out => + val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) + expect match + case S(expected) if result =/= expected => raise: + ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, + source = Diagnostic.Source.Runtime) + case _ => () + val anon = nme.isEmpty + if sym === resSym && correctResult.fold(false)(_ != result) then raise: + ErrorReport( + msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, + source = Diagnostic.Source.Runtime) + result match + case "undefined" if anon => + case "()" if anon => + case _ => + output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") + + output(deforestStat) + output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") + \ No newline at end of file From 2486b4d2e402bd514cfc749f192c3c148968961a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:39:14 +0800 Subject: [PATCH 163/303] lessen output --- .../src/test/mlscript/deforest/imperative.mls | 112 -- .../test/mlscript/deforest/nestedMatch.mls | 1544 ---------------- .../deforest/selectionsInNestedMatch.mls | 360 ---- .../src/test/mlscript/deforest/simple.mls | 1617 ----------------- 4 files changed, 3633 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index 035d286a2c..45894e3f22 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -12,74 +12,23 @@ object B // class Some(value) // * Not fused: two `x is A` consumers -:sjs let x = if true then A else B fun foo(x) = if x is A do print(123) if x is A then 1 B then 2 -//│ JS (unsanitized): -//│ let foo, x, scrut, tmp; -//│ foo = function foo(x1) { -//│ let tmp1; -//│ if (x1 instanceof A1.class) { -//│ tmp1 = Predef.print(123); -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ if (x1 instanceof A1.class) { -//│ return 1 -//│ } else if (x1 instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = A1; -//│ } else { -//│ tmp = B1; -//│ } -//│ x = tmp; -//│ No fusion opportunity //│ x = A //│ No fusion opportunity // * We could make it work. But it's a special case that's probably not very important -:sjs let x = if true then A else B fun foo(x) = if x is A do print(123) if x is B do print(456) -//│ JS (unsanitized): -//│ let foo1, x1, scrut1, tmp1; -//│ foo1 = function foo(x2) { -//│ let tmp2; -//│ if (x2 instanceof A1.class) { -//│ tmp2 = Predef.print(123); -//│ } else { -//│ tmp2 = runtime.Unit; -//│ } -//│ if (x2 instanceof B1.class) { -//│ return Predef.print(456) -//│ } else { -//│ return runtime.Unit -//│ } -//│ }; -//│ scrut1 = true; -//│ if (scrut1 === true) { -//│ tmp1 = A1; -//│ } else { -//│ tmp1 = B1; -//│ } -//│ x1 = tmp1; -//│ No fusion opportunity //│ x = A //│ No fusion opportunity -:sjs fun foo(k, x) = if x === 0 do k(A) k(if x > 0 @@ -89,67 +38,6 @@ fun bar(v) = if v is A then 1 B then 2 foo(bar, 123) -//│ JS (unsanitized): -//│ let bar, foo2; -//│ foo2 = function foo(k, x2) { -//│ let scrut2, scrut3, tmp2, tmp3; -//│ scrut2 = x2 === 0; -//│ if (scrut2 === true) { -//│ tmp2 = runtime.safeCall(k(A1)); -//│ } else { -//│ tmp2 = runtime.Unit; -//│ } -//│ scrut3 = x2 > 0; -//│ if (scrut3 === true) { -//│ tmp3 = A1; -//│ } else { -//│ tmp3 = B1; -//│ } -//│ return runtime.safeCall(k(tmp3)) -//│ }; -//│ bar = function bar(v) { -//│ if (v instanceof A1.class) { -//│ return 1 -//│ } else if (v instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ foo2(bar, 123) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let bar, foo2, match_v_branch_A; -//│ match_v_branch_A = function match_v_branch_A() { -//│ return 1 -//│ }; -//│ foo2 = function foo(k, x2) { -//│ let scrut2, scrut3, tmp2, tmp3; -//│ scrut2 = x2 === 0; -//│ if (scrut2 === true) { -//│ tmp2 = runtime.safeCall(k(() => { -//│ return match_v_branch_A() -//│ })); -//│ } else { -//│ tmp2 = runtime.Unit; -//│ } -//│ scrut3 = x2 > 0; -//│ if (scrut3 === true) { -//│ tmp3 = () => { -//│ return match_v_branch_A() -//│ }; -//│ } else { -//│ tmp3 = () => { -//│ return 2 -//│ }; -//│ } -//│ return runtime.safeCall(k(tmp3)) -//│ }; -//│ bar = function bar(v) { -//│ return runtime.safeCall(v()) -//│ }; -//│ foo2(bar, 123) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 80665d8040..7293d9d255 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -16,7 +16,6 @@ object None class Some(val x) -:sjs fun f(x, y) = let t = if x is AA(a) then @@ -29,108 +28,6 @@ fun g(x) = if x is AA then 0 let a = AA(AA(3)) f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) -//│ JS (unsanitized): -//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; -//│ f = function f(x, y) { -//│ let t, param0, b, param01, a1, m, param02, x1, tmp11, tmp12; -//│ if (x instanceof AA1.class) { -//│ param01 = x.x; -//│ a1 = param01; -//│ if (a1 instanceof AA1.class) { -//│ param02 = a1.x; -//│ x1 = param02; -//│ tmp11 = x1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp11; -//│ tmp12 = m + 9; -//│ } else if (x instanceof BB1.class) { -//│ param0 = x.x; -//│ b = param0; -//│ tmp12 = b; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp12; -//│ return t + y -//│ }; -//│ g = function g(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp = AA1(3); -//│ tmp1 = AA1(tmp); -//│ a = tmp1; -//│ tmp2 = f(a, 2); -//│ tmp3 = g(a); -//│ tmp4 = tmp2 + tmp3; -//│ tmp5 = BB1(3); -//│ tmp6 = f(tmp5, 2); -//│ tmp7 = tmp4 + tmp6; -//│ tmp8 = AA1(4); -//│ tmp9 = AA1(tmp8); -//│ tmp10 = f(tmp9, 5); -//│ tmp7 + tmp10 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f, g, a, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, _deforest_AA_x_tmp, match_a_branch_AA, _deforest_AA_x_tmp1; -//│ match_a_branch_AA = function match_a_branch_AA(y, _deforest_AA_x) { -//│ let t, m, param0, x, tmp11, tmp12; -//│ param0 = _deforest_AA_x; -//│ x = param0; -//│ tmp11 = x; -//│ m = tmp11; -//│ tmp12 = m + 9; -//│ t = tmp12; -//│ return t + y -//│ }; -//│ f = function f(x, y) { -//│ let t, param0, b, param01, a1, tmp11; -//│ if (x instanceof AA1.class) { -//│ param01 = x.x; -//│ a1 = param01; -//│ return runtime.safeCall(a1(y)) -//│ } else if (x instanceof BB1.class) { -//│ param0 = x.x; -//│ b = param0; -//│ tmp11 = b; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp11; -//│ return t + y -//│ }; -//│ g = function g(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x_tmp = 3; -//│ tmp = (y) => { -//│ return match_a_branch_AA(y, _deforest_AA_x_tmp) -//│ }; -//│ tmp1 = AA1(tmp); -//│ a = tmp1; -//│ tmp2 = f(a, 2); -//│ tmp3 = g(a); -//│ tmp4 = tmp2 + tmp3; -//│ tmp5 = BB1(3); -//│ tmp6 = f(tmp5, 2); -//│ tmp7 = tmp4 + tmp6; -//│ _deforest_AA_x_tmp1 = 4; -//│ tmp8 = (y) => { -//│ return match_a_branch_AA(y, _deforest_AA_x_tmp1) -//│ }; -//│ tmp9 = AA1(tmp8); -//│ tmp10 = f(tmp9, 5); -//│ tmp7 + tmp10 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 37 //│ a = AA(AA(3)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -142,7 +39,6 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) -:sjs fun f(x, y) = let n = if x is AA(a) then @@ -150,77 +46,6 @@ fun f(x, y) = AA(m) then m n + 2 + y f(AA(AA(3)), 9) + f(AA(AA(4)), 10) -//│ JS (unsanitized): -//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; -//│ f1 = function f(x, y) { -//│ let n, param0, a1, param01, m, tmp28, tmp29, tmp30; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a1 = param0; -//│ if (a1 instanceof AA1.class) { -//│ param01 = a1.x; -//│ m = param01; -//│ tmp28 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp29 = tmp28; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp29; -//│ tmp30 = n + 2; -//│ return tmp30 + y -//│ }; -//│ tmp22 = AA1(3); -//│ tmp23 = AA1(tmp22); -//│ tmp24 = f1(tmp23, 9); -//│ tmp25 = AA1(4); -//│ tmp26 = AA1(tmp25); -//│ tmp27 = f1(tmp26, 10); -//│ tmp24 + tmp27 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, _deforest_AA_x_tmp2, match_a_branch_AA1, _deforest_AA_x_tmp3, match_x_branch_AA, _deforest_AA_x_tmp4, _deforest_AA_x_tmp5; -//│ match_a_branch_AA1 = function match_a_branch_AA(y, _deforest_AA_x) { -//│ let n, param0, m, tmp28, tmp29, tmp30; -//│ param0 = _deforest_AA_x; -//│ m = param0; -//│ tmp28 = m; -//│ tmp29 = tmp28; -//│ n = tmp29; -//│ tmp30 = n + 2; -//│ return tmp30 + y -//│ }; -//│ match_x_branch_AA = function match_x_branch_AA(y, _deforest_AA_x) { -//│ let param0, a1; -//│ param0 = _deforest_AA_x; -//│ a1 = param0; -//│ return runtime.safeCall(a1(y)) -//│ }; -//│ f1 = function f(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ _deforest_AA_x_tmp2 = 3; -//│ tmp22 = (y) => { -//│ return match_a_branch_AA1(y, _deforest_AA_x_tmp2) -//│ }; -//│ _deforest_AA_x_tmp3 = tmp22; -//│ tmp23 = (y) => { -//│ return match_x_branch_AA(y, _deforest_AA_x_tmp3) -//│ }; -//│ tmp24 = f1(tmp23, 9); -//│ _deforest_AA_x_tmp4 = 4; -//│ tmp25 = (y) => { -//│ return match_a_branch_AA1(y, _deforest_AA_x_tmp4) -//│ }; -//│ _deforest_AA_x_tmp5 = tmp25; -//│ tmp26 = (y) => { -//│ return match_x_branch_AA(y, _deforest_AA_x_tmp5) -//│ }; -//│ tmp27 = f1(tmp26, 10); -//│ tmp24 + tmp27 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 30 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 30 @@ -233,7 +58,6 @@ f(AA(AA(3)), 9) + f(AA(AA(4)), 10) -:sjs fun f(x) = let n = if x is AA(a) then @@ -241,55 +65,6 @@ fun f(x) = AA(m) then m n + 2 f(AA(AA(3))) -//│ JS (unsanitized): -//│ let f2, tmp34, tmp35; -//│ f2 = function f(x) { -//│ let n, param0, a1, param01, m, tmp36, tmp37; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a1 = param0; -//│ if (a1 instanceof AA1.class) { -//│ param01 = a1.x; -//│ m = param01; -//│ tmp36 = m; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp37 = tmp36; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp37; -//│ return n + 2 -//│ }; -//│ tmp34 = AA1(3); -//│ tmp35 = AA1(tmp34); -//│ f2(tmp35) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f2, tmp34, tmp35, _deforest_AA_x, _deforest_AA_x1; -//│ f2 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x = 3; -//│ tmp34 = () => { -//│ let n, param0, m, tmp36, tmp37; -//│ param0 = _deforest_AA_x; -//│ m = param0; -//│ tmp36 = m; -//│ tmp37 = tmp36; -//│ n = tmp37; -//│ return n + 2 -//│ }; -//│ _deforest_AA_x1 = tmp34; -//│ tmp35 = () => { -//│ let param0, a1; -//│ param0 = _deforest_AA_x1; -//│ a1 = param0; -//│ return runtime.safeCall(a1()) -//│ }; -//│ f2(tmp35) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 @@ -300,77 +75,11 @@ f(AA(AA(3))) -:sjs fun f(x) = let t = if x is AA(AA(a)) then a t + 3 f(AA(AA(A))) + f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43; -//│ f3 = function f(x) { -//│ let t, param0, param01, a1, tmp44; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a1 = param01; -//│ tmp44 = a1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp44; -//│ return t + 3 -//│ }; -//│ tmp38 = AA1(A1); -//│ tmp39 = AA1(tmp38); -//│ tmp40 = f3(tmp39); -//│ tmp41 = AA1(A1); -//│ tmp42 = AA1(tmp41); -//│ tmp43 = f3(tmp42); -//│ tmp40 + tmp43 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f3, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, _deforest_AA_x_tmp6, match_param0_branch_AA, _deforest_AA_x_tmp7, match_x_branch_AA1, _deforest_AA_x_tmp8, _deforest_AA_x_tmp9; -//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x2) { -//│ let t, param0, a1, tmp44; -//│ param0 = _deforest_AA_x2; -//│ a1 = param0; -//│ tmp44 = a1; -//│ t = tmp44; -//│ return t + 3 -//│ }; -//│ match_x_branch_AA1 = function match_x_branch_AA(_deforest_AA_x2) { -//│ let param0; -//│ param0 = _deforest_AA_x2; -//│ return runtime.safeCall(param0()) -//│ }; -//│ f3 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x_tmp6 = A1; -//│ tmp38 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp6) -//│ }; -//│ _deforest_AA_x_tmp7 = tmp38; -//│ tmp39 = () => { -//│ return match_x_branch_AA1(_deforest_AA_x_tmp7) -//│ }; -//│ tmp40 = f3(tmp39); -//│ _deforest_AA_x_tmp8 = A1; -//│ tmp41 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp8) -//│ }; -//│ _deforest_AA_x_tmp9 = tmp41; -//│ tmp42 = () => { -//│ return match_x_branch_AA1(_deforest_AA_x_tmp9) -//│ }; -//│ tmp43 = f3(tmp42); -//│ tmp40 + tmp43 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "A3A3" //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "A3A3" @@ -383,51 +92,9 @@ f(AA(AA(A))) + f(AA(AA(A))) -:sjs fun c2(x) = if x is AA(AA(a)) then a c2(AA(AA(0))) -//│ JS (unsanitized): -//│ let c2, tmp50, tmp51; -//│ c2 = function c2(x) { -//│ let param0, param01, a1; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a1 = param01; -//│ return a1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp50 = AA1(0); -//│ tmp51 = AA1(tmp50); -//│ c2(tmp51) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c2, tmp50, tmp51, _deforest_AA_x2, _deforest_AA_x3; -//│ c2 = function c2(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x2 = 0; -//│ tmp50 = () => { -//│ let param0, a1; -//│ param0 = _deforest_AA_x2; -//│ a1 = param0; -//│ return a1 -//│ }; -//│ _deforest_AA_x3 = tmp50; -//│ tmp51 = () => { -//│ let param0; -//│ param0 = _deforest_AA_x3; -//│ return runtime.safeCall(param0()) -//│ }; -//│ c2(tmp51) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 0 @@ -438,55 +105,9 @@ c2(AA(AA(0))) -:sjs fun f(a) = if a is AA(BB(B)) then 3 f(AA(BB(B))) -//│ JS (unsanitized): -//│ let f4, tmp54, tmp55; -//│ f4 = function f(a1) { -//│ let param0, param01; -//│ if (a1 instanceof AA1.class) { -//│ param0 = a1.x; -//│ if (param0 instanceof BB1.class) { -//│ param01 = param0.x; -//│ if (param01 instanceof B1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp54 = BB1(B1); -//│ tmp55 = AA1(tmp54); -//│ f4(tmp55) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f4, tmp54, tmp55, _deforest_BB_x, _deforest_AA_x4; -//│ f4 = function f(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ _deforest_BB_x = () => { -//│ return 3 -//│ }; -//│ tmp54 = () => { -//│ let param0; -//│ param0 = _deforest_BB_x; -//│ return runtime.safeCall(param0()) -//│ }; -//│ _deforest_AA_x4 = tmp54; -//│ tmp55 = () => { -//│ let param0; -//│ param0 = _deforest_AA_x4; -//│ return runtime.safeCall(param0()) -//│ }; -//│ f4(tmp55) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -497,7 +118,6 @@ f(AA(BB(B))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test(x, y, z, i) = if x is AA(a) then let m = if y is @@ -506,99 +126,6 @@ fun test(x, y, z, i) = if x is BB(a2) then a2 - i m + n test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) -//│ JS (unsanitized): -//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65; -//│ test = function test(x, y, z, i) { -//│ let param0, a1, m, param01, a11, n, param02, a2, tmp66, tmp67; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a1 = param0; -//│ if (y instanceof AA1.class) { -//│ param01 = y.x; -//│ a11 = param01; -//│ tmp66 = a11 + i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp66; -//│ if (z instanceof BB1.class) { -//│ param02 = z.x; -//│ a2 = param02; -//│ tmp67 = a2 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp67; -//│ return m + n -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp58 = AA1(1); -//│ tmp59 = AA1(2); -//│ tmp60 = BB1(3); -//│ tmp61 = test(tmp58, tmp59, tmp60, 4); -//│ tmp62 = AA1(1); -//│ tmp63 = AA1(2); -//│ tmp64 = BB1(3); -//│ tmp65 = test(tmp62, tmp63, tmp64, 4); -//│ tmp61 + tmp65 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, _deforest_AA_x_tmp10, match_x_branch_AA2, _deforest_AA_x_tmp11, match_y_branch_AA, _deforest_BB_x_tmp, match_z_branch_BB, _deforest_AA_x_tmp12, _deforest_AA_x_tmp13, _deforest_BB_x_tmp1; -//│ match_x_branch_AA2 = function match_x_branch_AA(y, z, i, _deforest_AA_x5) { -//│ let param0, a1; -//│ param0 = _deforest_AA_x5; -//│ a1 = param0; -//│ return runtime.safeCall(y(z, i)) -//│ }; -//│ match_y_branch_AA = function match_y_branch_AA(z, i, _deforest_AA_x5) { -//│ let m, param0, a1, tmp66; -//│ param0 = _deforest_AA_x5; -//│ a1 = param0; -//│ tmp66 = a1 + i; -//│ m = tmp66; -//│ return runtime.safeCall(z(i, m)) -//│ }; -//│ match_z_branch_BB = function match_z_branch_BB(i, m, _deforest_BB_x1) { -//│ let n, param0, a2, tmp66; -//│ param0 = _deforest_BB_x1; -//│ a2 = param0; -//│ tmp66 = a2 - i; -//│ n = tmp66; -//│ return m + n -//│ }; -//│ test = function test(x, y, z, i) { -//│ return runtime.safeCall(x(y, z, i)) -//│ }; -//│ _deforest_AA_x_tmp10 = 1; -//│ tmp58 = (y, z, i) => { -//│ return match_x_branch_AA2(y, z, i, _deforest_AA_x_tmp10) -//│ }; -//│ _deforest_AA_x_tmp11 = 2; -//│ tmp59 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp11) -//│ }; -//│ _deforest_BB_x_tmp = 3; -//│ tmp60 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp) -//│ }; -//│ tmp61 = test(tmp58, tmp59, tmp60, 4); -//│ _deforest_AA_x_tmp12 = 1; -//│ tmp62 = (y, z, i) => { -//│ return match_x_branch_AA2(y, z, i, _deforest_AA_x_tmp12) -//│ }; -//│ _deforest_AA_x_tmp13 = 2; -//│ tmp63 = (z, i) => { -//│ return match_y_branch_AA(z, i, _deforest_AA_x_tmp13) -//│ }; -//│ _deforest_BB_x_tmp1 = 3; -//│ tmp64 = (i, m) => { -//│ return match_z_branch_BB(i, m, _deforest_BB_x_tmp1) -//│ }; -//│ tmp65 = test(tmp62, tmp63, tmp64, 4); -//│ tmp61 + tmp65 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -613,7 +140,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) // only the match in the middle is fused -:sjs fun f1(x) = if x is AA(x1) then if x1 is @@ -625,120 +151,6 @@ fun f3(x) = if x is CC then "f3" + x.x fun aa(x) = AA(x) let cc = CC("cc") f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) -//│ JS (unsanitized): -//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83; -//│ f11 = function f1(x) { -//│ let param0, x1, param01, x2, param02, x3; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ if (x1 instanceof BB1.class) { -//│ param01 = x1.x; -//│ x2 = param01; -//│ if (x2 instanceof CC1.class) { -//│ param02 = x2.x; -//│ x3 = param02; -//│ return x3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f21 = function f2(x) { -//│ if (x instanceof AA1.class) { -//│ return "f2" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f31 = function f3(x) { -//│ if (x instanceof CC1.class) { -//│ return "f3" + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ aa = function aa(x) { -//│ return AA1(x) -//│ }; -//│ tmp74 = CC1("cc"); -//│ cc = tmp74; -//│ tmp75 = BB1(cc); -//│ tmp76 = aa(tmp75); -//│ tmp77 = f11(tmp76); -//│ tmp78 = CC1(0); -//│ tmp79 = BB1(tmp78); -//│ tmp80 = aa(tmp79); -//│ tmp81 = f21(tmp80); -//│ tmp82 = tmp77 + tmp81; -//│ tmp83 = f31(cc); -//│ tmp82 + tmp83 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let aa, f11, f21, f31, cc, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, _deforest_BB_x_tmp2, match_x1_branch_BB, _deforest_BB_x_tmp3; -//│ match_x1_branch_BB = function match_x1_branch_BB(_deforest_BB_x1) { -//│ let param0, x2, param01, x3; -//│ param0 = _deforest_BB_x1; -//│ x2 = param0; -//│ if (x2 instanceof CC1.class) { -//│ param01 = x2.x; -//│ x3 = param01; -//│ return x3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f11 = function f1(x) { -//│ let param0, x1; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ x1 = param0; -//│ return runtime.safeCall(x1()) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f21 = function f2(x) { -//│ if (x instanceof AA1.class) { -//│ return "f2" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f31 = function f3(x) { -//│ if (x instanceof CC1.class) { -//│ return "f3" + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ aa = function aa(x) { -//│ return AA1(x) -//│ }; -//│ tmp74 = CC1("cc"); -//│ cc = tmp74; -//│ _deforest_BB_x_tmp2 = cc; -//│ tmp75 = () => { -//│ return match_x1_branch_BB(_deforest_BB_x_tmp2) -//│ }; -//│ tmp76 = aa(tmp75); -//│ tmp77 = f11(tmp76); -//│ tmp78 = CC1(0); -//│ _deforest_BB_x_tmp3 = tmp78; -//│ tmp79 = () => { -//│ return match_x1_branch_BB(_deforest_BB_x_tmp3) -//│ }; -//│ tmp80 = aa(tmp79); -//│ tmp81 = f21(tmp80); -//│ tmp82 = tmp77 + tmp81; -//│ tmp83 = f31(cc); -//│ tmp82 + tmp83 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "ccf2f3cc" //│ cc = CC("cc") //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -750,7 +162,6 @@ f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) -:sjs fun c(x, y) = if x is AA then let t = if y is @@ -758,43 +169,6 @@ fun c(x, y) = if x is t + x.x let y = A c(AA(2), y) -//│ JS (unsanitized): -//│ let c, y, tmp94; -//│ c = function c(x, y1) { -//│ let t, tmp95; -//│ if (x instanceof AA1.class) { -//│ if (y1 instanceof A1.class) { -//│ tmp95 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp95; -//│ return t + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ y = A1; -//│ tmp94 = AA1(2); -//│ c(tmp94, y) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, y, tmp94, _deforest_AA_x5; -//│ c = function c(x, y1) { -//│ return runtime.safeCall(x(y1)) -//│ }; -//│ y = (_deforest_AA_x6) => { -//│ let t, tmp95; -//│ tmp95 = 2; -//│ t = tmp95; -//│ return t + _deforest_AA_x6 -//│ }; -//│ _deforest_AA_x5 = 2; -//│ tmp94 = (y1) => { -//│ return runtime.safeCall(y1(_deforest_AA_x5)) -//│ }; -//│ c(tmp94, y) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 4 //│ y = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -805,7 +179,6 @@ c(AA(2), y) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun c(x, y) = if x is AA then let t = if y is @@ -814,63 +187,6 @@ fun c(x, y) = if x is fun c2(y) = if y is A then 3 let y = A c(AA(2), y) + c2(y) -//│ JS (unsanitized): -//│ let c1, c21, y1, tmp96, tmp97, tmp98; -//│ c1 = function c(x, y2) { -//│ let t, tmp99; -//│ if (x instanceof AA1.class) { -//│ if (y2 instanceof A1.class) { -//│ tmp99 = 2 + x.x; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp99; -//│ return t + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ c21 = function c2(y2) { -//│ if (y2 instanceof A1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ y1 = A1; -//│ tmp96 = AA1(2); -//│ tmp97 = c1(tmp96, y1); -//│ tmp98 = c21(y1); -//│ tmp97 + tmp98 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c1, c21, y1, tmp96, tmp97, tmp98, _deforest_AA_x6; -//│ c1 = function c(x, y2) { -//│ return runtime.safeCall(x(y2)) -//│ }; -//│ c21 = function c2(y2) { -//│ if (y2 instanceof A1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ y1 = A1; -//│ _deforest_AA_x6 = 2; -//│ tmp96 = (y2) => { -//│ let t, tmp99; -//│ if (y2 instanceof A1.class) { -//│ tmp99 = 2 + _deforest_AA_x6; -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ t = tmp99; -//│ return t + _deforest_AA_x6 -//│ }; -//│ tmp97 = c1(tmp96, y1); -//│ tmp98 = c21(y1); -//│ tmp97 + tmp98 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 9 //│ y = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -881,7 +197,6 @@ c(AA(2), y) + c2(y) // need to include computations of `rest` from more than one levels of parent matches -:sjs fun test(x) = let t = if x is AA(AA(AA(a))) then a @@ -891,104 +206,6 @@ fun f(a) = if a is AA(AA) then "0" let p = AA(AA(AA("10"))) test(p) + f(p) + test(AA(A)) -//│ JS (unsanitized): -//│ let test1, f5, p, tmp102, tmp103, tmp104, tmp105, tmp106, tmp107, tmp108, tmp109; -//│ test1 = function test(x) { -//│ let t, param0, param01, param02, a1, tmp110; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ if (param01 instanceof AA1.class) { -//│ param02 = param01.x; -//│ a1 = param02; -//│ tmp110 = a1; -//│ } else { -//│ tmp110 = "3"; -//│ } -//│ } else { -//│ tmp110 = "3"; -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp110; -//│ return t + "5" -//│ }; -//│ f5 = function f(a1) { -//│ let param0; -//│ if (a1 instanceof AA1.class) { -//│ param0 = a1.x; -//│ if (param0 instanceof AA1.class) { -//│ return "0" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp102 = AA1("10"); -//│ tmp103 = AA1(tmp102); -//│ tmp104 = AA1(tmp103); -//│ p = tmp104; -//│ tmp105 = test1(p); -//│ tmp106 = f5(p); -//│ tmp107 = tmp105 + tmp106; -//│ tmp108 = AA1(A1); -//│ tmp109 = test1(tmp108); -//│ tmp107 + tmp109 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test1, f5, p, tmp102, tmp103, tmp104, tmp105, tmp106, tmp107, tmp108, tmp109, _deforest_AA_x7; -//│ test1 = function test(x) { -//│ let t, param0, param01, tmp110; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ return runtime.safeCall(param01()) -//│ } else { -//│ tmp110 = "3"; -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp110; -//│ return t + "5" -//│ }; -//│ f5 = function f(a1) { -//│ let param0; -//│ if (a1 instanceof AA1.class) { -//│ param0 = a1.x; -//│ if (param0 instanceof AA1.class) { -//│ return "0" -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x7 = "10"; -//│ tmp102 = () => { -//│ let t, param0, a1, tmp110; -//│ param0 = _deforest_AA_x7; -//│ a1 = param0; -//│ tmp110 = a1; -//│ t = tmp110; -//│ return t + "5" -//│ }; -//│ tmp103 = AA1(tmp102); -//│ tmp104 = AA1(tmp103); -//│ p = tmp104; -//│ tmp105 = test1(p); -//│ tmp106 = f5(p); -//│ tmp107 = tmp105 + tmp106; -//│ tmp108 = AA1(A1); -//│ tmp109 = test1(tmp108); -//│ tmp107 + tmp109 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "105035" //│ p = AA(AA(AA("10"))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -1000,7 +217,6 @@ test(p) + f(p) + test(AA(A)) -:sjs fun f(x, y) = let tmp = BB(y + 1) if tmp is @@ -1013,80 +229,6 @@ fun f(x, y) = AA then m + tmp.x let aa = AA(3) f(aa, 3) + f(aa, 4) -//│ JS (unsanitized): -//│ let f6, aa1, tmp118, tmp119, tmp120; -//│ f6 = function f(x, y2) { -//│ let tmp121, m, scrut, param0, yf, scrut1, tmp122, tmp123, tmp124, tmp125; -//│ tmp122 = y2 + 1; -//│ tmp123 = BB1(tmp122); -//│ tmp121 = tmp123; -//│ if (tmp121 instanceof BB1.class) { -//│ if (x instanceof AA1.class) { -//│ scrut = AA1(2); -//│ if (scrut instanceof AA1.class) { -//│ param0 = scrut.x; -//│ yf = param0; -//│ tmp124 = yf; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp125 = tmp124; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp125; -//│ scrut1 = AA1(3); -//│ if (scrut1 instanceof AA1.class) { -//│ return m + tmp121.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp118 = AA1(3); -//│ aa1 = tmp118; -//│ tmp119 = f6(aa1, 3); -//│ tmp120 = f6(aa1, 4); -//│ tmp119 + tmp120 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f6, aa1, tmp118, tmp119, tmp120, _deforest_AA_x_unused; -//│ f6 = function f(x, y2) { -//│ let tmp121, tmp122, tmp123, _deforest_BB_x1; -//│ tmp122 = y2 + 1; -//│ _deforest_BB_x1 = tmp122; -//│ tmp123 = (x1) => { -//│ return runtime.safeCall(x1(_deforest_BB_x1)) -//│ }; -//│ tmp121 = tmp123; -//│ return runtime.safeCall(tmp121(x)) -//│ }; -//│ _deforest_AA_x_unused = 3; -//│ tmp118 = (_deforest_BB_x1) => { -//│ let scrut, _deforest_AA_x8; -//│ _deforest_AA_x8 = 2; -//│ scrut = (_deforest_BB_x2) => { -//│ let m, param0, yf, scrut1, tmp121, tmp122, _deforest_AA_x_unused1; -//│ param0 = _deforest_AA_x8; -//│ yf = param0; -//│ tmp121 = yf; -//│ tmp122 = tmp121; -//│ m = tmp122; -//│ _deforest_AA_x_unused1 = 3; -//│ scrut1 = (m1, _deforest_BB_x3) => { -//│ return m1 + _deforest_BB_x3 -//│ }; -//│ return runtime.safeCall(scrut1(m, _deforest_BB_x2)) -//│ }; -//│ return runtime.safeCall(scrut(_deforest_BB_x1)) -//│ }; -//│ aa1 = tmp118; -//│ tmp119 = f6(aa1, 3); -//│ tmp120 = f6(aa1, 4); -//│ tmp119 + tmp120 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 13 //│ aa = AA(3) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -1100,7 +242,6 @@ f(aa, 3) + f(aa, 4) -:sjs fun test(n, p, q) = n + if AA(0) is @@ -1110,87 +251,6 @@ fun test(n, p, q) = if q is AA(y) then k + y test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) -//│ JS (unsanitized): -//│ let test2, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129; -//│ test2 = function test(n, p1, q) { -//│ let scrut, k, param0, x, param01, y2, tmp130, tmp131, tmp132; -//│ scrut = AA1(0); -//│ if (scrut instanceof AA1.class) { -//│ if (p1 instanceof AA1.class) { -//│ param0 = p1.x; -//│ x = param0; -//│ tmp130 = x; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ k = tmp130; -//│ if (q instanceof AA1.class) { -//│ param01 = q.x; -//│ y2 = param01; -//│ tmp131 = k + y2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp132 = tmp131; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return n + tmp132 -//│ }; -//│ tmp124 = AA1(2); -//│ tmp125 = AA1(3); -//│ tmp126 = test2(3, tmp124, tmp125); -//│ tmp127 = AA1(2); -//│ tmp128 = AA1(3); -//│ tmp129 = test2(3, tmp127, tmp128); -//│ tmp126 + tmp129 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test2, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, _deforest_AA_x_tmp14, match_p_branch_AA, _deforest_AA_x_tmp15, match_q_branch_AA, _deforest_AA_x_tmp16, _deforest_AA_x_tmp17; -//│ match_p_branch_AA = function match_p_branch_AA(n, q, _deforest_AA_x8) { -//│ let k, param0, x, tmp130; -//│ param0 = _deforest_AA_x8; -//│ x = param0; -//│ tmp130 = x; -//│ k = tmp130; -//│ return runtime.safeCall(q(n, k)) -//│ }; -//│ match_q_branch_AA = function match_q_branch_AA(n, k, _deforest_AA_x8) { -//│ let param0, y2, tmp130, tmp131; -//│ param0 = _deforest_AA_x8; -//│ y2 = param0; -//│ tmp130 = k + y2; -//│ tmp131 = tmp130; -//│ return n + tmp131 -//│ }; -//│ test2 = function test(n, p1, q) { -//│ let scrut, _deforest_AA_x_unused1; -//│ _deforest_AA_x_unused1 = 0; -//│ scrut = (n1, p2, q1) => { -//│ return runtime.safeCall(p2(n1, q1)) -//│ }; -//│ return runtime.safeCall(scrut(n, p1, q)) -//│ }; -//│ _deforest_AA_x_tmp14 = 2; -//│ tmp124 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp14) -//│ }; -//│ _deforest_AA_x_tmp15 = 3; -//│ tmp125 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp15) -//│ }; -//│ tmp126 = test2(3, tmp124, tmp125); -//│ _deforest_AA_x_tmp16 = 2; -//│ tmp127 = (n, q) => { -//│ return match_p_branch_AA(n, q, _deforest_AA_x_tmp16) -//│ }; -//│ _deforest_AA_x_tmp17 = 3; -//│ tmp128 = (n, k) => { -//│ return match_q_branch_AA(n, k, _deforest_AA_x_tmp17) -//│ }; -//│ tmp129 = test2(3, tmp127, tmp128); -//│ tmp126 + tmp129 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 16 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 16 @@ -1204,7 +264,6 @@ test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) -:sjs fun test(n, p, q, a) = let o if a is @@ -1220,114 +279,6 @@ fun test(n, p, q, a) = let a = A fun c(a) = if a is A then 0 test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) -//│ JS (unsanitized): -//│ let c3, test3, a1, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143; -//│ test3 = function test(n, p1, q, a2) { -//│ let o, k, param0, x, param01, y2, tmp144, tmp145, tmp146, tmp147; -//│ o = undefined; -//│ if (a2 instanceof A1.class) { -//│ k = undefined; -//│ if (p1 instanceof AA1.class) { -//│ param0 = p1.x; -//│ x = param0; -//│ k = x; -//│ tmp144 = runtime.Unit; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (q instanceof AA1.class) { -//│ param01 = q.x; -//│ y2 = param01; -//│ tmp145 = k + y2; -//│ o = tmp145; -//│ tmp146 = runtime.Unit; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp147 = tmp146; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return o + n -//│ }; -//│ c3 = function c(a2) { -//│ if (a2 instanceof A1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ a1 = A1; -//│ tmp136 = AA1(2); -//│ tmp137 = AA1(3); -//│ tmp138 = test3(1, tmp136, tmp137, a1); -//│ tmp139 = AA1(2); -//│ tmp140 = AA1(3); -//│ tmp141 = test3(2, tmp139, tmp140, a1); -//│ tmp142 = tmp138 + tmp141; -//│ tmp143 = c3(a1); -//│ tmp142 + tmp143 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c3, test3, a1, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143, _deforest_AA_x_tmp18, match_p_branch_AA1, _deforest_AA_x_tmp19, match_q_branch_AA1, _deforest_AA_x_tmp20, _deforest_AA_x_tmp21; -//│ match_p_branch_AA1 = function match_p_branch_AA(n, q, _deforest_AA_x8) { -//│ let k, param0, x, tmp144; -//│ param0 = _deforest_AA_x8; -//│ x = param0; -//│ k = x; -//│ tmp144 = runtime.Unit; -//│ return runtime.safeCall(q(n, k)) -//│ }; -//│ match_q_branch_AA1 = function match_q_branch_AA(n, k, _deforest_AA_x8) { -//│ let o, param0, y2, tmp144, tmp145, tmp146; -//│ param0 = _deforest_AA_x8; -//│ y2 = param0; -//│ tmp144 = k + y2; -//│ o = tmp144; -//│ tmp145 = runtime.Unit; -//│ tmp146 = tmp145; -//│ return o + n -//│ }; -//│ test3 = function test(n, p1, q, a2) { -//│ let o, k; -//│ o = undefined; -//│ if (a2 instanceof A1.class) { -//│ k = undefined; -//│ return runtime.safeCall(p1(n, q)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ c3 = function c(a2) { -//│ if (a2 instanceof A1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ a1 = A1; -//│ _deforest_AA_x_tmp18 = 2; -//│ tmp136 = (n, q) => { -//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp18) -//│ }; -//│ _deforest_AA_x_tmp19 = 3; -//│ tmp137 = (n, k) => { -//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp19) -//│ }; -//│ tmp138 = test3(1, tmp136, tmp137, a1); -//│ _deforest_AA_x_tmp20 = 2; -//│ tmp139 = (n, q) => { -//│ return match_p_branch_AA1(n, q, _deforest_AA_x_tmp20) -//│ }; -//│ _deforest_AA_x_tmp21 = 3; -//│ tmp140 = (n, k) => { -//│ return match_q_branch_AA1(n, k, _deforest_AA_x_tmp21) -//│ }; -//│ tmp141 = test3(2, tmp139, tmp140, a1); -//│ tmp142 = tmp138 + tmp141; -//│ tmp143 = c3(a1); -//│ tmp142 + tmp143 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 13 //│ a = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -1341,7 +292,6 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) -:sjs fun test(x, y, z, i) = let k = if x is AA(a) then @@ -1352,103 +302,6 @@ fun test(x, y, z, i) = m + n k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) -//│ JS (unsanitized): -//│ let test4, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159; -//│ test4 = function test(x, y2, z, i) { -//│ let k, param0, a2, m, param01, a11, n, param02, a21, tmp160, tmp161, tmp162; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a2 = param0; -//│ if (y2 instanceof AA1.class) { -//│ param01 = y2.x; -//│ a11 = param01; -//│ tmp160 = a11 + i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp160; -//│ if (z instanceof BB1.class) { -//│ param02 = z.x; -//│ a21 = param02; -//│ tmp161 = a21 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp161; -//│ tmp162 = m + n; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ k = tmp162; -//│ return k + i -//│ }; -//│ tmp152 = AA1(1); -//│ tmp153 = AA1(2); -//│ tmp154 = BB1(3); -//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); -//│ tmp156 = AA1(1); -//│ tmp157 = AA1(2); -//│ tmp158 = BB1(3); -//│ tmp159 = test4(tmp156, tmp157, tmp158, 4); -//│ tmp155 + tmp159 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test4, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159, _deforest_AA_x_tmp22, match_x_branch_AA3, _deforest_AA_x_tmp23, match_y_branch_AA1, _deforest_BB_x_tmp4, match_z_branch_BB1, _deforest_AA_x_tmp24, _deforest_AA_x_tmp25, _deforest_BB_x_tmp5; -//│ match_x_branch_AA3 = function match_x_branch_AA(y2, z, i, _deforest_AA_x8) { -//│ let param0, a2; -//│ param0 = _deforest_AA_x8; -//│ a2 = param0; -//│ return runtime.safeCall(y2(z, i)) -//│ }; -//│ match_y_branch_AA1 = function match_y_branch_AA(z, i, _deforest_AA_x8) { -//│ let m, param0, a11, tmp160; -//│ param0 = _deforest_AA_x8; -//│ a11 = param0; -//│ tmp160 = a11 + i; -//│ m = tmp160; -//│ return runtime.safeCall(z(i, m)) -//│ }; -//│ match_z_branch_BB1 = function match_z_branch_BB(i, m, _deforest_BB_x1) { -//│ let k, n, param0, a2, tmp160, tmp161; -//│ param0 = _deforest_BB_x1; -//│ a2 = param0; -//│ tmp160 = a2 - i; -//│ n = tmp160; -//│ tmp161 = m + n; -//│ k = tmp161; -//│ return k + i -//│ }; -//│ test4 = function test(x, y2, z, i) { -//│ return runtime.safeCall(x(y2, z, i)) -//│ }; -//│ _deforest_AA_x_tmp22 = 1; -//│ tmp152 = (y2, z, i) => { -//│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp22) -//│ }; -//│ _deforest_AA_x_tmp23 = 2; -//│ tmp153 = (z, i) => { -//│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp23) -//│ }; -//│ _deforest_BB_x_tmp4 = 3; -//│ tmp154 = (i, m) => { -//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp4) -//│ }; -//│ tmp155 = test4(tmp152, tmp153, tmp154, 4); -//│ _deforest_AA_x_tmp24 = 1; -//│ tmp156 = (y2, z, i) => { -//│ return match_x_branch_AA3(y2, z, i, _deforest_AA_x_tmp24) -//│ }; -//│ _deforest_AA_x_tmp25 = 2; -//│ tmp157 = (z, i) => { -//│ return match_y_branch_AA1(z, i, _deforest_AA_x_tmp25) -//│ }; -//│ _deforest_BB_x_tmp5 = 3; -//│ tmp158 = (i, m) => { -//│ return match_z_branch_BB1(i, m, _deforest_BB_x_tmp5) -//│ }; -//│ tmp159 = test4(tmp156, tmp157, tmp158, 4); -//│ tmp155 + tmp159 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 18 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 18 @@ -1462,7 +315,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test(x, y, z, i) = let k = if x is AA(a) then @@ -1474,112 +326,6 @@ fun test(x, y, z, i) = m + n k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) -//│ JS (unsanitized): -//│ let test5, tmp168, tmp169, tmp170, tmp171, tmp172, tmp173, tmp174, tmp175; -//│ test5 = function test(x, y2, z, i) { -//│ let k, param0, a2, m, param01, b2, param02, a11, n, param03, a21, tmp176, tmp177, tmp178; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ a2 = param0; -//│ if (y2 instanceof AA1.class) { -//│ param02 = y2.x; -//│ a11 = param02; -//│ tmp176 = a11 + i; -//│ } else if (y2 instanceof BB1.class) { -//│ param01 = y2.x; -//│ b2 = param01; -//│ tmp176 = b2 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp176; -//│ if (z instanceof BB1.class) { -//│ param03 = z.x; -//│ a21 = param03; -//│ tmp177 = a21 - i; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp177; -//│ tmp178 = m + n; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ k = tmp178; -//│ return k + i -//│ }; -//│ tmp168 = AA1(1); -//│ tmp169 = AA1(2); -//│ tmp170 = BB1(3); -//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); -//│ tmp172 = AA1(1); -//│ tmp173 = BB1(2); -//│ tmp174 = BB1(3); -//│ tmp175 = test5(tmp172, tmp173, tmp174, 4); -//│ tmp171 + tmp175 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test5, tmp168, tmp169, tmp170, tmp171, tmp172, tmp173, tmp174, tmp175, _deforest_AA_x8, _deforest_BB_x1, _deforest_AA_x_tmp26, match_x_branch_AA4, match_y_rest, _deforest_BB_x_tmp6, match_z_branch_BB2, _deforest_AA_x_tmp27, _deforest_BB_x_tmp7; -//│ match_x_branch_AA4 = function match_x_branch_AA(y2, z, i, _deforest_AA_x9) { -//│ let param0, a2; -//│ param0 = _deforest_AA_x9; -//│ a2 = param0; -//│ return runtime.safeCall(y2(z, i)) -//│ }; -//│ match_z_branch_BB2 = function match_z_branch_BB(i, m, _deforest_BB_x2) { -//│ let k, n, param0, a2, tmp176, tmp177; -//│ param0 = _deforest_BB_x2; -//│ a2 = param0; -//│ tmp176 = a2 - i; -//│ n = tmp176; -//│ tmp177 = m + n; -//│ k = tmp177; -//│ return k + i -//│ }; -//│ match_y_rest = function match_y_rest(z, i, tmp176) { -//│ let m; -//│ m = tmp176; -//│ return runtime.safeCall(z(i, m)) -//│ }; -//│ test5 = function test(x, y2, z, i) { -//│ return runtime.safeCall(x(y2, z, i)) -//│ }; -//│ _deforest_AA_x_tmp26 = 1; -//│ tmp168 = (y2, z, i) => { -//│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp26) -//│ }; -//│ _deforest_AA_x8 = 2; -//│ tmp169 = (z, i) => { -//│ let param0, a11, tmp176; -//│ param0 = _deforest_AA_x8; -//│ a11 = param0; -//│ tmp176 = a11 + i; -//│ return match_y_rest(z, i, tmp176) -//│ }; -//│ _deforest_BB_x_tmp6 = 3; -//│ tmp170 = (i, m) => { -//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp6) -//│ }; -//│ tmp171 = test5(tmp168, tmp169, tmp170, 4); -//│ _deforest_AA_x_tmp27 = 1; -//│ tmp172 = (y2, z, i) => { -//│ return match_x_branch_AA4(y2, z, i, _deforest_AA_x_tmp27) -//│ }; -//│ _deforest_BB_x1 = 2; -//│ tmp173 = (z, i) => { -//│ let param0, b2, tmp176; -//│ param0 = _deforest_BB_x1; -//│ b2 = param0; -//│ tmp176 = b2 - i; -//│ return match_y_rest(z, i, tmp176) -//│ }; -//│ _deforest_BB_x_tmp7 = 3; -//│ tmp174 = (i, m) => { -//│ return match_z_branch_BB2(i, m, _deforest_BB_x_tmp7) -//│ }; -//│ tmp175 = test5(tmp172, tmp173, tmp174, 4); -//│ tmp171 + tmp175 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -1593,7 +339,6 @@ test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test(x) = let n = if x is AA(BB(b)) then b @@ -1603,107 +348,6 @@ fun test(x) = fun c(x) = if x is AA then 0 fun p(x) = AA(x) test(p(BB(3))) + test(p(CC(3))) + c(p(A)) -//│ JS (unsanitized): -//│ let p1, c4, test6, tmp184, tmp185, tmp186, tmp187, tmp188, tmp189, tmp190, tmp191, tmp192; -//│ test6 = function test(x) { -//│ let n, param0, param01, c5, param02, b, tmp193; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof BB1.class) { -//│ param02 = param0.x; -//│ b = param02; -//│ tmp193 = b; -//│ } else if (param0 instanceof CC1.class) { -//│ param01 = param0.x; -//│ c5 = param01; -//│ tmp193 = c5; -//│ } else { -//│ tmp193 = 0; -//│ } -//│ } else { -//│ tmp193 = 0; -//│ } -//│ n = tmp193; -//│ return n + 3 -//│ }; -//│ c4 = function c(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p1 = function p(x) { -//│ return AA1(x) -//│ }; -//│ tmp184 = BB1(3); -//│ tmp185 = p1(tmp184); -//│ tmp186 = test6(tmp185); -//│ tmp187 = CC1(3); -//│ tmp188 = p1(tmp187); -//│ tmp189 = test6(tmp188); -//│ tmp190 = tmp186 + tmp189; -//│ tmp191 = p1(A1); -//│ tmp192 = c4(tmp191); -//│ tmp190 + tmp192 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p1, c4, test6, tmp184, tmp185, tmp186, tmp187, tmp188, tmp189, tmp190, tmp191, tmp192, _deforest_BB_x2, _deforest_CC_x, match_param0_rest; -//│ match_param0_rest = function match_param0_rest(tmp193) { -//│ let n; -//│ n = tmp193; -//│ return n + 3 -//│ }; -//│ test6 = function test(x) { -//│ let n, param0, tmp193; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ return runtime.safeCall(param0()) -//│ } else { -//│ tmp193 = 0; -//│ } -//│ n = tmp193; -//│ return n + 3 -//│ }; -//│ c4 = function c(x) { -//│ if (x instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p1 = function p(x) { -//│ return AA1(x) -//│ }; -//│ _deforest_BB_x2 = 3; -//│ tmp184 = () => { -//│ let param0, b, tmp193; -//│ param0 = _deforest_BB_x2; -//│ b = param0; -//│ tmp193 = b; -//│ return match_param0_rest(tmp193) -//│ }; -//│ tmp185 = p1(tmp184); -//│ tmp186 = test6(tmp185); -//│ _deforest_CC_x = 3; -//│ tmp187 = () => { -//│ let param0, c5, tmp193; -//│ param0 = _deforest_CC_x; -//│ c5 = param0; -//│ tmp193 = c5; -//│ return match_param0_rest(tmp193) -//│ }; -//│ tmp188 = p1(tmp187); -//│ tmp189 = test6(tmp188); -//│ tmp190 = tmp186 + tmp189; -//│ tmp191 = p1(() => { -//│ let tmp193; -//│ tmp193 = 0; -//│ return match_param0_rest(tmp193) -//│ }); -//│ tmp192 = c4(tmp191); -//│ tmp190 + tmp192 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 12 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 12 @@ -1717,7 +361,6 @@ test(p(BB(3))) + test(p(CC(3))) + c(p(A)) -:sjs fun f(x, y, z, k) = let tmp = if z then BB(y + 1) else BB(y - 1) tmp.x + @@ -1730,102 +373,6 @@ fun f(x, y, z, k) = if AA(3) is AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) -//│ JS (unsanitized): -//│ let f7, tmp202, tmp203, tmp204, tmp205; -//│ f7 = function f(x, y2, z, k) { -//│ let tmp206, m, scrut, param0, yf, scrut1, tmp207, tmp208, tmp209, tmp210, tmp211, tmp212, tmp213; -//│ if (z === true) { -//│ tmp207 = y2 + 1; -//│ tmp208 = BB1(tmp207); -//│ } else { -//│ tmp209 = y2 - 1; -//│ tmp208 = BB1(tmp209); -//│ } -//│ tmp206 = tmp208; -//│ if (tmp206 instanceof BB1.class) { -//│ if (x instanceof AA1.class) { -//│ scrut = AA1(2); -//│ if (scrut instanceof AA1.class) { -//│ param0 = scrut.x; -//│ yf = param0; -//│ tmp210 = yf; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp211 = tmp210; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp211; -//│ scrut1 = AA1(3); -//│ if (scrut1 instanceof AA1.class) { -//│ tmp212 = m + k; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp213 = tmp212; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return tmp206.x + tmp213 -//│ }; -//│ tmp202 = AA1(3); -//│ tmp203 = f7(tmp202, 3, true, 10); -//│ tmp204 = AA1(5); -//│ tmp205 = f7(tmp204, 4, false, 20); -//│ tmp203 + tmp205 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f7, tmp202, tmp203, tmp204, tmp205, _deforest_AA_x_unused1, match_x_branch_AA5, _deforest_AA_x_unused2; -//│ match_x_branch_AA5 = function match_x_branch_AA(k, tmp206) { -//│ let scrut, _deforest_AA_x9; -//│ _deforest_AA_x9 = 2; -//│ scrut = (k1, tmp207) => { -//│ let m, param0, yf, scrut1, tmp208, tmp209, _deforest_AA_x_unused3; -//│ param0 = _deforest_AA_x9; -//│ yf = param0; -//│ tmp208 = yf; -//│ tmp209 = tmp208; -//│ m = tmp209; -//│ _deforest_AA_x_unused3 = 3; -//│ scrut1 = (k2, tmp210, m1) => { -//│ let tmp211, tmp212; -//│ tmp211 = m1 + k2; -//│ tmp212 = tmp211; -//│ return tmp210.x + tmp212 -//│ }; -//│ return runtime.safeCall(scrut1(k1, tmp207, m)) -//│ }; -//│ return runtime.safeCall(scrut(k, tmp206)) -//│ }; -//│ f7 = function f(x, y2, z, k) { -//│ let tmp206, tmp207, tmp208, tmp209; -//│ if (z === true) { -//│ tmp207 = y2 + 1; -//│ tmp208 = BB1(tmp207); -//│ } else { -//│ tmp209 = y2 - 1; -//│ tmp208 = BB1(tmp209); -//│ } -//│ tmp206 = tmp208; -//│ if (tmp206 instanceof BB1.class) { -//│ return runtime.safeCall(x(k, tmp206)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x_unused1 = 3; -//│ tmp202 = (k, tmp206) => { -//│ return match_x_branch_AA5(k, tmp206) -//│ }; -//│ tmp203 = f7(tmp202, 3, true, 10); -//│ _deforest_AA_x_unused2 = 5; -//│ tmp204 = (k, tmp206) => { -//│ return match_x_branch_AA5(k, tmp206) -//│ }; -//│ tmp205 = f7(tmp204, 4, false, 20); -//│ tmp203 + tmp205 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 41 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 41 @@ -1838,7 +385,6 @@ f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) -:sjs fun test(x) = let t = if x is AA(AA(AA(a))) then a @@ -1847,96 +393,6 @@ fun f(a) = if a is AA(AA) then 0 let p = AA(AA(AA(10))) test(p) + f(p) -//│ JS (unsanitized): -//│ let test7, f8, p2, tmp210, tmp211, tmp212, tmp213, tmp214; -//│ test7 = function test(x) { -//│ let t, param0, param01, param02, a2, tmp215; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ if (param01 instanceof AA1.class) { -//│ param02 = param01.x; -//│ a2 = param02; -//│ tmp215 = a2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp215; -//│ return t + 5 -//│ }; -//│ f8 = function f(a2) { -//│ let param0; -//│ if (a2 instanceof AA1.class) { -//│ param0 = a2.x; -//│ if (param0 instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp210 = AA1(10); -//│ tmp211 = AA1(tmp210); -//│ tmp212 = AA1(tmp211); -//│ p2 = tmp212; -//│ tmp213 = test7(p2); -//│ tmp214 = f8(p2); -//│ tmp213 + tmp214 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test7, f8, p2, tmp210, tmp211, tmp212, tmp213, tmp214, _deforest_AA_x9; -//│ test7 = function test(x) { -//│ let param0, param01; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ return runtime.safeCall(param01()) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f8 = function f(a2) { -//│ let param0; -//│ if (a2 instanceof AA1.class) { -//│ param0 = a2.x; -//│ if (param0 instanceof AA1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ _deforest_AA_x9 = 10; -//│ tmp210 = () => { -//│ let t, param0, a2, tmp215; -//│ param0 = _deforest_AA_x9; -//│ a2 = param0; -//│ tmp215 = a2; -//│ t = tmp215; -//│ return t + 5 -//│ }; -//│ tmp211 = AA1(tmp210); -//│ tmp212 = AA1(tmp211); -//│ p2 = tmp212; -//│ tmp213 = test7(p2); -//│ tmp214 = f8(p2); -//│ tmp213 + tmp214 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ p = AA(AA(AA(10))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index ebbee50c09..841fb70fab 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -17,45 +17,11 @@ class Some(val x) -:sjs fun c(x) = if x is AA then if A is A then x.x c(AA(2)) -//│ JS (unsanitized): -//│ let c, tmp; -//│ c = function c(x) { -//│ let scrut; -//│ if (x instanceof AA1.class) { -//│ scrut = A1; -//│ if (scrut instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp = AA1(2); -//│ c(tmp) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, tmp, _deforest_AA_x; -//│ c = function c(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x = 2; -//│ tmp = () => { -//│ let scrut; -//│ scrut = (_deforest_AA_x1) => { -//│ return _deforest_AA_x1 -//│ }; -//│ return runtime.safeCall(scrut(_deforest_AA_x)) -//│ }; -//│ c(tmp) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -65,41 +31,11 @@ c(AA(2)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun c(x, y) = if x is AA then if y is A then x.x c(AA(2), A) -//│ JS (unsanitized): -//│ let c1, tmp2; -//│ c1 = function c(x, y) { -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp2 = AA1(2); -//│ c1(tmp2, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c1, tmp2, _deforest_AA_x1; -//│ c1 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ _deforest_AA_x1 = 2; -//│ tmp2 = (y) => { -//│ return runtime.safeCall(y(_deforest_AA_x1)) -//│ }; -//│ c1(tmp2, (_deforest_AA_x2) => { -//│ return _deforest_AA_x2 -//│ }) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -110,49 +46,12 @@ c(AA(2), A) -:sjs fun c(x, y) = if x is AA then if y is A then x.x fun p() = AA(2) c(p(), A) -//│ JS (unsanitized): -//│ let p, c2, tmp4; -//│ c2 = function c(x, y) { -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ return x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p = function p() { -//│ return AA1(2) -//│ }; -//│ tmp4 = p(); -//│ c2(tmp4, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p, c2, tmp4; -//│ c2 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ p = function p() { -//│ let _deforest_AA_x2; -//│ _deforest_AA_x2 = 2; -//│ return (y) => { -//│ return runtime.safeCall(y(_deforest_AA_x2)) -//│ } -//│ }; -//│ tmp4 = p(); -//│ c2(tmp4, (_deforest_AA_x2) => { -//│ return _deforest_AA_x2 -//│ }) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -164,7 +63,6 @@ c(p(), A) -:sjs fun c(x, y) = if x is AA then let a = if y is @@ -172,48 +70,6 @@ fun c(x, y) = if x is a + x.x fun p() = AA(2) c(p(), A) -//│ JS (unsanitized): -//│ let p1, c3, tmp6; -//│ c3 = function c(x, y) { -//│ let a, tmp7; -//│ if (x instanceof AA1.class) { -//│ if (y instanceof A1.class) { -//│ tmp7 = 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ a = tmp7; -//│ return a + x.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p1 = function p() { -//│ return AA1(2) -//│ }; -//│ tmp6 = p1(); -//│ c3(tmp6, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p1, c3, tmp6; -//│ c3 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ p1 = function p() { -//│ let _deforest_AA_x2; -//│ _deforest_AA_x2 = 2; -//│ return (y) => { -//│ return runtime.safeCall(y(_deforest_AA_x2)) -//│ } -//│ }; -//│ tmp6 = p1(); -//│ c3(tmp6, (_deforest_AA_x2) => { -//│ let a, tmp7; -//│ tmp7 = 1; -//│ a = tmp7; -//│ return a + _deforest_AA_x2 -//│ }) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -225,95 +81,12 @@ c(p(), A) -:sjs fun f(x) = if x is AA(AA(a)) then g(a) fun g(x) = if x is AA(b) then f(b) A then 42 f(AA(AA(AA(AA(AA(A)))))) -//│ JS (unsanitized): -//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12; -//│ f = function f(x) { -//│ let param0, param01, a; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ return g(a) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ g = function g(x) { -//│ let param0, b; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ b = param0; -//│ return f(b) -//│ } else if (x instanceof A1.class) { -//│ return 42 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp8 = AA1(A1); -//│ tmp9 = AA1(tmp8); -//│ tmp10 = AA1(tmp9); -//│ tmp11 = AA1(tmp10); -//│ tmp12 = AA1(tmp11); -//│ f(tmp12) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f, g, tmp8, tmp9, tmp10, tmp11, tmp12, _deforest_AA_x2, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1, match_x_branch_AA, _deforest_AA_x_tmp2, _deforest_AA_x_tmp3; -//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x3) { -//│ let param0, a; -//│ param0 = _deforest_AA_x3; -//│ a = param0; -//│ return g(a) -//│ }; -//│ match_x_branch_AA = function match_x_branch_AA(_deforest_AA_x3) { -//│ let param0; -//│ param0 = _deforest_AA_x3; -//│ return runtime.safeCall(param0()) -//│ }; -//│ f = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ g = function g(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x_tmp = () => { -//│ return 42 -//│ }; -//│ tmp8 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp) -//│ }; -//│ _deforest_AA_x_tmp1 = tmp8; -//│ tmp9 = () => { -//│ return match_x_branch_AA(_deforest_AA_x_tmp1) -//│ }; -//│ _deforest_AA_x2 = tmp9; -//│ tmp10 = () => { -//│ let param0, b; -//│ param0 = _deforest_AA_x2; -//│ b = param0; -//│ return f(b) -//│ }; -//│ _deforest_AA_x_tmp2 = tmp10; -//│ tmp11 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp2) -//│ }; -//│ _deforest_AA_x_tmp3 = tmp11; -//│ tmp12 = () => { -//│ return match_x_branch_AA(_deforest_AA_x_tmp3) -//│ }; -//│ f(tmp12) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 42 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 42 @@ -326,51 +99,9 @@ f(AA(AA(AA(AA(AA(A)))))) //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun f(x) = if x is AA(AA(a)) then a f(AA(AA(A))) -//│ JS (unsanitized): -//│ let f1, tmp18, tmp19; -//│ f1 = function f(x) { -//│ let param0, param01, a; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp18 = AA1(A1); -//│ tmp19 = AA1(tmp18); -//│ f1(tmp19) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f1, tmp18, tmp19, _deforest_AA_x3, _deforest_AA_x4; -//│ f1 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ _deforest_AA_x3 = A1; -//│ tmp18 = () => { -//│ let param0, a; -//│ param0 = _deforest_AA_x3; -//│ a = param0; -//│ return a -//│ }; -//│ _deforest_AA_x4 = tmp18; -//│ tmp19 = () => { -//│ let param0; -//│ param0 = _deforest_AA_x4; -//│ return runtime.safeCall(param0()) -//│ }; -//│ f1(tmp19) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A @@ -379,60 +110,10 @@ f(AA(AA(A))) //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun f(x) = if x is AA(AA(a)) then a fun p() = AA(AA(A)) f(p()) -//│ JS (unsanitized): -//│ let p2, f2, tmp22; -//│ f2 = function f(x) { -//│ let param0, param01, a; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; -//│ if (param0 instanceof AA1.class) { -//│ param01 = param0.x; -//│ a = param01; -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p2 = function p() { -//│ let tmp23; -//│ tmp23 = AA1(A1); -//│ return AA1(tmp23) -//│ }; -//│ tmp22 = p2(); -//│ f2(tmp22) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p2, f2, tmp22; -//│ f2 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ p2 = function p() { -//│ let tmp23, _deforest_AA_x5, _deforest_AA_x6; -//│ _deforest_AA_x5 = A1; -//│ tmp23 = () => { -//│ let param0, a; -//│ param0 = _deforest_AA_x5; -//│ a = param0; -//│ return a -//│ }; -//│ _deforest_AA_x6 = tmp23; -//│ return () => { -//│ let param0; -//│ param0 = _deforest_AA_x6; -//│ return runtime.safeCall(param0()) -//│ } -//│ }; -//│ tmp22 = p2(); -//│ f2(tmp22) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A @@ -443,7 +124,6 @@ f(p()) -:sjs fun c(x, y) = let t = if x is AA then @@ -451,46 +131,6 @@ fun c(x, y) = A then x.x t + y c(AA(2), 10) -//│ JS (unsanitized): -//│ let c4, tmp24; -//│ c4 = function c(x, y) { -//│ let t, scrut, tmp25, tmp26; -//│ if (x instanceof AA1.class) { -//│ scrut = A1; -//│ if (scrut instanceof A1.class) { -//│ tmp25 = x.x; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ tmp26 = tmp25; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp26; -//│ return t + y -//│ }; -//│ tmp24 = AA1(2); -//│ c4(tmp24, 10) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c4, tmp24, _deforest_AA_x5; -//│ c4 = function c(x, y) { -//│ return runtime.safeCall(x(y)) -//│ }; -//│ _deforest_AA_x5 = 2; -//│ tmp24 = (y) => { -//│ let scrut; -//│ scrut = (y1, _deforest_AA_x6) => { -//│ let t, tmp25, tmp26; -//│ tmp25 = _deforest_AA_x6; -//│ tmp26 = tmp25; -//│ t = tmp26; -//│ return t + y1 -//│ }; -//│ return runtime.safeCall(scrut(y, _deforest_AA_x5)) -//│ }; -//│ c4(tmp24, 10) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 12 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 12 diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 579131f5ba..28660c90f4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -14,53 +14,12 @@ class CCC(val c) object None class Some(val value) -:sjs fun test() = let x = if true then A else B if x is A then 1 B then 2 test() -//│ JS (unsanitized): -//│ let test; -//│ test = function test() { -//│ let x, scrut, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = A1; -//│ } else { -//│ tmp = B1; -//│ } -//│ x = tmp; -//│ if (x instanceof A1.class) { -//│ return 1 -//│ } else if (x instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test; -//│ test = function test() { -//│ let x, scrut, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = () => { -//│ return 1 -//│ }; -//│ } else { -//│ tmp = () => { -//│ return 2 -//│ }; -//│ } -//│ x = tmp; -//│ return runtime.safeCall(x()) -//│ }; -//│ test() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 @@ -69,65 +28,12 @@ test() //│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test() = let x = if true then AA(A) else BB(B) if x is AA(x) then x BB(x) then x test() -//│ JS (unsanitized): -//│ let test1; -//│ test1 = function test() { -//│ let x, scrut, param0, x1, param01, x2, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ x = tmp; -//│ if (x instanceof AA1.class) { -//│ param01 = x.aa; -//│ x2 = param01; -//│ return x2 -//│ } else if (x instanceof BB1.class) { -//│ param0 = x.bb; -//│ x1 = param0; -//│ return x1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test1() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test1; -//│ test1 = function test() { -//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; -//│ scrut = true; -//│ if (scrut === true) { -//│ _deforest_AA_aa = A1; -//│ tmp = () => { -//│ let param0, x1; -//│ param0 = _deforest_AA_aa; -//│ x1 = param0; -//│ return x1 -//│ }; -//│ } else { -//│ _deforest_BB_bb = B1; -//│ tmp = () => { -//│ let param0, x1; -//│ param0 = _deforest_BB_bb; -//│ x1 = param0; -//│ return x1 -//│ }; -//│ } -//│ x = tmp; -//│ return runtime.safeCall(x()) -//│ }; -//│ test1() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = A @@ -137,7 +43,6 @@ test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun f(a) = if a is A then 1 B then 2 @@ -147,74 +52,6 @@ fun test() = AA(x) then f(x) BB(x) then f(x) test() -//│ JS (unsanitized): -//│ let test2, f; -//│ f = function f(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else if (a instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test2 = function test() { -//│ let x, scrut, param0, x1, param01, x2, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ x = tmp; -//│ if (x instanceof AA1.class) { -//│ param01 = x.aa; -//│ x2 = param01; -//│ return f(x2) -//│ } else if (x instanceof BB1.class) { -//│ param0 = x.bb; -//│ x1 = param0; -//│ return f(x1) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test2() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test2, f; -//│ f = function f(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ test2 = function test() { -//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; -//│ scrut = true; -//│ if (scrut === true) { -//│ _deforest_AA_aa = () => { -//│ return 1 -//│ }; -//│ tmp = () => { -//│ let param0, x1; -//│ param0 = _deforest_AA_aa; -//│ x1 = param0; -//│ return f(x1) -//│ }; -//│ } else { -//│ _deforest_BB_bb = () => { -//│ return 2 -//│ }; -//│ tmp = () => { -//│ let param0, x1; -//│ param0 = _deforest_BB_bb; -//│ x1 = param0; -//│ return f(x1) -//│ }; -//│ } -//│ x = tmp; -//│ return runtime.safeCall(x()) -//│ }; -//│ test2() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 @@ -227,7 +64,6 @@ test() // `x.x` is successfully fused -:sjs fun f1(a) = if a is A then 1 B then 2 @@ -242,80 +78,6 @@ fun test() = AA then f1(x.aa) BB then f2(x.bb) test() -//│ JS (unsanitized): -//│ let test3, f1, f2; -//│ f1 = function f1(a) { -//│ if (a instanceof A1.class) { -//│ return 1 -//│ } else if (a instanceof B1.class) { -//│ return 2 -//│ } else if (a instanceof C1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f2 = function f2(a) { -//│ if (a instanceof A1.class) { -//│ return 4 -//│ } else if (a instanceof B1.class) { -//│ return 5 -//│ } else if (a instanceof C1.class) { -//│ return 6 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test3 = function test() { -//│ let x, scrut, tmp; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp = AA1(A1); -//│ } else { -//│ tmp = BB1(B1); -//│ } -//│ x = tmp; -//│ if (x instanceof AA1.class) { -//│ return f1(x.aa) -//│ } else if (x instanceof BB1.class) { -//│ return f2(x.bb) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test3() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test3, f1, f2; -//│ f1 = function f1(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ f2 = function f2(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ test3 = function test() { -//│ let x, scrut, tmp, _deforest_AA_aa, _deforest_BB_bb; -//│ scrut = true; -//│ if (scrut === true) { -//│ _deforest_AA_aa = () => { -//│ return 1 -//│ }; -//│ tmp = () => { -//│ return f1(_deforest_AA_aa) -//│ }; -//│ } else { -//│ _deforest_BB_bb = () => { -//│ return 5 -//│ }; -//│ tmp = () => { -//│ return f2(_deforest_BB_bb) -//│ }; -//│ } -//│ x = tmp; -//│ return runtime.safeCall(x()) -//│ }; -//│ test3() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 @@ -327,7 +89,6 @@ test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test() = fun g(x) = if true then AA(11) else BB(22) fun c(x) = if x is @@ -335,71 +96,6 @@ fun test() = BB(x) then x c(g(true)) test() -//│ JS (unsanitized): -//│ let test4; -//│ test4 = function test() { -//│ let c, g, tmp; -//│ g = function g(x) { -//│ let scrut; -//│ scrut = true; -//│ if (scrut === true) { -//│ return AA1(11) -//│ } else { -//│ return BB1(22) -//│ } -//│ }; -//│ c = function c(x) { -//│ let param0, x1, param01, x2; -//│ if (x instanceof AA1.class) { -//│ param01 = x.aa; -//│ x2 = param01; -//│ return x2 -//│ } else if (x instanceof BB1.class) { -//│ param0 = x.bb; -//│ x1 = param0; -//│ return x1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp = g(true); -//│ return c(tmp) -//│ }; -//│ test4() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test4; -//│ test4 = function test() { -//│ let c, g, tmp; -//│ g = function g(x) { -//│ let scrut, _deforest_AA_aa, _deforest_BB_bb; -//│ scrut = true; -//│ if (scrut === true) { -//│ _deforest_AA_aa = 11; -//│ return () => { -//│ let param0, x1; -//│ param0 = _deforest_AA_aa; -//│ x1 = param0; -//│ return x1 -//│ } -//│ } else { -//│ _deforest_BB_bb = 22; -//│ return () => { -//│ let param0, x1; -//│ param0 = _deforest_BB_bb; -//│ x1 = param0; -//│ return x1 -//│ } -//│ } -//│ }; -//│ c = function c(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ tmp = g(true); -//│ return c(tmp) -//│ }; -//│ test4() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 11 @@ -418,76 +114,11 @@ test() object Nil class Cons(val h, val t) -:sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(ls) = if ls is Nil then Nil Cons(h, t) then Cons(h + 4, map(t)) map(enumFromTo(1, 4)) -//│ JS (unsanitized): -//│ let enumFromTo, map, tmp; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp1 = a + 1; -//│ tmp2 = enumFromTo(tmp1, b); -//│ return Cons1(a, tmp2) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ map = function map(ls) { -//│ let param0, param1, h, t, tmp1, tmp2; -//│ if (ls instanceof Nil1.class) { -//│ return Nil1 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp1 = h + 4; -//│ tmp2 = map(t); -//│ return Cons1(tmp1, tmp2) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp = enumFromTo(1, 4); -//│ map(tmp) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo, map, tmp; -//│ enumFromTo = function enumFromTo(a, b) { -//│ let scrut, tmp1, tmp2, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp1 = a + 1; -//│ tmp2 = enumFromTo(tmp1, b); -//│ _deforest_Cons_h = a; -//│ _deforest_Cons_t = tmp2; -//│ return () => { -//│ let param0, param1, h, t, tmp3, tmp4; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp3 = h + 4; -//│ tmp4 = map(t); -//│ return Cons1(tmp3, tmp4) -//│ } -//│ } else { -//│ return () => { -//│ return Nil1 -//│ } -//│ } -//│ }; -//│ map = function map(ls) { -//│ return runtime.safeCall(ls()) -//│ }; -//│ tmp = enumFromTo(1, 4); -//│ map(tmp) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -499,74 +130,11 @@ map(enumFromTo(1, 4)) -:sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun sum(ls) = if ls is Nil then 0 Cons(h, t) then h + sum(t) sum(enumFromTo(1,10)) -//│ JS (unsanitized): -//│ let enumFromTo1, sum, tmp2; -//│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp3, tmp4; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp3 = a + 1; -//│ tmp4 = enumFromTo1(tmp3, b); -//│ return Cons1(a, tmp4) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ sum = function sum(ls) { -//│ let param0, param1, h, t, tmp3; -//│ if (ls instanceof Nil1.class) { -//│ return 0 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp3 = sum(t); -//│ return h + tmp3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp2 = enumFromTo1(1, 10); -//│ sum(tmp2) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo1, sum, tmp2; -//│ enumFromTo1 = function enumFromTo(a, b) { -//│ let scrut, tmp3, tmp4, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp3 = a + 1; -//│ tmp4 = enumFromTo1(tmp3, b); -//│ _deforest_Cons_h = a; -//│ _deforest_Cons_t = tmp4; -//│ return () => { -//│ let param0, param1, h, t, tmp5; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp5 = sum(t); -//│ return h + tmp5 -//│ } -//│ } else { -//│ return () => { -//│ return 0 -//│ } -//│ } -//│ }; -//│ sum = function sum(ls) { -//│ return runtime.safeCall(ls()) -//│ }; -//│ tmp2 = enumFromTo1(1, 10); -//│ sum(tmp2) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 45 @@ -577,7 +145,6 @@ sum(enumFromTo(1,10)) // multiple match, no fusion -:sjs fun test() = let x = B if x is @@ -586,32 +153,11 @@ fun test() = if x is B then 2 test() -//│ JS (unsanitized): -//│ let test5; -//│ test5 = function test() { -//│ let x, tmp4; -//│ x = B1; -//│ if (x instanceof A1.class) { -//│ tmp4 = 1; -//│ } else if (x instanceof B1.class) { -//│ tmp4 = 3; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (x instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test5() -//│ No fusion opportunity //│ = 2 //│ No fusion opportunity -:sjs fun test() = let x = A let y = B @@ -620,41 +166,6 @@ fun test() = if y is B then 2 test() -//│ JS (unsanitized): -//│ let test6; -//│ test6 = function test() { -//│ let x, y, tmp4; -//│ x = A1; -//│ y = B1; -//│ if (x instanceof A1.class) { -//│ tmp4 = 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (y instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test6() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test6; -//│ test6 = function test() { -//│ let x, y; -//│ x = (y1) => { -//│ let tmp4; -//│ tmp4 = 1; -//│ return runtime.safeCall(y1()) -//│ }; -//│ y = () => { -//│ return 2 -//│ }; -//│ return runtime.safeCall(x(y)) -//│ }; -//│ test6() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -664,7 +175,6 @@ test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun c(a) = let x = if a is A then 1 @@ -672,48 +182,6 @@ fun c(a) = print(x) x c(A) + c(B) -//│ JS (unsanitized): -//│ let c, tmp4, tmp5; -//│ c = function c(a) { -//│ let x, tmp6, tmp7; -//│ if (a instanceof A1.class) { -//│ tmp6 = 1; -//│ } else if (a instanceof B1.class) { -//│ tmp6 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ x = tmp6; -//│ tmp7 = Predef.print(x); -//│ return x -//│ }; -//│ tmp4 = c(A1); -//│ tmp5 = c(B1); -//│ tmp4 + tmp5 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c, tmp4, tmp5, match_a_rest; -//│ match_a_rest = function match_a_rest(tmp6) { -//│ let x, tmp7; -//│ x = tmp6; -//│ tmp7 = Predef.print(x); -//│ return x -//│ }; -//│ c = function c(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ tmp4 = c(() => { -//│ let tmp6; -//│ tmp6 = 1; -//│ return match_a_rest(tmp6) -//│ }); -//│ tmp5 = c(() => { -//│ let tmp6; -//│ tmp6 = 2; -//│ return match_a_rest(tmp6) -//│ }); -//│ tmp4 + tmp5 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ > 1 //│ > 2 //│ = 3 @@ -729,83 +197,12 @@ c(A) + c(B) // simple free var example -:sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(f, ls) = if ls is Nil then Nil Cons(h, t) then Cons(f(h), map(f, t)) map(x => x + 4, enumFromTo(1, 4)) -//│ JS (unsanitized): -//│ let enumFromTo2, map1, tmp8, lambda; -//│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp9, tmp10; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp9 = a + 1; -//│ tmp10 = enumFromTo2(tmp9, b); -//│ return Cons1(a, tmp10) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ map1 = function map(f3, ls) { -//│ let param0, param1, h, t, tmp9, tmp10; -//│ if (ls instanceof Nil1.class) { -//│ return Nil1 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp9 = runtime.safeCall(f3(h)); -//│ tmp10 = map1(f3, t); -//│ return Cons1(tmp9, tmp10) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp8 = enumFromTo2(1, 4); -//│ lambda = (undefined, function (x) { -//│ return x + 4 -//│ }); -//│ map1(lambda, tmp8) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo2, map1, tmp8, lambda; -//│ enumFromTo2 = function enumFromTo(a, b) { -//│ let scrut, tmp9, tmp10, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp9 = a + 1; -//│ tmp10 = enumFromTo2(tmp9, b); -//│ _deforest_Cons_h = a; -//│ _deforest_Cons_t = tmp10; -//│ return (f3) => { -//│ let param0, param1, h, t, tmp11, tmp12; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp11 = runtime.safeCall(f3(h)); -//│ tmp12 = map1(f3, t); -//│ return Cons1(tmp11, tmp12) -//│ } -//│ } else { -//│ return (f3) => { -//│ return Nil1 -//│ } -//│ } -//│ }; -//│ map1 = function map(f3, ls) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ tmp8 = enumFromTo2(1, 4); -//│ lambda = (undefined, function (x) { -//│ return x + 4 -//│ }); -//│ map1(lambda, tmp8) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -814,7 +211,6 @@ map(x => x + 4, enumFromTo(1, 4)) //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(f, ls) = (if ls is @@ -822,99 +218,6 @@ fun map(f, ls) = Cons(h, t) then f => Cons(f(h), map(f, t)) )(f) map(x => x + 4, enumFromTo(1, 4)) -//│ JS (unsanitized): -//│ let enumFromTo3, map2, tmp10, lambda2; -//│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp11 = a + 1; -//│ tmp12 = enumFromTo3(tmp11, b); -//│ return Cons1(a, tmp12) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ map2 = function map(f3, ls) { -//│ let param0, param1, h, t, tmp11, tmp12, lambda3, lambda4; -//│ if (ls instanceof Nil1.class) { -//│ lambda3 = (undefined, function (f4) { -//│ return Nil1 -//│ }); -//│ tmp11 = lambda3; -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ lambda4 = (undefined, function (f4) { -//│ let tmp13, tmp14; -//│ tmp13 = runtime.safeCall(f4(h)); -//│ tmp14 = map2(f4, t); -//│ return Cons1(tmp13, tmp14) -//│ }); -//│ tmp12 = lambda4; -//│ tmp11 = tmp12; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return runtime.safeCall(tmp11(f3)) -//│ }; -//│ tmp10 = enumFromTo3(1, 4); -//│ lambda2 = (undefined, function (x) { -//│ return x + 4 -//│ }); -//│ map2(lambda2, tmp10) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo3, map2, tmp10, lambda2, match_ls_rest; -//│ match_ls_rest = function match_ls_rest(f3, tmp11) { -//│ return runtime.safeCall(tmp11(f3)) -//│ }; -//│ enumFromTo3 = function enumFromTo(a, b) { -//│ let scrut, tmp11, tmp12, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp11 = a + 1; -//│ tmp12 = enumFromTo3(tmp11, b); -//│ _deforest_Cons_h = a; -//│ _deforest_Cons_t = tmp12; -//│ return (f3) => { -//│ let param0, param1, h, t, tmp13, tmp14, lambda3; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ lambda3 = (undefined, function (f4) { -//│ let tmp15, tmp16; -//│ tmp15 = runtime.safeCall(f4(h)); -//│ tmp16 = map2(f4, t); -//│ return Cons1(tmp15, tmp16) -//│ }); -//│ tmp14 = lambda3; -//│ tmp13 = tmp14; -//│ return match_ls_rest(f3, tmp13) -//│ } -//│ } else { -//│ return (f3) => { -//│ let tmp13, lambda3; -//│ lambda3 = (undefined, function (f4) { -//│ return Nil1 -//│ }); -//│ tmp13 = lambda3; -//│ return match_ls_rest(f3, tmp13) -//│ } -//│ } -//│ }; -//│ map2 = function map(f3, ls) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ tmp10 = enumFromTo3(1, 4); -//│ lambda2 = (undefined, function (x) { -//│ return x + 4 -//│ }); -//│ map2(lambda2, tmp10) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -925,74 +228,11 @@ map(x => x + 4, enumFromTo(1, 4)) -:sjs fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun sum(ls, a) = if ls is Nil then a Cons(h, t) then sum(t, h + a) sum(enumFromTo(1, 10), 0) -//│ JS (unsanitized): -//│ let enumFromTo4, sum1, tmp12; -//│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ return Cons1(a, tmp14) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ sum1 = function sum(ls, a) { -//│ let param0, param1, h, t, tmp13; -//│ if (ls instanceof Nil1.class) { -//│ return a -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp13 = h + a; -//│ return sum1(t, tmp13) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ sum1(tmp12, 0) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let enumFromTo4, sum1, tmp12; -//│ enumFromTo4 = function enumFromTo(a, b) { -//│ let scrut, tmp13, tmp14, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = a < b; -//│ if (scrut === true) { -//│ tmp13 = a + 1; -//│ tmp14 = enumFromTo4(tmp13, b); -//│ _deforest_Cons_h = a; -//│ _deforest_Cons_t = tmp14; -//│ return (a1) => { -//│ let param0, param1, h, t, tmp15; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp15 = h + a1; -//│ return sum1(t, tmp15) -//│ } -//│ } else { -//│ return (a1) => { -//│ return a1 -//│ } -//│ } -//│ }; -//│ sum1 = function sum(ls, a) { -//│ return runtime.safeCall(ls(a)) -//│ }; -//│ tmp12 = enumFromTo4(1, 10); -//│ sum1(tmp12, 0) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 45 @@ -1005,65 +245,21 @@ sum(enumFromTo(1, 10), 0) :expect 5 -:sjs fun c(x) = let t = x.aa let n = if x is AA then 2 n + t c(AA(3)) -//│ JS (unsanitized): -//│ let c1, tmp14; -//│ c1 = function c(x) { -//│ let t, n, tmp15; -//│ t = x.aa; -//│ if (x instanceof AA1.class) { -//│ tmp15 = 2; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp15; -//│ return n + t -//│ }; -//│ tmp14 = AA1(3); -//│ c1(tmp14) -//│ No fusion opportunity //│ = 5 //│ No fusion opportunity -:sjs fun f(a, b) = if a is A then if b is B then 3 f(A, B) -//│ JS (unsanitized): -//│ let f3; -//│ f3 = function f(a, b) { -//│ if (a instanceof A1.class) { -//│ if (b instanceof B1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f3(A1, B1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f3; -//│ f3 = function f(a, b) { -//│ return runtime.safeCall(a(b)) -//│ }; -//│ f3((b) => { -//│ return runtime.safeCall(b()) -//│ }, () => { -//│ return 3 -//│ }) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -1073,69 +269,20 @@ f(A, B) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) -//│ JS (unsanitized): -//│ let f4, tmp15; -//│ f4 = function f(x) { -//│ let scrut, tmp16, tmp17; -//│ if (x instanceof Some1.class) { -//│ scrut = x.value > 1; -//│ if (scrut === true) { -//│ tmp16 = x.value - 1; -//│ tmp17 = Some1(tmp16); -//│ return f4(tmp17) -//│ } else { -//│ return 0 -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp15 = Some1(2); -//│ f4(tmp15) -//│ No fusion opportunity //│ = 0 //│ No fusion opportunity -:sjs let x = A let y = B if x is A then 1 if y is B then 2 -//│ JS (unsanitized): -//│ let x, y, tmp16; -//│ x = A1; -//│ y = B1; -//│ if (x instanceof A1.class) { -//│ tmp16 = 1; -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ if (y instanceof B1.class) { -//│ 2 -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let x, y; -//│ x = (y1) => { -//│ let tmp16; -//│ tmp16 = 1; -//│ return runtime.safeCall(y1()) -//│ }; -//│ y = () => { -//│ return 2 -//│ }; -//│ runtime.safeCall(x(y)) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ x = A //│ y = B @@ -1148,7 +295,6 @@ if y is -:sjs fun test() = let x = A let y = B @@ -1157,41 +303,6 @@ fun test() = if y is B then 2 test() -//│ JS (unsanitized): -//│ let test7; -//│ test7 = function test() { -//│ let x1, y1, tmp17; -//│ x1 = A1; -//│ y1 = B1; -//│ if (x1 instanceof A1.class) { -//│ tmp17 = 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ if (y1 instanceof B1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test7() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test7; -//│ test7 = function test() { -//│ let x1, y1; -//│ x1 = (y2) => { -//│ let tmp17; -//│ tmp17 = 1; -//│ return runtime.safeCall(y2()) -//│ }; -//│ y1 = () => { -//│ return 2 -//│ }; -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ test7() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -1202,107 +313,12 @@ test() -:sjs fun f(x, y) = let a = if x is AAA(n, m) then y + n - m BBB(n, m) then m + 1 - n a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) -//│ JS (unsanitized): -//│ let f5, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; -//│ f5 = function f(x1, y1) { -//│ let a, param0, param1, n, m, param01, param11, n1, m1, tmp27, tmp28, tmp29; -//│ if (x1 instanceof AAA1.class) { -//│ param01 = x1.x; -//│ param11 = x1.y; -//│ n1 = param01; -//│ m1 = param11; -//│ tmp27 = y1 + n1; -//│ tmp28 = tmp27 - m1; -//│ } else if (x1 instanceof BBB1.class) { -//│ param0 = x1.x; -//│ param1 = x1.y; -//│ n = param0; -//│ m = param1; -//│ tmp29 = m + 1; -//│ tmp28 = tmp29 - n; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ a = tmp28; -//│ return a + 3 -//│ }; -//│ tmp17 = AAA1(1, 3); -//│ tmp18 = f5(tmp17, 1); -//│ tmp19 = BBB1(2, 3); -//│ tmp20 = f5(tmp19, 2); -//│ tmp21 = tmp18 + tmp20; -//│ tmp22 = AAA1(3, 2); -//│ tmp23 = f5(tmp22, 4); -//│ tmp24 = tmp21 + tmp23; -//│ tmp25 = BBB1(4, 6); -//│ tmp26 = f5(tmp25, 0); -//│ tmp24 + tmp26 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f5, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp, match_x_rest, match_x_branch_AAA, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp, match_x_branch_BBB, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1; -//│ match_x_branch_AAA = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { -//│ let param0, param1, n, m, tmp27, tmp28; -//│ param0 = _deforest_AAA_x; -//│ param1 = _deforest_AAA_y; -//│ n = param0; -//│ m = param1; -//│ tmp27 = y1 + n; -//│ tmp28 = tmp27 - m; -//│ return match_x_rest(tmp28) -//│ }; -//│ match_x_branch_BBB = function match_x_branch_BBB(y1, _deforest_BBB_x, _deforest_BBB_y) { -//│ let param0, param1, n, m, tmp27, tmp28; -//│ param0 = _deforest_BBB_x; -//│ param1 = _deforest_BBB_y; -//│ n = param0; -//│ m = param1; -//│ tmp28 = m + 1; -//│ tmp27 = tmp28 - n; -//│ return match_x_rest(tmp27) -//│ }; -//│ match_x_rest = function match_x_rest(tmp27) { -//│ let a; -//│ a = tmp27; -//│ return a + 3 -//│ }; -//│ f5 = function f(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ _deforest_AAA_x_tmp = 1; -//│ _deforest_AAA_y_tmp = 3; -//│ tmp17 = (y1) => { -//│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp, _deforest_AAA_y_tmp) -//│ }; -//│ tmp18 = f5(tmp17, 1); -//│ _deforest_BBB_x_tmp = 2; -//│ _deforest_BBB_y_tmp = 3; -//│ tmp19 = (y1) => { -//│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp, _deforest_BBB_y_tmp) -//│ }; -//│ tmp20 = f5(tmp19, 2); -//│ tmp21 = tmp18 + tmp20; -//│ _deforest_AAA_x_tmp1 = 3; -//│ _deforest_AAA_y_tmp1 = 2; -//│ tmp22 = (y1) => { -//│ return match_x_branch_AAA(y1, _deforest_AAA_x_tmp1, _deforest_AAA_y_tmp1) -//│ }; -//│ tmp23 = f5(tmp22, 4); -//│ tmp24 = tmp21 + tmp23; -//│ _deforest_BBB_x_tmp1 = 4; -//│ _deforest_BBB_y_tmp1 = 6; -//│ tmp25 = (y1) => { -//│ return match_x_branch_BBB(y1, _deforest_BBB_x_tmp1, _deforest_BBB_y_tmp1) -//│ }; -//│ tmp26 = f5(tmp25, 0); -//│ tmp24 + tmp26 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 21 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 21 @@ -1314,7 +330,6 @@ f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun c1(x) = if x is AA then if A is @@ -1323,66 +338,6 @@ fun c2(x) = if x is AA then x.aa fun p(a) = c1(a) + c2(a) p(AA(1)) -//│ JS (unsanitized): -//│ let p, c11, c2, tmp37; -//│ c11 = function c1(x1) { -//│ let scrut; -//│ if (x1 instanceof AA1.class) { -//│ scrut = A1; -//│ if (scrut instanceof A1.class) { -//│ return x1.aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ c2 = function c2(x1) { -//│ if (x1 instanceof AA1.class) { -//│ return x1.aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p = function p(a) { -//│ let tmp38, tmp39; -//│ tmp38 = c11(a); -//│ tmp39 = c2(a); -//│ return tmp38 + tmp39 -//│ }; -//│ tmp37 = AA1(1); -//│ p(tmp37) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p, c11, c2, tmp37; -//│ c11 = function c1(x1) { -//│ let scrut; -//│ if (x1 instanceof AA1.class) { -//│ scrut = (x2) => { -//│ return x2.aa -//│ }; -//│ return runtime.safeCall(scrut(x1)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ c2 = function c2(x1) { -//│ if (x1 instanceof AA1.class) { -//│ return x1.aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ p = function p(a) { -//│ let tmp38, tmp39; -//│ tmp38 = c11(a); -//│ tmp39 = c2(a); -//│ return tmp38 + tmp39 -//│ }; -//│ tmp37 = AA1(1); -//│ p(tmp37) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -1392,47 +347,11 @@ p(AA(1)) -:sjs fun c(x, y) = if x is AA(a) then if y is A then a c(AA(2), A) -//│ JS (unsanitized): -//│ let c3, tmp39; -//│ c3 = function c(x1, y1) { -//│ let param0, a; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ a = param0; -//│ if (y1 instanceof A1.class) { -//│ return a -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp39 = AA1(2); -//│ c3(tmp39, A1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c3, tmp39, _deforest_AA_aa; -//│ c3 = function c(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ _deforest_AA_aa = 2; -//│ tmp39 = (y1) => { -//│ let param0, a; -//│ param0 = _deforest_AA_aa; -//│ a = param0; -//│ return runtime.safeCall(y1(a)) -//│ }; -//│ c3(tmp39, (a) => { -//│ return a -//│ }) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 2 @@ -1442,99 +361,12 @@ c(AA(2), A) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun f(x, y) = let a = if x is AAA(n, m) then y + n - m CCC(n) then n + 1 a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) -//│ JS (unsanitized): -//│ let f6, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50; -//│ f6 = function f(x1, y1) { -//│ let a, param0, n, param01, param1, n1, m, tmp51, tmp52; -//│ if (x1 instanceof AAA1.class) { -//│ param01 = x1.x; -//│ param1 = x1.y; -//│ n1 = param01; -//│ m = param1; -//│ tmp51 = y1 + n1; -//│ tmp52 = tmp51 - m; -//│ } else if (x1 instanceof CCC1.class) { -//│ param0 = x1.c; -//│ n = param0; -//│ tmp52 = n + 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ a = tmp52; -//│ return a + 3 -//│ }; -//│ tmp41 = AAA1(1, 3); -//│ tmp42 = f6(tmp41, 1); -//│ tmp43 = CCC1(2); -//│ tmp44 = f6(tmp43, 2); -//│ tmp45 = tmp42 + tmp44; -//│ tmp46 = AAA1(3, 2); -//│ tmp47 = f6(tmp46, 4); -//│ tmp48 = tmp45 + tmp47; -//│ tmp49 = CCC1(4); -//│ tmp50 = f6(tmp49, 0); -//│ tmp48 + tmp50 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f6, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2, match_x_rest1, match_x_branch_AAA1, _deforest_CCC_c_tmp, match_x_branch_CCC, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3, _deforest_CCC_c_tmp1; -//│ match_x_branch_AAA1 = function match_x_branch_AAA(y1, _deforest_AAA_x, _deforest_AAA_y) { -//│ let param0, param1, n, m, tmp51, tmp52; -//│ param0 = _deforest_AAA_x; -//│ param1 = _deforest_AAA_y; -//│ n = param0; -//│ m = param1; -//│ tmp51 = y1 + n; -//│ tmp52 = tmp51 - m; -//│ return match_x_rest1(tmp52) -//│ }; -//│ match_x_branch_CCC = function match_x_branch_CCC(y1, _deforest_CCC_c) { -//│ let param0, n, tmp51; -//│ param0 = _deforest_CCC_c; -//│ n = param0; -//│ tmp51 = n + 1; -//│ return match_x_rest1(tmp51) -//│ }; -//│ match_x_rest1 = function match_x_rest(tmp51) { -//│ let a; -//│ a = tmp51; -//│ return a + 3 -//│ }; -//│ f6 = function f(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ _deforest_AAA_x_tmp2 = 1; -//│ _deforest_AAA_y_tmp2 = 3; -//│ tmp41 = (y1) => { -//│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp2, _deforest_AAA_y_tmp2) -//│ }; -//│ tmp42 = f6(tmp41, 1); -//│ _deforest_CCC_c_tmp = 2; -//│ tmp43 = (y1) => { -//│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp) -//│ }; -//│ tmp44 = f6(tmp43, 2); -//│ tmp45 = tmp42 + tmp44; -//│ _deforest_AAA_x_tmp3 = 3; -//│ _deforest_AAA_y_tmp3 = 2; -//│ tmp46 = (y1) => { -//│ return match_x_branch_AAA1(y1, _deforest_AAA_x_tmp3, _deforest_AAA_y_tmp3) -//│ }; -//│ tmp47 = f6(tmp46, 4); -//│ tmp48 = tmp45 + tmp47; -//│ _deforest_CCC_c_tmp1 = 4; -//│ tmp49 = (y1) => { -//│ return match_x_branch_CCC(y1, _deforest_CCC_c_tmp1) -//│ }; -//│ tmp50 = f6(tmp49, 0); -//│ tmp48 + tmp50 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 24 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 24 @@ -1548,77 +380,12 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) -:sjs fun f(x, y) = let a = if x is AAA then y + x.y CCC(n) then n + 1 a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) -//│ JS (unsanitized): -//│ let f7, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67; -//│ f7 = function f(x1, y1) { -//│ let a, param0, n, tmp68; -//│ if (x1 instanceof AAA1.class) { -//│ tmp68 = y1 + x1.y; -//│ } else if (x1 instanceof CCC1.class) { -//│ param0 = x1.c; -//│ n = param0; -//│ tmp68 = n + 1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ a = tmp68; -//│ return a + 3 -//│ }; -//│ tmp61 = AAA1(1, 3); -//│ tmp62 = f7(tmp61, 1); -//│ tmp63 = CCC1(2); -//│ tmp64 = f7(tmp63, 2); -//│ tmp65 = tmp62 + tmp64; -//│ tmp66 = AAA1(3, 2); -//│ tmp67 = f7(tmp66, 4); -//│ tmp65 + tmp67 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f7, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67, _deforest_CCC_c, _deforest_AAA_x_unused, _deforest_AAA_y_tmp4, match_x_rest2, match_x_branch_AAA2, _deforest_AAA_x_unused1, _deforest_AAA_y_tmp5; -//│ match_x_branch_AAA2 = function match_x_branch_AAA(y1, _deforest_AAA_y) { -//│ let tmp68; -//│ tmp68 = y1 + _deforest_AAA_y; -//│ return match_x_rest2(tmp68) -//│ }; -//│ match_x_rest2 = function match_x_rest(tmp68) { -//│ let a; -//│ a = tmp68; -//│ return a + 3 -//│ }; -//│ f7 = function f(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ _deforest_AAA_x_unused = 1; -//│ _deforest_AAA_y_tmp4 = 3; -//│ tmp61 = (y1) => { -//│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp4) -//│ }; -//│ tmp62 = f7(tmp61, 1); -//│ _deforest_CCC_c = 2; -//│ tmp63 = (y1) => { -//│ let param0, n, tmp68; -//│ param0 = _deforest_CCC_c; -//│ n = param0; -//│ tmp68 = n + 1; -//│ return match_x_rest2(tmp68) -//│ }; -//│ tmp64 = f7(tmp63, 2); -//│ tmp65 = tmp62 + tmp64; -//│ _deforest_AAA_x_unused1 = 3; -//│ _deforest_AAA_y_tmp5 = 2; -//│ tmp66 = (y1) => { -//│ return match_x_branch_AAA2(y1, _deforest_AAA_y_tmp5) -//│ }; -//│ tmp67 = f7(tmp66, 4); -//│ tmp65 + tmp67 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 22 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 22 @@ -1629,37 +396,10 @@ f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun c(x, m) = if x is AA(n) then n + 1 else m c(BB(3), 0) -//│ JS (unsanitized): -//│ let c4, tmp75; -//│ c4 = function c(x1, m) { -//│ let param0, n; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ n = param0; -//│ return n + 1 -//│ } else { -//│ return m -//│ } -//│ }; -//│ tmp75 = BB1(3); -//│ c4(tmp75, 0) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c4, tmp75, _deforest_BB_bb_unused; -//│ c4 = function c(x1, m) { -//│ return runtime.safeCall(x1(m)) -//│ }; -//│ _deforest_BB_bb_unused = 3; -//│ tmp75 = (m) => { -//│ return m -//│ }; -//│ c4(tmp75, 0) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 0 @@ -1670,62 +410,11 @@ c(BB(3), 0) -:sjs fun f(x) = if x is AA(b) then g(b) fun g(b) = if b is AA(a) then a + 1 f(AA(AA(0))) -//│ JS (unsanitized): -//│ let f8, g, tmp77, tmp78; -//│ f8 = function f(x1) { -//│ let param0, b; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ b = param0; -//│ return g(b) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ g = function g(b) { -//│ let param0, a; -//│ if (b instanceof AA1.class) { -//│ param0 = b.aa; -//│ a = param0; -//│ return a + 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp77 = AA1(0); -//│ tmp78 = AA1(tmp77); -//│ f8(tmp78) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f8, g, tmp77, tmp78, _deforest_AA_aa1, _deforest_AA_aa2; -//│ f8 = function f(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ g = function g(b) { -//│ return runtime.safeCall(b()) -//│ }; -//│ _deforest_AA_aa1 = 0; -//│ tmp77 = () => { -//│ let param0, a; -//│ param0 = _deforest_AA_aa1; -//│ a = param0; -//│ return a + 1 -//│ }; -//│ _deforest_AA_aa2 = tmp77; -//│ tmp78 = () => { -//│ let param0, b; -//│ param0 = _deforest_AA_aa2; -//│ b = param0; -//│ return g(b) -//│ }; -//│ f8(tmp78) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 @@ -1736,21 +425,8 @@ f(AA(AA(0))) -:sjs fun f(x) = x.aa.aa f(AA(AA(3))) -//│ JS (unsanitized): -//│ let f9, tmp81, tmp82; -//│ f9 = function f(x1) { -//│ return x1.aa.aa -//│ }; -//│ tmp81 = AA1(3); -//│ tmp82 = AA1(tmp81); -//│ f9(tmp82) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f9, tmp81, tmp82; f9 = function f(x1) { return x1 }; tmp81 = 3; tmp82 = tmp81; f9(tmp82) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -1759,7 +435,6 @@ f(AA(AA(3))) //│ AA --sel--> `.Ident(aa)` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun inner(x, y) = let t = if x is AA(a) then a + y @@ -1777,127 +452,6 @@ fun outer2(x, y, z) = t + y let p = AA(AA(3)) outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) -//│ JS (unsanitized): -//│ let outer1, outer2, inner, p1, tmp85, tmp86, tmp87, tmp88, tmp89, tmp90, tmp91; -//│ inner = function inner(x1, y1) { -//│ let t, param0, b, param01, a, tmp92; -//│ if (x1 instanceof AA1.class) { -//│ param01 = x1.aa; -//│ a = param01; -//│ tmp92 = a + y1; -//│ } else if (x1 instanceof BB1.class) { -//│ param0 = x1.bb; -//│ b = param0; -//│ tmp92 = b - y1; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp92; -//│ return t * y1 -//│ }; -//│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp92; -//│ if (x1 instanceof AA1.class) { -//│ param01 = x1.aa; -//│ a = param01; -//│ tmp92 = inner(a, y1); -//│ } else if (x1 instanceof BB1.class) { -//│ param0 = x1.bb; -//│ b = param0; -//│ tmp92 = inner(b, y1); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp92; -//│ return t + z -//│ }; -//│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp92; -//│ if (x1 instanceof AA1.class) { -//│ param01 = x1.aa; -//│ a = param01; -//│ tmp92 = inner(a, z); -//│ } else if (x1 instanceof BB1.class) { -//│ param0 = x1.bb; -//│ b = param0; -//│ tmp92 = inner(b, y1); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp92; -//│ return t + y1 -//│ }; -//│ tmp85 = AA1(3); -//│ tmp86 = AA1(tmp85); -//│ p1 = tmp86; -//│ tmp87 = outer1(p1, 1, 2); -//│ tmp88 = outer2(p1, 3, 4); -//│ tmp89 = tmp87 + tmp88; -//│ tmp90 = AA1(5); -//│ tmp91 = inner(tmp90, 6); -//│ tmp89 + tmp91 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let outer1, outer2, inner, p1, tmp85, tmp86, tmp87, tmp88, tmp89, tmp90, tmp91, _deforest_AA_aa_tmp, match_x_branch_AA, _deforest_AA_aa_tmp1; -//│ match_x_branch_AA = function match_x_branch_AA(y1, _deforest_AA_aa3) { -//│ let t, param0, a, tmp92; -//│ param0 = _deforest_AA_aa3; -//│ a = param0; -//│ tmp92 = a + y1; -//│ t = tmp92; -//│ return t * y1 -//│ }; -//│ inner = function inner(x1, y1) { -//│ return runtime.safeCall(x1(y1)) -//│ }; -//│ outer1 = function outer1(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp92; -//│ if (x1 instanceof AA1.class) { -//│ param01 = x1.aa; -//│ a = param01; -//│ tmp92 = inner(a, y1); -//│ } else if (x1 instanceof BB1.class) { -//│ param0 = x1.bb; -//│ b = param0; -//│ tmp92 = inner(b, y1); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp92; -//│ return t + z -//│ }; -//│ outer2 = function outer2(x1, y1, z) { -//│ let t, param0, b, param01, a, tmp92; -//│ if (x1 instanceof AA1.class) { -//│ param01 = x1.aa; -//│ a = param01; -//│ tmp92 = inner(a, z); -//│ } else if (x1 instanceof BB1.class) { -//│ param0 = x1.bb; -//│ b = param0; -//│ tmp92 = inner(b, y1); -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ t = tmp92; -//│ return t + y1 -//│ }; -//│ _deforest_AA_aa_tmp = 3; -//│ tmp85 = (y1) => { -//│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp) -//│ }; -//│ tmp86 = AA1(tmp85); -//│ p1 = tmp86; -//│ tmp87 = outer1(p1, 1, 2); -//│ tmp88 = outer2(p1, 3, 4); -//│ tmp89 = tmp87 + tmp88; -//│ _deforest_AA_aa_tmp1 = 5; -//│ tmp90 = (y1) => { -//│ return match_x_branch_AA(y1, _deforest_AA_aa_tmp1) -//│ }; -//│ tmp91 = inner(tmp90, 6); -//│ tmp89 + tmp91 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 103 //│ p = AA(AA(3)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -1908,52 +462,9 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun mapHead(f, x) = if x is AAA(h, t) then f(h) mapHead(x => x, AAA(1, AAA(2, None))) -//│ JS (unsanitized): -//│ let mapHead, tmp99, tmp100, lambda4; -//│ mapHead = function mapHead(f10, x1) { -//│ let param0, param1, h, t; -//│ if (x1 instanceof AAA1.class) { -//│ param0 = x1.x; -//│ param1 = x1.y; -//│ h = param0; -//│ t = param1; -//│ return runtime.safeCall(f10(h)) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp99 = AAA1(2, None1); -//│ tmp100 = AAA1(1, tmp99); -//│ lambda4 = (undefined, function (x1) { -//│ return x1 -//│ }); -//│ mapHead(lambda4, tmp100) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let mapHead, tmp99, tmp100, lambda4, _deforest_AAA_x, _deforest_AAA_y; -//│ mapHead = function mapHead(f10, x1) { -//│ return runtime.safeCall(x1(f10)) -//│ }; -//│ tmp99 = AAA1(2, None1); -//│ _deforest_AAA_x = 1; -//│ _deforest_AAA_y = tmp99; -//│ tmp100 = (f10) => { -//│ let param0, param1, h, t; -//│ param0 = _deforest_AAA_x; -//│ param1 = _deforest_AAA_y; -//│ h = param0; -//│ t = param1; -//│ return runtime.safeCall(f10(h)) -//│ }; -//│ lambda4 = (undefined, function (x1) { -//│ return x1 -//│ }); -//│ mapHead(lambda4, tmp100) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 1 @@ -1962,7 +473,6 @@ mapHead(x => x, AAA(1, AAA(2, None))) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test() = let k if A is @@ -1970,37 +480,6 @@ fun test() = k = 3 k + 2 test() -//│ JS (unsanitized): -//│ let test8; -//│ test8 = function test() { -//│ let k, scrut, tmp103; -//│ k = undefined; -//│ scrut = A1; -//│ if (scrut instanceof A1.class) { -//│ k = 3; -//│ tmp103 = runtime.Unit; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ return k + 2 -//│ }; -//│ test8() -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test8; -//│ test8 = function test() { -//│ let k, scrut; -//│ k = undefined; -//│ scrut = () => { -//│ let tmp103; -//│ k = 3; -//│ tmp103 = runtime.Unit; -//│ return k + 2 -//│ }; -//│ return runtime.safeCall(scrut()) -//│ }; -//│ test8() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 @@ -2008,40 +487,10 @@ test() //│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs fun test(a) = if a is AA then 1 else 2 test(B) + test(C) -//│ JS (unsanitized): -//│ let test9, tmp103, tmp104; -//│ test9 = function test(a) { -//│ if (a instanceof AA1.class) { -//│ return 1 -//│ } else { -//│ return 2 -//│ } -//│ }; -//│ tmp103 = test9(B1); -//│ tmp104 = test9(C1); -//│ tmp103 + tmp104 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test9, tmp103, tmp104, match_a_branch_dflt; -//│ match_a_branch_dflt = function match_a_branch_dflt() { -//│ return 2 -//│ }; -//│ test9 = function test(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ tmp103 = test9(() => { -//│ return match_a_branch_dflt() -//│ }); -//│ tmp104 = test9(() => { -//│ return match_a_branch_dflt() -//│ }); -//│ tmp103 + tmp104 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 4 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 @@ -2053,7 +502,6 @@ test(B) + test(C) // technically ill-typed, so `AA(3)` (which flows to `z`) is not fused -:sjs fun test(x, y, z) = if x is AA(a) then let m = if y is @@ -2062,71 +510,6 @@ fun test(x, y, z) = if x is AA(a2) then a2 - z m + n test(AA(1), AA(2), AA(3)) -//│ JS (unsanitized): -//│ let test10, tmp107, tmp108, tmp109; -//│ test10 = function test(x1, y1, z) { -//│ let param0, a, m, param01, a1, n, param02, a2, tmp110, tmp111; -//│ if (x1 instanceof AA1.class) { -//│ param0 = x1.aa; -//│ a = param0; -//│ if (y1 instanceof AA1.class) { -//│ param01 = y1.aa; -//│ a1 = param01; -//│ tmp110 = a1 + z; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ m = tmp110; -//│ if (z instanceof AA1.class) { -//│ param02 = z.aa; -//│ a2 = param02; -//│ tmp111 = a2 - z; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ n = tmp111; -//│ return m + n -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp107 = AA1(1); -//│ tmp108 = AA1(2); -//│ tmp109 = AA1(3); -//│ test10(tmp107, tmp108, tmp109) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test10, tmp107, tmp108, tmp109, _deforest_AA_aa3, _deforest_AA_aa4; -//│ test10 = function test(x1, y1, z) { -//│ return runtime.safeCall(x1(y1, z)) -//│ }; -//│ _deforest_AA_aa3 = 1; -//│ tmp107 = (y1, z) => { -//│ let param0, a; -//│ param0 = _deforest_AA_aa3; -//│ a = param0; -//│ return runtime.safeCall(y1(z)) -//│ }; -//│ _deforest_AA_aa4 = 2; -//│ tmp108 = (z) => { -//│ let m, param0, a1, n, param01, a2, tmp110, tmp111; -//│ param0 = _deforest_AA_aa4; -//│ a1 = param0; -//│ tmp110 = a1 + z; -//│ m = tmp110; -//│ if (z instanceof AA1.class) { -//│ param01 = z.aa; -//│ a2 = param01; -//│ tmp111 = a2 - z; -//│ } else { -//│ throw new this.Error("match error"); -//│ } -//│ n = tmp111; -//│ return m + n -//│ }; -//│ tmp109 = AA1(3); -//│ test10(tmp107, tmp108, tmp109) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = "2AA(3)NaN" //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = "2AA(3)NaN" From f75d2be71d40027228977b40d99cf5636ac51c1f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 2 Apr 2025 23:48:04 +0800 Subject: [PATCH 164/303] use locally; use `data class` --- .../main/scala/hkmc2/codegen/Deforestation.scala | 3 +-- .../src/test/mlscript/deforest/nestedMatch.mls | 10 +++++----- .../mlscript/deforest/selectionsInNestedMatch.mls | 8 ++++---- hkmc2/shared/src/test/mlscript/deforest/simple.mls | 14 +++++++------- hkmc2/shared/src/test/mlscript/deforest/todos.mls | 10 +++++----- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 0c6ab93d6e..052e0ecd61 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -781,7 +781,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( scrutExprId, - { + locally: assert(m.scrut.uid === scrutExprId) val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m @@ -801,7 +801,6 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend traverser.applyBlock(realArm) traverser.result.toList.sortBy(_.uid) - } ) object matchArms: diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 7293d9d255..4d320f26b4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -5,15 +5,15 @@ object A object B object C -class AA(val x) -class BB(val x) -class CC(val x) +data class AA(x) +data class BB(x) +data class CC(x) object Nil -class Cons(val h, val t) +data class Cons(h, t) object None -class Some(val x) +data class Some(x) fun f(x, y) = diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 841fb70fab..0c379fab08 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -5,14 +5,14 @@ object A object B object C -class AA(val x) -class BB(val x) +data class AA(x) +data class BB(x) object Nil -class Cons(val h, val t) +data class Cons(h, t) object None -class Some(val x) +data class Some(x) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 28660c90f4..2253d2e34c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -6,13 +6,13 @@ object A object B object C -class AA(val aa) -class BB(val bb) -class AAA(val x, val y) -class BBB(val x, val y) -class CCC(val c) +data class AA(aa) +data class BB(bb) +data class AAA(x, y) +data class BBB(x, y) +data class CCC(c) object None -class Some(val value) +data class Some(value) fun test() = let x = if true then A else B @@ -112,7 +112,7 @@ test() object Nil -class Cons(val h, val t) +data class Cons(h, t) fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil fun map(ls) = if ls is diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 7b8a4bc770..d1ebbff422 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -5,15 +5,15 @@ object A object B object C -class AA(val x) -class BB(val x) -class CC(val x) +data class AA(x) +data class BB(x) +data class CC(x) object Nil -class Cons(val h, val t) +data class Cons(h, t) object None -class Some(val x) +data class Some(x) // similar as above, since `x.x` is considered as being consumed From b92abee97b4e071278dda35bedd6487374b3bc73 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 3 Apr 2025 08:34:48 +0800 Subject: [PATCH 165/303] cleanup jsbackenddiffmaker --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 3211289b1f..dde71ee29c 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -57,34 +57,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: h private var hostCreated = false - override def run(): Unit = - try super.run() finally - if hostCreated then host.terminate() - - - - def mkQuery(preStr: Str, jsStr: Str)(using host: ReplHost = host, r: Raise)(k: Str => Unit) = - val queryStr = jsStr.replaceAll("\n", " ") - val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) - reply match - case ReplHost.Result(content) => k(content) - case ReplHost.Empty => - case ReplHost.Unexecuted(message) => ??? - case ReplHost.Error(isSyntaxError, message, otherOutputs) => - if otherOutputs.nonEmpty then - otherOutputs.splitSane('\n').foreach: line => - output(s"> ${line}") - if (isSyntaxError) then - // If there is a syntax error in the generated code, - // it should be a code generation error. - raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, - source = Diagnostic.Source.Compilation)) - else - // Otherwise, it is considered a simple runtime error. - raise(ErrorReport(msg"${message}" -> N :: Nil, - source = Diagnostic.Source.Runtime)) - if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") + try super.run() finally if hostCreated then host.terminate() override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit = super.processTerm(blk, inImport) @@ -178,6 +152,27 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if showSanitizedJS.isSet then output(s"JS:") output(jsStr) + def mkQuery(preStr: Str, jsStr: Str)(k: Str => Unit) = + val queryStr = jsStr.replaceAll("\n", " ") + val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) + reply match + case ReplHost.Result(content) => k(content) + case ReplHost.Empty => + case ReplHost.Unexecuted(message) => ??? + case ReplHost.Error(isSyntaxError, message, otherOutputs) => + if otherOutputs.nonEmpty then + otherOutputs.splitSane('\n').foreach: line => + output(s"> ${line}") + if (isSyntaxError) then + // If there is a syntax error in the generated code, + // it should be a code generation error. + raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, + source = Diagnostic.Source.Compilation)) + else + // Otherwise, it is considered a simple runtime error. + raise(ErrorReport(msg"${message}" -> N :: Nil, + source = Diagnostic.Source.Runtime)) + if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") if traceJS.isSet then host.execute( @@ -191,8 +186,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) .foreach: line => output(s"> ${line}") - - if traceJS.isSet then host.execute("globalThis.Predef.TraceLogger.enabled = false") @@ -235,7 +228,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") if deforestFlag.isSet then - val deforestLow = ltl.givenIn: codegen.Lowering() val lowered0 = deforestLow.program(blk) From 3a47e3be6d3e1ec244b905c9904a1a0487664ab8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:24:34 +0800 Subject: [PATCH 166/303] better code: avoid local `object`s --- .../scala/hkmc2/codegen/Deforestation.scala | 80 +++++++++++-------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 052e0ecd61..a2a05d801e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -153,6 +153,10 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: ctx ++= paramSymbols applyBlock(l.body) ctx --= paramSymbols + + def analyze(b: Block) = + applyBlock(b) + result.toList.sortBy(_.uid) // Compute free vars for a Match block, considering deforestations. Used on non-transformed blocks // Make use of `freeVarsOfNonTransformedMatches`, which computes the free vars _after transformation_ @@ -190,47 +194,51 @@ class DeforestationFreeVarTraverser(using selsToBeReplaced.get(p.uid).fold(super.applyPath(p))(s => result += s) case _ => super.applyPath(p) +class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserShallow: + var flag = false + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + flag = + d.filteredDtors(scrut.uid) || + (arms.forall { case (_, b) => b.willBeNonEndTailBlock } && dflt.fold(true)(_.willBeNonEndTailBlock)) || + rest.willBeNonEndTailBlock + case _: End => () + case _: BlockTail => flag = true + case _ => super.applyBlock(b) + def analyze(b: Block): Bool = + applyBlock(b) + flag + +class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends + BlockTransformer(new SymbolSubst()): + override def applyValue(v: Value): Value = v match + case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) + case _ => super.applyValue(v) +object HasExplicitRetTraverser extends BlockTraverserShallow: + var flag = false + override def applyBlock(b: Block): Unit = b match + case Return(_, imp) => flag = !imp + case _ => super.applyBlock(b) + + def analyze(b: Block) = + flag = false + applyBlock(b) + flag + extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = - object ReplaceLocalSymTransformer extends BlockTransformer(new SymbolSubst()): - override def applyValue(v: Value): Value = v match - case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) - case _ => super.applyValue(v) - ReplaceLocalSymTransformer.applyBlock(b) + ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) def sortedFvsForTransformedBlocks(using alwaysDefined: Set[Symbol]) = - val traverser = FreeVarTraverser(alwaysDefined) - traverser.applyBlock(b) - traverser.result.toList.sortBy(_.uid) + FreeVarTraverser(alwaysDefined).analyze(b) def hasExplicitRet: Boolean = - object HasExplicitRetTraverser extends BlockTraverserShallow: - var flag = false - override def applyBlock(b: Block): Unit = b match - case Return(_, imp) => flag = !imp - case _ => super.applyBlock(b) - - HasExplicitRetTraverser.applyBlock(b) - HasExplicitRetTraverser.flag + HasExplicitRetTraverser.analyze(b) def willBeNonEndTailBlock(using d: Deforest): Bool = - object WillBeNonEndTailBlockTraverser extends BlockTraverserShallow: - var flag = false - override def applyBlock(b: Block): Unit = b match - case Match(scrut, arms, dflt, rest) => - flag = - d.filteredDtors(scrut.uid) || - (arms.forall { case (_, b) => b.willBeNonEndTailBlock } && dflt.fold(true)(_.willBeNonEndTailBlock)) || - rest.willBeNonEndTailBlock - case _: End => () - case _: BlockTail => flag = true - case _ => super.applyBlock(b) - WillBeNonEndTailBlockTraverser.applyBlock(b) - WillBeNonEndTailBlockTraverser.flag - - - + WillBeNonEndTailBlockTraverser().analyze(b) + class Deforest(using TL, Raise, Elaborator.State): object StratVarUidHandler extends Uid.Handler[StratVar]() @@ -972,7 +980,13 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - case Match(scrut, arms, dflt, rest) if dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } => + case Match(scrut, arms, dflt, rest) + if + // TODO: explain what this does; + // TODO: it will become unnecessary once we have proper binding declarations in the Block IR + // and all uses of never-assigned variables will be known to be dead code + dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } + => super.applyBlock(Match(scrut, arms, dflt, End(""))) case _ => super.applyBlock(b) From 33f470346bb8dc7eec8f3d86c71be69e9bdcb1a8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:10:43 +0800 Subject: [PATCH 167/303] tests from meeting --- .../src/test/mlscript/deforest/simple.mls | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 2253d2e34c..69318fa4c6 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -517,3 +517,40 @@ test(AA(1), AA(2), AA(3)) //│ AA --match--> `if x is ...` //│ AA --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun test(x) = + let t = id of + if x is + AA(a) then a + BB(a) then a + t(123).c + 5 * 4 - 3 + 2 - 1 +let p = if true then AA(CCC) else BB(CCC) +test(p) +//│ = 141 +//│ p = AA([function CCC]) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 141 +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ BB --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun test(x, y) = + let t = + if y is CCC then + if x is + AA(a) then a + BB(a) then a + t(123).c + 5 * 4 - 3 + 2 - 1 +let p = if true then AA(CCC) else BB(CCC) +test(p, id(CCC(123))) +//│ = 141 +//│ p = AA([function CCC]) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 141 +//│ 2 fusion opportunities: +//│ AA --match--> `if x is ...` +//│ BB --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 1553cc8938765dfc84c060c7d83caeba8970ebd3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:26:15 +0800 Subject: [PATCH 168/303] more comments --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a2a05d801e..a080ab6dbd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -982,7 +982,9 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend Return(Call(scrut, freeVars)(false, false), !needExplicitRet) case Match(scrut, arms, dflt, rest) if - // TODO: explain what this does; + // If all the arms end with non-`End` blocks, then the `rest` of this `Match` will never be executed, + // and we remove the `rest` in this case. This prevents `rest` to use variables that become + // undefined because computation in arms that defines them are moved away. // TODO: it will become unnecessary once we have proper binding declarations in the Block IR // and all uses of never-assigned variables will be known to be dead code dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } From ca9a63fab46f4b940505f9a006e683f1266f6aef Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:01:24 +0800 Subject: [PATCH 169/303] wip: more needs to be done for things like `id` to block fusion --- .../src/test/mlscript/deforest/todos.mls | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index d1ebbff422..ced08bf9dd 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -212,3 +212,42 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +:fixme +:sjs +fun f(x) = if x is + A then 1 +f(id(A)) + f(A) +//│ JS (unsanitized): +//│ let f2, tmp28, tmp29, tmp30; +//│ f2 = function f(x) { +//│ if (x instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp28 = Predef.id(A1); +//│ tmp29 = f2(tmp28); +//│ tmp30 = f2(A1); +//│ tmp29 + tmp30 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f2, tmp28, tmp29, tmp30; +//│ f2 = function f(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ tmp28 = Predef.id(A1); +//│ tmp29 = f2(tmp28); +//│ tmp30 = f2(() => { +//│ return 1 +//│ }); +//│ tmp29 + tmp30 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ═══[RUNTIME ERROR] TypeError: x is not a function +//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("2") +//│ 1 fusion opportunities: +//│ A --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From a3b4308095973c88e23962374417472428e23be5 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:48:53 +0800 Subject: [PATCH 170/303] use of functions without defn now really blocks fusion --- .../scala/hkmc2/codegen/Deforestation.scala | 31 ++++++++++----- .../src/test/mlscript/deforest/simple.mls | 22 +++++++++++ .../src/test/mlscript/deforest/todos.mls | 38 ------------------- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a080ab6dbd..2ac8e4e80e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -310,17 +310,30 @@ class Deforest(using TL, Raise, Elaborator.State): val matchScrutToParentMatchScrut = mutable.Map.empty[ResultId, Option[ResultId]] object symToStrat: val store = mutable.Map.empty[Symbol, ProdVar] + val funSymsWithDefn = mutable.Set.empty[BlockMemberSymbol] + val usedFunSym = mutable.Set.empty[BlockMemberSymbol] - def init(p: Block) = if store.isEmpty then - object FreshVarForAllVars extends BlockTraverser: - override def applySymbol(s: Symbol): Unit = s match - case _: BlockMemberSymbol => store += s -> freshVar(s.nme)._1 - case _: TempSymbol => store += s -> freshVar(s.nme)._1 - case _: VarSymbol => store += s -> freshVar(s.nme)._1 - case _: TermSymbol => store += s -> freshVar(s.nme)._1 - case _ => () + def init(p: Block) = + if store.isEmpty then + object FreshVarForAllVars extends BlockTraverser: + override def applySymbol(s: Symbol): Unit = s match + case b: BlockMemberSymbol => + store += s -> freshVar(s.nme)._1 + b.trmImplTree.foreach: t => + if t.k is syntax.Fun then usedFunSym += b + case _: TempSymbol => store += s -> freshVar(s.nme)._1 + case _: VarSymbol => store += s -> freshVar(s.nme)._1 + case _: TermSymbol => store += s -> freshVar(s.nme)._1 + case _ => () - FreshVarForAllVars.applyBlock(p) + override def applyFunDefn(fun: FunDefn): Unit = + funSymsWithDefn += fun.sym + super.applyFunDefn(fun) + + FreshVarForAllVars.applyBlock(p) + usedFunSym.diff(funSymsWithDefn).foreach: funSymsWithoutDefn => + constrain(NoProd, store(funSymsWithoutDefn).asConsStrat) + // TODO: ctor as a function? def getStratOfSym(s: Symbol) = diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 69318fa4c6..4c485b0660 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -554,3 +554,25 @@ test(p, id(CCC(123))) //│ AA --match--> `if x is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:sjs +fun f(x) = if x is + A then 1 +f(id(A)) + f(A) +//│ JS (unsanitized): +//│ let f10, tmp121, tmp122, tmp123; +//│ f10 = function f(x1) { +//│ if (x1 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp121 = Predef.id(A1); +//│ tmp122 = f10(tmp121); +//│ tmp123 = f10(A1); +//│ tmp122 + tmp123 +//│ No fusion opportunity +//│ = 2 +//│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index ced08bf9dd..c5b6f1c011 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -213,41 +213,3 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:fixme -:sjs -fun f(x) = if x is - A then 1 -f(id(A)) + f(A) -//│ JS (unsanitized): -//│ let f2, tmp28, tmp29, tmp30; -//│ f2 = function f(x) { -//│ if (x instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp28 = Predef.id(A1); -//│ tmp29 = f2(tmp28); -//│ tmp30 = f2(A1); -//│ tmp29 + tmp30 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f2, tmp28, tmp29, tmp30; -//│ f2 = function f(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ tmp28 = Predef.id(A1); -//│ tmp29 = f2(tmp28); -//│ tmp30 = f2(() => { -//│ return 1 -//│ }); -//│ tmp29 + tmp30 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -//│ = 2 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ═══[RUNTIME ERROR] TypeError: x is not a function -//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("2") -//│ 1 fusion opportunities: -//│ A --match--> `if x is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From ce79cb566638ec3a1f0c65ffed3db2afcc2bf315 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:03:25 +0800 Subject: [PATCH 171/303] more helpful deforestation debug output from `tl.log` --- .../scala/hkmc2/codegen/Deforestation.scala | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 2ac8e4e80e..8f3659794e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -81,7 +81,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Set[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) case Sel(s: ResultId) trait FieldSelTrait: @@ -262,20 +262,25 @@ class Deforest(using TL, Raise, Elaborator.State): resolveConstraints - - tl.log("upper:") - upperBounds.foreach(u => tl.log("\t" + u)) - tl.log("lower:") - lowerBounds.foreach(l => tl.log("\t" + l)) tl.log("-----------------------------------------") - tl.log("ctor -> dtor") - resolveClashes._1.foreach(u => tl.log("\t" + u)) - tl.log("dtor -> ctor") - resolveClashes._2.foreach(l => tl.log("\t" + l)) + ctorDests.ctorDests.foreach: + case (ctorExprId, CtorDest(matches, sels, noCons)) => + val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" + val matchExprScruts = "if " + matches.map{(s, m) => + m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" + }.toList.sorted.mkString(" | ") + " then ... " + val selExpr = sels.map{ + case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" + }.toList.sorted.mkString(" | ") + tl.log(s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons") + tl.log("-----------------------------------------") + dtorSources.dtorSources.foreach: + case (d, DtorSource(ctors, noProd)) => + tl.log(s"$d <--- ${ctors.map(c => c.getClsSymOfUid.nme + s"(id:$c)").toList.mkString(" | ")} <--- (NoProd: $noProd)") tl.log("-----------------------------------------") filteredCtorDests.foreach: - case (ctorUid, CtorFinalDest.Sel(s)) => tl.log("\t" + ctorUid + " --sel--> " + s) - case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log("\t" + ctorUid + " --mat-->" + scrut ) + case (ctorUid, CtorFinalDest.Sel(s)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --sel--> " + s) + case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --mat--> " + scrut ) val fusionStat = filteredCtorDests.map: case (ctorUid, CtorFinalDest.Sel(s)) => @@ -492,19 +497,19 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool) + case class CtorDest(matches: Map[ResultId, Match], sels: Set[FieldSel], noCons: Bool) case class DtorSource(ctors: Set[CtorExpr], noProd: Bool) object ctorDests: - val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false)) + val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Set.empty, false)) def update(ctor: CtorExpr, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) - case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false)) + case None => Some(CtorDest(Map(m.scrut.uid -> m), Set.empty, false)) def update(ctor: CtorExpr, s: FieldSel) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, s :: sels, noCons)) - case None => Some(CtorDest(Map.empty, s :: Nil, false)) + case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels + s, noCons)) + case None => Some(CtorDest(Map.empty, Set(s), false)) def update(ctor: CtorExpr, n: NoCons.type) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels, true)) - case None => Some(CtorDest(Map.empty, Nil, true)) + case None => Some(CtorDest(Map.empty, Set.empty, true)) def get(ctor: CtorExpr) = ctorDests.get(ctor) object dtorSources: @@ -603,6 +608,7 @@ class Deforest(using TL, Raise, Elaborator.State): if rm.isEmpty then ctorDests -> dtorSources else + tl.log("rm ctor: " + rm.map(c => c.getClsSymOfUid.nme).mkString(" | ")) val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) @@ -612,6 +618,7 @@ class Deforest(using TL, Raise, Elaborator.State): if rm.isEmpty then ctorDests -> dtorSources else + tl.log("rm dtor: " + rm.mkString(" | ")) val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) removeCtor(ctorDests, newDtorSources, toDelete.values.map(_.ctors).flatten.toSet) From f0c022c51099ff2093fdc8a99ed69ed1193e2286 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 8 Apr 2025 18:34:01 +0800 Subject: [PATCH 172/303] fresh type var for `throw` to avoid blocking fusion --- .../scala/hkmc2/codegen/Deforestation.scala | 4 +++- .../src/test/mlscript/deforest/simple.mls | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8f3659794e..e50feeec02 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -405,7 +405,9 @@ class Deforest(using TL, Raise, Elaborator.State): case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") processBlock(rest) case End(msg) => NoProd - case Throw(exc) => NoProd + // make it a type var instead of `NoProd` so that things like `throw match error` in + // default else branches do not block fusion... + case Throw(exc) => freshVar("throw")._1 def constrFun(params: Ls[Param], body: Block)(using inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 4c485b0660..7c0bc2b0df 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -576,3 +576,21 @@ f(id(A)) + f(A) //│ No fusion opportunity //│ = 2 //│ No fusion opportunity + + + +fun f(x) = if x is + A then 0 + B then 1 +fun g(x) = if x is + Nil then A + Cons then B +f(g(Nil)) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 0 +//│ 3 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if x is ...` +//│ Nil --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 0e96f9eeeb301e5a3d6e67e1ee17f2663956505a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 8 Apr 2025 21:48:24 +0800 Subject: [PATCH 173/303] more recursive tests --- .../src/test/mlscript/deforest/recursive.mls | 181 ++++++++++++++++++ .../src/test/mlscript/deforest/simple.mls | 97 +--------- 2 files changed, 190 insertions(+), 88 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/recursive.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls new file mode 100644 index 0000000000..d769338a1b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls @@ -0,0 +1,181 @@ +:js +:deforest + +//│ No fusion opportunity + + +object Nil +data class Cons(h, t) + + +fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) +fun map(f, xs) = if xs is + Nil then Nil + Cons(x, xs) then Cons(f(x), map(f, xs)) +fun map1(f, xs) = if xs is + Nil then Nil + Cons(x, xs) then Cons(f(x), map1(f, xs)) +fun incr(x) = x + 1 +fun double(x) = x * 2 +fun test(ls) = map1(incr, map(double, ls)) +test(id(mk(5))) +//│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if xs is ...` +//│ Nil --match--> `if xs is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(ls) = if ls is + Nil then Nil + Cons(h, t) then Cons(h + 4, map(t)) +map(enumFromTo(1, 4)) +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun sum(ls) = if ls is + Nil then 0 + Cons(h, t) then h + sum(t) +sum(enumFromTo(1,10)) +//│ = 45 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 45 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + if ls is + Nil then Nil + Cons(h, t) then Cons(f(h), map(f, t)) +map(x => x + 4, enumFromTo(1, 4)) +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun map(f, ls) = + (if ls is + Nil then f => Nil + Cons(h, t) then f => Cons(f(h), map(f, t)) + )(f) +map(x => x + 4, enumFromTo(1, 4)) +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(5, Cons(6, Cons(7, Nil))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun sum(ls, a) = if ls is + Nil then a + Cons(h, t) then sum(t, h + a) +sum(enumFromTo(1, 10), 0) +//│ = 45 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 45 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) +fun incr(x) = x + 1 +fun map(f, xs_map) = if xs_map is + Nil then Nil + Cons(x, xs) then Cons(f(x), map(f, xs)) +fun rev(xs_rev, acc) = if xs_rev is + Cons(x, xs) then rev(xs, Cons(x, acc)) + Nil then acc +fun test(xs) = map(incr, rev(xs, Nil)) +test(id(mk(5))) +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if xs_map is ...` +//│ Nil --match--> `if xs_map is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) +fun incr(x) = x + 1 +fun map(f, xs_map) = if xs_map is + Nil then Nil + Cons(x, xs) then Cons(f(x), map(f, xs)) +fun rev(xs_rev, acc) = if xs_rev is + Cons(x, xs) then rev(xs, Cons(x, acc)) + Nil then acc +fun test(xs) = rev(map(incr, xs), Nil) +test(id(mk(3))) +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) +//│ 2 fusion opportunities: +//│ Cons --match--> `if xs_rev is ...` +//│ Nil --match--> `if xs_rev is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +data class Pair(a, b) + + +fun pair_up(xs) = + if xs is + Cons(x, xss) then + if xss is + Cons(y, xs) then Cons(Pair(x, y), pair_up(xs)) + else Nil + else Nil +fun mk(n) = if n > 0 then Cons(n - 1, Cons(n, Cons(n + 1, mk(n - 1)))) else Nil +fun test(x) = pair_up(mk(x)) +test(4) +//│ = Cons(Pair(3, 4), Cons(Pair(5, 2), Cons(Pair(3, 4), Cons(Pair(1, 2), Cons(Pair(3, 0), Cons(Pair(1, 2), Nil)))))) +//│ No fusion opportunity + + + +fun mk(n) = if n > 0 then Cons(n, mk(n - 1)) else Nil +fun mk2d(n) = if n > 0 then Cons(mk(n), mk2d(n - 1)) else Nil +fun append(xs, ys) = if xs is + Nil then ys + Cons(x, xs) then Cons(x, append(xs, ys)) +fun sum(ls_sum) = if ls_sum is + Nil then 0 + Cons(x, xs) then x + sum(xs) +fun flatten(ls_flatten) = if ls_flatten is + Nil then Nil + Cons(x, xs) then append(x, flatten(xs)) +fun test(ls) = sum(flatten(ls)) +test(id(mk2d(4))) +//│ = 20 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 20 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls_sum is ...` +//│ Nil --match--> `if ls_sum is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 7c0bc2b0df..650e6fdc0c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -107,43 +107,6 @@ test() - - - - -object Nil -data class Cons(h, t) - -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(ls) = if ls is - Nil then Nil - Cons(h, t) then Cons(h + 4, map(t)) -map(enumFromTo(1, 4)) -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - - -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun sum(ls) = if ls is - Nil then 0 - Cons(h, t) then h + sum(t) -sum(enumFromTo(1,10)) -//│ = 45 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 45 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - // multiple match, no fusion fun test() = let x = B @@ -197,49 +160,7 @@ c(A) + c(B) // simple free var example -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(f, ls) = - if ls is - Nil then Nil - Cons(h, t) then Cons(f(h), map(f, t)) -map(x => x + 4, enumFromTo(1, 4)) -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun map(f, ls) = - (if ls is - Nil then f => Nil - Cons(h, t) then f => Cons(f(h), map(f, t)) - )(f) -map(x => x + 4, enumFromTo(1, 4)) -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil -fun sum(ls, a) = if ls is - Nil then a - Cons(h, t) then sum(t, h + a) -sum(enumFromTo(1, 10), 0) -//│ = 45 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 45 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -561,7 +482,7 @@ fun f(x) = if x is A then 1 f(id(A)) + f(A) //│ JS (unsanitized): -//│ let f10, tmp121, tmp122, tmp123; +//│ let f10, tmp111, tmp112, tmp113; //│ f10 = function f(x1) { //│ if (x1 instanceof A1.class) { //│ return 1 @@ -569,10 +490,10 @@ f(id(A)) + f(A) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp121 = Predef.id(A1); -//│ tmp122 = f10(tmp121); -//│ tmp123 = f10(A1); -//│ tmp122 + tmp123 +//│ tmp111 = Predef.id(A1); +//│ tmp112 = f10(tmp111); +//│ tmp113 = f10(A1); +//│ tmp112 + tmp113 //│ No fusion opportunity //│ = 2 //│ No fusion opportunity @@ -583,14 +504,14 @@ fun f(x) = if x is A then 0 B then 1 fun g(x) = if x is - Nil then A - Cons then B -f(g(Nil)) + AA then A + BB then B +f(g(AA(1))) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 0 //│ 3 fusion opportunities: //│ A --match--> `if x is ...` +//│ AA --match--> `if x is ...` //│ B --match--> `if x is ...` -//│ Nil --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 418ee33e289098406b7abfe880c22f76b02e783c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:03:16 +0800 Subject: [PATCH 174/303] zip unzip tests --- .../src/test/mlscript/deforest/zipunzip.mls | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls new file mode 100644 index 0000000000..ad14604af3 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls @@ -0,0 +1,78 @@ +:js +:deforest + +//│ No fusion opportunity + + +object Nil +data class Cons(h, t) +data class Pair(a, b) + +fun zip(xs_zip, ys_zip) = if + xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + else Nil +fun unzip(ls_unzip) = if ls_unzip is + Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Nil then Pair(Nil, Nil) +fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun testUnzipZip(n) = unzip(zip(id(enumFromTo(1, n)), id(enumFromTo(2, n + 3)))) +testUnzipZip(3) +//│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) +//│ 4 fusion opportunities: +//│ Cons --match--> `if ls_unzip is ...` +//│ Nil --match--> `if ls_unzip is ...` +//│ Nil --match--> `if ls_unzip is ...` +//│ Pair --match--> `if param0 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun zip(xs_zip, ys_zip) = if + xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + else Nil +fun unzip(ls_unzip) = if ls_unzip is + Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Nil then Pair(Nil, Nil) +fun makeZippedList(n) = if n > 0 then Cons(Pair(n, n + 1), makeZippedList(n - 1)) else Nil +fun testZipUnzip(n) = if unzip(id(makeZippedList(n))) is + Pair(xs, ys) then zip(xs, ys) +testZipUnzip(3) +//│ = Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) +//│ 4 fusion opportunities: +//│ Cons --match--> `if xs_zip is ...` +//│ Cons --match--> `if ys_zip is ...` +//│ Nil --match--> `if xs_zip is ...` +//│ Nil --match--> `if ys_zip is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun zip(xs_zip, ys_zip) = if + xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + else Nil +fun unzip(ls_unzip) = if ls_unzip is + Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Nil then Pair(Nil, Nil) +fun map(f, ls_map) = if ls_map is + Cons(h, t) then Cons(f(h), map(f, t)) + Nil then Nil +fun makeZippedList(n) = if n > 0 then Cons(Pair(n, n + 1), makeZippedList(n - 1)) else Nil +fun testZipMapBothUnzip(n) = if unzip(id(makeZippedList(n))) is + Pair(xs, ys) then zip( + map(x => x + 1, xs), + map(x => x * x, ys) + ) +testZipMapBothUnzip(4) +//│ = Cons(Pair(5, 25), Cons(Pair(4, 16), Cons(Pair(3, 9), Cons(Pair(2, 4), Nil)))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(Pair(5, 25), Cons(Pair(4, 16), Cons(Pair(3, 9), Cons(Pair(2, 4), Nil)))) +//│ 4 fusion opportunities: +//│ Cons --match--> `if ls_map is ...` +//│ Cons --match--> `if ls_map is ...` +//│ Nil --match--> `if ls_map is ...` +//│ Nil --match--> `if ls_map is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From c91409c5c83a514dd0c64e7a7753c64b847dcc36 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:42:04 +0800 Subject: [PATCH 175/303] use infix `::`; more tests --- .../src/test/mlscript/deforest/append.mls | 58 +++++++++++++++++ .../src/test/mlscript/deforest/recursive.mls | 64 +++++++++---------- .../src/test/mlscript/deforest/zipunzip.mls | 20 +++--- 3 files changed, 100 insertions(+), 42 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/append.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/append.mls b/hkmc2/shared/src/test/mlscript/deforest/append.mls new file mode 100644 index 0000000000..e5716d154b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/append.mls @@ -0,0 +1,58 @@ +:js +:deforest + +//│ No fusion opportunity + + +object Nil +data class (::) Cons(h, t) + + + +fun append1(xs1, ys) = if xs1 is + h :: t then h :: append1(t, ys) + Nil then ys +fun append2(xs2, ys) = if xs2 is + h :: t then h :: append2(t, ys) + Nil then ys +fun appendThree(xs, ys, zs) = + append1(append2(xs, ys), zs) +appendThree of + id(1 :: 2 :: Nil) + id(3 :: 4 :: Nil) + id(5 :: 6 :: Nil) +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ No fusion opportunity + + + + +fun idList(l) = if l is + h :: t then h :: idList(t) + Nil then Nil +fun append(xs, ys) = if xs is + h :: t then h :: append(t, ys) + Nil then idList(ys) +fun appendThree(xs, ys, zs) = + append(append(xs, ys), zs) +appendThree of + id(1 :: 2 :: Nil) + id(3 :: 4 :: Nil) + id(5 :: 6 :: Nil) +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ No fusion opportunity + + + +fun append(xs, ys) = if xs is + h :: t then h :: append(t, ys) + Nil then idList(ys) +fun concat(lss) = if lss is + hh :: tt then append(hh, concat(tt)) + Nil then Nil +concat of id of + (1 :: 2 :: Nil) :: + (3 :: 4 :: Nil) :: + (5 :: 6 :: Nil) :: Nil +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls index d769338a1b..af782bfa2a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls @@ -5,16 +5,16 @@ object Nil -data class Cons(h, t) +data class (::) Cons(h, t) -fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) -fun map(f, xs) = if xs is +fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) +fun map(f, ls_map) = if ls_map is + h :: t then f(h) :: map(f, t) Nil then Nil - Cons(x, xs) then Cons(f(x), map(f, xs)) -fun map1(f, xs) = if xs is +fun map1(f, ls_map1) = if ls_map1 is + h :: t then f(h) :: map1(f, t) Nil then Nil - Cons(x, xs) then Cons(f(x), map1(f, xs)) fun incr(x) = x + 1 fun double(x) = x * 2 fun test(ls) = map1(incr, map(double, ls)) @@ -23,16 +23,16 @@ test(id(mk(5))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) //│ 2 fusion opportunities: -//│ Cons --match--> `if xs is ...` -//│ Nil --match--> `if xs is ...` +//│ Cons --match--> `if ls_map1 is ...` +//│ Nil --match--> `if ls_map1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(ls) = if ls is Nil then Nil - Cons(h, t) then Cons(h + 4, map(t)) + h :: t then (h + 4) :: map(t) map(enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -42,10 +42,10 @@ map(enumFromTo(1, 4)) //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun sum(ls) = if ls is Nil then 0 - Cons(h, t) then h + sum(t) + h :: t then h + sum(t) sum(enumFromTo(1,10)) //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -55,11 +55,11 @@ sum(enumFromTo(1,10)) //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(f, ls) = if ls is Nil then Nil - Cons(h, t) then Cons(f(h), map(f, t)) + h :: t then f(h) :: map(f, t) map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -69,11 +69,11 @@ map(x => x + 4, enumFromTo(1, 4)) //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(f, ls) = (if ls is Nil then f => Nil - Cons(h, t) then f => Cons(f(h), map(f, t)) + h :: t then f => f(h) :: map(f, t) )(f) map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) @@ -86,10 +86,10 @@ map(x => x + 4, enumFromTo(1, 4)) -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun sum(ls, a) = if ls is Nil then a - Cons(h, t) then sum(t, h + a) + h :: t then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -101,13 +101,13 @@ sum(enumFromTo(1, 10), 0) -fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) +fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun incr(x) = x + 1 fun map(f, xs_map) = if xs_map is Nil then Nil - Cons(x, xs) then Cons(f(x), map(f, xs)) + x :: xs then f(x) :: map(f, xs) fun rev(xs_rev, acc) = if xs_rev is - Cons(x, xs) then rev(xs, Cons(x, acc)) + x :: xs then rev(xs, x :: acc) Nil then acc fun test(xs) = map(incr, rev(xs, Nil)) test(id(mk(5))) @@ -121,13 +121,13 @@ test(id(mk(5))) -fun mk(n) = if n < 0 then Nil else Cons(n, mk(n - 1)) +fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun incr(x) = x + 1 fun map(f, xs_map) = if xs_map is Nil then Nil - Cons(x, xs) then Cons(f(x), map(f, xs)) + x :: xs then f(x) :: map(f, xs) fun rev(xs_rev, acc) = if xs_rev is - Cons(x, xs) then rev(xs, Cons(x, acc)) + x :: xs then rev(xs, x :: acc) Nil then acc fun test(xs) = rev(map(incr, xs), Nil) test(id(mk(3))) @@ -146,12 +146,12 @@ data class Pair(a, b) fun pair_up(xs) = if xs is - Cons(x, xss) then + x :: xss then if xss is - Cons(y, xs) then Cons(Pair(x, y), pair_up(xs)) + y :: xs then Pair(x, y) :: pair_up(xs) else Nil else Nil -fun mk(n) = if n > 0 then Cons(n - 1, Cons(n, Cons(n + 1, mk(n - 1)))) else Nil +fun mk(n) = if n > 0 then (n - 1) :: n :: (n + 1) :: mk(n - 1) else Nil fun test(x) = pair_up(mk(x)) test(4) //│ = Cons(Pair(3, 4), Cons(Pair(5, 2), Cons(Pair(3, 4), Cons(Pair(1, 2), Cons(Pair(3, 0), Cons(Pair(1, 2), Nil)))))) @@ -159,17 +159,17 @@ test(4) -fun mk(n) = if n > 0 then Cons(n, mk(n - 1)) else Nil -fun mk2d(n) = if n > 0 then Cons(mk(n), mk2d(n - 1)) else Nil +fun mk(n) = if n > 0 then n :: mk(n - 1) else Nil +fun mk2d(n) = if n > 0 then mk(n) :: mk2d(n - 1) else Nil fun append(xs, ys) = if xs is Nil then ys - Cons(x, xs) then Cons(x, append(xs, ys)) + x :: xs then x :: append(xs, ys) fun sum(ls_sum) = if ls_sum is Nil then 0 - Cons(x, xs) then x + sum(xs) + x :: xs then x + sum(xs) fun flatten(ls_flatten) = if ls_flatten is Nil then Nil - Cons(x, xs) then append(x, flatten(xs)) + x :: xs then append(x, flatten(xs)) fun test(ls) = sum(flatten(ls)) test(id(mk2d(4))) //│ = 20 diff --git a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls index ad14604af3..73097e5461 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls @@ -5,16 +5,16 @@ object Nil -data class Cons(h, t) +data class (::) Cons(h, t) data class Pair(a, b) fun zip(xs_zip, ys_zip) = if - xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is - Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) -fun enumFromTo(a, b) = if a < b then Cons(a, enumFromTo(a + 1, b)) else Nil +fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun testUnzipZip(n) = unzip(zip(id(enumFromTo(1, n)), id(enumFromTo(2, n + 3)))) testUnzipZip(3) //│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) @@ -30,12 +30,12 @@ testUnzipZip(3) fun zip(xs_zip, ys_zip) = if - xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is - Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) -fun makeZippedList(n) = if n > 0 then Cons(Pair(n, n + 1), makeZippedList(n - 1)) else Nil +fun makeZippedList(n) = if n > 0 then Pair(n, n + 1) :: makeZippedList(n - 1) else Nil fun testZipUnzip(n) = if unzip(id(makeZippedList(n))) is Pair(xs, ys) then zip(xs, ys) testZipUnzip(3) @@ -52,13 +52,13 @@ testZipUnzip(3) fun zip(xs_zip, ys_zip) = if - xs_zip is Cons(x, xt) and ys_zip is Cons(y, yt) then Cons(Pair(x, y), zip(xt, yt)) + xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is - Cons(Pair(a, b), t) and unzip(t) is Pair(atail, btail) then Pair(Cons(a, atail), Cons(b, btail)) + Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) fun map(f, ls_map) = if ls_map is - Cons(h, t) then Cons(f(h), map(f, t)) + h :: t then f(h) :: map(f, t) Nil then Nil fun makeZippedList(n) = if n > 0 then Cons(Pair(n, n + 1), makeZippedList(n - 1)) else Nil fun testZipMapBothUnzip(n) = if unzip(id(makeZippedList(n))) is From 987e7bd5c8245aacf1e1b6c496511c69389578cb Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 9 Apr 2025 18:03:25 +0800 Subject: [PATCH 176/303] more tests --- .../src/test/mlscript/deforest/recursive.mls | 120 +++++++++++++++++- .../src/test/mlscript/deforest/simple.mls | 13 ++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls index af782bfa2a..45434baa57 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls @@ -6,7 +6,12 @@ object Nil data class (::) Cons(h, t) - +object None +data class Some(v) +object A +object B +data class T(n, l, r) +object L fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun map(f, ls_map) = if ls_map is @@ -179,3 +184,116 @@ test(id(mk2d(4))) //│ Cons --match--> `if ls_sum is ...` //│ Nil --match--> `if ls_sum is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun count(c, xs) = if xs is + h :: t then count(c + 1, t) + Nil then c +fun rev(a, ys) = if ys is + h :: t then rev(h :: a, t) + Nil then a +count(0, rev(Nil, 1 :: 2 :: Nil)) +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 2 +//│ 5 fusion opportunities: +//│ Cons --match--> `if xs is ...` +//│ Cons --match--> `if ys is ...` +//│ Cons --match--> `if ys is ...` +//│ Nil --match--> `if xs is ...` +//│ Nil --match--> `if ys is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun last(ls) = if ls is + h :: t and t is + Nil then Some(h) + _ :: _ then last(t) + Nil then None +let p = 1 :: 2 :: Nil +last(p) +//│ = Some(2) +//│ p = Cons(1, Cons(2, Nil)) +//│ No fusion opportunity + + + +fun map(f, xs_map) = if xs_map is + Nil then Nil + x :: xs then f(x) :: map(f, xs) +map(x => if x is A then 1 else 0, A :: B :: Nil) +//│ = Cons(1, Cons(0, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(1, Cons(0, Nil)) +//│ 5 fusion opportunities: +//│ A --match--> `if x is ...` +//│ B --match--> `if x is ...` +//│ Cons --match--> `if xs_map is ...` +//│ Cons --match--> `if xs_map is ...` +//│ Nil --match--> `if xs_map is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun c(x) = if x is + T(n, l, r) then T of + if n is + A then 0 + B then 1 + c(l) + c(r) + L then L +c(T(A, T(B, L, L), T(A, L, L))) +//│ = T(0, T(1, L, L), T(0, L, L)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = T(0, T(1, L, L), T(0, L, L)) +//│ 10 fusion opportunities: +//│ A --match--> `if n is ...` +//│ A --match--> `if n is ...` +//│ B --match--> `if n is ...` +//│ L --match--> `if x is ...` +//│ L --match--> `if x is ...` +//│ L --match--> `if x is ...` +//│ L --match--> `if x is ...` +//│ T --match--> `if x is ...` +//│ T --match--> `if x is ...` +//│ T --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun max(ms, m) = if ms is + h :: t and + h > m then max(t, h) + else max(t, m) + Nil then m +max(3 :: 2 :: 4 :: 1 :: 0 :: Nil, 0) +//│ = 4 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 4 +//│ 6 fusion opportunities: +//│ Cons --match--> `if ms is ...` +//│ Cons --match--> `if ms is ...` +//│ Cons --match--> `if ms is ...` +//│ Cons --match--> `if ms is ...` +//│ Cons --match--> `if ms is ...` +//│ Nil --match--> `if ms is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun filter(ls, f) = if ls is + h :: t and + f(h) then h :: filter(t, f) + else filter(t, f) + Nil then Nil +fun last(ls_last) = + fun go(a, ls_go) = if ls_go is + Nil then a + h :: t then go(h, t) + if ls_last is + h :: t then Some(go(h, t)) + Nil then None +fun lastFilter(ls, f) = last(filter(ls, f)) +lastFilter(id(1 :: 2 :: 3 :: 4 :: 5 :: Nil), x => (x % 2 == 0)) +//│ = Some(4) +//│ No fusion opportunity + + diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 650e6fdc0c..272eaa411d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -515,3 +515,16 @@ f(g(AA(1))) //│ AA --match--> `if x is ...` //│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun f(a, b) = if a is + A and b is B then 1 + else 0 +f(A, B) +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 1 +//│ 2 fusion opportunities: +//│ A --match--> `if a is ...` +//│ B --match--> `if b is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 2efe8c6186ea19d0b2c445985a389b59d1093b82 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:27:03 +0800 Subject: [PATCH 177/303] fix: match block now correctly contain the scrut as a f.v. (if it is indeed a f.v.) --- .../scala/hkmc2/codegen/Deforestation.scala | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e50feeec02..6a3923d458 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -168,8 +168,9 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: // and this is handled by freeVarsOfNonTransformedMatches class DeforestationFreeVarTraverser(using alwaysDefined: Set[Symbol], - selsToBeReplaced: Map[ResultId, Symbol] = Map.empty, - selsReplacementByCurrentMatch: Iterable[Symbol], + selsToBeReplaced: Map[ResultId, Symbol], + selsReplacementByCurrentMatch: Map[ResultId, Symbol], + currentMatchScrut: Symbol, dt: DeforestTransformer ) extends FreeVarTraverser(alwaysDefined): override def applyBlock(b: Block): Unit = b match @@ -185,13 +186,16 @@ class DeforestationFreeVarTraverser(using // free vars in nested-matches reported by freeVarsOfNonTransformedMatches may also contain // spurious ones: those that are going to be substitued by the current match, // and those that are in the ctx - result --= selsReplacementByCurrentMatch + result --= selsReplacementByCurrentMatch.values result --= ctx case _ => super.applyBlock(b) override def applyPath(p: Path): Unit = p match - case p @ Select(qual, name) => - selsToBeReplaced.get(p.uid).fold(super.applyPath(p))(s => result += s) + case p @ Select(qual, name) => selsToBeReplaced.get(p.uid) match + case None => qual match + case Value.Ref(l) if l == currentMatchScrut => () + case _ => super.applyPath(p) + case Some(s) => result += s case _ => super.applyPath(p) class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserShallow: @@ -821,8 +825,7 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys - val traverser = DeforestationFreeVarTraverser(using nonFreeVars + l, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId).values) - traverser.applyPath(m.scrut) + val traverser = DeforestationFreeVarTraverser(using nonFreeVars, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId), l) (arms.map(_._2) ++ dflt).foreach: a => // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. From 6ade5c3f3a76498cc337d824212cb2fd8185906a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:52:16 +0800 Subject: [PATCH 178/303] update fix --- .../scala/hkmc2/codegen/Deforestation.scala | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6a3923d458..533ff3c6e4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -166,7 +166,7 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: // - the free vars caused by the substitution of selections of scrutinees of their parent matches // Otherwise, additional free vars come from fusing matches that are contained in the block, // and this is handled by freeVarsOfNonTransformedMatches -class DeforestationFreeVarTraverser(using +class DeforestationFreeVarTraverserForMatch( alwaysDefined: Set[Symbol], selsToBeReplaced: Map[ResultId, Symbol], selsReplacementByCurrentMatch: Map[ResultId, Symbol], @@ -193,10 +193,27 @@ class DeforestationFreeVarTraverser(using override def applyPath(p: Path): Unit = p match case p @ Select(qual, name) => selsToBeReplaced.get(p.uid) match case None => qual match + // if it is the scrut of current match and the computation containing + // this selection is moved, then the selection will be replaced and there will be no free vars case Value.Ref(l) if l == currentMatchScrut => () case _ => super.applyPath(p) case Some(s) => result += s case _ => super.applyPath(p) + + override def analyze(m: Block): List[Symbol] = + require(m.isInstanceOf[Match]) + val matchExpr@Match(scrut@Value.Ref(l), arms, dflt, rest) = m + val parentMatchRest = dt.allParentMatches(scrut.uid).foldRight[Block](End("")): (p, acc) => + Begin(dt.d.matchScrutToMatchBlock(p).rest, acc) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + // Also take care of the `rest`s of its parent match blocks. + val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened + applyBlock(realArm) + + result.toList.sortBy(_.uid) + class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserShallow: var flag = false @@ -779,8 +796,8 @@ class Deforest(using TL, Raise, Elaborator.State): val newDefsArms = deforestTransformer.matchArms.getAllFunDefs newDefsArms(newDefsRest(rest)) -class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): - given DeforestTransformer = this +class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): + self => given nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet val replaceSelInfo: Map[ResultId, Symbol] = @@ -817,23 +834,15 @@ class DeforestTransformer(using d: Deforest, elabState: Elaborator.State) extend scrutExprId, locally: assert(m.scrut.uid === scrutExprId) - val matchExpr@Match(Value.Ref(l), arms, dflt, rest) = m - - val parentMatchRest = allParentMatches(m.scrut.uid).foldRight[Block](End("")): (p, acc) => - Begin(d.matchScrutToMatchBlock(p).rest, acc) - - + val Match(Value.Ref(l), _, _, _) = m val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys - - val traverser = DeforestationFreeVarTraverser(using nonFreeVars, selReplacementNotForThisSel, toBeReplacedForAllBranches(scrutExprId), l) - (arms.map(_._2) ++ dflt).foreach: a => - // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. - // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - // Also take care of the `rest`s of its parent match blocks. - val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened - traverser.applyBlock(realArm) - - traverser.result.toList.sortBy(_.uid) + DeforestationFreeVarTraverserForMatch( + nonFreeVars, + selReplacementNotForThisSel, + toBeReplacedForAllBranches(scrutExprId), + l, + self + ).analyze(m) ) object matchArms: From 8f7639de09402adecf3fd3154ac4a08a87be90a3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:03:36 +0800 Subject: [PATCH 179/303] fix nested definitions using `definedVar`; more tests --- .../scala/hkmc2/codegen/Deforestation.scala | 27 +++--- .../mlscript/deforest/listComprehension.mls | 96 +++++++++++++++++++ .../src/test/mlscript/deforest/todos.mls | 9 ++ 3 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 533ff3c6e4..35aa7a9da1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -145,7 +145,12 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: case _ => super.applyBlock(b) override def applyValue(v: Value): Unit = v match - case Value.Ref(l) => if !ctx.contains(l) then result += l + case Value.Ref(l) => l match + // builtin symbols and toplevel symbols are always in scope + case _: (BuiltinSymbol | TopLevelSymbol) => () + // NOTE: assume all class definitions are in the toplevel + case b: BlockMemberSymbol if b.asClsLike.isDefined => () + case _ => if !ctx.contains(l) then result += l case _ => super.applyValue(v) override def applyLam(l: Value.Lam): Unit = @@ -251,7 +256,7 @@ extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) - def sortedFvsForTransformedBlocks(using alwaysDefined: Set[Symbol]) = + def sortedFvsForTransformedBlocks(alwaysDefined: Set[Symbol]) = FreeVarTraverser(alwaysDefined).analyze(b) def hasExplicitRet: Boolean = @@ -321,14 +326,7 @@ class Deforest(using TL, Raise, Elaborator.State): def apply(s: Symbol) = store.contains(s) - def init(b: Block) = - (new BlockTraverser: - override def applySymbol(sym: Symbol): Unit = sym match - case _: TopLevelSymbol => store += sym - case _: BlockMemberSymbol => store += sym - case _: BuiltinSymbol => store += sym - case _ => () - ).applyBlock(b) + def init(b: Block) = store ++= b.definedVars var constraints: Ls[ProdStrat -> ConsStrat] = Nil @@ -357,6 +355,7 @@ class Deforest(using TL, Raise, Elaborator.State): super.applyFunDefn(fun) FreshVarForAllVars.applyBlock(p) + // `NoProd` to block fusion for those functions that are imported from elsewhere usedFunSym.diff(funSymsWithDefn).foreach: funSymsWithoutDefn => constrain(NoProd, store(funSymsWithoutDefn).asConsStrat) @@ -798,7 +797,7 @@ class Deforest(using TL, Raise, Elaborator.State): class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): self => - given nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet + val nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet val replaceSelInfo: Map[ResultId, Symbol] = d.filteredCtorDests.values.flatMap { @@ -874,7 +873,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex Return( Call( Value.Ref(f), - rewrittenRest.sortedFvsForTransformedBlocks.map(a => Arg(false, Value.Ref(a))))(true, false), + rewrittenRest.sortedFvsForTransformedBlocks(nonFreeVars).map(a => Arg(false, Value.Ref(a))))(true, false), false ) ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: @@ -975,7 +974,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex // and the transformed `rest` is b case bd -> (Some(s), b) => Begin( applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) + Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) nonFlatten.flattened @@ -988,7 +987,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex else // build a new function and update the store val scrutName = s.getResult.asInstanceOf[Value.Ref].l.nme val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(using nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap + val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap val newFunDef = FunDefn( N, sym, diff --git a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls new file mode 100644 index 0000000000..e22cabd87f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls @@ -0,0 +1,96 @@ +:js +:deforest + +//│ No fusion opportunity +object Nil +data class (::) Cons(h, t) +data class Pair(a, b) +object A +object B + +fun zip(xs_zip, ys_zip) = if + xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) + else Nil +fun enumFromTo(a, b) = if a < (b + 1) then a :: enumFromTo(a + 1, b) else Nil +fun test() = + fun lscomp1(ls) = if ls is + Pair(x, y1) :: t then + fun lscomp2(ls2) = if ls2 is + Pair(y2, z) :: t2 and + y1 == y2 then Pair(x, z) :: lscomp2(t2) + else lscomp2(t2) + else lscomp1(t) + lscomp2(zip(enumFromTo(x, x + 2), enumFromTo(y1, y1 + 1))) + else Nil + lscomp1(zip(enumFromTo(1, 3), enumFromTo(2, 4))) +test() +//│ = Cons(Pair(1, 3), Cons(Pair(2, 4), Cons(Pair(3, 5), Nil))) +//│ No fusion opportunity + + +fun append(xs, ys) = if xs is + h :: t then h :: append(t, ys) + Nil then ys +fun concatMap(f, ls) = if ls is + h :: t then append(f(h), concatMap(f, t)) + Nil then Nil +fun test() = + fun f1(a1) = if a1 is + Pair(a, b) then + fun f2(a2) = if a2 is Pair(c, d) then Pair(a, c) :: Nil else Nil + concatMap(f2, Pair(1, 3) :: Pair(2, 3) :: Pair(a, b) :: Nil) + else Nil + concatMap(f1, Pair(5, 10) :: Nil) +test() +//│ = Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) +//│ 6 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +fun lscomp1(ls1, ls2) = if ls1 is + h1 :: t1 then lscomp2(ls2, h1, t1) + Nil then Nil +fun lscomp2(ls2, h1, t1) = if ls2 is + h2 :: t2 then Pair(h1, h2) :: lscomp2(t2, h1, t1) + Nil then lscomp1(t1, ls2) +lscomp1(1 :: 2 :: Nil, 3 :: 4 :: Nil) +//│ = Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) +//│ 6 fusion opportunities: +//│ Cons --match--> `if ls1 is ...` +//│ Cons --match--> `if ls1 is ...` +//│ Cons --match--> `if ls2 is ...` +//│ Cons --match--> `if ls2 is ...` +//│ Nil --match--> `if ls1 is ...` +//│ Nil --match--> `if ls2 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun test(k, ls) = + fun lscomp(ls) = if ls is + h :: t then (h + 1) :: lscomp(t) + Nil then Nil + if k is + A then lscomp(ls) + B then Nil +test(A, 1 :: 2 :: 3 :: Nil) +//│ = Cons(2, Cons(3, Cons(4, Nil))) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(2, Cons(3, Cons(4, Nil))) +//│ 5 fusion opportunities: +//│ A --match--> `if k is ...` +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index c5b6f1c011..e327ae5a11 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -213,3 +213,12 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +// TODO: currently nothing is done at all when seeing +// `Define` blocks defining things other than functions +data class Global(x) +fun test() = + data class Local(x) + if Global(1) is + Global(x) then Local(x + 1) +test() +//│ = Local(2) From 2519a808b00bed7e25a508c2e14e8088df0041b4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 10 Apr 2025 22:43:05 +0800 Subject: [PATCH 180/303] style: use `locally:` --- .../scala/hkmc2/codegen/Deforestation.scala | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 35aa7a9da1..88926157a0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -735,44 +735,45 @@ class Deforest(using TL, Raise, Elaborator.State): else if dtors.size == 1 then val currentCtorCls = getClsSymOfUid(ctor) val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - handledMatches.getOrElseUpdate(scrutRef.uid -> currentCtorCls, { - - if sels.forall{ s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) - case _ => false - } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] - - dtors.head._2.arms.foreach: - case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => - val c = cOrMod.asCls.get - // if this arm is used more than once, should be var symbol because the arm body will be - // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } - - selsInArms.foreach: fs => - assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(fs.field): - case Some(v) => Some(v) - case None => Some(if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) - else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) - val sym = fieldNameToSymToBeReplaced(fs.field) + handledMatches.getOrElseUpdate( + scrutRef.uid -> currentCtorCls, + locally: + if sels.forall{ s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) + case _ => false + } then + val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] + + dtors.head._2.arms.foreach: + case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => + val c = cOrMod.asCls.get + // if this arm is used more than once, should be var symbol because the arm body will be + // extracted to a function, otherwise just temp symbol + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } - selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) - case _ => () - Some(CtorFinalDest.Match( - dtors.head._1, - dtors.head._2, - sels.map(_.expr), - fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )) - else - throw Error("more than one consumer") - None - }) + selsInArms.foreach: fs => + assert(getClsFields(c).map(_.id).contains(fs.field)) + fieldNameToSymToBeReplaced.updateWith(fs.field): + case Some(v) => Some(v) + case None => Some(if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) + else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) + val sym = fieldNameToSymToBeReplaced(fs.field) + + selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) + case _ => () + Some(CtorFinalDest.Match( + dtors.head._1, + dtors.head._2, + sels.map(_.expr), + fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap + )) + else + throw Error("more than one consumer") + None + ) else ??? } res.updateWith(ctor){_ => filteredDtor} From 7e835e1802858d4321a7c7d2efb5eb434f8b1eaf Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:02:39 +0800 Subject: [PATCH 181/303] trailing whitespaces --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 88926157a0..670c09d9ad 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -233,7 +233,7 @@ class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserSh case _ => super.applyBlock(b) def analyze(b: Block): Bool = applyBlock(b) - flag + flag class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): @@ -899,7 +899,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex funSym, ParamList( ParamListFlags.empty, - freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList + freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N)).toList ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index dde71ee29c..d5b4c54828 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -297,4 +297,3 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(deforestStat) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") - \ No newline at end of file From dbd9173e2b10166435a7fef24d92ad8b1a86a81d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 11 Apr 2025 16:58:43 +0800 Subject: [PATCH 182/303] remove useless `locally`; add reified tests --- .../scala/hkmc2/codegen/Deforestation.scala | 69 +++++++++---------- .../src/test/mlscript/deforest/append.mls | 18 ++++- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 670c09d9ad..9d49ef23b2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -737,42 +737,41 @@ class Deforest(using TL, Raise, Elaborator.State): val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult handledMatches.getOrElseUpdate( scrutRef.uid -> currentCtorCls, - locally: - if sels.forall{ s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) - case _ => false - } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] - - dtors.head._2.arms.foreach: - case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => - val c = cOrMod.asCls.get - // if this arm is used more than once, should be var symbol because the arm body will be - // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } + if sels.forall{ s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) + case _ => false + } then + val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] + + dtors.head._2.arms.foreach: + case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => + val c = cOrMod.asCls.get + // if this arm is used more than once, should be var symbol because the arm body will be + // extracted to a function, otherwise just temp symbol + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } + + selsInArms.foreach: fs => + assert(getClsFields(c).map(_.id).contains(fs.field)) + fieldNameToSymToBeReplaced.updateWith(fs.field): + case Some(v) => Some(v) + case None => Some(if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) + else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) + val sym = fieldNameToSymToBeReplaced(fs.field) - selsInArms.foreach: fs => - assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(fs.field): - case Some(v) => Some(v) - case None => Some(if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) - else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) - val sym = fieldNameToSymToBeReplaced(fs.field) - - selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) - case _ => () - Some(CtorFinalDest.Match( - dtors.head._1, - dtors.head._2, - sels.map(_.expr), - fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )) - else - throw Error("more than one consumer") - None + selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) + case _ => () + Some(CtorFinalDest.Match( + dtors.head._1, + dtors.head._2, + sels.map(_.expr), + fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap + )) + else + throw Error("more than one consumer") + None ) else ??? } diff --git a/hkmc2/shared/src/test/mlscript/deforest/append.mls b/hkmc2/shared/src/test/mlscript/deforest/append.mls index e5716d154b..7f1effc580 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/append.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/append.mls @@ -24,7 +24,23 @@ appendThree of //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) //│ No fusion opportunity - +// maybe the fusion target for the previous program +fun appendReified(ys, zs) = if ys is + h :: t then h :: appendReified(t, zs) + Nil then zs +fun append1(ys, zs) = ys(zs) +fun append2(xs, ys) = + if xs is + h :: t then + zs => h :: append1(append2(t, ys), zs) // normal fusion + Nil then zs => appendReified(ys, zs) // reified +fun test(xs, ys, zs) = append1(append2(xs, ys), zs) +test of + id(1 :: 2 :: Nil) + id(3 :: 4 :: Nil) + id(5 :: 6 :: Nil) +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ No fusion opportunity fun idList(l) = if l is From 15d1524cf005c69142a2ec7fae66b97c85e91a51 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 15 Apr 2025 17:58:28 +0800 Subject: [PATCH 183/303] minor --- .../scala/hkmc2/codegen/Deforestation.scala | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 9d49ef23b2..5e6b67411a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -290,7 +290,7 @@ class Deforest(using TL, Raise, Elaborator.State): tl.log("-----------------------------------------") ctorDests.ctorDests.foreach: - case (ctorExprId, CtorDest(matches, sels, noCons)) => + case (ctorExprId, CtorDest(matches, sels, noCons)) => tl.log: val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" val matchExprScruts = "if " + matches.map{(s, m) => m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" @@ -298,7 +298,7 @@ class Deforest(using TL, Raise, Elaborator.State): val selExpr = sels.map{ case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" }.toList.sorted.mkString(" | ") - tl.log(s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons") + s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" tl.log("-----------------------------------------") dtorSources.dtorSources.foreach: case (d, DtorSource(ctors, noProd)) => @@ -1035,48 +1035,44 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex override def applyResult2(r: Result)(k: Result => Block): Block = r match case call@Call(f, args) if d.filteredCtorDests.isDefinedAt(call.uid) => - def handleCtorCall(c: ClassSymbol) = - d.filteredCtorDests.get(call.uid).get match - case CtorFinalDest.Match(scrut, expr, sels, selsMap) => - val body = expr.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === c }.map(_._2).orElse(expr.dflt).get - - // use pre-determined symbols, create temp symbols for un-used fields - val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 - val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => - f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"_deforest_${c.name}_${f.id.name}_unused")) - - // if all vars are temp vars, no need to create more temp vars - // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) - val assignedTempSyms = - if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then - allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) - else - allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match - case ts: TempSymbol => id -> ts - case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") - } - - val newArgs = args.map(_ => TempSymbol(N)) - - val bodyAndRestInLam = matchArms.getOrElseUpdate( - scrut, - expr, - c, - sels.toSet, - assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, - selsMap._1 -> selsMap._2) - - args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): - case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } - - case CtorFinalDest.Sel(s) => - val selFieldName = s.getResult match { case Select(p, nme) => nme } - val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) - k(args(idx).value) - f match - case s: Select => handleCtorCall(s.symbol.get.asCls.get) - case Value.Ref(l) => handleCtorCall(l.asCls.get) + val c = f match + case s: Select => s.symbol.get.asCls.get + case Value.Ref(l) => l.asCls.get case _ => ??? + d.filteredCtorDests.get(call.uid).get match + case CtorFinalDest.Match(scrut, expr, sels, selsMap) => + // use pre-determined symbols, create temp symbols for un-used fields + val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 + val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => + f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"_deforest_${c.name}_${f.id.name}_unused")) + + // if all vars are temp vars, no need to create more temp vars + // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) + val assignedTempSyms = + if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then + allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) + else + allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match + case ts: TempSymbol => id -> ts + case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") + } + + val bodyAndRestInLam = matchArms.getOrElseUpdate( + scrut, + expr, + c, + sels.toSet, + assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, + selsMap._1 -> selsMap._2) + + args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): + case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } + + case CtorFinalDest.Sel(s) => + val selFieldName = s.getResult match { case Select(p, nme) => nme } + val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) + k(args(idx).value) + case _ => super.applyResult2(r)(k) def handleObjFusing(objCallExprUid: CtorExpr, objClsSym: ModuleSymbol) = From 51485b3276b7b3da9b49cbbaf2887049630e0c93 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:41:24 +0800 Subject: [PATCH 184/303] fix `Deforetation.scala` --- .../scala/hkmc2/codegen/Deforestation.scala | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5e6b67411a..b348a9a3c7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -4,7 +4,7 @@ package codegen import semantics.* import semantics.Elaborator.State import syntax.{Literal, Tree} -import utils.{TL, tl, SymbolSubst} +import utils.* import mlscript.utils.*, shorthands.* import scala.collection.mutable import scala.collection.mutable.LinkedHashMap @@ -61,14 +61,16 @@ case object NoProd extends ProdStrat -case class Dtor(scrut: ResultId)(val expr: Match, val outterMatch: Option[ResultId])(using d: Deforest) extends ConsStrat: - assert(scrut === expr.scrut.uid) - d.matchScrutToMatchBlock.updateWith(scrut): +class Dtor(val expr: Match, val outterMatch: Option[ResultId])(using d: Deforest) extends ConsStrat: + d.matchScrutToMatchBlock.updateWith(expr.scrut.uid): case None => Some(expr) - case Some(exist) => ??? // should only update once - d.matchScrutToParentMatchScrut.updateWith(scrut): + case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") + d.matchScrutToParentMatchScrut.updateWith(expr.scrut.uid): case None => Some(outterMatch) - case Some(_) => ??? // should only update once + case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") +object Dtor: + def unapply(d: Dtor): Opt[ResultId] = S(d.expr.scrut.uid) + case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: LinkedHashMap[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat @@ -235,8 +237,7 @@ class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserSh applyBlock(b) flag -class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends - BlockTransformer(new SymbolSubst()): +class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): override def applyValue(v: Value): Value = v match case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) case _ => super.applyValue(v) @@ -383,7 +384,7 @@ class Deforest(using TL, Raise, Elaborator.State): ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(scrut.uid)(m, matching.lastOption.map(_._1))(using this)) + constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1))(using this)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: case (Case.Cls(s, _), body) => @@ -415,13 +416,11 @@ class Deforest(using TL, Raise, Elaborator.State): case FunDefn(_, sym, params, body) => val funSymStratVar = symToStrat(sym) val param = params.head match - case ParamList(flags, params, restParam) => params - val funStrat = constrFun(param, body) // TODO: handle mutiple param list + case ParamList(flags, params, N) => params // TODO: handle mutiple param list + val funStrat = constrFun(param, body) constrain(funStrat, funSymStratVar.asConsStrat) funSymStratVar case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") - // only handle code inside module for now to show the - // todo case of if scrut being not the same as what the user writes case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") processBlock(rest) case End(msg) => NoProd From 104852976ca4fc4acb10003e8731dc225672abd0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:52:26 +0800 Subject: [PATCH 185/303] deduplicate logic in `JSBackendDiffMaker.scala` --- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 211 ++++++++---------- 1 file changed, 90 insertions(+), 121 deletions(-) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index d5b4c54828..43254d1e90 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -120,43 +120,40 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsb = ltl.givenIn: new JSBuilder with JSBuilderArgNumSanityChecks - val resSym = new TempSymbol(S(blk), "block$res") - val lowered0 = low.program(blk) - val le = lowered0.copy(main = lowered0.main.mapTail: - case e: End => - Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) - case Return(res, implct) => - assert(implct) - Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) - case tl: (Throw | Break | Continue) => tl - ) - if showLoweredTree.isSet then - output(s"Lowered:") - output(le.showAsTree) - // * We used to do this to avoid needlessly generating new variable names in separate blocks: - // val nestedScp = baseScp.nest - val nestedScp = baseScp - // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed + def getResSymAndResNme(n: Str) = + val resSym = new TempSymbol(S(blk), n) + resSym -> baseScp.allocateName(resSym) - val resNme = nestedScp.allocateName(resSym) + def assignResultSymForBlock(lowered: Program, resSym: TempSymbol) = + lowered.copy(main = lowered.main.mapTail: + case e: End => + Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) + case Return(res, implct) => + assert(implct) + Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) + case tl: (Throw | Break | Continue) => tl + ) - if ppLoweredTree.isSet then - output(s"Pretty Lowered:") - output(Printer.mkDocument(le)(using summon[Raise], nestedScp).toString) + def mkJS(le: Program) = + val (pre, js) = baseScp.givenIn: + jsb.worksheet(le) + val preStr = pre.stripBreaks.mkString(100) + val jsStr = js.stripBreaks.mkString(100) + if showSanitizedJS.isSet then + output(s"JS:") + output(jsStr) + preStr -> jsStr - val (pre, js) = nestedScp.givenIn: - jsb.worksheet(le) - val preStr = pre.stripBreaks.mkString(100) - val jsStr = js.stripBreaks.mkString(100) - if showSanitizedJS.isSet then - output(s"JS:") - output(jsStr) - def mkQuery(preStr: Str, jsStr: Str)(k: Str => Unit) = + def mkQuery(preStr: Str, jsStr: Str)(handleResult: Iterable[Str] => Unit) = val queryStr = jsStr.replaceAll("\n", " ") val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && fixme.isUnset && todo.isUnset) reply match - case ReplHost.Result(content) => k(content) + case ReplHost.Result(content) => + val res :+ end = content.splitSane('\n') : @unchecked + // TODO: seems that not all programs end with "undefined" now + // assert(end == "undefined") + handleResult(res) case ReplHost.Empty => case ReplHost.Unexecuted(message) => ??? case ReplHost.Error(isSyntaxError, message, otherOutputs) => @@ -174,20 +171,60 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: source = Diagnostic.Source.Runtime)) if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") - if traceJS.isSet then - host.execute( - "globalThis.Predef.TraceLogger.enabled = true; " + - "globalThis.Predef.TraceLogger.resetIndent(0)") + def executeJS(preStr: Str, jsStr: Str, resNme: Str) = + if traceJS.isSet then + host.execute( + "globalThis.Predef.TraceLogger.enabled = true; " + + "globalThis.Predef.TraceLogger.resetIndent(0)") + + // * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first + host.execute(s"$resNme = undefined") + + mkQuery(preStr, jsStr): stdout => + stdout.foreach: line => + output(s"> ${line}") + if traceJS.isSet then + host.execute("globalThis.Predef.TraceLogger.enabled = false") - // * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first - host.execute(s"$resNme = undefined") + def handleDefinedValues(nme: Str, sym: Symbol, expect: Opt[Str])(handleResult: Str => Unit) = + val le = + import codegen.* + Return( + Call( + Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), + Arg(false, Value.Ref(sym)) :: Nil)(true, false), + implct = true) + val je = baseScp.givenIn: + jsb.block(le, endSemi = false) + val jsStr = je.stripBreaks.mkString(100) + mkQuery("", jsStr): out => + val result = out.mkString + expect match + case S(expected) if result =/= expected => raise: + ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, + source = Diagnostic.Source.Runtime) + case _ => () + val anon = nme.isEmpty + handleResult(result) + result match + case "undefined" if anon => + case "()" if anon => + case _ => + output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") - mkQuery(preStr, jsStr): stdout => - stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) - .foreach: line => - output(s"> ${line}") - if traceJS.isSet then - host.execute("globalThis.Predef.TraceLogger.enabled = false") + val lowered0 = low.program(blk) + val resSym -> resNme = getResSymAndResNme("block$res") + val le = assignResultSymForBlock(lowered0, resSym) + if showLoweredTree.isSet then + output(s"Lowered:") + output(le.showAsTree) + + if ppLoweredTree.isSet then + output(s"Pretty Lowered:") + output(Printer.mkDocument(le)(using summon[Raise], baseScp).toString) + + val (preStr, jsStr) = mkJS(le) + executeJS(preStr, jsStr, resNme) if silent.isUnset then import Elaborator.Ctx.* @@ -201,31 +238,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case _ => N case _ => N val valuesToPrint = ("", resSym, expect.get) +: definedValues.toSeq.sortBy(_._1) - valuesToPrint.foreach: (nme, sym, expect) => - val le = - import codegen.* - Return( - Call( - Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), - Arg(false, Value.Ref(sym)) :: Nil)(true, false), - implct = true) - val je = nestedScp.givenIn: - jsb.block(le, endSemi = false) - val jsStr = je.stripBreaks.mkString(100) - mkQuery("", jsStr): out => - val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) - expect match - case S(expected) if result =/= expected => raise: - ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, - source = Diagnostic.Source.Runtime) - case _ => () - val anon = nme.isEmpty - if sym === resSym then correctResult = S(result) - result match - case "undefined" if anon => - case "()" if anon => - case _ => - output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") + valuesToPrint.foreach: (nme, sym, expected) => + handleDefinedValues(nme, sym, expected)(if sym === resSym then r => correctResult = S(r) else _ => ()) if deforestFlag.isSet then val deforestLow = ltl.givenIn: @@ -238,62 +252,17 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case Some(_) if num == 0 => output("No fusion opportunity") case Some(deforestRes) => output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") - val resSym = new TempSymbol(S(blk), "block$res_deforest") - val resNme = nestedScp.allocateName(resSym) - val le = deforestRes.copy(main = deforestRes.main.mapTail: - case e: End => - Assign(resSym, Value.Lit(syntax.Tree.UnitLit(false)), e) - case Return(res, implct) => - assert(implct) - Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) - case tl: (Throw | Break | Continue) => tl - ) - val (pre, js) = nestedScp.givenIn: - jsb.worksheet(le) - val preStr = pre.stripBreaks.mkString(100) - val jsStr = js.stripBreaks.mkString(100) - if showSanitizedJS.isSet then - output(s"JS:") - output(jsStr) - - host.execute(s"$resNme = undefined") - - mkQuery(preStr, jsStr): stdout => - stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) - .foreach: line => - output(s"> ${line}") + val resSym -> resNme = getResSymAndResNme("block$res_deforest") + val le = assignResultSymForBlock(deforestRes, resSym) + val (preStr, jsStr) = mkJS(le) + executeJS(preStr, jsStr, resNme) if silent.isUnset then - import Elaborator.Ctx.* - val valuesToPrint = ("", resSym, expect.get) :: Nil - valuesToPrint.foreach: (nme, sym, expect) => - val le = - import codegen.* - Return( - Call( - Value.Ref(Elaborator.State.globalThisSymbol).selSN("Predef").selSN("printRaw"), - Arg(false, Value.Ref(sym)) :: Nil)(true, false), - implct = true) - val je = nestedScp.givenIn: - jsb.block(le, endSemi = false) - val jsStr = je.stripBreaks.mkString(100) - mkQuery("", jsStr): out => - val result = out.splitSane('\n').init.mkString // should always ends with "undefined" (TODO: check) - expect match - case S(expected) if result =/= expected => raise: - ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, - source = Diagnostic.Source.Runtime) - case _ => () - val anon = nme.isEmpty - if sym === resSym && correctResult.fold(false)(_ != result) then raise: - ErrorReport( - msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, - source = Diagnostic.Source.Runtime) - result match - case "undefined" if anon => - case "()" if anon => - case _ => - output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}") + handleDefinedValues("", resSym, expect.get): result => + if correctResult.fold(false)(_ != result) then raise: + ErrorReport( + msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, + source = Diagnostic.Source.Runtime) output(deforestStat) output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") From d7ce214143e0a239b7ea25c362d5767edd9a5f54 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:13:33 +0800 Subject: [PATCH 186/303] use identity for ResultId and remove global state --- .../src/main/scala/hkmc2/codegen/Block.scala | 23 +++++++++++-------- .../scala/hkmc2/codegen/Deforestation.scala | 23 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 1da17cbb76..7844ca436f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -406,15 +406,14 @@ enum Case: sealed trait TrivialResult extends Result -type ResultId = Uid[Result] -object ResultUidHandler extends Uid.Handler[Result] -object ResultUid extends ResultUidHandler.State: - val uidToResult = collection.mutable.Map.empty[ResultId, Result] - def apply(id: ResultId) = uidToResult(id) +object Result: + opaque type ResultId = Int + given Ordering[ResultId] with + def compare(x: ResultId, y: ResultId): Int = x.compare(y) + private def ResultId(v: Int): ResultId = v - sealed abstract class Result extends AutoLocated: protected def children: List[Located] = this match @@ -468,10 +467,14 @@ sealed abstract class Result extends AutoLocated: case DynSelect(qual, fld, arrayIdx) => qual.freeVarsLLIR ++ fld.freeVarsLLIR case Value.Rcd(args) => args.flatMap(arg => arg.idx.fold(Set.empty)(_.freeVarsLLIR) ++ arg.value.freeVarsLLIR).toSet - lazy val uid = - val id = ResultUid.nextUid - ResultUid.uidToResult.addOne(id -> this) - id + // for deforestation + import Result.* + lazy val uidValue: ResultId = ResultId(System.identityHashCode(this)) + def uid(using d: Deforest) = + d.resultIdToResult.updateWith(this.uidValue): + case N => S(this) + case S(r) => assert(this is r); S(this) + uidValue // type Local = LocalSymbol type Local = Symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b348a9a3c7..83c1ab5d66 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -8,6 +8,7 @@ import utils.* import mlscript.utils.*, shorthands.* import scala.collection.mutable import scala.collection.mutable.LinkedHashMap +import Result.ResultId type StratVar type StratVarId = Uid[StratVar] @@ -38,8 +39,8 @@ object StratVarState: type CtorExpr = ResultId extension (i: ResultId) - def getResult = ResultUid(i) - def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A) = + def getResult(using d: Deforest) = d.resultIdToResult(i) + def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A)(using Deforest) = i.getResult match case Call(fun, args) => fun match case s: Select if s.symbol.flatMap(_.asCls).isDefined => @@ -52,7 +53,7 @@ extension (i: ResultId) case v: Value.Ref if v.l.asObj.isDefined => Some(k(i, v, v.l.asObj.get, Nil)) case _ => None - def getClsSymOfUid = i.handleCtorIds((_, _, s, _) => s).get + def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: CtorExpr) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat @@ -69,7 +70,7 @@ class Dtor(val expr: Match, val outterMatch: Option[ResultId])(using d: Deforest case None => Some(outterMatch) case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") object Dtor: - def unapply(d: Dtor): Opt[ResultId] = S(d.expr.scrut.uid) + def unapply(d: Dtor)(using Deforest): Opt[ResultId] = S(d.expr.scrut.uid) case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: LinkedHashMap[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait @@ -179,7 +180,8 @@ class DeforestationFreeVarTraverserForMatch( selsReplacementByCurrentMatch: Map[ResultId, Symbol], currentMatchScrut: Symbol, dt: DeforestTransformer -) extends FreeVarTraverser(alwaysDefined): +) extends FreeVarTraverser(alwaysDefined): + given Deforest = dt.d override def applyBlock(b: Block): Unit = b match // a nested match case m@Match(scrut, arms, dflt, rest) => @@ -270,8 +272,12 @@ class Deforest(using TL, Raise, Elaborator.State): object StratVarUidHandler extends Uid.Handler[StratVar]() given Uid.Handler[StratVar]#State = StratVarUidHandler.State() + given Deforest = this import StratVarState.freshVar + + val resultIdToResult = mutable.Map.empty[ResultId, Result] + def apply(p: Program): Opt[Program] -> String -> Int = val mainBlk = p.main @@ -311,7 +317,7 @@ class Deforest(using TL, Raise, Elaborator.State): val fusionStat = filteredCtorDests.map: case (ctorUid, CtorFinalDest.Sel(s)) => - "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${ResultUid(s).asInstanceOf[Select].name}`" + "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${resultIdToResult(s).asInstanceOf[Select].name}`" case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" @@ -384,7 +390,7 @@ class Deforest(using TL, Raise, Elaborator.State): ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1))(using this)) + constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1))) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: case (Case.Cls(s, _), body) => @@ -447,7 +453,6 @@ class Deforest(using TL, Raise, Elaborator.State): case c@Call(f, args) => val argsTpe = args.map: case Arg(false, value) => processResult(value) - f match case s@Select(p, nme) => s.symbol.map(_.asCls) match @@ -788,7 +793,7 @@ class Deforest(using TL, Raise, Elaborator.State): }.toSet def rewrite(p: Block) = - val deforestTransformer = DeforestTransformer(using this) + val deforestTransformer = DeforestTransformer() val rest = deforestTransformer.applyBlock(p) val newDefsRest = deforestTransformer.matchRest.getAllFunDefs val newDefsArms = deforestTransformer.matchArms.getAllFunDefs From 48467a786bfbd0fbff88967a9aa85c4586fd5a97 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:20:17 +0800 Subject: [PATCH 187/303] remove awkward type projection --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 83c1ab5d66..2beeaa0efc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -26,9 +26,9 @@ class StratVarState(val uid: StratVarId, val name: Str = ""): override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" +object StratVarUidHandler extends Uid.Handler[StratVar] object StratVarState: - - def freshVar(nme: String = "")(using vuid: Uid.Handler[StratVar]#State) = + def freshVar(nme: String = "")(using vuid: StratVarUidHandler.State) = val newId = vuid.nextUid val s = StratVarState(newId, nme) val p = s.asProdStrat @@ -270,8 +270,7 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): - object StratVarUidHandler extends Uid.Handler[StratVar]() - given Uid.Handler[StratVar]#State = StratVarUidHandler.State() + given StratVarUidHandler.State = StratVarUidHandler.State() given Deforest = this import StratVarState.freshVar From cadb80b19bae57a63de0bae0617ae128fbb7db24 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:38:23 +0800 Subject: [PATCH 188/303] better documentation on the todo about ctor as function; properly block the fusion when this happens --- .../scala/hkmc2/codegen/Deforestation.scala | 9 ++++++++- .../src/test/mlscript/deforest/simple.mls | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 2beeaa0efc..996442e9bd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -366,11 +366,18 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(NoProd, store(funSymsWithoutDefn).asConsStrat) - // TODO: ctor as a function? def getStratOfSym(s: Symbol) = s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd + // TODO: cannot fuse intermediate values created by + // calling data constructors passed around like functions, + // like `fun app(ctor) = ctor(1); if app(AA) is AA(x) then x`; + // immediate data constructor calls are handled directly, + // so if this method is called on a ClsLike symbol, + // it means that this constructor is passed around like a function, + // which we can't fuse for now + case _ if s.asCls.isDefined => NoProd case _: BlockMemberSymbol => store(s) case _: LocalSymbol => store(s) def +=(e: Symbol -> ProdVar) = store += e diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index 272eaa411d..f37849adaf 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -528,3 +528,23 @@ f(A, B) //│ A --match--> `if a is ...` //│ B --match--> `if b is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// cannot fuse intermediate values created by +// calling data constructors passed around like functions, +// so `c1` is not fused. +fun app(f) = f(1) +fun identity(x) = x +fun c1(x) = if x is + AA(i) then i +fun c2(x) = if x is + AA(i) then i + A then 0 +c1(app(AA)) + c1(AA(2)) + c2(AA(3)) + c2(A) +//│ = 6 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 6 +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ AA --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 79e38c718af8d564fc64eda111067642345c2d80 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:45:12 +0800 Subject: [PATCH 189/303] just use `ResultId` --- .../scala/hkmc2/codegen/Deforestation.scala | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 996442e9bd..db2a8e9909 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -36,8 +36,6 @@ object StratVarState: p -> c -type CtorExpr = ResultId - extension (i: ResultId) def getResult(using d: Deforest) = d.resultIdToResult(i) def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A)(using Deforest) = @@ -55,7 +53,7 @@ extension (i: ResultId) case _ => None def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: CtorExpr) extends ProdStrat +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat @@ -530,19 +528,19 @@ class Deforest(using TL, Raise, Elaborator.State): val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) case class CtorDest(matches: Map[ResultId, Match], sels: Set[FieldSel], noCons: Bool) - case class DtorSource(ctors: Set[CtorExpr], noProd: Bool) + case class DtorSource(ctors: Set[ResultId], noProd: Bool) object ctorDests: val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Set.empty, false)) - def update(ctor: CtorExpr, m: Match) = ctorDests.updateWith(ctor): + def update(ctor: ResultId, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) case None => Some(CtorDest(Map(m.scrut.uid -> m), Set.empty, false)) - def update(ctor: CtorExpr, s: FieldSel) = ctorDests.updateWith(ctor): + def update(ctor: ResultId, s: FieldSel) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels + s, noCons)) case None => Some(CtorDest(Map.empty, Set(s), false)) - def update(ctor: CtorExpr, n: NoCons.type) = ctorDests.updateWith(ctor): + def update(ctor: ResultId, n: NoCons.type) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels, true)) case None => Some(CtorDest(Map.empty, Set.empty, true)) - def get(ctor: CtorExpr) = ctorDests.get(ctor) + def get(ctor: ResultId) = ctorDests.get(ctor) object dtorSources: val dtorSources = mutable.Map.empty[DtorExpr, DtorSource].withDefaultValue(DtorSource(Set.empty, false)) @@ -633,10 +631,10 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[CtorExpr, CtorDest] + type CtorToDtor = Map[ResultId, CtorDest] type DtorToCtor = Map[DtorExpr, DtorSource] - def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[CtorExpr]): CtorToDtor -> DtorToCtor = + def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[ResultId]): CtorToDtor -> DtorToCtor = if rm.isEmpty then ctorDests -> dtorSources else @@ -676,7 +674,7 @@ class Deforest(using TL, Raise, Elaborator.State): val removeCycle = { - def getCtorInArm(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = + def getCtorInArm(ctor: ResultId, dtor: Match): Set[ResultId] = val ctorSym = getClsSymOfUid(ctor) val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get @@ -698,9 +696,9 @@ class Deforest(using TL, Raise, Elaborator.State): GetCtorsTraverser.applyBlock(arm) GetCtorsTraverser.ctors.toSet - def findCycle(ctor: CtorExpr, dtor: Match): Set[CtorExpr] = + def findCycle(ctor: ResultId, dtor: Match): Set[ResultId] = val cache = mutable.Set(ctor) - def go(ctorAndMatches: Set[CtorExpr -> Match]): Set[CtorExpr] = + def go(ctorAndMatches: Set[ResultId -> Match]): Set[ResultId] = val newCtorsAndNewMatches = ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => removeClashes._1.get(c).flatMap: @@ -727,8 +725,8 @@ class Deforest(using TL, Raise, Elaborator.State): - lazy val filteredCtorDests: Map[CtorExpr, CtorFinalDest] = - val res = mutable.Map.empty[CtorExpr, CtorFinalDest] + lazy val filteredCtorDests: Map[ResultId, CtorFinalDest] = + val res = mutable.Map.empty[ResultId, CtorFinalDest] // we need only one CtorFinalDest per arm for each pat mat expr val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, Opt[CtorFinalDest]] @@ -1085,7 +1083,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex case _ => super.applyResult2(r)(k) - def handleObjFusing(objCallExprUid: CtorExpr, objClsSym: ModuleSymbol) = + def handleObjFusing(objCallExprUid: ResultId, objClsSym: ModuleSymbol) = // must be a pat mat on objects; no support for selection on objects yet val CtorFinalDest.Match(scrut, expr, sels, selsMap) = d.filteredCtorDests(objCallExprUid): @unchecked val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === objClsSym }.map(_._2).orElse(expr.dflt).get From 10c9d2299ba344cd8b3f8d6bee74f9dce9565e44 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:29:45 +0800 Subject: [PATCH 190/303] better handling of throw and instantiate --- .../scala/hkmc2/codegen/Deforestation.scala | 44 ++++++++++++------- .../src/test/mlscript/deforest/simple.mls | 22 ++++++++++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index db2a8e9909..cc1de7fdfd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -39,13 +39,15 @@ object StratVarState: extension (i: ResultId) def getResult(using d: Deforest) = d.resultIdToResult(i) def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A)(using Deforest) = + def handleCallLike(f: Path, args: Ls[Arg]) = f match + case s: Select if s.symbol.flatMap(_.asCls).isDefined => + Some(k(i, s, s.symbol.get.asCls.get, args)) + case v: Value.Ref if v.l.asCls.isDefined => + Some(k(i, v, v.l.asCls.get, args)) + case _ => None i.getResult match - case Call(fun, args) => fun match - case s: Select if s.symbol.flatMap(_.asCls).isDefined => - Some(k(i, s, s.symbol.get.asCls.get, args)) - case v: Value.Ref if v.l.asCls.isDefined => - Some(k(i, v, v.l.asCls.get, args)) - case _ => None + case Call(fun, args) => handleCallLike(fun, args) + case Instantiate(cls, args) => handleCallLike(cls, args.map(Arg(false, _))) case s: Select if s.symbol.flatMap(_.asObj).isDefined => Some(k(i, s, s.symbol.get.asObj.get, Nil)) case v: Value.Ref if v.l.asObj.isDefined => @@ -436,7 +438,9 @@ class Deforest(using TL, Raise, Elaborator.State): case End(msg) => NoProd // make it a type var instead of `NoProd` so that things like `throw match error` in // default else branches do not block fusion... - case Throw(exc) => freshVar("throw")._1 + case Throw(exc) => + processResult(exc) + freshVar("throw")._1 def constrFun(params: Ls[Param], body: Block)(using inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], @@ -453,10 +457,9 @@ class Deforest(using TL, Raise, Elaborator.State): def processResult(r: Result)(using inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] - ): ProdStrat = r match - case c@Call(f, args) => - val argsTpe = args.map: - case Arg(false, value) => processResult(value) + ): ProdStrat = + def handleCallLike(f: Path, args: Ls[Path], c: Result) = + val argsTpe = args.map(processResult) f match case s@Select(p, nme) => s.symbol.map(_.asCls) match @@ -493,8 +496,10 @@ class Deforest(using TL, Raise, Elaborator.State): case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") case Value.Lit(lit) => ??? case Value.Arr(elems) => ??? + r match + case c@Call(f, args) => handleCallLike(f, args.map {case Arg(false, value) => value}, c) - case Instantiate(cls, args) => throw NotDeforestableException("No support for `instantiate` yet") + case i@Instantiate(cls, args) => handleCallLike(cls, args, i) case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => @@ -1041,13 +1046,13 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex super.applyResult(r) case _ => super.applyResult(r) - override def applyResult2(r: Result)(k: Result => Block): Block = r match - case call@Call(f, args) if d.filteredCtorDests.isDefinedAt(call.uid) => + override def applyResult2(r: Result)(k: Result => Block): Block = + def handleCallLike(f: Path, args: Ls[Path], uid: ResultId) = val c = f match case s: Select => s.symbol.get.asCls.get case Value.Ref(l) => l.asCls.get case _ => ??? - d.filteredCtorDests.get(call.uid).get match + d.filteredCtorDests.get(uid).get match case CtorFinalDest.Match(scrut, expr, sels, selsMap) => // use pre-determined symbols, create temp symbols for un-used fields val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 @@ -1074,13 +1079,18 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex selsMap._1 -> selsMap._2) args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): - case ((a, tmp), rest) => applyResult2(a.value) { r => Assign(tmp, r, rest) } + case ((a, tmp), rest) => applyResult2(a) { r => Assign(tmp, r, rest) } case CtorFinalDest.Sel(s) => val selFieldName = s.getResult match { case Select(p, nme) => nme } val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) - k(args(idx).value) + k(args(idx)) + r match + case call@Call(f, args) if d.filteredCtorDests.isDefinedAt(call.uid) => + handleCallLike(f, args.map { case Arg(false, value) => value }, call.uid) + case ins@Instantiate(cls, args) if d.filteredCtorDests.isDefinedAt(ins.uid) => + handleCallLike(cls, args, ins.uid) case _ => super.applyResult2(r)(k) def handleObjFusing(objCallExprUid: ResultId, objClsSym: ModuleSymbol) = diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index f37849adaf..ecd3ea9fba 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -548,3 +548,25 @@ c1(app(AA)) + c1(AA(2)) + c2(AA(3)) + c2(A) //│ A --match--> `if x is ...` //│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + +:re +fun p(x) = if x + then new AA(2) + else BB(3) +fun c(x) = if x is + AA(a) then a + BB(b) then b + else throw (if A is A then Error("e1") else Error("e2")) +print(c(p(true)) + c(p(false))) +c(B) +//│ > 5 +//│ ═══[RUNTIME ERROR] Error: e1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ > 5 +//│ ═══[RUNTIME ERROR] Error: e1 +//│ 4 fusion opportunities: +//│ A --match--> `if scrut is ...` +//│ AA --match--> `if x is ...` +//│ B --match--> `if x is ...` +//│ BB --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From daf8749d16852151becac4f4f6e271d2d26fc025 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:45:24 +0800 Subject: [PATCH 191/303] update inappropriate uses of `???` in `Deforestation.scala` --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cc1de7fdfd..69dd9f0134 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -552,7 +552,7 @@ class Deforest(using TL, Raise, Elaborator.State): private def getDtorExprOfResultId(i: ResultId) = i.getResult match case s: Select => DtorExpr.Sel(i) case r: Value.Ref => DtorExpr.Match(i) - case _ => ??? // unreachable + case r => lastWords(s"try to get dtor expr from ResultId, but get $r") def update(dtor: ResultId, ctor: ResultId) = val dtorExpr = getDtorExprOfResultId(dtor) dtorSources.updateWith(dtorExpr): @@ -610,7 +610,7 @@ class Deforest(using TL, Raise, Elaborator.State): handle(prod -> u) else () - case (_: ProdVar, _) => ??? // unreachable, should be handled above + case (_: ProdVar, _) => die case _ => handle(prod -> u) case (Ctor(ctor, args, expr), NoCons) => ctorDests.update(expr, NoCons) @@ -786,7 +786,7 @@ class Deforest(using TL, Raise, Elaborator.State): throw Error("more than one consumer") None ) - else ??? + else die } res.updateWith(ctor){_ => filteredDtor} } From 793c57e2ae24c1ef510d406c85aec1e8d0730dce Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:58:34 +0800 Subject: [PATCH 192/303] minor update on comment --- hkmc2/shared/src/test/mlscript/deforest/todos.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index e327ae5a11..1dc8dfcb2c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -16,7 +16,7 @@ object None data class Some(x) -// similar as above, since `x.x` is considered as being consumed +// since `x.x` is considered as being consumed // at two places,the inner match is not fused :sjs fun f(x) = if x is From fd306ac62f851eabee06212017fecd75c9837480 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:10:33 +0800 Subject: [PATCH 193/303] minor --- .../scala/hkmc2/codegen/Deforestation.scala | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 69dd9f0134..0722e838f4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -734,17 +734,15 @@ class Deforest(using TL, Raise, Elaborator.State): val res = mutable.Map.empty[ResultId, CtorFinalDest] // we need only one CtorFinalDest per arm for each pat mat expr - val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, Opt[CtorFinalDest]] + val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels, false)) => val filteredDtor = { - if dtors.size == 0 && sels.size == 1 then Some(CtorFinalDest.Sel(sels.head.expr)) + if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) else if dtors.size == 0 && sels.size > 1 then - throw Error("more than one consumer") - None + lastWords("more than one consumer") else if dtors.size > 1 then - throw Error("more than one consumer") - None + lastWords("more than one consumer") else if dtors.size == 1 then val currentCtorCls = getClsSymOfUid(ctor) val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult @@ -776,19 +774,18 @@ class Deforest(using TL, Raise, Elaborator.State): selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) case _ => () - Some(CtorFinalDest.Match( + CtorFinalDest.Match( dtors.head._1, dtors.head._2, sels.map(_.expr), fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - )) + ) else - throw Error("more than one consumer") - None + lastWords("more than one consumer") ) else die } - res.updateWith(ctor){_ => filteredDtor} + res.updateWith(ctor){_ => Some(filteredDtor)} } res.toMap From e382b8e21a9a20379f53f96f5a3e6c6a41657fd9 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:11:08 +0800 Subject: [PATCH 194/303] wip: use linkedhashmap for determinism; mutate map instead of generating new ones --- .../scala/hkmc2/codegen/Deforestation.scala | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 649e9cde9f..8d61fa0ff9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -535,7 +535,7 @@ class Deforest(using TL, Raise, Elaborator.State): case class CtorDest(matches: Map[ResultId, Match], sels: Set[FieldSel], noCons: Bool) case class DtorSource(ctors: Set[ResultId], noProd: Bool) object ctorDests: - val ctorDests = mutable.Map.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Set.empty, false)) + val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Set.empty, false)) def update(ctor: ResultId, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) case None => Some(CtorDest(Map(m.scrut.uid -> m), Set.empty, false)) @@ -636,34 +636,32 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - type CtorToDtor = Map[ResultId, CtorDest] - type DtorToCtor = Map[DtorExpr, DtorSource] - def removeCtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[ResultId]): CtorToDtor -> DtorToCtor = - if rm.isEmpty then - ctorDests -> dtorSources + val ctorToDtor = ctorDests.ctorDests + val dtorToCtor = dtorSources.dtorSources + + def removeCtor(rm: Set[ResultId]): Unit = + if rm.isEmpty then () else tl.log("rm ctor: " + rm.map(c => c.getClsSymOfUid.nme).mkString(" | ")) - val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) - removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels, _) => - mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) - }.toSet) + val toDeleteDtors = rm.flatMap(r => ctorToDtor.remove(r)).flatMap: + case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) + removeDtor(toDeleteDtors) + // val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) + // removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels, _) => + // mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) + // }.toSet) - def removeDtor(ctorDests: CtorToDtor, dtorSources: DtorToCtor, rm: Set[DtorExpr]): CtorToDtor -> DtorToCtor = - if rm.isEmpty then - ctorDests -> dtorSources + def removeDtor(rm: Set[DtorExpr]): Unit = + if rm.isEmpty then () else tl.log("rm dtor: " + rm.mkString(" | ")) - val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) - removeCtor(ctorDests, newDtorSources, toDelete.values.map(_.ctors).flatten.toSet) - - val ctorToDtor = ctorDests.ctorDests.toMap - val dtorToCtor = dtorSources.dtorSources.toMap + // val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) + val toDeleteCtors = rm.flatMap(r => dtorToCtor.remove(r)).flatMap(_.ctors) + removeCtor(toDeleteCtors) val removeClashes = - val tmpRes = removeCtor( - ctorToDtor, - dtorToCtor, + removeCtor( ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => ((dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { @@ -673,9 +671,9 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => false } })) && !noCons - }.keySet + }.keySet.toSet ) - removeDtor(tmpRes._1, tmpRes._2, tmpRes._2.filter(_._2.noProd).keySet) + removeDtor(dtorToCtor.filter(_._2.noProd).keySet.toSet) val removeCycle = { @@ -706,7 +704,7 @@ class Deforest(using TL, Raise, Elaborator.State): def go(ctorAndMatches: Set[ResultId -> Match]): Set[ResultId] = val newCtorsAndNewMatches = ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => - removeClashes._1.get(c).flatMap: + ctorToDtor.get(c).flatMap: case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) if newCtorsAndNewMatches.isEmpty then @@ -717,16 +715,17 @@ class Deforest(using TL, Raise, Elaborator.State): go(newCtorsAndNewMatches) go(Set(ctor -> dtor)) - val toRmCtor = removeClashes._1.flatMap: + val toRmCtor = ctorToDtor.flatMap: case (c, CtorDest(matches, sels, false)) => assert(matches.size <= 1) matches.values.flatMap(m => findCycle(c, m)) - removeCtor(removeClashes._1, removeClashes._2, toRmCtor.toSet) + removeCtor(toRmCtor.toSet) } val finalRes = removeCycle - finalRes + // finalRes + ctorToDtor -> dtorToCtor @@ -736,7 +735,7 @@ class Deforest(using TL, Raise, Elaborator.State): // we need only one CtorFinalDest per arm for each pat mat expr val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] - resolveClashes._1.toSortedMap.foreach { case (ctor, CtorDest(dtors, sels, false)) => + resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) else if dtors.size == 0 && sels.size > 1 then From 8b2b77aecd1c17bd3b860c38dabbee3f2bc69264 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 27 Apr 2025 16:33:22 +0800 Subject: [PATCH 195/303] fix another nondeterministic behavior; add some tests --- .../scala/hkmc2/codegen/Deforestation.scala | 18 +- .../test/mlscript/deforest/determinism.mls | 164 ++++++++++++++++++ 2 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/determinism.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8d61fa0ff9..a0cae5b9d9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -84,7 +84,7 @@ enum DtorExpr: case Sel(s: ResultId) enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Set[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) + case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) case Sel(s: ResultId) trait FieldSelTrait: @@ -244,7 +244,7 @@ class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) e case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) case _ => super.applyValue(v) -object HasExplicitRetTraverser extends BlockTraverserShallow: +class HasExplicitRetTraverser extends BlockTraverserShallow: var flag = false override def applyBlock(b: Block): Unit = b match case Return(_, imp) => flag = !imp @@ -263,7 +263,7 @@ extension (b: Block) FreeVarTraverser(alwaysDefined).analyze(b) def hasExplicitRet: Boolean = - HasExplicitRetTraverser.analyze(b) + HasExplicitRetTraverser().analyze(b) def willBeNonEndTailBlock(using d: Deforest): Bool = WillBeNonEndTailBlockTraverser().analyze(b) @@ -532,19 +532,19 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - case class CtorDest(matches: Map[ResultId, Match], sels: Set[FieldSel], noCons: Bool) + case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool) case class DtorSource(ctors: Set[ResultId], noProd: Bool) object ctorDests: - val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Set.empty, false)) + val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false)) def update(ctor: ResultId, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) - case None => Some(CtorDest(Map(m.scrut.uid -> m), Set.empty, false)) + case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false)) def update(ctor: ResultId, s: FieldSel) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels + s, noCons)) - case None => Some(CtorDest(Map.empty, Set(s), false)) + case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, s :: sels, noCons)) + case None => Some(CtorDest(Map.empty, s :: Nil, false)) def update(ctor: ResultId, n: NoCons.type) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels, true)) - case None => Some(CtorDest(Map.empty, Set.empty, true)) + case None => Some(CtorDest(Map.empty, Nil, true)) def get(ctor: ResultId) = ctorDests.get(ctor) object dtorSources: diff --git a/hkmc2/shared/src/test/mlscript/deforest/determinism.mls b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls new file mode 100644 index 0000000000..7c9d6853f7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls @@ -0,0 +1,164 @@ +:js + +data class A(a) +data class B(a, b, c, d, e) + +:deforest +:sjs +if B(1,2,3,4,5) is + B(a,b,c,d,e) then 0 +//│ JS (unsanitized): +//│ let scrut, param0, param1, param2, param3, param4, a, b, c, d, e; +//│ scrut = B1(1, 2, 3, 4, 5); +//│ if (scrut instanceof B1.class) { +//│ param0 = scrut.a; +//│ param1 = scrut.b; +//│ param2 = scrut.c; +//│ param3 = scrut.d; +//│ param4 = scrut.e; +//│ a = param0; +//│ b = param1; +//│ c = param2; +//│ d = param3; +//│ e = param4; +//│ 0 +//│ } else { +//│ throw new this.Error("match error"); +//│ } +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let scrut, _deforest_B_e, _deforest_B_d, _deforest_B_c, _deforest_B_b, _deforest_B_a; +//│ _deforest_B_a = 1; +//│ _deforest_B_b = 2; +//│ _deforest_B_c = 3; +//│ _deforest_B_d = 4; +//│ _deforest_B_e = 5; +//│ scrut = () => { +//│ let param0, param1, param2, param3, param4, a, b, c, d, e; +//│ param0 = _deforest_B_a; +//│ param1 = _deforest_B_b; +//│ param2 = _deforest_B_c; +//│ param3 = _deforest_B_d; +//│ param4 = _deforest_B_e; +//│ a = param0; +//│ b = param1; +//│ c = param2; +//│ d = param3; +//│ e = param4; +//│ return 0 +//│ }; +//│ runtime.safeCall(scrut()) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 0 +//│ 1 fusion opportunities: +//│ B --match--> `if scrut is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:sjs +:deforest +fun c2(x) = if x is + A(A(A(A(A(A(a)))))) then a +c2 of A(A(A(A(A(A(1)))))) +//│ JS (unsanitized): +//│ let c2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; +//│ c2 = function c2(x) { +//│ let param01, param02, param03, param04, param05, param06, a1; +//│ if (x instanceof A1.class) { +//│ param01 = x.a; +//│ if (param01 instanceof A1.class) { +//│ param02 = param01.a; +//│ if (param02 instanceof A1.class) { +//│ param03 = param02.a; +//│ if (param03 instanceof A1.class) { +//│ param04 = param03.a; +//│ if (param04 instanceof A1.class) { +//│ param05 = param04.a; +//│ if (param05 instanceof A1.class) { +//│ param06 = param05.a; +//│ a1 = param06; +//│ return a1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = A1(1); +//│ tmp1 = A1(tmp); +//│ tmp2 = A1(tmp1); +//│ tmp3 = A1(tmp2); +//│ tmp4 = A1(tmp3); +//│ tmp5 = A1(tmp4); +//│ c2(tmp5) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let c2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, _deforest_A_a, _deforest_A_a1, _deforest_A_a2, _deforest_A_a3, _deforest_A_a4, _deforest_A_a5; +//│ c2 = function c2(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ _deforest_A_a5 = 1; +//│ tmp = () => { +//│ let param01, a1; +//│ param01 = _deforest_A_a5; +//│ a1 = param01; +//│ return a1 +//│ }; +//│ _deforest_A_a4 = tmp; +//│ tmp1 = () => { +//│ let param01; +//│ param01 = _deforest_A_a4; +//│ return runtime.safeCall(param01()) +//│ }; +//│ _deforest_A_a3 = tmp1; +//│ tmp2 = () => { +//│ let param01; +//│ param01 = _deforest_A_a3; +//│ return runtime.safeCall(param01()) +//│ }; +//│ _deforest_A_a2 = tmp2; +//│ tmp3 = () => { +//│ let param01; +//│ param01 = _deforest_A_a2; +//│ return runtime.safeCall(param01()) +//│ }; +//│ _deforest_A_a1 = tmp3; +//│ tmp4 = () => { +//│ let param01; +//│ param01 = _deforest_A_a1; +//│ return runtime.safeCall(param01()) +//│ }; +//│ _deforest_A_a = tmp4; +//│ tmp5 = () => { +//│ let param01; +//│ param01 = _deforest_A_a; +//│ return runtime.safeCall(param01()) +//│ }; +//│ c2(tmp5) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 1 +//│ 6 fusion opportunities: +//│ A --match--> `if param0 is ...` +//│ A --match--> `if param0 is ...` +//│ A --match--> `if param0 is ...` +//│ A --match--> `if param0 is ...` +//│ A --match--> `if param0 is ...` +//│ A --match--> `if x is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From f5da463622116fbfacf5aaa4f54d8fc8d9016633 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 27 Apr 2025 16:45:02 +0800 Subject: [PATCH 196/303] remove the useless ordering and the unnecessary lazy val which only stores identityHashCode --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 7844ca436f..f1b2540e72 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -408,9 +408,6 @@ sealed trait TrivialResult extends Result object Result: opaque type ResultId = Int - given Ordering[ResultId] with - def compare(x: ResultId, y: ResultId): Int = x.compare(y) - private def ResultId(v: Int): ResultId = v @@ -468,10 +465,10 @@ sealed abstract class Result extends AutoLocated: case Value.Rcd(args) => args.flatMap(arg => arg.idx.fold(Set.empty)(_.freeVarsLLIR) ++ arg.value.freeVarsLLIR).toSet // for deforestation - import Result.* - lazy val uidValue: ResultId = ResultId(System.identityHashCode(this)) def uid(using d: Deforest) = - d.resultIdToResult.updateWith(this.uidValue): + import Result.* + val uidValue = ResultId(System.identityHashCode(this)) + d.resultIdToResult.updateWith(uidValue): case N => S(this) case S(r) => assert(this is r); S(this) uidValue From e92a50f0192a49f9e74354c348088c42b5a4f966 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 27 Apr 2025 18:12:36 +0800 Subject: [PATCH 197/303] cleanup --- .../scala/hkmc2/codegen/Deforestation.scala | 137 ++++++++---------- 1 file changed, 60 insertions(+), 77 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a0cae5b9d9..965f4bf7cf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -14,11 +14,9 @@ type StratVar type StratVarId = Uid[StratVar] type ClsOrModSymbol = ClassLikeSymbol -sealed abstract class Strat +sealed abstract class ProdStrat -sealed abstract class ProdStrat extends Strat - -sealed abstract class ConsStrat extends Strat +sealed abstract class ConsStrat class StratVarState(val uid: StratVarId, val name: Str = ""): lazy val asProdStrat = ProdVar(this) @@ -325,8 +323,6 @@ class Deforest(using TL, Raise, Elaborator.State): else S(p) -> s"0 fusion opportunity" -> 0 - // these are never considered as free vars (because of their symbol type) - // consider TopLevelSym, BlockMemberSymbols and BuiltInSyms as globally defined... object globallyDefinedVars: val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) @@ -391,7 +387,7 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def processBlock(b: Block)(using - inArm: LinkedHashMap[ProdVar, ClsOrModSymbol] = LinkedHashMap.empty[ProdVar, ClsOrModSymbol], + inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol] ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => @@ -443,7 +439,7 @@ class Deforest(using TL, Raise, Elaborator.State): freshVar("throw")._1 def constrFun(params: Ls[Param], body: Block)(using - inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], + inArm: Map[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] ) = val paramSyms = params.map: @@ -455,7 +451,7 @@ class Deforest(using TL, Raise, Elaborator.State): ProdFun(paramStrats.map(s => s.asConsStrat), res._1) def processResult(r: Result)(using - inArm: LinkedHashMap[ProdVar, ClsOrModSymbol], + inArm: Map[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] ): ProdStrat = def handleCallLike(f: Path, args: Ls[Path], c: Result) = @@ -636,7 +632,6 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== lazy val resolveClashes = - val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources @@ -647,84 +642,72 @@ class Deforest(using TL, Raise, Elaborator.State): val toDeleteDtors = rm.flatMap(r => ctorToDtor.remove(r)).flatMap: case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) removeDtor(toDeleteDtors) - // val (newCtorDests, toDelete) = ctorDests.partition(c => !rm(c._1)) - // removeDtor(newCtorDests, dtorSources, toDelete.values.flatMap[DtorExpr]{ case CtorDest(mat, sels, _) => - // mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) - // }.toSet) def removeDtor(rm: Set[DtorExpr]): Unit = if rm.isEmpty then () else tl.log("rm dtor: " + rm.mkString(" | ")) - // val (newDtorSources, toDelete) = dtorSources.partition(d => !rm(d._1)) val toDeleteCtors = rm.flatMap(r => dtorToCtor.remove(r)).flatMap(_.ctors) removeCtor(toDeleteCtors) - val removeClashes = - removeCtor( - ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => - ((dtors.size == 0 && sels.size == 1) - || (dtors.size == 1 && { - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - sels.forall { s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee - case _ => false } - })) - && !noCons - }.keySet.toSet - ) - removeDtor(dtorToCtor.filter(_._2.noProd).keySet.toSet) - + // remove clashes: + removeCtor( + ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => + ((dtors.size == 0 && sels.size == 1) + || (dtors.size == 1 && { + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult + sels.forall { s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee + case _ => false } + })) + && !noCons + }.keySet.toSet + ) + removeDtor(dtorToCtor.filter(_._2.noProd).keySet.toSet) - val removeCycle = { - def getCtorInArm(ctor: ResultId, dtor: Match): Set[ResultId] = - val ctorSym = getClsSymOfUid(ctor) - val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - - object GetCtorsTraverser extends BlockTraverser: - val ctors = mutable.Set.empty[ResultId] - override def applyResult(r: Result): Unit = - r.uid.handleCtorIds{ (id, f, clsOrMod, args) => - ctors += id - args.foreach { case Arg(_, value) => applyResult(value) } - } match - case Some(_) => () - case None => r match - case Call(_, args) => - args.foreach { case Arg(_, value) => applyResult(value) } - case Instantiate(cls, args) => - args.foreach(applyResult) - case _ => () - - GetCtorsTraverser.applyBlock(arm) - GetCtorsTraverser.ctors.toSet - - def findCycle(ctor: ResultId, dtor: Match): Set[ResultId] = - val cache = mutable.Set(ctor) - def go(ctorAndMatches: Set[ResultId -> Match]): Set[ResultId] = - val newCtorsAndNewMatches = - ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => - ctorToDtor.get(c).flatMap: - case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) - val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) - if newCtorsAndNewMatches.isEmpty then - Set.empty - else if cycled.nonEmpty then - cycled.map(_._1) - else - go(newCtorsAndNewMatches) - go(Set(ctor -> dtor)) - - val toRmCtor = ctorToDtor.flatMap: - case (c, CtorDest(matches, sels, false)) => - assert(matches.size <= 1) - matches.values.flatMap(m => findCycle(c, m)) + // remove cycle: + def getCtorInArm(ctor: ResultId, dtor: Match): Set[ResultId] = + val ctorSym = getClsSymOfUid(ctor) + val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - removeCtor(toRmCtor.toSet) - } + object GetCtorsTraverser extends BlockTraverser: + val ctors = mutable.Set.empty[ResultId] + override def applyResult(r: Result): Unit = + r.uid.handleCtorIds{ (id, f, clsOrMod, args) => + ctors += id + args.foreach { case Arg(_, value) => applyResult(value) } + } match + case Some(_) => () + case None => r match + case Call(_, args) => + args.foreach { case Arg(_, value) => applyResult(value) } + case Instantiate(cls, args) => + args.foreach(applyResult) + case _ => () + + GetCtorsTraverser.applyBlock(arm) + GetCtorsTraverser.ctors.toSet + def findCycle(ctor: ResultId, dtor: Match): Set[ResultId] = + val cache = mutable.Set(ctor) + def go(ctorAndMatches: Set[ResultId -> Match]): Set[ResultId] = + val newCtorsAndNewMatches = + ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => + ctorToDtor.get(c).flatMap: + case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) + val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) + if newCtorsAndNewMatches.isEmpty then + Set.empty + else if cycled.nonEmpty then + cycled.map(_._1) + else + go(newCtorsAndNewMatches) + go(Set(ctor -> dtor)) + val toRmCtor = ctorToDtor.flatMap: + case (c, CtorDest(matches, sels, false)) => + assert(matches.size <= 1) + matches.values.flatMap(m => findCycle(c, m)) + removeCtor(toRmCtor.toSet) - val finalRes = removeCycle - // finalRes ctorToDtor -> dtorToCtor From a529545bfb15fee1bc59e3464da9253b0defa012 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:39:24 +0800 Subject: [PATCH 198/303] do not silently discard extra parameter lists --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 965f4bf7cf..5ead2c5569 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -423,8 +423,9 @@ class Deforest(using TL, Raise, Elaborator.State): defn match case FunDefn(_, sym, params, body) => val funSymStratVar = symToStrat(sym) - val param = params.head match - case ParamList(flags, params, N) => params // TODO: handle mutiple param list + val param = params match + // TODO: handle `restParam` and mutiple param list + case ParamList(flags, params, N) :: Nil => params val funStrat = constrFun(param, body) constrain(funStrat, funSymStratVar.asConsStrat) funSymStratVar From f4f7f95771b22e7bf222f73388a3392e50fe786d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:49:30 +0800 Subject: [PATCH 199/303] clarify comment --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5ead2c5569..30c9ba6def 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -101,7 +101,8 @@ trait StratVarTrait(stratState: StratVarState): final case class NotDeforestableException(msg: String) extends Exception(msg) -// Compute free vars for a block, without considering deforestation, used on transformed blocks +// Compute free vars for a block, without considering deforestation. +// Used on blocks after the deforestation transformation. // This means that for matches we don't need to consider the extra // free vars that may be introduced by deforestation: // 1. the free vars from the `rest` of the their parent matches From 0e662c37be104d99908298686d3b5f8d7fce056f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:14:25 +0800 Subject: [PATCH 200/303] track where a ctor expr come from --- .../scala/hkmc2/codegen/Deforestation.scala | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5b881e0675..c8a2dc05ad 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -53,7 +53,7 @@ extension (i: ResultId) case _ => None def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId) extends ProdStrat +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId)(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat @@ -389,7 +389,8 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol] + matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol], + inDef: Opt[BlockMemberSymbol] = N ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) @@ -427,7 +428,7 @@ class Deforest(using TL, Raise, Elaborator.State): val param = params match // TODO: handle `restParam` and mutiple param list case ParamList(flags, params, N) :: Nil => params - val funStrat = constrFun(param, body) + val funStrat = constrFun(param, body)(using inArm, matching, S(sym)) constrain(funStrat, funSymStratVar.asConsStrat) funSymStratVar case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") @@ -442,7 +443,8 @@ class Deforest(using TL, Raise, Elaborator.State): def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol] + matching: LinkedHashMap[ResultId, ClsOrModSymbol], + inDef: Opt[BlockMemberSymbol] ) = val paramSyms = params.map: case Param(sym = sym, _) => sym @@ -454,7 +456,8 @@ class Deforest(using TL, Raise, Elaborator.State): def processResult(r: Result)(using inArm: Map[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol] + matching: LinkedHashMap[ResultId, ClsOrModSymbol], + inDef: Opt[BlockMemberSymbol] ): ProdStrat = def handleCallLike(f: Path, args: Ls[Path], c: Result) = val argsTpe = args.map(processResult) @@ -475,12 +478,12 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) case _ => // then it is a function val appRes = freshVar("call_" + l.nme + "_res") constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) @@ -501,7 +504,7 @@ class Deforest(using TL, Raise, Elaborator.State): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => - Ctor(s.asObj.get, Map.empty, sel.uid) + Ctor(s.asObj.get, Map.empty, sel.uid)(inDef) case _ => val pStrat = processResult(p) pStrat match @@ -518,7 +521,7 @@ class Deforest(using TL, Raise, Elaborator.State): case v@Value.Ref(l) => l.asObj match case None => symToStrat.getStratOfSym(l) - case Some(m) => Ctor(m, Map.empty, v.uid) + case Some(m) => Ctor(m, Map.empty, v.uid)(inDef) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") case Value.Lit(lit) => NoProd From 5d3c3ff1690448e1029ee0a4ace36d9484aa1bc3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 29 Apr 2025 01:50:07 +0800 Subject: [PATCH 201/303] minor fix --- .../scala/hkmc2/codegen/Deforestation.scala | 18 +++++++++--------- .../src/test/mlscript/deforest/simple.mls | 12 ++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 30c9ba6def..5b881e0675 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -228,7 +228,7 @@ class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserSh override def applyBlock(b: Block): Unit = b match case Match(scrut, arms, dflt, rest) => flag = - d.filteredDtors(scrut.uid) || + d.rewritingMatchConsumers(scrut.uid) || (arms.forall { case (_, b) => b.willBeNonEndTailBlock } && dflt.fold(true)(_.willBeNonEndTailBlock)) || rest.willBeNonEndTailBlock case _: End => () @@ -773,13 +773,12 @@ class Deforest(using TL, Raise, Elaborator.State): } res.toMap - lazy val rewritingSelConsumer = filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, _, _) => None - case CtorFinalDest.Sel(s) => Some(s) + lazy val rewritingSelConsumers = filteredCtorDests.values.collect { + case CtorFinalDest.Sel(s) => s }.toSet - lazy val filteredDtors = filteredCtorDests.values.collect { - case CtorFinalDest.Match(scrut, _, _, _) => scrut + lazy val rewritingMatchConsumers = filteredCtorDests.values.collect { + case CtorFinalDest.Match(scrut = s, _) => s }.toSet def rewrite(p: Block) = @@ -803,7 +802,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex def parentMatchesUptoAFusingOne(scrutId: ResultId) = def go(scrutId: ResultId): List[ResultId] -> Opt[ResultId] = d.matchScrutToParentMatchScrut(scrutId).fold(Nil -> N): r => - if d.filteredDtors.contains(r) + if d.rewritingMatchConsumers.contains(r) then Nil -> S(r) else val res = go(r) @@ -822,6 +821,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex d.filteredCtorDests.values.foreach: case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) + case CtorFinalDest.Sel(s) => () def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( scrutExprId, @@ -999,7 +999,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex override def applyBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.filteredDtors.contains(scrut.uid) => + case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.rewritingMatchConsumers.contains(scrut.uid) => // since all fusing matches will be considered to be in the tail position, // if any of the parent `rest`s has explicit return, the rewritten match will have explicit return val oneOfParentMatchRestHasExplicitRet = allParentMatches(scrut.uid).foldRight(false) { (pid, acc) => acc || d.matchScrutToMatchBlock(pid).rest.hasExplicitRet } @@ -1081,7 +1081,7 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex override def applyPath(p: Path): Path = p match // a selection which is a consumer on its own - case s@Select(p, nme) if d.rewritingSelConsumer.contains(s.uid) => applyPath(p) + case s@Select(p, nme) if d.rewritingSelConsumers.contains(s.uid) => applyPath(p) // a selection inside a fusing match that needs to be replaced by pre-computed symbols case s@Select(p, nme) if replaceSelInfo.get(s.uid).isDefined => Value.Ref(replaceSelInfo(s.uid)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index ecd3ea9fba..abd673d700 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -570,3 +570,15 @@ c(B) //│ B --match--> `if x is ...` //│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +fun f(a, b) = if a is + AA(x) then x + b.bb +f(AA(1), BB(2)) +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 3 +//│ 2 fusion opportunities: +//│ AA --match--> `if a is ...` +//│ BB --sel--> `.Ident(bb)` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 245a01123a9c0eaa65793ea2fe1f4b0666c90840 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:02:10 +0800 Subject: [PATCH 202/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 20 ++++++++++++----- .../test/mlscript/deforest/def-dup/simple.mls | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index c8a2dc05ad..72ef1ebc30 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str = ""): +class StratVarState(val uid: StratVarId, val name: Str = "", val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -26,9 +26,13 @@ class StratVarState(val uid: StratVarId, val name: Str = ""): object StratVarUidHandler extends Uid.Handler[StratVar] object StratVarState: - def freshVar(nme: String = "")(using vuid: StratVarUidHandler.State) = + // funRetOrArg: + // None: not representing the parameter type or return type of a function + // Some(Left): parameter type + // Some(right): return type + def freshVar(nme: String = "", funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = val newId = vuid.nextUid - val s = StratVarState(newId, nme) + val s = StratVarState(newId, nme, funRetOrArg) val p = s.asProdStrat val c = s.asConsStrat p -> c @@ -343,19 +347,23 @@ class Deforest(using TL, Raise, Elaborator.State): def init(p: Block) = if store.isEmpty then object FreshVarForAllVars extends BlockTraverser: + var funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N + override def applySymbol(s: Symbol): Unit = s match case b: BlockMemberSymbol => store += s -> freshVar(s.nme)._1 b.trmImplTree.foreach: t => if t.k is syntax.Fun then usedFunSym += b case _: TempSymbol => store += s -> freshVar(s.nme)._1 - case _: VarSymbol => store += s -> freshVar(s.nme)._1 + case _: VarSymbol => store += s -> freshVar(s.nme, funRetOrArg)._1 case _: TermSymbol => store += s -> freshVar(s.nme)._1 case _ => () override def applyFunDefn(fun: FunDefn): Unit = funSymsWithDefn += fun.sym + funRetOrArg = S(L(fun.sym)) super.applyFunDefn(fun) + funRetOrArg = N FreshVarForAllVars.applyBlock(p) // `NoProd` to block fusion for those functions that are imported from elsewhere @@ -449,8 +457,8 @@ class Deforest(using TL, Raise, Elaborator.State): val paramSyms = params.map: case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) - symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar() + // symToStrat.addAll(paramSyms.zip(paramStrats)) + val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", inDef.map(L.apply)) constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls new file mode 100644 index 0000000000..2e4f5fcc58 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -0,0 +1,22 @@ +:js +:deforest + +//│ No fusion opportunity + +data class A(x) + + +// `A(1)` is in the definition of `test`, but +// no duplication is helpful +fun test() = + let p = A(1) + let a = if p is + A(x) then x + let b = if p is + A(x) then x + a + b +test() + test() +//│ = 4 +//│ No fusion opportunity + + From e56612e0a8eea9f3f03ef1c660781258e5149f2c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 30 Apr 2025 00:32:46 +0800 Subject: [PATCH 203/303] remove unnecessary default arg; better type var name --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 72ef1ebc30..f2e73f8984 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str = "", val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]]): +class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -498,7 +498,7 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) - val appRes = freshVar() + val appRes = freshVar("call_lam_res") constrain(funTpe, ConsFun(argsTpe, appRes._2)) appRes._1 From 028f5f4c6222bed7f1e2faa71b5068dbed59e4f7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 1 May 2025 16:54:06 +0800 Subject: [PATCH 204/303] improve fusion clash resolving impl --- .../scala/hkmc2/codegen/Deforestation.scala | 104 ++++++++++-------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5b881e0675..25ba642687 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -253,6 +253,21 @@ class HasExplicitRetTraverser extends BlockTraverserShallow: flag = false applyBlock(b) flag + +class GetCtorsTraverser(using Deforest) extends BlockTraverser: + val ctors = mutable.Set.empty[ResultId] + override def applyResult(r: Result): Unit = + r.uid.handleCtorIds{ (id, f, clsOrMod, args) => + ctors += id + args.foreach { case Arg(_, value) => applyResult(value) } + } match + case Some(_) => () + case None => r match + case Call(_, args) => + args.foreach { case Arg(_, value) => applyResult(value) } + case Instantiate(cls, args) => + args.foreach(applyResult) + case _ => () extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = @@ -637,19 +652,29 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources - def removeCtor(rm: Set[ResultId]): Unit = - if rm.isEmpty then () - else + def removeCtor(rm: Iterable[ResultId]): Unit = + if rm.nonEmpty then tl.log("rm ctor: " + rm.map(c => c.getClsSymOfUid.nme).mkString(" | ")) - val toDeleteDtors = rm.flatMap(r => ctorToDtor.remove(r)).flatMap: - case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) + var toDeleteDtors: Ls[DtorExpr] = Nil + for + r <- rm + CtorDest(mat, sels, _) <- ctorToDtor.remove(r) + do + mat.keys.foreach: s => + toDeleteDtors = DtorExpr.Match(s) :: toDeleteDtors + sels.foreach: s => + toDeleteDtors = DtorExpr.Sel(s.expr) :: toDeleteDtors removeDtor(toDeleteDtors) - def removeDtor(rm: Set[DtorExpr]): Unit = - if rm.isEmpty then () - else + def removeDtor(rm: Iterable[DtorExpr]): Unit = + if rm.nonEmpty then tl.log("rm dtor: " + rm.mkString(" | ")) - val toDeleteCtors = rm.flatMap(r => dtorToCtor.remove(r)).flatMap(_.ctors) + var toDeleteCtors: Ls[ResultId] = Nil + for + r <- rm + c <- dtorToCtor.remove(r) + x <- c.ctors + do toDeleteCtors = x :: toDeleteCtors removeCtor(toDeleteCtors) // remove clashes: @@ -663,52 +688,45 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => false } })) && !noCons - }.keySet.toSet + }.keys ) - removeDtor(dtorToCtor.filter(_._2.noProd).keySet.toSet) + removeDtor(dtorToCtor.filter(_._2.noProd).keys) // remove cycle: - def getCtorInArm(ctor: ResultId, dtor: Match): Set[ResultId] = + def getCtorInArm(ctor: ResultId, dtor: Match) = val ctorSym = getClsSymOfUid(ctor) val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - - object GetCtorsTraverser extends BlockTraverser: - val ctors = mutable.Set.empty[ResultId] - override def applyResult(r: Result): Unit = - r.uid.handleCtorIds{ (id, f, clsOrMod, args) => - ctors += id - args.foreach { case Arg(_, value) => applyResult(value) } - } match - case Some(_) => () - case None => r match - case Call(_, args) => - args.foreach { case Arg(_, value) => applyResult(value) } - case Instantiate(cls, args) => - args.foreach(applyResult) - case _ => () - - GetCtorsTraverser.applyBlock(arm) - GetCtorsTraverser.ctors.toSet - def findCycle(ctor: ResultId, dtor: Match): Set[ResultId] = + val traverser = GetCtorsTraverser() + traverser.applyBlock(arm) + traverser.ctors + + def findCycle(ctor: ResultId, dtor: Match): Ls[ResultId] = val cache = mutable.Set(ctor) - def go(ctorAndMatches: Set[ResultId -> Match]): Set[ResultId] = - val newCtorsAndNewMatches = - ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => - ctorToDtor.get(c).flatMap: - case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) + def go(ctorAndMatches: Ls[ResultId -> Match]): Ls[ResultId] = + var newCtorsAndNewMatches: Ls[ResultId -> Match] = Nil + for + (c, m) <- ctorAndMatches + c <- getCtorInArm(c, m) + CtorDest(matches, sels, _) <- ctorToDtor.get(c) + m <- matches.values.headOption + do newCtorsAndNewMatches = (c -> m) :: newCtorsAndNewMatches val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) if newCtorsAndNewMatches.isEmpty then - Set.empty + Nil else if cycled.nonEmpty then cycled.map(_._1) else go(newCtorsAndNewMatches) - go(Set(ctor -> dtor)) - val toRmCtor = ctorToDtor.flatMap: - case (c, CtorDest(matches, sels, false)) => - assert(matches.size <= 1) - matches.values.flatMap(m => findCycle(c, m)) - removeCtor(toRmCtor.toSet) + go(Ls(ctor -> dtor)) + + var toRmCtor: Ls[ResultId] = Nil + for + (c, CtorDest(matches, sels, _)) <- ctorToDtor + m <- matches.values + x <- findCycle(c, m) + do toRmCtor = x :: toRmCtor + + removeCtor(toRmCtor) ctorToDtor -> dtorToCtor From 05468003a100a0e96b1515870689d1f12eca5bc4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 1 May 2025 23:24:56 +0800 Subject: [PATCH 205/303] improve stratvar uid impl --- hkmc2/shared/src/main/scala/hkmc2/Uid.scala | 1 + .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala index 235b375c37..1b9ea27b05 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala @@ -13,6 +13,7 @@ object Uid: curUid def reset = curUid = -1 object Symbol extends Handler[semantics.Symbol] + object StratVar extends Handler[codegen.StratVar] extension [T] (x: Uid[T]) def <=(rhs: Uid[T]) = x <= rhs diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 25ba642687..b181f77e20 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -24,9 +24,8 @@ class StratVarState(val uid: StratVarId, val name: Str = ""): override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" -object StratVarUidHandler extends Uid.Handler[StratVar] object StratVarState: - def freshVar(nme: String = "")(using vuid: StratVarUidHandler.State) = + def freshVar(nme: String = "")(using vuid: Uid.StratVar.State) = val newId = vuid.nextUid val s = StratVarState(newId, nme) val p = s.asProdStrat @@ -284,7 +283,7 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): - given StratVarUidHandler.State = StratVarUidHandler.State() + given Uid.StratVar.State = Uid.StratVar.State() given Deforest = this import StratVarState.freshVar From 0da282290201cdba6f7dbc9929376748205bc58e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 2 May 2025 00:15:06 +0800 Subject: [PATCH 206/303] further improve clash resolving impl --- .../scala/hkmc2/codegen/Deforestation.scala | 62 +++++++------------ 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b181f77e20..9f86649b93 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -651,45 +651,30 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources - def removeCtor(rm: Iterable[ResultId]): Unit = - if rm.nonEmpty then - tl.log("rm ctor: " + rm.map(c => c.getClsSymOfUid.nme).mkString(" | ")) - var toDeleteDtors: Ls[DtorExpr] = Nil - for - r <- rm - CtorDest(mat, sels, _) <- ctorToDtor.remove(r) - do - mat.keys.foreach: s => - toDeleteDtors = DtorExpr.Match(s) :: toDeleteDtors - sels.foreach: s => - toDeleteDtors = DtorExpr.Sel(s.expr) :: toDeleteDtors - removeDtor(toDeleteDtors) + def removeCtor(rm: ResultId): Unit = + for CtorDest(mat, sels, _) <- ctorToDtor.remove(rm) do + for s <- mat.keys do removeDtor(DtorExpr.Match(s)) + for s <- sels do removeDtor(DtorExpr.Sel(s.expr)) - def removeDtor(rm: Iterable[DtorExpr]): Unit = - if rm.nonEmpty then - tl.log("rm dtor: " + rm.mkString(" | ")) - var toDeleteCtors: Ls[ResultId] = Nil - for - r <- rm - c <- dtorToCtor.remove(r) - x <- c.ctors - do toDeleteCtors = x :: toDeleteCtors - removeCtor(toDeleteCtors) + def removeDtor(rm: DtorExpr) = + for + c <- dtorToCtor.remove(rm) + x <- c.ctors + do + removeCtor(x) // remove clashes: - removeCtor( - ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => - ((dtors.size == 0 && sels.size == 1) - || (dtors.size == 1 && { - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - sels.forall { s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee - case _ => false } - })) - && !noCons - }.keys - ) - removeDtor(dtorToCtor.filter(_._2.noProd).keys) + ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => + ((dtors.size == 0 && sels.size == 1) + || (dtors.size == 1 && { + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult + sels.forall { s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee + case _ => false } + })) + && !noCons + }.keys.foreach(removeCtor) + dtorToCtor.filter(_._2.noProd).keys.foreach(removeDtor) // remove cycle: def getCtorInArm(ctor: ResultId, dtor: Match) = @@ -718,14 +703,11 @@ class Deforest(using TL, Raise, Elaborator.State): go(newCtorsAndNewMatches) go(Ls(ctor -> dtor)) - var toRmCtor: Ls[ResultId] = Nil for (c, CtorDest(matches, sels, _)) <- ctorToDtor m <- matches.values x <- findCycle(c, m) - do toRmCtor = x :: toRmCtor - - removeCtor(toRmCtor) + do removeCtor(x) ctorToDtor -> dtorToCtor From 53bccba76479b206a23ea22079dc7a903138ae45 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 2 May 2025 14:10:51 +0800 Subject: [PATCH 207/303] also track if a type var represent a call result --- .../scala/hkmc2/codegen/Deforestation.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f2e73f8984..e68243bc53 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]]): +class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val isCallRes: Opt[ResultId]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -30,9 +30,9 @@ object StratVarState: // None: not representing the parameter type or return type of a function // Some(Left): parameter type // Some(right): return type - def freshVar(nme: String = "", funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = + def freshVar(nme: String = "", isCallRes: Opt[ResultId], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = val newId = vuid.nextUid - val s = StratVarState(newId, nme, funRetOrArg) + val s = StratVarState(newId, nme, funRetOrArg, isCallRes) val p = s.asProdStrat val c = s.asConsStrat p -> c @@ -351,12 +351,12 @@ class Deforest(using TL, Raise, Elaborator.State): override def applySymbol(s: Symbol): Unit = s match case b: BlockMemberSymbol => - store += s -> freshVar(s.nme)._1 + store += s -> freshVar(s.nme, N)._1 b.trmImplTree.foreach: t => if t.k is syntax.Fun then usedFunSym += b - case _: TempSymbol => store += s -> freshVar(s.nme)._1 - case _: VarSymbol => store += s -> freshVar(s.nme, funRetOrArg)._1 - case _: TermSymbol => store += s -> freshVar(s.nme)._1 + case _: TempSymbol => store += s -> freshVar(s.nme, N)._1 + case _: VarSymbol => store += s -> freshVar(s.nme, N, funRetOrArg)._1 + case _: TermSymbol => store += s -> freshVar(s.nme, N)._1 case _ => () override def applyFunDefn(fun: FunDefn): Unit = @@ -416,7 +416,7 @@ class Deforest(using TL, Raise, Elaborator.State): val dfltRes = dflt.map(processBlock) rest match case End(msg) => - val matchRes = freshVar() + val matchRes = freshVar("", N) armsRes.appendedAll(dfltRes).foreach: r => constrain(r, matchRes._2) matchRes._1 @@ -447,7 +447,7 @@ class Deforest(using TL, Raise, Elaborator.State): // default else branches do not block fusion... case Throw(exc) => processResult(exc) - freshVar("throw")._1 + freshVar("throw", N)._1 def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], @@ -458,7 +458,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) // symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", inDef.map(L.apply)) + val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) @@ -474,14 +474,14 @@ class Deforest(using TL, Raise, Elaborator.State): s.symbol.map(_.asCls) match case None => val pStrat = processResult(p) - val tpeVar = freshVar() + val tpeVar = freshVar("", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) - val appRes = freshVar() + val appRes = freshVar("", S(c.uid)) constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 case Some(None) => val funSym = s.symbol.get - val appRes = freshVar("call_" + funSym.nme + "_res") + val appRes = freshVar("call_" + funSym.nme + "_res", S(c.uid)) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => @@ -493,12 +493,12 @@ class Deforest(using TL, Raise, Elaborator.State): val clsFields = getClsFields(s) Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) case _ => // then it is a function - val appRes = freshVar("call_" + l.nme + "_res") + val appRes = freshVar("call_" + l.nme + "_res", S(c.uid)) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) - val appRes = freshVar("call_lam_res") + val appRes = freshVar("call_lam_res", S(c.uid)) constrain(funTpe, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -517,13 +517,13 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) pStrat match case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => - val tpeVar = freshVar() + val tpeVar = freshVar("", N) val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => - val tpeVar = freshVar() + val tpeVar = freshVar("", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 From c8455b9d3231abd21c888c63e26adf1c07f8e3c0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 2 May 2025 16:42:23 +0800 Subject: [PATCH 208/303] wip --- .../scala/hkmc2/codegen/Deforestation.scala | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e68243bc53..a9b5b735fc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val isCallRes: Opt[ResultId]): +class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val isCallRes: Opt[ResultId -> Symbol]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -30,7 +30,7 @@ object StratVarState: // None: not representing the parameter type or return type of a function // Some(Left): parameter type // Some(right): return type - def freshVar(nme: String = "", isCallRes: Opt[ResultId], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = + def freshVar(nme: String = "", isCallRes: Opt[ResultId -> Symbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = val newId = vuid.nextUid val s = StratVarState(newId, nme, funRetOrArg, isCallRes) val p = s.asProdStrat @@ -279,6 +279,7 @@ class Deforest(using TL, Raise, Elaborator.State): val resultIdToResult = mutable.Map.empty[ResultId, Result] + val funSymToFunDef = mutable.Map.empty[BlockMemberSymbol, FunDefn] def apply(p: Program): Opt[Program] -> String -> Int = val mainBlk = p.main @@ -431,7 +432,8 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(rest) case Define(defn, rest) => defn match - case FunDefn(_, sym, params, body) => + case fDef@FunDefn(_, sym, params, body) => + funSymToFunDef += sym -> fDef val funSymStratVar = symToStrat(sym) val param = params match // TODO: handle `restParam` and mutiple param list @@ -476,12 +478,15 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) val tpeVar = freshVar("", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) - val appRes = freshVar("", S(c.uid)) + val appRes = freshVar("", N) // unknown function symbol constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 case Some(None) => val funSym = s.symbol.get - val appRes = freshVar("call_" + funSym.nme + "_res", S(c.uid)) + val appRes = freshVar( + "call_" + funSym.nme + "_res", + s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)) // if `f` has a blockMemberSymbol, then record it + ) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => @@ -493,12 +498,15 @@ class Deforest(using TL, Raise, Elaborator.State): val clsFields = getClsFields(s) Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) case _ => // then it is a function - val appRes = freshVar("call_" + l.nme + "_res", S(c.uid)) + val appRes = freshVar( + "call_" + l.nme + "_res", + l.asBlkMember.map(c.uid -> _) // if `f` has a blockMemberSymbol, then record it + ) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) - val appRes = freshVar("call_lam_res", S(c.uid)) + val appRes = freshVar("call_lam_res", N) constrain(funTpe, ConsFun(argsTpe, appRes._2)) appRes._1 From b0b9c8d34cd33ab61031b4da9e8120a0a321e14f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 2 May 2025 21:49:05 +0800 Subject: [PATCH 209/303] wip: keep track of intermediate vars as ctor dests --- .../main/scala/hkmc2/codegen/Deforestation.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index a9b5b735fc..8376f35b8c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -549,19 +549,19 @@ class Deforest(using TL, Raise, Elaborator.State): val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool) + case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool, callResVars: Ls[StratVarState]) case class DtorSource(ctors: Set[ResultId], noProd: Bool) object ctorDests: - val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false)) + val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false, Nil)) def update(ctor: ResultId, m: Match) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons)) - case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false)) + case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons, vars)) + case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false, Nil)) def update(ctor: ResultId, s: FieldSel) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, s :: sels, noCons)) - case None => Some(CtorDest(Map.empty, s :: Nil, false)) + case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, s :: sels, noCons, vars)) + case None => Some(CtorDest(Map.empty, s :: Nil, false, Nil)) def update(ctor: ResultId, n: NoCons.type) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons)) => Some(CtorDest(matches, sels, true)) - case None => Some(CtorDest(Map.empty, Nil, true)) + case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, sels, true, vars)) + case None => Some(CtorDest(Map.empty, Nil, true, Nil)) def get(ctor: ResultId) = ctorDests.get(ctor) object dtorSources: From 0bbce591e84e1a1dc14ac3d412c1a27fce2ece7d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 5 May 2025 14:02:47 +0800 Subject: [PATCH 210/303] only track vars that represent call res --- .../scala/hkmc2/codegen/Deforestation.scala | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8376f35b8c..00435fe205 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val isCallRes: Opt[ResultId -> Symbol]): +class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val callResOf: Opt[ResultId -> Symbol]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -30,9 +30,9 @@ object StratVarState: // None: not representing the parameter type or return type of a function // Some(Left): parameter type // Some(right): return type - def freshVar(nme: String = "", isCallRes: Opt[ResultId -> Symbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = + def freshVar(nme: String = "", callResOf: Opt[ResultId -> Symbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: StratVarUidHandler.State) = val newId = vuid.nextUid - val s = StratVarState(newId, nme, funRetOrArg, isCallRes) + val s = StratVarState(newId, nme, funRetOrArg, callResOf) val p = s.asProdStrat val c = s.asConsStrat p -> c @@ -300,7 +300,7 @@ class Deforest(using TL, Raise, Elaborator.State): tl.log("-----------------------------------------") ctorDests.ctorDests.foreach: - case (ctorExprId, CtorDest(matches, sels, noCons)) => tl.log: + case (ctorExprId, CtorDest(matches, sels, noCons, _)) => tl.log: val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" val matchExprScruts = "if " + matches.map{(s, m) => m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" @@ -562,6 +562,9 @@ class Deforest(using TL, Raise, Elaborator.State): def update(ctor: ResultId, n: NoCons.type) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, sels, true, vars)) case None => Some(CtorDest(Map.empty, Nil, true, Nil)) + def update(ctor: ResultId, v: StratVarState) = ctorDests.updateWith(ctor): + case Some(dests) => Some(dests.copy(callResVars = v :: dests.callResVars)) + case None => Some(CtorDest(Map.empty, Nil, false, v :: Nil)) def get(ctor: ResultId) = ctorDests.get(ctor) object dtorSources: @@ -619,6 +622,10 @@ class Deforest(using TL, Raise, Elaborator.State): () case _ => handle(l -> cons) case (_, c: ConsVar) => + prod -> c match + case Ctor(expr = e, _) -> _ if c.s.callResOf.isDefined => ctorDests.update(e, c.s) + case _ => () + lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) upperBounds(c.uid).foreach: u => (prod, u) match @@ -661,7 +668,7 @@ class Deforest(using TL, Raise, Elaborator.State): else tl.log("rm ctor: " + rm.map(c => c.getClsSymOfUid.nme).mkString(" | ")) val toDeleteDtors = rm.flatMap(r => ctorToDtor.remove(r)).flatMap: - case CtorDest(mat, sels, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) + case CtorDest(mat, sels, _, _) => mat.keySet.map(s => DtorExpr.Match(s)) ++ sels.map(s => DtorExpr.Sel(s.expr)) removeDtor(toDeleteDtors) def removeDtor(rm: Set[DtorExpr]): Unit = @@ -673,7 +680,7 @@ class Deforest(using TL, Raise, Elaborator.State): // remove clashes: removeCtor( - ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons) => + ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons, _) => ((dtors.size == 0 && sels.size == 1) || (dtors.size == 1 && { val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult @@ -714,7 +721,7 @@ class Deforest(using TL, Raise, Elaborator.State): val newCtorsAndNewMatches = ctorAndMatches.flatMap((c, m) => getCtorInArm(c, m)).flatMap: c => ctorToDtor.get(c).flatMap: - case CtorDest(matches, sels, false) => matches.values.headOption.map(m => c -> m) + case CtorDest(matches, sels, false, _) => matches.values.headOption.map(m => c -> m) val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) if newCtorsAndNewMatches.isEmpty then Set.empty @@ -724,7 +731,7 @@ class Deforest(using TL, Raise, Elaborator.State): go(newCtorsAndNewMatches) go(Set(ctor -> dtor)) val toRmCtor = ctorToDtor.flatMap: - case (c, CtorDest(matches, sels, false)) => + case (c, CtorDest(matches, sels, false, _)) => assert(matches.size <= 1) matches.values.flatMap(m => findCycle(c, m)) removeCtor(toRmCtor.toSet) @@ -739,7 +746,7 @@ class Deforest(using TL, Raise, Elaborator.State): // we need only one CtorFinalDest per arm for each pat mat expr val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] - resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false)) => + resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false, _)) => val filteredDtor = { if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) else if dtors.size == 0 && sels.size > 1 then From 06484e7dd3ffe57328a6eeca9d2ba4ccffdef3be Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 5 May 2025 20:13:24 +0800 Subject: [PATCH 211/303] wip: find def dup chances --- .../scala/hkmc2/codegen/Deforestation.scala | 26 +++++++++++++++++++ .../test/mlscript/deforest/def-dup/simple.mls | 16 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 00435fe205..5729d38595 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -659,6 +659,32 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== + lazy val findDefDupChances = + // clash potentially solvable by duplicating def only if *all* the call-res vars have exact only one dtor + def checkStratVar(v: StratVarState): Bool -> Opt[Dtor] = + var dtor: Opt[Dtor] = N + var dtorCount = 0 + var hasNoCons = false + upperBounds(v.uid).foreach: + case d: Dtor => dtorCount += 1; dtor = S(d) + // TODO: consider about field selection as dtor... also about field sel inside branches + case FieldSel(expr, inMatching) => ??? + case ConsFun(l, r) => lastWords("ctor has ConsFun") + case ConsVar(s) => () + case NoCons => hasNoCons = true + if dtorCount == 1 && !hasNoCons then true -> dtor + else if dtorCount == 0 && !hasNoCons then true -> N + else false -> N + def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = + val info = vs.map(x => x -> checkStratVar(x)) + // if all of the call-res vars only have one dtor, then + // find the call-reses that causes clash and thus need to be duplicated + if info.forall(_._2._1) then + info.map(x => x._1.callResOf.get -> x._2._2.get).groupBy(_._2).values.withFilter(_.size > 1).flatMap(l => l.map(_._1)) + else + Nil + ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)) + lazy val resolveClashes = val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 2e4f5fcc58..2f4e901f00 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -5,6 +5,9 @@ data class A(x) +data class (::) Cons(h, t) +object Nil + // `A(1)` is in the definition of `test`, but // no duplication is helpful @@ -20,3 +23,16 @@ test() + test() //│ No fusion opportunity +// two calls to `to` should be able to be duplicated +fun to(n) = if n > 0 then n :: to(n - 1) else Nil +fun f1(ls) = if ls is + h :: t then h + Nil then 2 +fun f2(ls) = if ls is + h :: t then h + 1 + Nil then 3 +f1(to(4)) + f2(to(5)) +//│ = 10 +//│ No fusion opportunity + + From fc63b1ac277c204aaf6ac384ff081f88814c49a4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 6 May 2025 15:31:18 +0800 Subject: [PATCH 212/303] wip: still need to compute all upperBounds --- .../scala/hkmc2/codegen/Deforestation.scala | 106 +++++++++++------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5729d38595..052f835b00 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -281,7 +281,7 @@ class Deforest(using TL, Raise, Elaborator.State): val resultIdToResult = mutable.Map.empty[ResultId, Result] val funSymToFunDef = mutable.Map.empty[BlockMemberSymbol, FunDefn] - def apply(p: Program): Opt[Program] -> String -> Int = + def apply(p: Program, duplicate: Bool = false): Opt[Program] -> String -> Int = val mainBlk = p.main globallyDefinedVars.init(mainBlk) @@ -298,36 +298,47 @@ class Deforest(using TL, Raise, Elaborator.State): resolveConstraints - tl.log("-----------------------------------------") - ctorDests.ctorDests.foreach: - case (ctorExprId, CtorDest(matches, sels, noCons, _)) => tl.log: - val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" - val matchExprScruts = "if " + matches.map{(s, m) => - m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" - }.toList.sorted.mkString(" | ") + " then ... " - val selExpr = sels.map{ - case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" - }.toList.sorted.mkString(" | ") - s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" - tl.log("-----------------------------------------") - dtorSources.dtorSources.foreach: - case (d, DtorSource(ctors, noProd)) => - tl.log(s"$d <--- ${ctors.map(c => c.getClsSymOfUid.nme + s"(id:$c)").toList.mkString(" | ")} <--- (NoProd: $noProd)") - tl.log("-----------------------------------------") - filteredCtorDests.foreach: - case (ctorUid, CtorFinalDest.Sel(s)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --sel--> " + s) - case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --mat--> " + scrut ) - - val fusionStat = filteredCtorDests.map: - case (ctorUid, CtorFinalDest.Sel(s)) => - "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${resultIdToResult(s).asInstanceOf[Select].name}`" - case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => - "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" - - if filteredCtorDests.nonEmpty then - S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size + val defDuplicateInfo = findDefDupChances + tl.log("duplication chances:") + defDuplicateInfo.foreach: (r, s) => + tl.log(s"\t${r.getResult} <-- dup --> $s") + if duplicate then + val defDuplicateInfo = findDefDupChances + ??? else - S(p) -> s"0 fusion opportunity" -> 0 + // tl.log("-----------------------------------------") + // upperBounds.foreach: (v, u) => + + tl.log("-----------------------------------------") + ctorDests.ctorDests.foreach: + case (ctorExprId, CtorDest(matches, sels, noCons, _)) => tl.log: + val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" + val matchExprScruts = "if " + matches.map{(s, m) => + m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" + }.toList.sorted.mkString(" | ") + " then ... " + val selExpr = sels.map{ + case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" + }.toList.sorted.mkString(" | ") + s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" + tl.log("-----------------------------------------") + dtorSources.dtorSources.foreach: + case (d, DtorSource(ctors, noProd)) => + tl.log(s"$d <--- ${ctors.map(c => c.getClsSymOfUid.nme + s"(id:$c)").toList.mkString(" | ")} <--- (NoProd: $noProd)") + tl.log("-----------------------------------------") + filteredCtorDests.foreach: + case (ctorUid, CtorFinalDest.Sel(s)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --sel--> " + s) + case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --mat--> " + scrut ) + + val fusionStat = filteredCtorDests.map: + case (ctorUid, CtorFinalDest.Sel(s)) => + "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${resultIdToResult(s).asInstanceOf[Select].name}`" + case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => + "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" + + if filteredCtorDests.nonEmpty then + S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size + else + S(p) -> s"0 fusion opportunity" -> 0 object globallyDefinedVars: val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) @@ -460,7 +471,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) // symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) + val res = freshVar(s"${inDef.fold("wer")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) @@ -476,9 +487,9 @@ class Deforest(using TL, Raise, Elaborator.State): s.symbol.map(_.asCls) match case None => val pStrat = processResult(p) - val tpeVar = freshVar("", N) + val tpeVar = freshVar("1", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) - val appRes = freshVar("", N) // unknown function symbol + val appRes = freshVar("2", N) // unknown function symbol constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 case Some(None) => @@ -525,13 +536,13 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) pStrat match case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => - val tpeVar = freshVar("", N) + val tpeVar = freshVar("3", N) val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => - val tpeVar = freshVar("", N) + val tpeVar = freshVar("4", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 @@ -658,15 +669,25 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== - + + // def finishUpperAndLowerBounds = + def allUpperBoundsOf(k: StratVarId, cache: Set[StratVarId]): Set[ConsStrat] = + upperBounds(k).toSet.flatMap: + case u@ConsVar(s) if !cache.contains(s.uid) => allUpperBoundsOf(s.uid, cache + s.uid) + u + case u => Set(u) + lazy val findDefDupChances = // clash potentially solvable by duplicating def only if *all* the call-res vars have exact only one dtor def checkStratVar(v: StratVarState): Bool -> Opt[Dtor] = + assert(v.callResOf.isDefined) + tl.log(allUpperBoundsOf(v.uid, Set(v.uid))) var dtor: Opt[Dtor] = N var dtorCount = 0 var hasNoCons = false - upperBounds(v.uid).foreach: - case d: Dtor => dtorCount += 1; dtor = S(d) + allUpperBoundsOf(v.uid, Set(v.uid)).foreach: + case d: Dtor => + tl.log("dtor") + dtorCount += 1; dtor = S(d) // TODO: consider about field selection as dtor... also about field sel inside branches case FieldSel(expr, inMatching) => ??? case ConsFun(l, r) => lastWords("ctor has ConsFun") @@ -676,11 +697,16 @@ class Deforest(using TL, Raise, Elaborator.State): else if dtorCount == 0 && !hasNoCons then true -> N else false -> N def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = + tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})")).mkString(" | ")) val info = vs.map(x => x -> checkStratVar(x)) // if all of the call-res vars only have one dtor, then // find the call-reses that causes clash and thus need to be duplicated if info.forall(_._2._1) then - info.map(x => x._1.callResOf.get -> x._2._2.get).groupBy(_._2).values.withFilter(_.size > 1).flatMap(l => l.map(_._1)) + // tl.log("true") + val tmp = info.withFilter(_._2._2.isDefined).map(x => x._1.callResOf.get -> x._2._2.get) + tl.log(tmp.size) + tmp.groupBy(_._2).values.withFilter(_.size > 1).flatMap(l => l.map(_._1)) + else Nil ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)) @@ -838,7 +864,7 @@ class Deforest(using TL, Raise, Elaborator.State): val rest = deforestTransformer.applyBlock(p) val newDefsRest = deforestTransformer.matchRest.getAllFunDefs val newDefsArms = deforestTransformer.matchArms.getAllFunDefs - newDefsArms(newDefsRest(rest)) + newDefsArms(newDefsRest(rest)) class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): self => From ee345292356ba56b12833e92473cb24ecec7489d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 6 May 2025 16:16:23 +0800 Subject: [PATCH 213/303] minor cleanup --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 052f835b00..30ea43021f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -471,7 +471,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) // symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar(s"${inDef.fold("wer")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) + val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) @@ -487,9 +487,9 @@ class Deforest(using TL, Raise, Elaborator.State): s.symbol.map(_.asCls) match case None => val pStrat = processResult(p) - val tpeVar = freshVar("1", N) + val tpeVar = freshVar("", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) - val appRes = freshVar("2", N) // unknown function symbol + val appRes = freshVar("", N) // unknown function symbol constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 case Some(None) => @@ -536,13 +536,13 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) pStrat match case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => - val tpeVar = freshVar("3", N) + val tpeVar = freshVar("", N) val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => - val tpeVar = freshVar("4", N) + val tpeVar = freshVar("", N) constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 From e26a234988610ad33d470343acad2b87d3a15386 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 7 May 2025 14:52:25 +0800 Subject: [PATCH 214/303] find def dup chance --- .../scala/hkmc2/codegen/Deforestation.scala | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 30ea43021f..f5f6f9a25b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -670,7 +670,6 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== - // def finishUpperAndLowerBounds = def allUpperBoundsOf(k: StratVarId, cache: Set[StratVarId]): Set[ConsStrat] = upperBounds(k).toSet.flatMap: case u@ConsVar(s) if !cache.contains(s.uid) => allUpperBoundsOf(s.uid, cache + s.uid) + u @@ -680,16 +679,16 @@ class Deforest(using TL, Raise, Elaborator.State): // clash potentially solvable by duplicating def only if *all* the call-res vars have exact only one dtor def checkStratVar(v: StratVarState): Bool -> Opt[Dtor] = assert(v.callResOf.isDefined) - tl.log(allUpperBoundsOf(v.uid, Set(v.uid))) + // tl.log(allUpperBoundsOf(v.uid, Set(v.uid))) var dtor: Opt[Dtor] = N var dtorCount = 0 var hasNoCons = false allUpperBoundsOf(v.uid, Set(v.uid)).foreach: case d: Dtor => - tl.log("dtor") + // tl.log("dtor") dtorCount += 1; dtor = S(d) // TODO: consider about field selection as dtor... also about field sel inside branches - case FieldSel(expr, inMatching) => ??? + case FieldSel(expr, inMatching) => () case ConsFun(l, r) => lastWords("ctor has ConsFun") case ConsVar(s) => () case NoCons => hasNoCons = true @@ -697,16 +696,25 @@ class Deforest(using TL, Raise, Elaborator.State): else if dtorCount == 0 && !hasNoCons then true -> N else false -> N def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = - tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})")).mkString(" | ")) + // tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})").get).mkString(" | ")) val info = vs.map(x => x -> checkStratVar(x)) // if all of the call-res vars only have one dtor, then // find the call-reses that causes clash and thus need to be duplicated if info.forall(_._2._1) then - // tl.log("true") - val tmp = info.withFilter(_._2._2.isDefined).map(x => x._1.callResOf.get -> x._2._2.get) - tl.log(tmp.size) - tmp.groupBy(_._2).values.withFilter(_.size > 1).flatMap(l => l.map(_._1)) - + // TODO: optimize logic + info + .withFilter(_._2._2.isDefined) // discard those without a dtor + .map(x => x._1.callResOf.get -> x._2._2.get) // get a list of (callSiteInfo, Dtor) + .groupBy(_._2) // group by the dtor + .toList + .sortBy(entry => entry._2.size) // sort by the number of call sites for lesser duplication + match + // only has one dtor, no need to duplicate + case h :: Nil => Nil + // more than one dtors, duplicate those with less call sites + case heads :+ last => heads.flatMap(x => x._2.map(_._1)) + // no dtor, no need for duplication + case _ => Nil else Nil ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)) From c7fe134e1592981466122b269835f6056fc3e579 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 7 May 2025 15:43:56 +0800 Subject: [PATCH 215/303] need a toMap to dedup --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f5f6f9a25b..cae68b2d21 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -717,7 +717,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => Nil else Nil - ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)) + ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)).toMap lazy val resolveClashes = val ctorToDtor = ctorDests.ctorDests From 375933169f8dca5123ac5eb319f7c17d90ed5d3d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 7 May 2025 18:11:32 +0800 Subject: [PATCH 216/303] improve later: flag for deforest def dup --- .../scala/hkmc2/codegen/Deforestation.scala | 17 +++++++++-------- .../test/mlscript/deforest/def-dup/simple.mls | 17 ++++++++++++++++- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 5 +++-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index cae68b2d21..d240afc2ea 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -281,7 +281,7 @@ class Deforest(using TL, Raise, Elaborator.State): val resultIdToResult = mutable.Map.empty[ResultId, Result] val funSymToFunDef = mutable.Map.empty[BlockMemberSymbol, FunDefn] - def apply(p: Program, duplicate: Bool = false): Opt[Program] -> String -> Int = + def apply(p: Program, duplicate: Bool = false, output: String => Unit): Opt[Program] -> String -> Int = val mainBlk = p.main globallyDefinedVars.init(mainBlk) @@ -298,14 +298,14 @@ class Deforest(using TL, Raise, Elaborator.State): resolveConstraints - val defDuplicateInfo = findDefDupChances - tl.log("duplication chances:") - defDuplicateInfo.foreach: (r, s) => - tl.log(s"\t${r.getResult} <-- dup --> $s") + if duplicate then - val defDuplicateInfo = findDefDupChances - ??? - else + // val defDuplicateInfo = findDefDupChances + output("duplication chances:") + findDefDupChances.foreach: (r, s) => + output(s"\t${r.getResult} <-- dup --> $s") + // TODO: def dup: change later + if true then // tl.log("-----------------------------------------") // upperBounds.foreach: (v, u) => @@ -339,6 +339,7 @@ class Deforest(using TL, Raise, Elaborator.State): S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size else S(p) -> s"0 fusion opportunity" -> 0 + else die object globallyDefinedVars: val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 2f4e901f00..ee5d7aeba4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -8,7 +8,7 @@ data class A(x) data class (::) Cons(h, t) object Nil - +:deforestDup // `A(1)` is in the definition of `test`, but // no duplication is helpful fun test() = @@ -20,9 +20,22 @@ fun test() = a + b test() + test() //│ = 4 +//│ duplication chances: +//│ No fusion opportunity + +:deforestDup +fun p() = A(3) +fun c1(x) = if x is A then 1 +fun c2(x) = if x is A then 2 +c1(p()) + c2(p()) +//│ = 3 +//│ duplication chances: +//│ Call(Ref(member:p),List()) <-- dup --> member:p //│ No fusion opportunity + +:deforestDup // two calls to `to` should be able to be duplicated fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is @@ -33,6 +46,8 @@ fun f2(ls) = if ls is Nil then 3 f1(to(4)) + f2(to(5)) //│ = 10 +//│ duplication chances: +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to //│ No fusion opportunity diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 707bb8b0d5..6c5518dac3 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -25,6 +25,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val showRepl = NullaryCommand("showRepl") val traceJS = NullaryCommand("traceJS") val deforestFlag = NullaryCommand("deforest") + val deforestDupFlag = NullaryCommand("deforestDup") val deforestInfo = NullaryCommand("deforestInfo") val expect = Command("expect"): ln => ln.trim @@ -88,7 +89,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if deforestFlag.isSet then val deforest = new Deforest(using deforestTL) - val deforestRes -> _ -> num = deforest(le) + val deforestRes -> _ -> num = deforest(le, deforestDupFlag.isSet, output.apply) deforestRes match case None => () case Some(_) if num == 0 => output("No fusion opportunity") @@ -246,7 +247,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: codegen.Lowering() val lowered0 = deforestLow.program(blk) val deforest = new Deforest(using deforestTL) - val maybeDeforestRes -> deforestStat -> num = deforest(lowered0) + val maybeDeforestRes -> deforestStat -> num = deforest(lowered0, deforestDupFlag.isSet, output.apply) maybeDeforestRes match case None => () case Some(_) if num == 0 => output("No fusion opportunity") From 5951318571a48a35a2a263e6a6d398fec9300bfd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 7 May 2025 21:46:02 +0800 Subject: [PATCH 217/303] try to fix nondeterminism --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 6 +++--- .../src/test/mlscript/deforest/def-dup/simple.mls | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index d240afc2ea..7df62f3084 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -705,15 +705,15 @@ class Deforest(using TL, Raise, Elaborator.State): // TODO: optimize logic info .withFilter(_._2._2.isDefined) // discard those without a dtor - .map(x => x._1.callResOf.get -> x._2._2.get) // get a list of (callSiteInfo, Dtor) + .map(x => x._1 -> x._2._2.get) // get a list of (callSiteInfoVar, Dtor) .groupBy(_._2) // group by the dtor .toList - .sortBy(entry => entry._2.size) // sort by the number of call sites for lesser duplication + .sortBy((_, callsites) => callsites.size -> callsites.headOption.map(_._1.uid)) // sort by the number of call sites for lesser duplication, and the callres var id for determinism match // only has one dtor, no need to duplicate case h :: Nil => Nil // more than one dtors, duplicate those with less call sites - case heads :+ last => heads.flatMap(x => x._2.map(_._1)) + case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get)) // no dtor, no need for duplication case _ => Nil else diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index ee5d7aeba4..6d75494946 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -24,13 +24,15 @@ test() + test() //│ No fusion opportunity :deforestDup -fun p() = A(3) +fun p(d) = A(3) fun c1(x) = if x is A then 1 fun c2(x) = if x is A then 2 -c1(p()) + c2(p()) +fun test() = + c1(p(1)) + c2(p(2)) +test() //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List()) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p //│ No fusion opportunity @@ -47,7 +49,7 @@ fun f2(ls) = if ls is f1(to(4)) + f2(to(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to //│ No fusion opportunity From f9c216b865eaa891ed3443ade8080700a198339d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 7 May 2025 23:02:14 +0800 Subject: [PATCH 218/303] minor; update test --- .../scala/hkmc2/codegen/Deforestation.scala | 2 +- .../test/mlscript/deforest/def-dup/simple.mls | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7df62f3084..dfc705f529 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -686,7 +686,7 @@ class Deforest(using TL, Raise, Elaborator.State): var hasNoCons = false allUpperBoundsOf(v.uid, Set(v.uid)).foreach: case d: Dtor => - // tl.log("dtor") + tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") dtorCount += 1; dtor = S(d) // TODO: consider about field selection as dtor... also about field sel inside branches case FieldSel(expr, inMatching) => () diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 6d75494946..8dcea8893e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -38,18 +38,31 @@ test() :deforestDup -// two calls to `to` should be able to be duplicated -fun to(n) = if n > 0 then n :: to(n - 1) else Nil -fun f1(ls) = if ls is +// calls to `to` should be able to be duplicated +fun to(n) = + if n > 0 then + let m = n - 1 + n :: to(m) + else + Nil +fun f1(ls1) = if ls1 is h :: t then h Nil then 2 -fun f2(ls) = if ls is - h :: t then h + 1 +fun f2(ls2) = if ls2 is + h :: t then h Nil then 3 -f1(to(4)) + f2(to(5)) -//│ = 10 +fun f3(ls3) = if ls3 is + h :: t then h + Nil then 4 +fun f4(ls4) = if ls4 is + h :: t then h + Nil then 5 +f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) +//│ = 22 //│ duplication chances: //│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to //│ No fusion opportunity From b59f46c7cfbb3829cb07d96fa5b5de14e4a0e47f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 8 May 2025 15:30:27 +0800 Subject: [PATCH 219/303] ignore obvious recursive callres --- .../scala/hkmc2/codegen/Deforestation.scala | 63 +++++++++++++++---- .../test/mlscript/deforest/def-dup/simple.mls | 27 ++++++-- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index dfc705f529..ebb4632dd6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -56,6 +56,12 @@ extension (i: ResultId) Some(k(i, v, v.l.asObj.get, Nil)) case _ => None def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get + def getFunCallBlkMemSyn(using Deforest) = i.getResult match + case Call(fun, _) => fun match + case s: Select => s.symbol.flatMap(_.asBlkMember) + case v: Value.Ref => v.l.asBlkMember + case _ => N + case _ => N case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId)(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat @@ -408,6 +414,22 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c + object callInfo: + val store = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] + val callSiteInDefInfo = mutable.Map.empty[ResultId, Symbol] + def update(caller: Symbol, callee: Symbol, callResultId: ResultId) = + store.updateWith(caller): + case None => Some((callResultId -> callee) :: Nil) + case Some(l) => Some((callResultId -> callee) :: l) + callSiteInDefInfo.updateWith(callResultId): + case None => Some(caller) + case _ => die + def isObviousRecursiveCall(c: ResultId) = + tl.log("checking callsite: " + c.getResult.toString() + s"@$c") + val sym = c.getFunCallBlkMemSyn.get + callSiteInDefInfo.get(c).fold(false)(_ is sym) + + def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol], @@ -499,6 +521,10 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + funSym.nme + "_res", s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)) // if `f` has a blockMemberSymbol, then record it ) + for + caller <- inDef + callee <- s.symbol.flatMap(_.asBlkMember) + do callInfo.update(caller, callee, c.uid) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => @@ -514,6 +540,10 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + l.nme + "_res", l.asBlkMember.map(c.uid -> _) // if `f` has a blockMemberSymbol, then record it ) + for + caller <- inDef + callee <- l.asBlkMember + do callInfo.update(caller, callee, c.uid) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => @@ -677,25 +707,32 @@ class Deforest(using TL, Raise, Elaborator.State): case u => Set(u) lazy val findDefDupChances = - // clash potentially solvable by duplicating def only if *all* the call-res vars have exact only one dtor + // clash potentially solvable by duplicating def only if *all* the call-res vars that we care have exact only one dtor def checkStratVar(v: StratVarState): Bool -> Opt[Dtor] = assert(v.callResOf.isDefined) // tl.log(allUpperBoundsOf(v.uid, Set(v.uid))) var dtor: Opt[Dtor] = N var dtorCount = 0 var hasNoCons = false - allUpperBoundsOf(v.uid, Set(v.uid)).foreach: - case d: Dtor => - tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") - dtorCount += 1; dtor = S(d) - // TODO: consider about field selection as dtor... also about field sel inside branches - case FieldSel(expr, inMatching) => () - case ConsFun(l, r) => lastWords("ctor has ConsFun") - case ConsVar(s) => () - case NoCons => hasNoCons = true - if dtorCount == 1 && !hasNoCons then true -> dtor - else if dtorCount == 0 && !hasNoCons then true -> N - else false -> N + + // ignore obvious recursive call sites: won't duplicate them anyway since we are not aligning recursion length + if callInfo.isObviousRecursiveCall(v.callResOf.get._1) then true -> N + else + allUpperBoundsOf(v.uid, Set(v.uid)).foreach: + case d: Dtor => + tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") + dtorCount += 1; dtor = S(d) + // TODO: consider about field selection as dtor... also about field sel inside branches + case FieldSel(expr, inMatching) => () + case ConsFun(l, r) => lastWords("ctor has ConsFun") + case ConsVar(s) => () + case NoCons => hasNoCons = true + + if dtorCount == 1 && !hasNoCons then true -> dtor + else if dtorCount == 0 && !hasNoCons then true -> N + else false -> N + + def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = // tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})").get).mkString(" | ")) val info = vs.map(x => x -> checkStratVar(x)) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 8dcea8893e..cdbb1f49d6 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -36,6 +36,23 @@ test() //│ No fusion opportunity +:deforestDup +fun p(x) = A(x) +fun f1(a1) = if a1 is A(aa) then aa +fun f2(a2) = if a2 is A(aa) then aa +fun f3(a3) = if a3 is A(aa) then aa +fun f4(a4) = if a4 is A(aa) then aa +fun f5(a5) = if a5 is A(aa) then aa +f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) +//│ = 15 +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p +//│ No fusion opportunity + + :deforestDup // calls to `to` should be able to be duplicated @@ -46,19 +63,19 @@ fun to(n) = else Nil fun f1(ls1) = if ls1 is - h :: t then h + h :: t then h + f1(t) Nil then 2 fun f2(ls2) = if ls2 is - h :: t then h + h :: t then h + f2(t) Nil then 3 fun f3(ls3) = if ls3 is - h :: t then h + h :: t then h + f3(t) Nil then 4 fun f4(ls4) = if ls4 is - h :: t then h + h :: t then h + f4(t) Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) -//│ = 22 +//│ = 88 //│ duplication chances: //│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to //│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to From c457613b4a660334e9f74e735dcd43d962bf49f3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 8 May 2025 15:33:09 +0800 Subject: [PATCH 220/303] minor --- .../shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ebb4632dd6..5453b3c4c6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -310,7 +310,7 @@ class Deforest(using TL, Raise, Elaborator.State): output("duplication chances:") findDefDupChances.foreach: (r, s) => output(s"\t${r.getResult} <-- dup --> $s") - // TODO: def dup: change later + // DefDupTODO: def dup: change later if true then // tl.log("-----------------------------------------") // upperBounds.foreach: (v, u) => @@ -722,7 +722,7 @@ class Deforest(using TL, Raise, Elaborator.State): case d: Dtor => tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") dtorCount += 1; dtor = S(d) - // TODO: consider about field selection as dtor... also about field sel inside branches + // DefDupTODO: consider about field selection as dtor... also about field sel inside branches case FieldSel(expr, inMatching) => () case ConsFun(l, r) => lastWords("ctor has ConsFun") case ConsVar(s) => () @@ -739,7 +739,7 @@ class Deforest(using TL, Raise, Elaborator.State): // if all of the call-res vars only have one dtor, then // find the call-reses that causes clash and thus need to be duplicated if info.forall(_._2._1) then - // TODO: optimize logic + // DefDupTODO: optimize logic info .withFilter(_._2._2.isDefined) // discard those without a dtor .map(x => x._1 -> x._2._2.get) // get a list of (callSiteInfoVar, Dtor) From fe02fa7ec3fde75d2b228e6d6337c265650e8c60 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 8 May 2025 17:20:19 +0800 Subject: [PATCH 221/303] cache some result --- .../scala/hkmc2/codegen/Deforestation.scala | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5453b3c4c6..907ab17797 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -425,7 +425,7 @@ class Deforest(using TL, Raise, Elaborator.State): case None => Some(caller) case _ => die def isObviousRecursiveCall(c: ResultId) = - tl.log("checking callsite: " + c.getResult.toString() + s"@$c") + // tl.log("checking callsite: " + c.getResult.toString() + s"@$c") val sym = c.getFunCallBlkMemSyn.get callSiteInDefInfo.get(c).fold(false)(_ is sym) @@ -708,29 +708,30 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val findDefDupChances = // clash potentially solvable by duplicating def only if *all* the call-res vars that we care have exact only one dtor - def checkStratVar(v: StratVarState): Bool -> Opt[Dtor] = - assert(v.callResOf.isDefined) - // tl.log(allUpperBoundsOf(v.uid, Set(v.uid))) - var dtor: Opt[Dtor] = N - var dtorCount = 0 - var hasNoCons = false - - // ignore obvious recursive call sites: won't duplicate them anyway since we are not aligning recursion length - if callInfo.isObviousRecursiveCall(v.callResOf.get._1) then true -> N - else - allUpperBoundsOf(v.uid, Set(v.uid)).foreach: - case d: Dtor => - tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") - dtorCount += 1; dtor = S(d) - // DefDupTODO: consider about field selection as dtor... also about field sel inside branches - case FieldSel(expr, inMatching) => () - case ConsFun(l, r) => lastWords("ctor has ConsFun") - case ConsVar(s) => () - case NoCons => hasNoCons = true + object checkStratVar: + val cache = mutable.Map.empty[StratVarState, Bool -> Opt[Dtor]] + def apply(v: StratVarState): Bool -> Opt[Dtor] = cache.getOrElseUpdate.curried(v): + assert(v.callResOf.isDefined) + var dtor: Opt[Dtor] = N + var dtorCount = 0 + var hasNoCons = false - if dtorCount == 1 && !hasNoCons then true -> dtor - else if dtorCount == 0 && !hasNoCons then true -> N - else false -> N + // ignore obvious recursive call sites: won't duplicate them anyway since we are not aligning recursion length + if callInfo.isObviousRecursiveCall(v.callResOf.get._1) then true -> N + else + allUpperBoundsOf(v.uid, Set(v.uid)).foreach: + case d: Dtor => + tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") + dtorCount += 1; dtor = S(d) + // DefDupTODO: consider about field selection as dtor... also about field sel inside branches + case FieldSel(expr, inMatching) => () + case ConsFun(l, r) => lastWords("ctor has ConsFun") + case ConsVar(s) => () + case NoCons => hasNoCons = true + + if dtorCount == 1 && !hasNoCons then true -> dtor + else if dtorCount == 0 && !hasNoCons then true -> N + else false -> N def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = From 3a4f269098bb69afedfc5c4b0233b15dee7bc7ac Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 9 May 2025 14:44:11 +0800 Subject: [PATCH 222/303] improve where to check obvious recursive calls --- .../scala/hkmc2/codegen/Deforestation.scala | 62 +++++++++++-------- .../test/mlscript/deforest/def-dup/simple.mls | 19 ++++++ 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 907ab17797..abc793af72 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -307,6 +307,7 @@ class Deforest(using TL, Raise, Elaborator.State): if duplicate then // val defDuplicateInfo = findDefDupChances + // DefDupTODO: do not use `output` from difftest here output("duplication chances:") findDefDupChances.foreach: (r, s) => output(s"\t${r.getResult} <-- dup --> $s") @@ -707,49 +708,58 @@ class Deforest(using TL, Raise, Elaborator.State): case u => Set(u) lazy val findDefDupChances = - // clash potentially solvable by duplicating def only if *all* the call-res vars that we care have exact only one dtor object checkStratVar: val cache = mutable.Map.empty[StratVarState, Bool -> Opt[Dtor]] + // A clash from a function callsite potentially solvable by duplicating def only + // if *all* the call-res vars that we care have exact only one dtor. + // This function checks if a single callsite is solvable, and + // return (solvable -> corresponding pat mat dtor). def apply(v: StratVarState): Bool -> Opt[Dtor] = cache.getOrElseUpdate.curried(v): assert(v.callResOf.isDefined) var dtor: Opt[Dtor] = N var dtorCount = 0 var hasNoCons = false - // ignore obvious recursive call sites: won't duplicate them anyway since we are not aligning recursion length - if callInfo.isObviousRecursiveCall(v.callResOf.get._1) then true -> N - else - allUpperBoundsOf(v.uid, Set(v.uid)).foreach: - case d: Dtor => - tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") - dtorCount += 1; dtor = S(d) - // DefDupTODO: consider about field selection as dtor... also about field sel inside branches - case FieldSel(expr, inMatching) => () - case ConsFun(l, r) => lastWords("ctor has ConsFun") - case ConsVar(s) => () - case NoCons => hasNoCons = true - - if dtorCount == 1 && !hasNoCons then true -> dtor - else if dtorCount == 0 && !hasNoCons then true -> N - else false -> N + allUpperBoundsOf(v.uid, Set(v.uid)).foreach: + case d: Dtor => + tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") + dtorCount += 1; dtor = S(d) + // DefDupTODO: consider about field selection as dtor... also about field sel inside branches + case FieldSel(expr, inMatching) => () + case ConsFun(l, r) => lastWords("ctor has ConsFun") + case ConsVar(s) => () + case NoCons => hasNoCons = true + + if dtorCount == 1 && !hasNoCons then true -> dtor + else if dtorCount == 0 && !hasNoCons then true -> N + else false -> N def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = // tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})").get).mkString(" | ")) val info = vs.map(x => x -> checkStratVar(x)) - // if all of the call-res vars only have one dtor, then - // find the call-reses that causes clash and thus need to be duplicated - if info.forall(_._2._1) then - // DefDupTODO: optimize logic + // If all of the call-res vars are "solvable", then + // find the call-reses that causes clash and thus need to be duplicated. + // Optimistically ignore problems caused by + // obvious recursive call sites (which will not be duplicated anyway currently) + if info.forall: + case (callSite, isSolvable -> dtor) => + isSolvable || + callInfo.isObviousRecursiveCall(callSite.callResOf.get._1) + then info - .withFilter(_._2._2.isDefined) // discard those without a dtor - .map(x => x._1 -> x._2._2.get) // get a list of (callSiteInfoVar, Dtor) - .groupBy(_._2) // group by the dtor + .filter: + case (callSite, isSolvable -> dtor) => + dtor.isDefined // discard those without a dtor + !callInfo.isObviousRecursiveCall(callSite.callResOf.get._1) // and those that are obviously recursive calls + .groupBy(_._2._2.get) // group by the dtor .toList - .sortBy((_, callsites) => callsites.size -> callsites.headOption.map(_._1.uid)) // sort by the number of call sites for lesser duplication, and the callres var id for determinism + .sortBy: (_, callsites) => + // sort by the number of call sites for lesser duplication, and the callres var id for determinism + callsites.size -> callsites.headOption.map(_._1.uid) match // only has one dtor, no need to duplicate - case h :: Nil => Nil + case h :: Nil => Nil // h._2.map(_._1.callResOf.get) // more than one dtors, duplicate those with less call sites case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get)) // no dtor, no need for duplication diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index cdbb1f49d6..2114255afa 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -83,3 +83,22 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ No fusion opportunity +:deforestDup +fun to(n) = + if n > 0 then + let m = n - 1 + n :: to(m) + else + Nil +fun f1(ls1) = if ls1 is + h :: t then h + f1(t) + Nil then 2 +f1(to(4)) +//│ = 12 +//│ duplication chances: +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 12 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls1 is ...` +//│ Nil --match--> `if ls1 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 7c49c57d499eeaacde944033f26619713aa90284 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 9 May 2025 14:53:24 +0800 Subject: [PATCH 223/303] minor fix --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index abc793af72..3469d6e84e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -750,7 +750,7 @@ class Deforest(using TL, Raise, Elaborator.State): info .filter: case (callSite, isSolvable -> dtor) => - dtor.isDefined // discard those without a dtor + dtor.isDefined && // discard those without a dtor !callInfo.isObviousRecursiveCall(callSite.callResOf.get._1) // and those that are obviously recursive calls .groupBy(_._2._2.get) // group by the dtor .toList From ee963caa591f29a679d2d16e45532bcead8d2d77 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 9 May 2025 15:38:03 +0800 Subject: [PATCH 224/303] fix the passing of `inDef` --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 3469d6e84e..ec3a47fb67 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -444,7 +444,8 @@ class Deforest(using TL, Raise, Elaborator.State): case (Case.Cls(s, _), body) => processBlock(body)( using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), - matching + (scrut.uid -> s) + matching + (scrut.uid -> s), + inDef ) else arms.map: @@ -491,6 +492,7 @@ class Deforest(using TL, Raise, Elaborator.State): matching: LinkedHashMap[ResultId, ClsOrModSymbol], inDef: Opt[BlockMemberSymbol] ) = + tl.log(s"constr fun body: $inDef") val paramSyms = params.map: case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) @@ -504,6 +506,7 @@ class Deforest(using TL, Raise, Elaborator.State): matching: LinkedHashMap[ResultId, ClsOrModSymbol], inDef: Opt[BlockMemberSymbol] ): ProdStrat = + tl.log(s"========== processing: ${r.toString()} <<<<< in $inDef") def handleCallLike(f: Path, args: Ls[Path], c: Result) = val argsTpe = args.map(processResult) f match @@ -759,7 +762,9 @@ class Deforest(using TL, Raise, Elaborator.State): callsites.size -> callsites.headOption.map(_._1.uid) match // only has one dtor, no need to duplicate - case h :: Nil => Nil // h._2.map(_._1.callResOf.get) + case h :: Nil => + // h._2.map(_._1.callResOf.get) + Nil // more than one dtors, duplicate those with less call sites case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get)) // no dtor, no need for duplication From 85cc27f5a2af0db9afabe37c62b6613d38a025a0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 9 May 2025 20:02:51 +0800 Subject: [PATCH 225/303] another duplication chance --- .../scala/hkmc2/codegen/Deforestation.scala | 17 ++++-- .../test/mlscript/deforest/def-dup/simple.mls | 16 ++++++ .../test/mlscript/deforest/def-dup/todo.mls | 55 +++++++++++++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ec3a47fb67..1c85cd4ccb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -70,7 +70,7 @@ case object NoProd extends ProdStrat -class Dtor(val expr: Match, val outterMatch: Option[ResultId])(using d: Deforest) extends ConsStrat: +class Dtor(val expr: Match, val outterMatch: Option[ResultId], val inDef: Option[BlockMemberSymbol])(using d: Deforest) extends ConsStrat: d.matchScrutToMatchBlock.updateWith(expr.scrut.uid): case None => Some(expr) case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") @@ -438,7 +438,7 @@ class Deforest(using TL, Raise, Elaborator.State): ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1))) + constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1), inDef)) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: case (Case.Cls(s, _), body) => @@ -761,10 +761,15 @@ class Deforest(using TL, Raise, Elaborator.State): // sort by the number of call sites for lesser duplication, and the callres var id for determinism callsites.size -> callsites.headOption.map(_._1.uid) match - // only has one dtor, no need to duplicate - case h :: Nil => - // h._2.map(_._1.callResOf.get) - Nil + // only has one dtor + case (dtor, callsites) :: Nil => + callsites + .withFilter: (callsite, info) => + assert(info._2.get is dtor) + // it is a potential duplicate where the call res of a function + // is consumed by the body of the same function + dtor.inDef.fold(false)(_ is callsite.callResOf.get._2) + .map(_._1.callResOf.get) // more than one dtors, duplicate those with less call sites case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get)) // no dtor, no need for duplication diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 2114255afa..2a63d3231b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -102,3 +102,19 @@ f1(to(4)) //│ Cons --match--> `if ls1 is ...` //│ Nil --match--> `if ls1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +:deforestDup +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +fun succ(x) = x + 1 +fun double(x) = x * 2 +fun test() = + map(map(1 :: 2 :: Nil, succ), double) +test() +//│ = Cons(4, Cons(6, Nil)) +//│ duplication chances: +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map +//│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls new file mode 100644 index 0000000000..04b96ea5c9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -0,0 +1,55 @@ +:js +:deforest + +//│ No fusion opportunity + +data class A(x) + +data class (::) Cons(h, t) +object Nil + + +:deforestDup +// TODO: how to duplicate this? +// * The real flow of the program: +// ctor─►toRes┌─►wrapRes1─►f1Consumer +// └─►wrapRes2─►f2Consumer +// * The flow we currently got: +// ┌────►f1Consumer +// ctor┬─►toRes──►f2Consumer +// ├───►wrapRes1─►f1Consumer +// └───►wrapRes2─►f2Consumer +// and there are programs that +// have this flow and cannot be optimized after duplication +fun to(n) = if n > 0 then n :: to(n - 1) else Nil +fun f1(ls) = if ls is + h :: t then h + Nil then 2 +fun f2(ls) = if ls is + h :: t then h + 1 + Nil then 3 +fun wrap(n) = to(n) +f1(wrap(4)) + f2(wrap(5)) +//│ = 10 +//│ duplication chances: +//│ No fusion opportunity + + +:deforestDup +fun to(n) = if n > 0 then n :: to(n - 1) else Nil +fun f1(ls) = if ls is + h :: t then h + Nil then 2 +fun f2(ls) = if ls is + h :: t then h + 1 + Nil then 3 +fun badWrap(n) = + let x = to(n) + f1(x) + f2(x) + x +f1(wrap(4)) + f2(wrap(5)) +//│ = 10 +//│ duplication chances: +//│ No fusion opportunity + + From d9d9ace8aa0cd14a293e28dd1a169a1724cb7e03 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 10 May 2025 00:12:56 +0800 Subject: [PATCH 226/303] update comment to include an example for dead code using never assigned vars --- .../scala/hkmc2/codegen/Deforestation.scala | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 9f86649b93..017c5233f3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -1010,6 +1010,46 @@ class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) ex // If all the arms end with non-`End` blocks, then the `rest` of this `Match` will never be executed, // and we remove the `rest` in this case. This prevents `rest` to use variables that become // undefined because computation in arms that defines them are moved away. + // One example illustrating the case of "deadcode using never assigned variable causing scope error during JS generation" is as follows: + // The mlscript program is: + // ``` + // fun test(x) = + // let t = if x is + // AA(AA(a)) then a + // t + 5 + // fun f(a) = if a is + // AA then 0 + // let p = AA(AA(10)) + // test(p) + f(p) + // ``` + // After lowering, it is essentially: + // ``` + // fun test(x) = + // if x is AA(param0) then + // if param0 is AA(param1) then + // a = param1 + // tmpRes = a + // else throw "match error" + // else throw "match error" + // t = tmpRes + // return t + 5 + // fun f(a) = if a is AA then 0 + // let p = AA(AA(10)) + // test(p) + f(p) + // ``` + // And after fusion, the program (before the removal of dead code causing scope error) is: + // ``` + // fun test(x) = + // if x is AA(param0) then + // param0() + // else throw "match error" + // t = tmpRes // <--- this `tmpRes` without binding site causes scope error + // return t + 5 + // fun f(a) = if a is AA then 0 + // let p = AA of + // () => a = 10; tmpRes = a; t = tmpRes; return t + 5; + // test(p) + f(p) + // ``` // TODO: it will become unnecessary once we have proper binding declarations in the Block IR // and all uses of never-assigned variables will be known to be dead code dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } From d693b263f81ace848726453ff88ece32a9f0f2da Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 10 May 2025 13:02:49 +0800 Subject: [PATCH 227/303] fix tests --- hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 04b96ea5c9..83dddb79ae 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -47,7 +47,7 @@ fun badWrap(n) = let x = to(n) f1(x) + f2(x) x -f1(wrap(4)) + f2(wrap(5)) +f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: //│ No fusion opportunity From f5065fa574b230d95c61d9577c6f0d5f5ca6d28f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 10 May 2025 14:15:35 +0800 Subject: [PATCH 228/303] add stub implementation for `notOnlyOneCallSite` filtering of duplicating chances --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 93a00ac969..e7df5c3fc7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -324,6 +324,7 @@ class Deforest(using TL, Raise, Elaborator.State): // DefDupTODO: do not use `output` from difftest here output("duplication chances:") findDefDupChances.foreach: (r, s) => + assert(r.getFunCallBlkMemSyn.get is s) output(s"\t${r.getResult} <-- dup --> $s") // DefDupTODO: def dup: change later if true then @@ -753,6 +754,9 @@ class Deforest(using TL, Raise, Elaborator.State): def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = + def notOnlyOneCallSite(s: Symbol) = + // TODO: + true // tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})").get).mkString(" | ")) val info = vs.map(x => x -> checkStratVar(x)) // If all of the call-res vars are "solvable", then @@ -772,8 +776,9 @@ class Deforest(using TL, Raise, Elaborator.State): .groupBy(_._2._2.get) // group by the dtor .toList .sortBy: (_, callsites) => + // DefDupTODO: sorting should be based on the number of distinct callers // sort by the number of call sites for lesser duplication, and the callres var id for determinism - callsites.size -> callsites.headOption.map(_._1.uid) + callsites.map(_._1.callResOf.get._2).filter(notOnlyOneCallSite).toSet.size -> callsites.headOption.map(_._1.uid) match // only has one dtor case (dtor, callsites) :: Nil => @@ -785,7 +790,7 @@ class Deforest(using TL, Raise, Elaborator.State): dtor.inDef.fold(false)(_ is callsite.callResOf.get._2) .map(_._1.callResOf.get) // more than one dtors, duplicate those with less call sites - case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get)) + case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get).filter(callSite => notOnlyOneCallSite(callSite._2))) // no dtor, no need for duplication case _ => Nil else From a07b201db6f2f1ecf2a38b3e444178c6ea8597c2 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 10 May 2025 21:03:17 +0800 Subject: [PATCH 229/303] more todos and tothinks... --- .../test/mlscript/deforest/def-dup/todo.mls | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 83dddb79ae..a69cf60ee1 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -19,7 +19,8 @@ object Nil // ctor┬─►toRes──►f2Consumer // ├───►wrapRes1─►f1Consumer // └───►wrapRes2─►f2Consumer -// and there are programs that +// This program can be fused if we duplicate `wrap(4)` and `wrap(5)` AND the `to(n)` inside them. +// But there are programs that // have this flow and cannot be optimized after duplication fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is @@ -34,7 +35,8 @@ f1(wrap(4)) + f2(wrap(5)) //│ duplication chances: //│ No fusion opportunity - +// This program cannot be fused no matter how we duplicate, +// and it has essentially the same flow as the program above :deforestDup fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is @@ -53,3 +55,86 @@ f1(badWrap(4)) + f2(badWrap(5)) //│ No fusion opportunity + +// TODO: want to find duplication chance for either `p(1)` or `p(2)` (probably `p(2)`) +// ctor─┬►pRes1───┬──►f1Consumer +// │ └──►f2Consumer +// └►pRes2──────►f3Consumer +:deforestDup +fun p(x) = A(x) +let one = p(1) +let two = p(2) +fun f1(p) = if p is A(a) then a + 1 +fun f2(p) = if p is A(b) then b + 2 +fun f3(p) = if p is A(c) then c + 3 +f1(one) + f2(one) + f3(two) +//│ = 10 +//│ one = A(1) +//│ two = A(2) +//│ duplication chances: +//│ No fusion opportunity + + +// TODO: no duplication of `p(1)` or `p(2)` is helpful in this case +// but duplicating `f2(two)` AND `p(2)` is helpful... +// ctor─┬─►pRes1─┬──►f1Consumer +// │ └──►f2Consumer +// │ ▲ +// └─►pRes2 ────────┘ +:deforestDup +fun p(x) = A(x) +let one = p(1) +let two = p(2) +fun f1(p) = if p is A(a) then a + 1 +fun f2(p) = if p is A(b) then b + 2 +f1(one) + f2(one) + f2(two) +//│ = 9 +//│ one = A(1) +//│ two = A(2) +//│ duplication chances: +//│ No fusion opportunity + + +// `wrap` is not duplicated because we should sort by the number of distinct caller symbols, instead of the number of callsites +// TODO: group callsites, so that `p(3)` and `p(4)` uses the same duplicated `p` +// TODO: g2 is marked as duplicated because its res is also consumed by f2, +// but we can filter it since it's only called once +:deforestDup +fun p(x) = A(x) +fun g1(b) = if b then p(1) else p(2) +fun wrap(b) = g1(b) +fun g2(b) = if b then p(3) else p(4) +fun f1(p) = if p is A(a) then a + 1 +fun f2(p) = if p is A(b) then b + 2 +f1(wrap(true)) + f2(g2(false)) +//│ = 8 +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p +//│ Call(Ref(member:g2),List(Arg(false,Lit(BoolLit(false))))) <-- dup --> member:g2 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p +//│ No fusion opportunity + + + + +// TODO: no duplication seems to be helpful for this program +// can we avoid duplicating this? since the constructor calls +// are not in the definitions that we are duplicating... +:deforestDup +fun p(a) = a +fun f1(a1) = if a1 is A(x1) then x1 +fun f2(a2) = if a2 is A(x2) then x2 + 1 +fun test() = + let x = A(1) + let y = A(2) + let res1 = p(if true then x else y) // both x and y flows into this callsiteres + let res2 = p(x) // x flows into this callsiteres + let res3 = p(y) // y flows into this callsiteres + // f1(res1) + f2(res1) + f1(res2) + f2(res3) + f1(res1) + f1(res2) + f2(res3) +test() +//│ = 5 +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p +//│ No fusion opportunity From 4fef072dab714cac2fc9b292263f94eb557d401b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 11 May 2025 19:53:04 +0800 Subject: [PATCH 230/303] rewrite find def dup chance --- .../scala/hkmc2/codegen/Deforestation.scala | 105 ++++++++++++++---- .../test/mlscript/deforest/def-dup/simple.mls | 21 ++-- .../test/mlscript/deforest/def-dup/todo.mls | 38 ++++++- 3 files changed, 130 insertions(+), 34 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index e7df5c3fc7..365581d045 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -323,9 +323,8 @@ class Deforest(using TL, Raise, Elaborator.State): // val defDuplicateInfo = findDefDupChances // DefDupTODO: do not use `output` from difftest here output("duplication chances:") - findDefDupChances.foreach: (r, s) => - assert(r.getFunCallBlkMemSyn.get is s) - output(s"\t${r.getResult} <-- dup --> $s") + findDefDupChances2.foreach: (r, s) => + output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") // DefDupTODO: def dup: change later if true then // tl.log("-----------------------------------------") @@ -431,20 +430,36 @@ class Deforest(using TL, Raise, Elaborator.State): def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c object callInfo: - val store = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] + val callerToCallSitesInside = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] val callSiteInDefInfo = mutable.Map.empty[ResultId, Symbol] - def update(caller: Symbol, callee: Symbol, callResultId: ResultId) = - store.updateWith(caller): - case None => Some((callResultId -> callee) :: Nil) - case Some(l) => Some((callResultId -> callee) :: l) - callSiteInDefInfo.updateWith(callResultId): - case None => Some(caller) - case _ => die + val fnSymToAllCallSites = mutable.Map.empty[Symbol, Ls[ResultId]] + + var callSiteStableId = 0 + val callSiteStableIdStore = mutable.Map.empty[ResultId, Int] + def update(caller: Opt[Symbol], callee: Symbol, callResultId: ResultId) = + caller.foreach: caller => + callerToCallSitesInside.updateWith(caller): + case None => Some((callResultId -> callee) :: Nil) + case Some(l) => Some((callResultId -> callee) :: l) + callSiteInDefInfo.updateWith(callResultId): + case None => Some(caller) + case _ => die + fnSymToAllCallSites.updateWith(callee): + case None => Some(callResultId :: Nil) + case Some(l) => Some(callResultId :: l) + callSiteStableIdStore.updateWith(callResultId): + case None => callSiteStableId += 1; Some(callSiteStableId) + case Some(_) => die + def isObviousRecursiveCall(c: ResultId) = // tl.log("checking callsite: " + c.getResult.toString() + s"@$c") val sym = c.getFunCallBlkMemSyn.get callSiteInDefInfo.get(c).fold(false)(_ is sym) - + def getAllCallSites(s: Symbol) = fnSymToAllCallSites(s) + + def getCallSiteStableId(callSiteId: ResultId) = + callSiteStableIdStore(callSiteId) + def processBlock(b: Block)(using inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], @@ -540,10 +555,7 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + funSym.nme + "_res", s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)) // if `f` has a blockMemberSymbol, then record it ) - for - caller <- inDef - callee <- s.symbol.flatMap(_.asBlkMember) - do callInfo.update(caller, callee, c.uid) + for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => @@ -559,10 +571,7 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + l.nme + "_res", l.asBlkMember.map(c.uid -> _) // if `f` has a blockMemberSymbol, then record it ) - for - caller <- inDef - callee <- l.asBlkMember - do callInfo.update(caller, callee, c.uid) + for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => @@ -796,7 +805,63 @@ class Deforest(using TL, Raise, Elaborator.State): else Nil ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)).toMap + + lazy val findDefDupChances2 = + val callResToCtorsCallsFlowingIntoThem = + ctorDests.ctorDests + .flatMap[ResultId -> StratVarState]: + case (ctorCallId, CtorDest(callResVars = vs, _)) => vs.map(ctorCallId -> _) + .groupBy(_._2) + def duplicatable(v: StratVarState): Opt[ResultId -> Symbol -> Dtor] = + val callSiteId -> calleeSym = v.callResOf.get + // if this is the only call site, no reason to duplicate + if callInfo.getAllCallSites(calleeSym).size == 1 then N + // if this is a obvious recursive call site, do not duplicate + else if callInfo.isObviousRecursiveCall(callSiteId) then N + else + val dtors = allUpperBoundsOf(v.uid, Set.empty).filter(c => c.isInstanceOf[Dtor]) + // if this call result is not consumed by any dtor, do not duplicate + if dtors.size == 0 then N + // if this call result is consumed by multiple dtors, do not duplicate because it's helpless + else if dtors.size > 1 then N + else // if there is only one dtor for the call result... + val dtor = dtors.head.asInstanceOf[Dtor] + // it is a potential duplicate chance if the call res of a function + // is consumed by the body of the same function + if dtor.inDef.fold(false)(_ is calleeSym) then S(callSiteId -> calleeSym -> dtor) + else + // if all ctors flowing into this call res does not have a clash, no dup + if callResToCtorsCallsFlowingIntoThem(v).forall: (ctorCallId, fnCallSite) => + val CtorDest(matches, sels, noCons, _) = ctorDests.get(ctorCallId).get + !noCons && matches.size == 1 + then N + else S(callSiteId -> calleeSym -> dtor) + + val allDuplicatableCallSites = ctorDests.ctorDests + .flatMap: + case _ -> CtorDest(callResVars = vs) => vs + .toSet + .flatMap(duplicatable) + + allDuplicatableCallSites + .groupBy: + case _ -> calleeSym -> dtor => calleeSym -> dtor + .values + .toList + .sortBy(x => callInfo.getCallSiteStableId(x.head._1._1)) + .flatMap: setOfCallSites => + val sharedCalleeSym = setOfCallSites.head._1._2 + val newCalleeSym = new BlockMemberSymbol(sharedCalleeSym.nme + "_duplicated", Nil, true) + setOfCallSites.map: + case callSiteId -> calleeSym -> _ => + assert(calleeSym is sharedCalleeSym) + callSiteId -> newCalleeSym + + + + + lazy val resolveClashes = val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 2a63d3231b..babe8592e8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -32,7 +32,8 @@ fun test() = test() //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@937 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@938 //│ No fusion opportunity @@ -46,10 +47,11 @@ fun f5(a5) = if a5 is A(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ = 15 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 //│ No fusion opportunity @@ -77,9 +79,10 @@ fun f4(ls4) = if ls4 is f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ = 88 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1118 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1119 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1120 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1121 //│ No fusion opportunity @@ -116,5 +119,5 @@ fun test() = test() //│ = Cons(4, Cons(6, Nil)) //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1199 //│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index a69cf60ee1..e23aeea320 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -33,6 +33,8 @@ fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ = 10 //│ duplication chances: +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@929 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@930 //│ No fusion opportunity // This program cannot be fused no matter how we duplicate, @@ -52,6 +54,8 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@993 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@994 //│ No fusion opportunity @@ -72,6 +76,7 @@ f1(one) + f2(one) + f3(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1045 //│ No fusion opportunity @@ -92,6 +97,7 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1088 //│ No fusion opportunity @@ -109,9 +115,10 @@ fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p -//│ Call(Ref(member:g2),List(Arg(false,Lit(BoolLit(false))))) <-- dup --> member:g2 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1140 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1140 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1141 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1141 //│ No fusion opportunity @@ -135,6 +142,27 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1201 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1201 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1202 +//│ No fusion opportunity + + + + +:deforestDup +fun f1(a1) = if a1 is A(x1) then x1 +fun f2(a2) = if a2 is A(x2) then x2 + 1 +fun f3(a3) = if a3 is A(x3) then x3 + 2 +fun f4(a4) = if a4 is A(x4) then x4 + 3 +fun p(x, clashedCtor) = + if true then A(x) + else clashedCtor +let clashed = A(3) +f1(p(1, clashed)) + f2(p(2, clashed)) + f3(clashed) + f4(clashed) +//│ = 15 +//│ clashed = A(3) +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1268 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1269 //│ No fusion opportunity From 5fa7a0df3c3204d1b609fd75bd99c9805035795b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 11 May 2025 20:16:36 +0800 Subject: [PATCH 231/303] fix nondeterminism --- hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 365581d045..ac2929ce21 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -841,7 +841,8 @@ class Deforest(using TL, Raise, Elaborator.State): val allDuplicatableCallSites = ctorDests.ctorDests .flatMap: case _ -> CtorDest(callResVars = vs) => vs - .toSet + .toList + .distinct .flatMap(duplicatable) allDuplicatableCallSites From 82f7846ce8588655949f2b5276103c970e2c3561 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 11 May 2025 20:30:06 +0800 Subject: [PATCH 232/303] remove old impl --- .../scala/hkmc2/codegen/Deforestation.scala | 76 +------------------ 1 file changed, 2 insertions(+), 74 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index ac2929ce21..6865b795af 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -323,7 +323,7 @@ class Deforest(using TL, Raise, Elaborator.State): // val defDuplicateInfo = findDefDupChances // DefDupTODO: do not use `output` from difftest here output("duplication chances:") - findDefDupChances2.foreach: (r, s) => + findDefDupChances.foreach: (r, s) => output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") // DefDupTODO: def dup: change later if true then @@ -733,80 +733,8 @@ class Deforest(using TL, Raise, Elaborator.State): upperBounds(k).toSet.flatMap: case u@ConsVar(s) if !cache.contains(s.uid) => allUpperBoundsOf(s.uid, cache + s.uid) + u case u => Set(u) - - lazy val findDefDupChances = - object checkStratVar: - val cache = mutable.Map.empty[StratVarState, Bool -> Opt[Dtor]] - // A clash from a function callsite potentially solvable by duplicating def only - // if *all* the call-res vars that we care have exact only one dtor. - // This function checks if a single callsite is solvable, and - // return (solvable -> corresponding pat mat dtor). - def apply(v: StratVarState): Bool -> Opt[Dtor] = cache.getOrElseUpdate.curried(v): - assert(v.callResOf.isDefined) - var dtor: Opt[Dtor] = N - var dtorCount = 0 - var hasNoCons = false - - allUpperBoundsOf(v.uid, Set(v.uid)).foreach: - case d: Dtor => - tl.log(s"> ${v.callResOf.map(_._1.getResult).get} ::: dtor ::: ${d.expr}") - dtorCount += 1; dtor = S(d) - // DefDupTODO: consider about field selection as dtor... also about field sel inside branches - case FieldSel(expr, inMatching) => () - case ConsFun(l, r) => lastWords("ctor has ConsFun") - case ConsVar(s) => () - case NoCons => hasNoCons = true - - if dtorCount == 1 && !hasNoCons then true -> dtor - else if dtorCount == 0 && !hasNoCons then true -> N - else false -> N - - - def getDuplicatableCalls(vs: Ls[StratVarState]): Iterable[ResultId -> Symbol] = - def notOnlyOneCallSite(s: Symbol) = - // TODO: - true - // tl.log(vs.map(v => v.callResOf.map((r, s) => s"(${r.getResult}, ${s.nme})").get).mkString(" | ")) - val info = vs.map(x => x -> checkStratVar(x)) - // If all of the call-res vars are "solvable", then - // find the call-reses that causes clash and thus need to be duplicated. - // Optimistically ignore problems caused by - // obvious recursive call sites (which will not be duplicated anyway currently) - if info.forall: - case (callSite, isSolvable -> dtor) => - isSolvable || - callInfo.isObviousRecursiveCall(callSite.callResOf.get._1) - then - info - .filter: - case (callSite, isSolvable -> dtor) => - dtor.isDefined && // discard those without a dtor - !callInfo.isObviousRecursiveCall(callSite.callResOf.get._1) // and those that are obviously recursive calls - .groupBy(_._2._2.get) // group by the dtor - .toList - .sortBy: (_, callsites) => - // DefDupTODO: sorting should be based on the number of distinct callers - // sort by the number of call sites for lesser duplication, and the callres var id for determinism - callsites.map(_._1.callResOf.get._2).filter(notOnlyOneCallSite).toSet.size -> callsites.headOption.map(_._1.uid) - match - // only has one dtor - case (dtor, callsites) :: Nil => - callsites - .withFilter: (callsite, info) => - assert(info._2.get is dtor) - // it is a potential duplicate where the call res of a function - // is consumed by the body of the same function - dtor.inDef.fold(false)(_ is callsite.callResOf.get._2) - .map(_._1.callResOf.get) - // more than one dtors, duplicate those with less call sites - case heads :+ last => heads.flatMap(x => x._2.map(_._1.callResOf.get).filter(callSite => notOnlyOneCallSite(callSite._2))) - // no dtor, no need for duplication - case _ => Nil - else - Nil - ctorDests.ctorDests.values.flatMap(x => getDuplicatableCalls(x.callResVars)).toMap - lazy val findDefDupChances2 = + lazy val findDefDupChances = val callResToCtorsCallsFlowingIntoThem = ctorDests.ctorDests .flatMap[ResultId -> StratVarState]: From d59d322cb482bfcb3cc24ab7f7282afdf12fdebd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 11 May 2025 20:43:20 +0800 Subject: [PATCH 233/303] update tests --- .../test/mlscript/deforest/def-dup/simple.mls | 17 ++++++ .../test/mlscript/deforest/def-dup/todo.mls | 59 +++++++++---------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index babe8592e8..35f5e60ecc 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -121,3 +121,20 @@ test() //│ duplication chances: //│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1199 //│ No fusion opportunity + + +:deforestDup +fun p(x) = A(x) +fun g1(b) = if b then p(1) else p(2) +fun wrap(b) = g1(b) +fun g2(b) = if b then p(3) else p(4) +fun f1(p) = if p is A(a) then a + 1 +fun f2(p) = if p is A(b) then b + 2 +f1(wrap(true)) + f2(g2(false)) +//│ = 8 +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1251 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1251 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1252 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1252 +//│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index e23aeea320..f3326b7656 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -101,28 +101,6 @@ f1(one) + f2(one) + f2(two) //│ No fusion opportunity -// `wrap` is not duplicated because we should sort by the number of distinct caller symbols, instead of the number of callsites -// TODO: group callsites, so that `p(3)` and `p(4)` uses the same duplicated `p` -// TODO: g2 is marked as duplicated because its res is also consumed by f2, -// but we can filter it since it's only called once -:deforestDup -fun p(x) = A(x) -fun g1(b) = if b then p(1) else p(2) -fun wrap(b) = g1(b) -fun g2(b) = if b then p(3) else p(4) -fun f1(p) = if p is A(a) then a + 1 -fun f2(p) = if p is A(b) then b + 2 -f1(wrap(true)) + f2(g2(false)) -//│ = 8 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1140 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1140 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1141 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1141 -//│ No fusion opportunity - - - // TODO: no duplication seems to be helpful for this program // can we avoid duplicating this? since the constructor calls @@ -142,27 +120,44 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1201 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1201 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1202 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1148 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1148 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1149 //│ No fusion opportunity - +// TODO: no duplication seem to be helpful, and maybe we can also +// avoid this, because for the `clashedCtor` flows into pRes1 and pRes2 +// its ctor expr also does not locate in the definition of `p` :deforestDup fun f1(a1) = if a1 is A(x1) then x1 fun f2(a2) = if a2 is A(x2) then x2 + 1 -fun f3(a3) = if a3 is A(x3) then x3 + 2 -fun f4(a4) = if a4 is A(x4) then x4 + 3 fun p(x, clashedCtor) = if true then A(x) else clashedCtor let clashed = A(3) -f1(p(1, clashed)) + f2(p(2, clashed)) + f3(clashed) + f4(clashed) -//│ = 15 +f1(p(1, clashed)) + f2(p(2, clashed)) +//│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1268 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1269 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1189 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1190 +//│ No fusion opportunity + + + +// TODO: but for this example it will be a pity to not duplicate `id(A(1))` and `id(A(2))`... +// A(1)─┬─►id1Res───►f1Consumer +// │ +// A(2)─┴─►id2Res───►f2Consumer +:deforestDup +fun id(x) = x +fun f1(a) = if a is A(i) then i +fun f2(a) = if a is A(i) then i + 1 +f1(id(A(1))) + f2(id(A(2))) +//│ = 4 +//│ duplication chances: +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1229 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1230 //│ No fusion opportunity From f3b05d8a27501344d5f36059c396f03b09d634fc Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 11 May 2025 20:58:05 +0800 Subject: [PATCH 234/303] more updates to tests --- .../test/mlscript/deforest/def-dup/simple.mls | 38 ++++++++++++++ .../test/mlscript/deforest/def-dup/todo.mls | 49 +++---------------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 35f5e60ecc..c2e7ad23e9 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -138,3 +138,41 @@ f1(wrap(true)) + f2(g2(false)) //│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1252 //│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1252 //│ No fusion opportunity + + + + +// ctor─┬►pRes1───┬──►f1Consumer +// │ └──►f2Consumer +// └►pRes2──────►f3Consumer +:deforestDup +fun p(x) = A(x) +let one = p(1) +let two = p(2) +fun f1(p) = if p is A(a) then a + 1 +fun f2(p) = if p is A(b) then b + 2 +fun f3(p) = if p is A(c) then c + 3 +f1(one) + f2(one) + f3(two) +//│ = 10 +//│ one = A(1) +//│ two = A(2) +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1302 +//│ No fusion opportunity + + +// for this example it will be a pity to not duplicate `id(A(1))` and `id(A(2))`, +// although the ctors are not located in the definition of `id` +// A(1)─┬─►id1Res───►f1Consumer +// │ +// A(2)─┴─►id2Res───►f2Consumer +:deforestDup +fun id(x) = x +fun f1(a) = if a is A(i) then i +fun f2(a) = if a is A(i) then i + 1 +f1(id(A(1))) + f2(id(A(2))) +//│ = 4 +//│ duplication chances: +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1341 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1342 +//│ No fusion opportunity diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index f3326b7656..7525a902f0 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -60,26 +60,6 @@ f1(badWrap(4)) + f2(badWrap(5)) -// TODO: want to find duplication chance for either `p(1)` or `p(2)` (probably `p(2)`) -// ctor─┬►pRes1───┬──►f1Consumer -// │ └──►f2Consumer -// └►pRes2──────►f3Consumer -:deforestDup -fun p(x) = A(x) -let one = p(1) -let two = p(2) -fun f1(p) = if p is A(a) then a + 1 -fun f2(p) = if p is A(b) then b + 2 -fun f3(p) = if p is A(c) then c + 3 -f1(one) + f2(one) + f3(two) -//│ = 10 -//│ one = A(1) -//│ two = A(2) -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1045 -//│ No fusion opportunity - - // TODO: no duplication of `p(1)` or `p(2)` is helpful in this case // but duplicating `f2(two)` AND `p(2)` is helpful... // ctor─┬─►pRes1─┬──►f1Consumer @@ -97,7 +77,7 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1088 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1038 //│ No fusion opportunity @@ -120,9 +100,9 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1148 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1148 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1149 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1098 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1098 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1099 //│ No fusion opportunity @@ -141,23 +121,6 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1189 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1190 -//│ No fusion opportunity - - - -// TODO: but for this example it will be a pity to not duplicate `id(A(1))` and `id(A(2))`... -// A(1)─┬─►id1Res───►f1Consumer -// │ -// A(2)─┴─►id2Res───►f2Consumer -:deforestDup -fun id(x) = x -fun f1(a) = if a is A(i) then i -fun f2(a) = if a is A(i) then i + 1 -f1(id(A(1))) + f2(id(A(2))) -//│ = 4 -//│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1229 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1230 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1139 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1140 //│ No fusion opportunity From 32696c577aa6b2ada8b0c4989511371e7a90849e Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 03:01:10 +0800 Subject: [PATCH 235/303] wip: basic def dup impl with very rough driver code --- .../scala/hkmc2/codegen/Deforestation.scala | 89 ++- .../test/mlscript/deforest/def-dup/simple.mls | 549 ++++++++++++++++-- .../test/mlscript/deforest/def-dup/todo.mls | 46 +- 3 files changed, 628 insertions(+), 56 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6865b795af..da9d2de485 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -322,11 +322,17 @@ class Deforest(using TL, Raise, Elaborator.State): if duplicate then // val defDuplicateInfo = findDefDupChances // DefDupTODO: do not use `output` from difftest here - output("duplication chances:") - findDefDupChances.foreach: (r, s) => - output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") + val numOfChances = findDefDupChances.size + if numOfChances > 0 then + output("duplication chances:") + findDefDupChances.foreach: (r, s) => + output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") + output("=========") + val defDupper = new DefDupTransformer + S(defDupper(p)) -> s"$numOfChances dup chances" -> numOfChances + else N -> "no duplication chance" -> 0 // DefDupTODO: def dup: change later - if true then + else // tl.log("-----------------------------------------") // upperBounds.foreach: (v, u) => @@ -360,7 +366,6 @@ class Deforest(using TL, Raise, Elaborator.State): S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size else S(p) -> s"0 fusion opportunity" -> 0 - else die object globallyDefinedVars: val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) @@ -786,7 +791,8 @@ class Deforest(using TL, Raise, Elaborator.State): case callSiteId -> calleeSym -> _ => assert(calleeSym is sharedCalleeSym) callSiteId -> newCalleeSym - + + lazy val defDupMap = findDefDupChances.toMap @@ -929,8 +935,77 @@ class Deforest(using TL, Raise, Elaborator.State): val rest = deforestTransformer.applyBlock(p) val newDefsRest = deforestTransformer.matchRest.getAllFunDefs val newDefsArms = deforestTransformer.matchArms.getAllFunDefs - newDefsArms(newDefsRest(rest)) + newDefsArms(newDefsRest(rest)) + +// this duplicator needs to deeply copy: +// the ResultIds (related to ctor ids and match scrut ids) should not be messed up +class DefDuplicator(newFunSym: BlockMemberSymbol)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): + val argSubst = mutable.Map.empty[Symbol, VarSymbol] + + override def applyResult(r: Result): Result = r match + case c@Call(f, args) if d.defDupMap.contains(r.uid) => + val newSym = d.defDupMap(r.uid) + defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym) + val args2 = args.mapConserve(applyArg) + Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) + case c@Call(fun, args) => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) + case Instantiate(cls, args) => Instantiate(applyPath(cls), args.map(applyPath)) + case p: Path => applyPath(p) + + override def applyPath(p: Path): Path = p match + case s@Select(p, symbol) => Select(applyPath(p), symbol)(s.symbol) + case DynSelect(qual, fld, arrayIdx) => DynSelect(applyPath(qual), applyPath(fld), arrayIdx) + case v: Value => applyValue(v) + + override def applyValue(v: Value): Value = v match + case v@Value.Ref(s) => argSubst.get(s).fold(v.copy())(Value.Ref(_)) + case v: Value.This => v.copy() + case v: Value.Lit => v.copy() + case v: Value.Lam => v.copy() + case v: Value.Arr => v.copy() + case v: Value.Rcd => v.copy() + + override def applyParamList(pl: ParamList): ParamList = + def applyParam(p: Param): Param = + val sym2 = VarSymbol(p.sym.id) + argSubst += p.sym -> sym2 + p.copy(sym = sym2) + val params2 = pl.params.map(applyParam) + val rest2 = pl.restParam.map(applyParam) + ParamList(pl.flags, params2, rest2) + override def applyFunDefn(fun: FunDefn): FunDefn = + val params2 = fun.params.map(applyParamList) + val body2 = applySubBlock(fun.body) + FunDefn(fun.owner, newFunSym, params2, body2) + + +class DefDupTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): + self => + object newDefs: + val store = mutable.Map.empty[Symbol, FunDefn] + def makeDefn(oldS: BlockMemberSymbol, newS: BlockMemberSymbol): Unit = store.getOrElseUpdate.curried(newS): + val originalDefn = d.funSymToFunDef(oldS) + self.givenIn: + DefDuplicator(newS).applyFunDefn(originalDefn) + def apply(b: Block) = + store.values.toList.sortBy(_.sym.uid).foldRight(b)(Define(_, _)) + + override def applyResult(r: Result): Result = r match + case c@Call(f, args) if d.defDupMap.contains(r.uid) => + val newSym = d.defDupMap(r.uid) + newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym) + val args2 = args.mapConserve(applyArg) + Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) + case _ => super.applyResult(r) + + def apply(p: Program): Program = + val newMainBlock = applyBlock(p.main) + val newMainBlockWithNewDefns = newDefs(newMainBlock) + Program(p.imports, newMainBlockWithNewDefns) + + + class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): self => val nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index c2e7ad23e9..46ba7500f8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -20,9 +20,8 @@ fun test() = a + b test() + test() //│ = 4 -//│ duplication chances: -//│ No fusion opportunity +:sjs :deforestDup fun p(d) = A(3) fun c1(x) = if x is A then 1 @@ -30,13 +29,86 @@ fun c2(x) = if x is A then 2 fun test() = c1(p(1)) + c2(p(2)) test() +//│ JS (unsanitized): +//│ let p, test1, c1, c2; +//│ p = function p(d) { +//│ return A1(3) +//│ }; +//│ c1 = function c1(x) { +//│ if (x instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c2 = function c2(x) { +//│ if (x instanceof A1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test1 = function test() { +//│ let tmp2, tmp3, tmp4, tmp5; +//│ tmp2 = p(1); +//│ tmp3 = c1(tmp2); +//│ tmp4 = p(2); +//│ tmp5 = c2(tmp4); +//│ return tmp3 + tmp5 +//│ }; +//│ test1() +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@931 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@932 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let p, test1, c1, c2, p_duplicated, p_duplicated1; +//│ p_duplicated = function p_duplicated(d) { +//│ return A1(3) +//│ }; +//│ p_duplicated1 = function p_duplicated(d) { +//│ return A1(3) +//│ }; +//│ p = function p(d) { +//│ return A1(3) +//│ }; +//│ c1 = function c1(x) { +//│ if (x instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ c2 = function c2(x) { +//│ if (x instanceof A1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test1 = function test() { +//│ let tmp2, tmp3, tmp4, tmp5; +//│ tmp2 = p_duplicated(1); +//│ tmp3 = c1(tmp2); +//│ tmp4 = p_duplicated1(2); +//│ tmp5 = c2(tmp4); +//│ return tmp3 + tmp5 +//│ }; +//│ test1() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@937 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@938 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@945 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@946 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 3 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +:sjs :deforestDup fun p(x) = A(x) fun f1(a1) = if a1 is A(aa) then aa @@ -45,17 +117,183 @@ fun f3(a3) = if a3 is A(aa) then aa fun f4(a4) = if a4 is A(aa) then aa fun f5(a5) = if a5 is A(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) +//│ JS (unsanitized): +//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; +//│ p1 = function p(x) { +//│ return A1(x) +//│ }; +//│ f1 = function f1(a1) { +//│ let param0, aa; +//│ if (a1 instanceof A1.class) { +//│ param0 = a1.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f2 = function f2(a2) { +//│ let param0, aa; +//│ if (a2 instanceof A1.class) { +//│ param0 = a2.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f3 = function f3(a3) { +//│ let param0, aa; +//│ if (a3 instanceof A1.class) { +//│ param0 = a3.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f4 = function f4(a4) { +//│ let param0, aa; +//│ if (a4 instanceof A1.class) { +//│ param0 = a4.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f5 = function f5(a5) { +//│ let param0, aa; +//│ if (a5 instanceof A1.class) { +//│ param0 = a5.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp2 = p1(1); +//│ tmp3 = f1(tmp2); +//│ tmp4 = p1(2); +//│ tmp5 = f2(tmp4); +//│ tmp6 = tmp3 + tmp5; +//│ tmp7 = p1(3); +//│ tmp8 = f3(tmp7); +//│ tmp9 = tmp6 + tmp8; +//│ tmp10 = p1(4); +//│ tmp11 = f4(tmp10); +//│ tmp12 = tmp9 + tmp11; +//│ tmp13 = p1(5); +//│ tmp14 = f5(tmp13); +//│ tmp12 + tmp14 +//│ duplication chances: +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1012 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1013 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1014 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1015 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1016 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; +//│ p_duplicated2 = function p_duplicated(x) { +//│ return A1(x) +//│ }; +//│ p_duplicated3 = function p_duplicated(x) { +//│ return A1(x) +//│ }; +//│ p_duplicated4 = function p_duplicated(x) { +//│ return A1(x) +//│ }; +//│ p_duplicated5 = function p_duplicated(x) { +//│ return A1(x) +//│ }; +//│ p_duplicated6 = function p_duplicated(x) { +//│ return A1(x) +//│ }; +//│ p1 = function p(x) { +//│ return A1(x) +//│ }; +//│ f1 = function f1(a1) { +//│ let param0, aa; +//│ if (a1 instanceof A1.class) { +//│ param0 = a1.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f2 = function f2(a2) { +//│ let param0, aa; +//│ if (a2 instanceof A1.class) { +//│ param0 = a2.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f3 = function f3(a3) { +//│ let param0, aa; +//│ if (a3 instanceof A1.class) { +//│ param0 = a3.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f4 = function f4(a4) { +//│ let param0, aa; +//│ if (a4 instanceof A1.class) { +//│ param0 = a4.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f5 = function f5(a5) { +//│ let param0, aa; +//│ if (a5 instanceof A1.class) { +//│ param0 = a5.x; +//│ aa = param0; +//│ return aa +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp2 = p_duplicated2(1); +//│ tmp3 = f1(tmp2); +//│ tmp4 = p_duplicated3(2); +//│ tmp5 = f2(tmp4); +//│ tmp6 = tmp3 + tmp5; +//│ tmp7 = p_duplicated4(3); +//│ tmp8 = f3(tmp7); +//│ tmp9 = tmp6 + tmp8; +//│ tmp10 = p_duplicated5(4); +//│ tmp11 = f4(tmp10); +//│ tmp12 = tmp9 + tmp11; +//│ tmp13 = p_duplicated6(5); +//│ tmp14 = f5(tmp13); +//│ tmp12 + tmp14 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 -//│ No fusion opportunity - +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1050 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1051 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1052 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1053 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1054 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 15 +//│ 5 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +:sjs :deforestDup // calls to `to` should be able to be duplicated fun to(n) = @@ -77,13 +315,243 @@ fun f4(ls4) = if ls4 is h :: t then h + f4(t) Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) +//│ JS (unsanitized): +//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; +//│ to = function to(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ f11 = function f1(ls1) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls1 instanceof Cons1.class) { +//│ param0 = ls1.h; +//│ param1 = ls1.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f11(t); +//│ return h + tmp38 +//│ } else if (ls1 instanceof Nil1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f21 = function f2(ls2) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls2 instanceof Cons1.class) { +//│ param0 = ls2.h; +//│ param1 = ls2.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f21(t); +//│ return h + tmp38 +//│ } else if (ls2 instanceof Nil1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f31 = function f3(ls3) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls3 instanceof Cons1.class) { +//│ param0 = ls3.h; +//│ param1 = ls3.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f31(t); +//│ return h + tmp38 +//│ } else if (ls3 instanceof Nil1.class) { +//│ return 4 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f41 = function f4(ls4) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls4 instanceof Cons1.class) { +//│ param0 = ls4.h; +//│ param1 = ls4.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f41(t); +//│ return h + tmp38 +//│ } else if (ls4 instanceof Nil1.class) { +//│ return 5 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp28 = to(4); +//│ tmp29 = f11(tmp28); +//│ tmp30 = to(5); +//│ tmp31 = f21(tmp30); +//│ tmp32 = tmp29 + tmp31; +//│ tmp33 = to(6); +//│ tmp34 = f31(tmp33); +//│ tmp35 = tmp32 + tmp34; +//│ tmp36 = to(7); +//│ tmp37 = f41(tmp36); +//│ tmp35 + tmp37 +//│ duplication chances: +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1140 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1141 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1142 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1143 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; +//│ to_duplicated = function to_duplicated(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ to_duplicated1 = function to_duplicated(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ to_duplicated2 = function to_duplicated(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ to_duplicated3 = function to_duplicated(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ to = function to(n) { +//│ let scrut, m, tmp38, tmp39; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ f11 = function f1(ls1) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls1 instanceof Cons1.class) { +//│ param0 = ls1.h; +//│ param1 = ls1.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f11(t); +//│ return h + tmp38 +//│ } else if (ls1 instanceof Nil1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f21 = function f2(ls2) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls2 instanceof Cons1.class) { +//│ param0 = ls2.h; +//│ param1 = ls2.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f21(t); +//│ return h + tmp38 +//│ } else if (ls2 instanceof Nil1.class) { +//│ return 3 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f31 = function f3(ls3) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls3 instanceof Cons1.class) { +//│ param0 = ls3.h; +//│ param1 = ls3.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f31(t); +//│ return h + tmp38 +//│ } else if (ls3 instanceof Nil1.class) { +//│ return 4 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f41 = function f4(ls4) { +//│ let param0, param1, h, t, tmp38; +//│ if (ls4 instanceof Cons1.class) { +//│ param0 = ls4.h; +//│ param1 = ls4.t; +//│ h = param0; +//│ t = param1; +//│ tmp38 = f41(t); +//│ return h + tmp38 +//│ } else if (ls4 instanceof Nil1.class) { +//│ return 5 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp28 = to_duplicated(4); +//│ tmp29 = f11(tmp28); +//│ tmp30 = to_duplicated1(5); +//│ tmp31 = f21(tmp30); +//│ tmp32 = tmp29 + tmp31; +//│ tmp33 = to_duplicated2(6); +//│ tmp34 = f31(tmp33); +//│ tmp35 = tmp32 + tmp34; +//│ tmp36 = to_duplicated3(7); +//│ tmp37 = f41(tmp36); +//│ tmp35 + tmp37 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 88 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1118 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1119 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1120 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1121 -//│ No fusion opportunity +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1182 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1183 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1184 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1185 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 88 +//│ 4 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforestDup @@ -98,13 +566,6 @@ fun f1(ls1) = if ls1 is Nil then 2 f1(to(4)) //│ = 12 -//│ duplication chances: -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 12 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls1 is ...` -//│ Nil --match--> `if ls1 is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -119,8 +580,12 @@ fun test() = test() //│ = Cons(4, Cons(6, Nil)) //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1199 -//│ No fusion opportunity +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1265 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = Cons(4, Cons(6, Nil)) +//│ 1 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforestDup @@ -133,11 +598,15 @@ fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1251 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1251 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1252 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1252 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1320 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1320 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1321 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1321 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 8 +//│ 4 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -157,8 +626,12 @@ f1(one) + f2(one) + f3(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1302 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1374 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 10 +//│ 1 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // for this example it will be a pity to not duplicate `id(A(1))` and `id(A(2))`, @@ -173,6 +646,10 @@ fun f2(a) = if a is A(i) then i + 1 f1(id(A(1))) + f2(id(A(2))) //│ = 4 //│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1341 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1342 -//│ No fusion opportunity +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1415 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1416 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 4 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 7525a902f0..4d4c75b98e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -35,7 +35,11 @@ f1(wrap(4)) + f2(wrap(5)) //│ duplication chances: //│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@929 //│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@930 -//│ No fusion opportunity +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 10 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // This program cannot be fused no matter how we duplicate, // and it has essentially the same flow as the program above @@ -54,9 +58,13 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@993 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@994 -//│ No fusion opportunity +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@996 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@997 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 10 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -77,8 +85,12 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1038 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1044 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 9 +//│ 1 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -100,10 +112,14 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1098 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1098 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1099 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1106 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1106 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1107 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 5 +//│ 3 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -121,6 +137,10 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1139 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1140 -//│ No fusion opportunity +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1150 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1151 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 4 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From d0b60e8b4144065abeefff3bc56b4f33350098ee Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 16:41:42 +0800 Subject: [PATCH 236/303] also dup those inner calls whose result flows into the call results that are duplicated --- .../scala/hkmc2/codegen/Deforestation.scala | 53 +++++++++++++------ .../test/mlscript/deforest/def-dup/todo.mls | 16 +++--- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index da9d2de485..8d3426661c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -438,10 +438,11 @@ class Deforest(using TL, Raise, Elaborator.State): val callerToCallSitesInside = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] val callSiteInDefInfo = mutable.Map.empty[ResultId, Symbol] val fnSymToAllCallSites = mutable.Map.empty[Symbol, Ls[ResultId]] + val callSiteIdToCallResVar = mutable.Map.empty[ResultId, StratVarState] var callSiteStableId = 0 val callSiteStableIdStore = mutable.Map.empty[ResultId, Int] - def update(caller: Opt[Symbol], callee: Symbol, callResultId: ResultId) = + def update(caller: Opt[Symbol], callee: Symbol, callResultId: ResultId, callResVar: StratVarState) = caller.foreach: caller => callerToCallSitesInside.updateWith(caller): case None => Some((callResultId -> callee) :: Nil) @@ -455,6 +456,9 @@ class Deforest(using TL, Raise, Elaborator.State): callSiteStableIdStore.updateWith(callResultId): case None => callSiteStableId += 1; Some(callSiteStableId) case Some(_) => die + callSiteIdToCallResVar.updateWith(callResultId): + case None => Some(callResVar) + case Some(_) => die def isObviousRecursiveCall(c: ResultId) = // tl.log("checking callsite: " + c.getResult.toString() + s"@$c") @@ -464,6 +468,8 @@ class Deforest(using TL, Raise, Elaborator.State): def getCallSiteStableId(callSiteId: ResultId) = callSiteStableIdStore(callSiteId) + def getCallSiteResultVar(callSiteId: ResultId) = + callSiteIdToCallResVar.get(callSiteId) def processBlock(b: Block)(using @@ -560,7 +566,7 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + funSym.nme + "_res", s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)) // if `f` has a blockMemberSymbol, then record it ) - for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid) + for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => @@ -576,7 +582,7 @@ class Deforest(using TL, Raise, Elaborator.State): "call_" + l.nme + "_res", l.asBlkMember.map(c.uid -> _) // if `f` has a blockMemberSymbol, then record it ) - for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid) + for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => @@ -734,10 +740,12 @@ class Deforest(using TL, Raise, Elaborator.State): // ======== after resolving constraints ====== - def allUpperBoundsOf(k: StratVarId, cache: Set[StratVarId]): Set[ConsStrat] = - upperBounds(k).toSet.flatMap: - case u@ConsVar(s) if !cache.contains(s.uid) => allUpperBoundsOf(s.uid, cache + s.uid) + u - case u => Set(u) + def allUpperBoundsOf(k: StratVarId) = + def go(k: StratVarId, cache: Set[StratVarId]): Set[ConsStrat] = + upperBounds(k).toSet.flatMap: + case u@ConsVar(s) if !cache.contains(s.uid) => go(s.uid, cache + s.uid) + u + case u => Set(u) + go(k, Set.empty) lazy val findDefDupChances = val callResToCtorsCallsFlowingIntoThem = @@ -753,7 +761,7 @@ class Deforest(using TL, Raise, Elaborator.State): // if this is a obvious recursive call site, do not duplicate else if callInfo.isObviousRecursiveCall(callSiteId) then N else - val dtors = allUpperBoundsOf(v.uid, Set.empty).filter(c => c.isInstanceOf[Dtor]) + val dtors = allUpperBoundsOf(v.uid).filter(c => c.isInstanceOf[Dtor]) // if this call result is not consumed by any dtor, do not duplicate if dtors.size == 0 then N // if this call result is consumed by multiple dtors, do not duplicate because it's helpless @@ -937,18 +945,31 @@ class Deforest(using TL, Raise, Elaborator.State): val newDefsArms = deforestTransformer.matchArms.getAllFunDefs newDefsArms(newDefsRest(rest)) -// this duplicator needs to deeply copy: +// this duplicator needs to deeply copy, because // the ResultIds (related to ctor ids and match scrut ids) should not be messed up -class DefDuplicator(newFunSym: BlockMemberSymbol)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): +class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): val argSubst = mutable.Map.empty[Symbol, VarSymbol] override def applyResult(r: Result): Result = r match case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym) - val args2 = args.mapConserve(applyArg) + defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym, c.uid) + val args2 = args.map(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) - case c@Call(fun, args) => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) + case c@Call(fun, args) => + val currentCallSiteResVar = d.callInfo.getCallSiteResultVar(c.uid) + val flowsIntoTheDuplicatedDef = currentCallSiteResVar.fold(false): v => + val callerCallSiteStratVar = d.callInfo.getCallSiteResultVar(callSiteId).get + d.allUpperBoundsOf(v.uid).contains(callerCallSiteStratVar.asConsStrat) + val symOfFun = c.uid.getFunCallBlkMemSyn + symOfFun match + case Some(sym) if flowsIntoTheDuplicatedDef => + // further duplicate this call site + val newSym = new BlockMemberSymbol(sym.nme + "_duplicated", Nil, true) + defDupTransformer.newDefs.makeDefn(sym, newSym, c.uid) + val args2 = args.map(applyArg) + Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) + case _ => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) case Instantiate(cls, args) => Instantiate(applyPath(cls), args.map(applyPath)) case p: Path => applyPath(p) @@ -984,17 +1005,17 @@ class DefDupTransformer(using val d: Deforest, elabState: Elaborator.State) exte self => object newDefs: val store = mutable.Map.empty[Symbol, FunDefn] - def makeDefn(oldS: BlockMemberSymbol, newS: BlockMemberSymbol): Unit = store.getOrElseUpdate.curried(newS): + def makeDefn(oldS: BlockMemberSymbol, newS: BlockMemberSymbol, callSiteId: ResultId): Unit = store.getOrElseUpdate.curried(newS): val originalDefn = d.funSymToFunDef(oldS) self.givenIn: - DefDuplicator(newS).applyFunDefn(originalDefn) + DefDuplicator(newS, callSiteId).applyFunDefn(originalDefn) def apply(b: Block) = store.values.toList.sortBy(_.sym.uid).foldRight(b)(Define(_, _)) override def applyResult(r: Result): Result = r match case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym) + newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym, c.uid) val args2 = args.mapConserve(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) case _ => super.applyResult(r) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 4d4c75b98e..337823cefc 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -58,8 +58,8 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@996 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@997 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@1000 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@1001 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -85,7 +85,7 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1044 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1052 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 9 @@ -112,9 +112,9 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1106 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1106 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1107 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1114 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1114 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1115 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 @@ -137,8 +137,8 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1150 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1151 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1158 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1159 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 From bb10714de641b1eba25060bdca965f6b6aab3e6c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 16:55:39 +0800 Subject: [PATCH 237/303] update tests --- .../test/mlscript/deforest/def-dup/simple.mls | 36 +++++++++++ .../test/mlscript/deforest/def-dup/todo.mls | 63 +++++++------------ 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 46ba7500f8..13503f0cbb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -653,3 +653,39 @@ f1(id(A(1))) + f2(id(A(2))) //│ = 4 //│ 2 dup chances //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + +:deforestDup +// * The real flow of the program: +// ctor─►toRes┌─►wrapRes1─►f1Consumer +// └─►wrapRes2─►f2Consumer +// * The flow we currently got: +// ┌────►f1Consumer +// ctor┬─►toRes──►f2Consumer +// ├───►wrapRes1─►f1Consumer +// └───►wrapRes2─►f2Consumer +// This program can be fused if we duplicate `wrap(4)` and `wrap(5)` AND the `to(n)` inside them. +// But there are programs that +// have this same flow and cannot be optimized after duplication +fun to(n) = if n > 0 then n :: to(n - 1) else Nil +fun f1(ls) = if ls is + h :: t then h + Nil then 2 +fun f2(ls) = if ls is + h :: t then h + 1 + Nil then 3 +fun wrap(n) = to(n) +f1(wrap(4)) + f2(wrap(5)) +//│ = 10 +//│ duplication chances: +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1470 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1471 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 10 +//│ 2 dup chances +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 337823cefc..c0df013d74 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -9,40 +9,19 @@ data class (::) Cons(h, t) object Nil -:deforestDup -// TODO: how to duplicate this? -// * The real flow of the program: -// ctor─►toRes┌─►wrapRes1─►f1Consumer -// └─►wrapRes2─►f2Consumer -// * The flow we currently got: -// ┌────►f1Consumer -// ctor┬─►toRes──►f2Consumer -// ├───►wrapRes1─►f1Consumer -// └───►wrapRes2─►f2Consumer -// This program can be fused if we duplicate `wrap(4)` and `wrap(5)` AND the `to(n)` inside them. -// But there are programs that -// have this flow and cannot be optimized after duplication -fun to(n) = if n > 0 then n :: to(n - 1) else Nil -fun f1(ls) = if ls is - h :: t then h - Nil then 2 -fun f2(ls) = if ls is - h :: t then h + 1 - Nil then 3 -fun wrap(n) = to(n) -f1(wrap(4)) + f2(wrap(5)) -//│ = 10 -//│ duplication chances: -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@929 -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@930 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 10 -//│ 2 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - -// This program cannot be fused no matter how we duplicate, -// and it has essentially the same flow as the program above +// TODO: This program cannot be fused no matter how we duplicate, and we creately useless duplications. +// But it has essentially the same flow as the program below, which we create useful duplications +// ``` +// fun to(n) = if n > 0 then n :: to(n - 1) else Nil +// fun f1(ls) = if ls is +// h :: t then h +// Nil then 2 +// fun f2(ls) = if ls is +// h :: t then h + 1 +// Nil then 3 +// fun wrap(n) = to(n) +// f1(wrap(4)) + f2(wrap(5)) +// ``` :deforestDup fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is @@ -58,8 +37,8 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@1000 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@1001 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@941 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@942 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -85,7 +64,7 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1052 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@993 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 9 @@ -112,9 +91,9 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1114 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1114 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1115 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1055 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1055 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1056 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 5 @@ -137,8 +116,8 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1158 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1159 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1099 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1100 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 From 31a56ae15a5b63e037340566a1bb95210d23d49a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 17:09:51 +0800 Subject: [PATCH 238/303] typo --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8d3426661c..5f1469f0e5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -55,7 +55,7 @@ extension (i: ResultId) Some(k(i, v, v.l.asObj.get, Nil)) case _ => None def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get - def getFunCallBlkMemSyn(using Deforest) = i.getResult match + def getFunCallBlkMemSym(using Deforest) = i.getResult match case Call(fun, _) => fun match case s: Select => s.symbol.flatMap(_.asBlkMember) case v: Value.Ref => v.l.asBlkMember @@ -462,7 +462,7 @@ class Deforest(using TL, Raise, Elaborator.State): def isObviousRecursiveCall(c: ResultId) = // tl.log("checking callsite: " + c.getResult.toString() + s"@$c") - val sym = c.getFunCallBlkMemSyn.get + val sym = c.getFunCallBlkMemSym.get callSiteInDefInfo.get(c).fold(false)(_ is sym) def getAllCallSites(s: Symbol) = fnSymToAllCallSites(s) @@ -953,7 +953,7 @@ class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using va override def applyResult(r: Result): Result = r match case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym, c.uid) + defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid) val args2 = args.map(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) case c@Call(fun, args) => @@ -961,7 +961,7 @@ class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using va val flowsIntoTheDuplicatedDef = currentCallSiteResVar.fold(false): v => val callerCallSiteStratVar = d.callInfo.getCallSiteResultVar(callSiteId).get d.allUpperBoundsOf(v.uid).contains(callerCallSiteStratVar.asConsStrat) - val symOfFun = c.uid.getFunCallBlkMemSyn + val symOfFun = c.uid.getFunCallBlkMemSym symOfFun match case Some(sym) if flowsIntoTheDuplicatedDef => // further duplicate this call site @@ -1015,7 +1015,7 @@ class DefDupTransformer(using val d: Deforest, elabState: Elaborator.State) exte override def applyResult(r: Result): Result = r match case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - newDefs.makeDefn(r.uid.getFunCallBlkMemSyn.get, newSym, c.uid) + newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid) val args2 = args.mapConserve(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) case _ => super.applyResult(r) From 771c0c9b322d7ecfee962d04a32dda49b21690e4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 17:40:44 +0800 Subject: [PATCH 239/303] pipeline: deforest the duplicated program; update tests --- .../scala/hkmc2/codegen/Deforestation.scala | 7 +- .../test/mlscript/deforest/def-dup/simple.mls | 354 ++++++------------ .../test/mlscript/deforest/def-dup/todo.mls | 32 +- 3 files changed, 136 insertions(+), 257 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 5f1469f0e5..8d5903c6a1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -323,7 +323,7 @@ class Deforest(using TL, Raise, Elaborator.State): // val defDuplicateInfo = findDefDupChances // DefDupTODO: do not use `output` from difftest here val numOfChances = findDefDupChances.size - if numOfChances > 0 then + val dupRes = if numOfChances > 0 then output("duplication chances:") findDefDupChances.foreach: (r, s) => output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") @@ -331,6 +331,11 @@ class Deforest(using TL, Raise, Elaborator.State): val defDupper = new DefDupTransformer S(defDupper(p)) -> s"$numOfChances dup chances" -> numOfChances else N -> "no duplication chance" -> 0 + + val newDeforest = new Deforest + val pAfterDup = if dupRes._2 > 0 then dupRes._1._1.get else p + newDeforest(pAfterDup, false, output) + // DefDupTODO: def dup: change later else // tl.log("-----------------------------------------") diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 13503f0cbb..5dddfa5141 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -20,6 +20,7 @@ fun test() = a + b test() + test() //│ = 4 +//│ No fusion opportunity :sjs :deforestDup @@ -65,27 +66,27 @@ test() //│ ==== JS (deforested): ==== //│ let p, test1, c1, c2, p_duplicated, p_duplicated1; //│ p_duplicated = function p_duplicated(d) { -//│ return A1(3) +//│ let _deforest_A_x_unused; +//│ _deforest_A_x_unused = 3; +//│ return () => { +//│ return 1 +//│ } //│ }; //│ p_duplicated1 = function p_duplicated(d) { -//│ return A1(3) +//│ let _deforest_A_x_unused; +//│ _deforest_A_x_unused = 3; +//│ return () => { +//│ return 2 +//│ } //│ }; //│ p = function p(d) { //│ return A1(3) //│ }; //│ c1 = function c1(x) { -//│ if (x instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(x()) //│ }; //│ c2 = function c2(x) { -//│ if (x instanceof A1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(x()) //│ }; //│ test1 = function test() { //│ let tmp2, tmp3, tmp4, tmp5; @@ -99,12 +100,14 @@ test() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@945 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@946 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@947 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@948 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 -//│ 2 dup chances +//│ 2 fusion opportunities: +//│ A --match--> `if x is ...` +//│ A --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -187,82 +190,82 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ tmp14 = f5(tmp13); //│ tmp12 + tmp14 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1012 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1013 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1014 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1015 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1016 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; //│ p_duplicated2 = function p_duplicated(x) { -//│ return A1(x) +//│ let _deforest_A_x; +//│ _deforest_A_x = x; +//│ return () => { +//│ let param0, aa; +//│ param0 = _deforest_A_x; +//│ aa = param0; +//│ return aa +//│ } //│ }; //│ p_duplicated3 = function p_duplicated(x) { -//│ return A1(x) +//│ let _deforest_A_x; +//│ _deforest_A_x = x; +//│ return () => { +//│ let param0, aa; +//│ param0 = _deforest_A_x; +//│ aa = param0; +//│ return aa +//│ } //│ }; //│ p_duplicated4 = function p_duplicated(x) { -//│ return A1(x) +//│ let _deforest_A_x; +//│ _deforest_A_x = x; +//│ return () => { +//│ let param0, aa; +//│ param0 = _deforest_A_x; +//│ aa = param0; +//│ return aa +//│ } //│ }; //│ p_duplicated5 = function p_duplicated(x) { -//│ return A1(x) +//│ let _deforest_A_x; +//│ _deforest_A_x = x; +//│ return () => { +//│ let param0, aa; +//│ param0 = _deforest_A_x; +//│ aa = param0; +//│ return aa +//│ } //│ }; //│ p_duplicated6 = function p_duplicated(x) { -//│ return A1(x) +//│ let _deforest_A_x; +//│ _deforest_A_x = x; +//│ return () => { +//│ let param0, aa; +//│ param0 = _deforest_A_x; +//│ aa = param0; +//│ return aa +//│ } //│ }; //│ p1 = function p(x) { //│ return A1(x) //│ }; //│ f1 = function f1(a1) { -//│ let param0, aa; -//│ if (a1 instanceof A1.class) { -//│ param0 = a1.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(a1()) //│ }; //│ f2 = function f2(a2) { -//│ let param0, aa; -//│ if (a2 instanceof A1.class) { -//│ param0 = a2.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(a2()) //│ }; //│ f3 = function f3(a3) { -//│ let param0, aa; -//│ if (a3 instanceof A1.class) { -//│ param0 = a3.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(a3()) //│ }; //│ f4 = function f4(a4) { -//│ let param0, aa; -//│ if (a4 instanceof A1.class) { -//│ param0 = a4.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(a4()) //│ }; //│ f5 = function f5(a5) { -//│ let param0, aa; -//│ if (a5 instanceof A1.class) { -//│ param0 = a5.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } +//│ return runtime.safeCall(a5()) //│ }; //│ tmp2 = p_duplicated2(1); //│ tmp3 = f1(tmp2); @@ -281,15 +284,20 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1050 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1051 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1052 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1053 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1054 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1059 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1060 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1061 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1062 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1063 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 15 -//│ 5 dup chances +//│ 5 fusion opportunities: +//│ A --match--> `if a1 is ...` +//│ A --match--> `if a2 is ...` +//│ A --match--> `if a3 is ...` +//│ A --match--> `if a4 is ...` +//│ A --match--> `if a5 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -401,157 +409,20 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ tmp37 = f41(tmp36); //│ tmp35 + tmp37 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1140 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1141 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1142 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1143 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1154 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1155 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1156 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1157 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; -//│ to_duplicated = function to_duplicated(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ to_duplicated1 = function to_duplicated(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ to_duplicated2 = function to_duplicated(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ to_duplicated3 = function to_duplicated(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ to = function to(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ f11 = function f1(ls1) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls1 instanceof Cons1.class) { -//│ param0 = ls1.h; -//│ param1 = ls1.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f11(t); -//│ return h + tmp38 -//│ } else if (ls1 instanceof Nil1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f21 = function f2(ls2) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls2 instanceof Cons1.class) { -//│ param0 = ls2.h; -//│ param1 = ls2.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f21(t); -//│ return h + tmp38 -//│ } else if (ls2 instanceof Nil1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f31 = function f3(ls3) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls3 instanceof Cons1.class) { -//│ param0 = ls3.h; -//│ param1 = ls3.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f31(t); -//│ return h + tmp38 -//│ } else if (ls3 instanceof Nil1.class) { -//│ return 4 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f41 = function f4(ls4) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls4 instanceof Cons1.class) { -//│ param0 = ls4.h; -//│ param1 = ls4.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f41(t); -//│ return h + tmp38 -//│ } else if (ls4 instanceof Nil1.class) { -//│ return 5 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp28 = to_duplicated(4); -//│ tmp29 = f11(tmp28); -//│ tmp30 = to_duplicated1(5); -//│ tmp31 = f21(tmp30); -//│ tmp32 = tmp29 + tmp31; -//│ tmp33 = to_duplicated2(6); -//│ tmp34 = f31(tmp33); -//│ tmp35 = tmp32 + tmp34; -//│ tmp36 = to_duplicated3(7); -//│ tmp37 = f41(tmp36); -//│ tmp35 + tmp37 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity //│ = 88 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1182 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1183 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1184 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1185 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1196 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1197 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1198 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1199 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 88 -//│ 4 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity :deforestDup @@ -566,7 +437,12 @@ fun f1(ls1) = if ls1 is Nil then 2 f1(to(4)) //│ = 12 - +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 12 +//│ 2 fusion opportunities: +//│ Cons --match--> `if ls1 is ...` +//│ Nil --match--> `if ls1 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforestDup @@ -580,11 +456,12 @@ fun test() = test() //│ = Cons(4, Cons(6, Nil)) //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1265 +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1281 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(4, Cons(6, Nil)) -//│ 1 dup chances +//│ 1 fusion opportunities: +//│ Cons --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -598,14 +475,16 @@ fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1320 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1320 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1321 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1321 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1339 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1339 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1340 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1340 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 8 -//│ 4 dup chances +//│ 2 fusion opportunities: +//│ A --match--> `if p is ...` +//│ A --match--> `if p is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -626,11 +505,12 @@ f1(one) + f2(one) + f3(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1374 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1395 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 -//│ 1 dup chances +//│ 1 fusion opportunities: +//│ A --match--> `if p is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -646,12 +526,14 @@ fun f2(a) = if a is A(i) then i + 1 f1(id(A(1))) + f2(id(A(2))) //│ = 4 //│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1415 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1416 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1437 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1438 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 -//│ 2 dup chances +//│ 2 fusion opportunities: +//│ A --match--> `if a is ...` +//│ A --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -682,10 +564,14 @@ fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1470 -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1471 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1494 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1495 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 -//│ 2 dup chances +//│ 4 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index c0df013d74..f1bd83e053 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -40,10 +40,7 @@ f1(badWrap(4)) + f2(badWrap(5)) //│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@941 //│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@942 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 10 -//│ 2 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -64,12 +61,9 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@993 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@992 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 9 -//│ 1 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -91,14 +85,11 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1055 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1055 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1056 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1053 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1053 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1054 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 5 -//│ 3 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -116,10 +107,7 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1099 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1100 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1096 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1097 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 4 -//│ 2 dup chances -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity From 22415f9d59b7d77fb914fc1735c7556234c6a473 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 18:32:18 +0800 Subject: [PATCH 240/303] to fix: fix dup of recursive calls --- .../scala/hkmc2/codegen/Deforestation.scala | 83 +++++++++++++------ 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 8d5903c6a1..b964e727c7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -334,6 +334,9 @@ class Deforest(using TL, Raise, Elaborator.State): val newDeforest = new Deforest val pAfterDup = if dupRes._2 > 0 then dupRes._1._1.get else p + + output("\n\n\n\n\nvvvvvvvvvvvvv\n dup done\n^^^^^^^^^^^^^^^^^\n\n\n\n\n\n\n\n") + newDeforest(pAfterDup, false, output) // DefDupTODO: def dup: change later @@ -351,7 +354,7 @@ class Deforest(using TL, Raise, Elaborator.State): val selExpr = sels.map{ case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" }.toList.sorted.mkString(" | ") - s"$ctorName\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" + s"${ctorExprId.getResult}\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" tl.log("-----------------------------------------") dtorSources.dtorSources.foreach: case (d, DtorSource(ctors, noProd)) => @@ -551,8 +554,7 @@ class Deforest(using TL, Raise, Elaborator.State): inArm: Map[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol], inDef: Opt[BlockMemberSymbol] - ): ProdStrat = - tl.log(s"========== processing: ${r.toString()} <<<<< in $inDef") + ): ProdStrat = tl.trace[ProdStrat](s"========== processing: ${r.toString()} <<<<< in $inDef", _.toString()): def handleCallLike(f: Path, args: Ls[Path], c: Result) = val argsTpe = args.map(processResult) f match @@ -952,29 +954,42 @@ class Deforest(using TL, Raise, Elaborator.State): // this duplicator needs to deeply copy, because // the ResultIds (related to ctor ids and match scrut ids) should not be messed up -class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): +class DefDuplicator( + oldFunSym: BlockMemberSymbol, + newFunSym: BlockMemberSymbol, + callSiteId: ResultId, + cache: Map[ResultId, BlockMemberSymbol] +)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): val argSubst = mutable.Map.empty[Symbol, VarSymbol] override def applyResult(r: Result): Result = r match + // if this is a callsite that is pre-computed to have a duplication: case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid) + defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid, cache + (c.uid -> newSym)) val args2 = args.map(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) case c@Call(fun, args) => - val currentCallSiteResVar = d.callInfo.getCallSiteResultVar(c.uid) - val flowsIntoTheDuplicatedDef = currentCallSiteResVar.fold(false): v => - val callerCallSiteStratVar = d.callInfo.getCallSiteResultVar(callSiteId).get - d.allUpperBoundsOf(v.uid).contains(callerCallSiteStratVar.asConsStrat) val symOfFun = c.uid.getFunCallBlkMemSym - symOfFun match - case Some(sym) if flowsIntoTheDuplicatedDef => - // further duplicate this call site - val newSym = new BlockMemberSymbol(sym.nme + "_duplicated", Nil, true) - defDupTransformer.newDefs.makeDefn(sym, newSym, c.uid) - val args2 = args.map(applyArg) - Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) - case _ => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) + // if this is a call site that is obviously recursive + if symOfFun.fold(false)(_ is oldFunSym) then + Call(Value.Ref(newFunSym), args.map(applyArg))(true, c.mayRaiseEffects) + else + cache.get(c.uid) match + case Some(sym) => Call(Value.Ref(sym), args.map(applyArg))(true, c.mayRaiseEffects) + case None => + val currentCallSiteResVar = d.callInfo.getCallSiteResultVar(c.uid) + val flowsIntoTheDuplicatedDef = currentCallSiteResVar.fold(false): v => + val callerCallSiteStratVar = d.callInfo.getCallSiteResultVar(callSiteId).get + d.allUpperBoundsOf(v.uid).contains(callerCallSiteStratVar.asConsStrat) + symOfFun match + case Some(sym) if flowsIntoTheDuplicatedDef => + // further duplicate this call site + val newSym = new BlockMemberSymbol(sym.nme + "_nested_duplicated", Nil, true) + defDupTransformer.newDefs.makeDefn(sym, newSym, c.uid, cache + (c.uid -> newSym)) + val args2 = args.map(applyArg) + Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) + case _ => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) case Instantiate(cls, args) => Instantiate(applyPath(cls), args.map(applyPath)) case p: Path => applyPath(p) @@ -983,13 +998,21 @@ class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using va case DynSelect(qual, fld, arrayIdx) => DynSelect(applyPath(qual), applyPath(fld), arrayIdx) case v: Value => applyValue(v) + // override def applyValue(v: Value): Value = v match + // case v@Value.Ref(s) => argSubst.get(s).fold(v.copy())(Value.Ref(_)) + // case v: Value.This => v.copy() + // case v: Value.Lit => v.copy() + // case v: Value.Lam => v.copy() + // case v: Value.Arr => v.copy() + // case v: Value.Rcd => v.copy() override def applyValue(v: Value): Value = v match - case v@Value.Ref(s) => argSubst.get(s).fold(v.copy())(Value.Ref(_)) - case v: Value.This => v.copy() - case v: Value.Lit => v.copy() - case v: Value.Lam => v.copy() - case v: Value.Arr => v.copy() - case v: Value.Rcd => v.copy() + case Value.Ref(l) => argSubst.get(l).fold(Value.Ref(l))(Value.Ref(_)) + case Value.This(sym) => Value.This(sym) + case Value.Lit(lit) => Value.Lit(lit) + case Value.Lam(params, body) => Value.Lam(params, body) + case Value.Arr(elems) => Value.Arr(elems) + case Value.Rcd(elems) => Value.Rcd(elems) + override def applyParamList(pl: ParamList): ParamList = def applyParam(p: Param): Param = @@ -1004,23 +1027,31 @@ class DefDuplicator(newFunSym: BlockMemberSymbol, callSiteId: ResultId)(using va val params2 = fun.params.map(applyParamList) val body2 = applySubBlock(fun.body) FunDefn(fun.owner, newFunSym, params2, body2) + + def apply(fun: FunDefn) = + applyFunDefn(fun) class DefDupTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): self => object newDefs: val store = mutable.Map.empty[Symbol, FunDefn] - def makeDefn(oldS: BlockMemberSymbol, newS: BlockMemberSymbol, callSiteId: ResultId): Unit = store.getOrElseUpdate.curried(newS): + def makeDefn( + oldS: BlockMemberSymbol, + newS: BlockMemberSymbol, + callSiteId: ResultId, + cache: Map[ResultId, BlockMemberSymbol] + ): Unit = store.getOrElseUpdate.curried(newS): val originalDefn = d.funSymToFunDef(oldS) self.givenIn: - DefDuplicator(newS, callSiteId).applyFunDefn(originalDefn) + DefDuplicator(oldS, newS, callSiteId, cache)(originalDefn) def apply(b: Block) = store.values.toList.sortBy(_.sym.uid).foldRight(b)(Define(_, _)) override def applyResult(r: Result): Result = r match case c@Call(f, args) if d.defDupMap.contains(r.uid) => val newSym = d.defDupMap(r.uid) - newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid) + newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid, Map(c.uid -> newSym)) val args2 = args.mapConserve(applyArg) Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) case _ => super.applyResult(r) From f41f9b4d2e552c697878fc9f803706b2a8e2d24f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 21:47:09 +0800 Subject: [PATCH 241/303] local defined vars should also be renewed when duplicating defns --- .../scala/hkmc2/codegen/Deforestation.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b964e727c7..b636f1b39f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -960,7 +960,7 @@ class DefDuplicator( callSiteId: ResultId, cache: Map[ResultId, BlockMemberSymbol] )(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): - val argSubst = mutable.Map.empty[Symbol, VarSymbol] + val localSymSubst = mutable.Map.empty[Symbol, Symbol] override def applyResult(r: Result): Result = r match // if this is a callsite that is pre-computed to have a duplication: @@ -998,15 +998,8 @@ class DefDuplicator( case DynSelect(qual, fld, arrayIdx) => DynSelect(applyPath(qual), applyPath(fld), arrayIdx) case v: Value => applyValue(v) - // override def applyValue(v: Value): Value = v match - // case v@Value.Ref(s) => argSubst.get(s).fold(v.copy())(Value.Ref(_)) - // case v: Value.This => v.copy() - // case v: Value.Lit => v.copy() - // case v: Value.Lam => v.copy() - // case v: Value.Arr => v.copy() - // case v: Value.Rcd => v.copy() override def applyValue(v: Value): Value = v match - case Value.Ref(l) => argSubst.get(l).fold(Value.Ref(l))(Value.Ref(_)) + case Value.Ref(l) => localSymSubst.get(l).fold(Value.Ref(l))(Value.Ref(_)) case Value.This(sym) => Value.This(sym) case Value.Lit(lit) => Value.Lit(lit) case Value.Lam(params, body) => Value.Lam(params, body) @@ -1017,14 +1010,21 @@ class DefDuplicator( override def applyParamList(pl: ParamList): ParamList = def applyParam(p: Param): Param = val sym2 = VarSymbol(p.sym.id) - argSubst += p.sym -> sym2 + localSymSubst += p.sym -> sym2 p.copy(sym = sym2) val params2 = pl.params.map(applyParam) val rest2 = pl.restParam.map(applyParam) ParamList(pl.flags, params2, rest2) + override def applyLocal(sym: Local): Local = + localSymSubst.getOrElse.curried(sym): + // tl.log(s"$sym not subst") + sym + override def applyFunDefn(fun: FunDefn): FunDefn = val params2 = fun.params.map(applyParamList) + fun.body.definedVars.foreach: v => + localSymSubst += v -> TempSymbol(N, v.nme) val body2 = applySubBlock(fun.body) FunDefn(fun.owner, newFunSym, params2, body2) From dc03d251a00465a4899a6735dd3f16d7b0b9fe36 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 12 May 2025 22:03:38 +0800 Subject: [PATCH 242/303] wip: cleanup; update tests --- .../scala/hkmc2/codegen/Deforestation.scala | 2 +- .../test/mlscript/deforest/def-dup/simple.mls | 492 ++++++++++++++---- .../test/mlscript/deforest/def-dup/todo.mls | 69 ++- 3 files changed, 441 insertions(+), 122 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index b636f1b39f..381127dec3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -335,7 +335,7 @@ class Deforest(using TL, Raise, Elaborator.State): val newDeforest = new Deforest val pAfterDup = if dupRes._2 > 0 then dupRes._1._1.get else p - output("\n\n\n\n\nvvvvvvvvvvvvv\n dup done\n^^^^^^^^^^^^^^^^^\n\n\n\n\n\n\n\n") + // output("\n\n\n\n\nvvvvvvvvvvvvv\n dup done\n^^^^^^^^^^^^^^^^^\n\n\n\n\n\n\n\n") newDeforest(pAfterDup, false, output) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 5dddfa5141..be547200c8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -1,7 +1,9 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class A(x) @@ -20,7 +22,10 @@ fun test() = a + b test() + test() //│ = 4 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 4 +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :sjs :deforestDup @@ -50,17 +55,17 @@ test() //│ } //│ }; //│ test1 = function test() { -//│ let tmp2, tmp3, tmp4, tmp5; -//│ tmp2 = p(1); -//│ tmp3 = c1(tmp2); -//│ tmp4 = p(2); -//│ tmp5 = c2(tmp4); -//│ return tmp3 + tmp5 +//│ let tmp4, tmp5, tmp6, tmp7; +//│ tmp4 = p(1); +//│ tmp5 = c1(tmp4); +//│ tmp6 = p(2); +//│ tmp7 = c2(tmp6); +//│ return tmp5 + tmp7 //│ }; //│ test1() //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@931 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@932 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@929 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@930 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== @@ -89,19 +94,19 @@ test() //│ return runtime.safeCall(x()) //│ }; //│ test1 = function test() { -//│ let tmp2, tmp3, tmp4, tmp5; -//│ tmp2 = p_duplicated(1); -//│ tmp3 = c1(tmp2); -//│ tmp4 = p_duplicated1(2); -//│ tmp5 = c2(tmp4); -//│ return tmp3 + tmp5 +//│ let tmp4, tmp5, tmp6, tmp7; +//│ tmp4 = p_duplicated(1); +//│ tmp5 = c1(tmp4); +//│ tmp6 = p_duplicated1(2); +//│ tmp7 = c2(tmp6); +//│ return tmp5 + tmp7 //│ }; //│ test1() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@947 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@948 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@944 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@945 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -121,7 +126,7 @@ fun f4(a4) = if a4 is A(aa) then aa fun f5(a5) = if a5 is A(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ JS (unsanitized): -//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; +//│ let f1, f5, f3, p1, f4, f2, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; //│ p1 = function p(x) { //│ return A1(x) //│ }; @@ -175,30 +180,30 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp2 = p1(1); -//│ tmp3 = f1(tmp2); -//│ tmp4 = p1(2); -//│ tmp5 = f2(tmp4); -//│ tmp6 = tmp3 + tmp5; -//│ tmp7 = p1(3); -//│ tmp8 = f3(tmp7); -//│ tmp9 = tmp6 + tmp8; -//│ tmp10 = p1(4); -//│ tmp11 = f4(tmp10); -//│ tmp12 = tmp9 + tmp11; -//│ tmp13 = p1(5); -//│ tmp14 = f5(tmp13); -//│ tmp12 + tmp14 +//│ tmp4 = p1(1); +//│ tmp5 = f1(tmp4); +//│ tmp6 = p1(2); +//│ tmp7 = f2(tmp6); +//│ tmp8 = tmp5 + tmp7; +//│ tmp9 = p1(3); +//│ tmp10 = f3(tmp9); +//│ tmp11 = tmp8 + tmp10; +//│ tmp12 = p1(4); +//│ tmp13 = f4(tmp12); +//│ tmp14 = tmp11 + tmp13; +//│ tmp15 = p1(5); +//│ tmp16 = f5(tmp15); +//│ tmp14 + tmp16 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1013 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1014 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1015 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1016 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1017 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; +//│ let f1, f5, f3, p1, f4, f2, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; //│ p_duplicated2 = function p_duplicated(x) { //│ let _deforest_A_x; //│ _deforest_A_x = x; @@ -267,28 +272,28 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ f5 = function f5(a5) { //│ return runtime.safeCall(a5()) //│ }; -//│ tmp2 = p_duplicated2(1); -//│ tmp3 = f1(tmp2); -//│ tmp4 = p_duplicated3(2); -//│ tmp5 = f2(tmp4); -//│ tmp6 = tmp3 + tmp5; -//│ tmp7 = p_duplicated4(3); -//│ tmp8 = f3(tmp7); -//│ tmp9 = tmp6 + tmp8; -//│ tmp10 = p_duplicated5(4); -//│ tmp11 = f4(tmp10); -//│ tmp12 = tmp9 + tmp11; -//│ tmp13 = p_duplicated6(5); -//│ tmp14 = f5(tmp13); -//│ tmp12 + tmp14 +//│ tmp4 = p_duplicated2(1); +//│ tmp5 = f1(tmp4); +//│ tmp6 = p_duplicated3(2); +//│ tmp7 = f2(tmp6); +//│ tmp8 = tmp5 + tmp7; +//│ tmp9 = p_duplicated4(3); +//│ tmp10 = f3(tmp9); +//│ tmp11 = tmp8 + tmp10; +//│ tmp12 = p_duplicated5(4); +//│ tmp13 = f4(tmp12); +//│ tmp14 = tmp11 + tmp13; +//│ tmp15 = p_duplicated6(5); +//│ tmp16 = f5(tmp15); +//│ tmp14 + tmp16 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1059 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1060 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1061 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1062 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1063 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1055 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1056 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1057 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1058 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1059 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 15 @@ -324,28 +329,28 @@ fun f4(ls4) = if ls4 is Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ JS (unsanitized): -//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; +//│ let f11, to, f31, f41, f21, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39; //│ to = function to(n) { -//│ let scrut, m, tmp38, tmp39; +//│ let scrut, m, tmp40, tmp41; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) +//│ tmp40 = n - 1; +//│ m = tmp40; +//│ tmp41 = to(m); +//│ return Cons1(n, tmp41) //│ } else { //│ return Nil1 //│ } //│ }; //│ f11 = function f1(ls1) { -//│ let param0, param1, h, t, tmp38; +//│ let param0, param1, h, t, tmp40; //│ if (ls1 instanceof Cons1.class) { //│ param0 = ls1.h; //│ param1 = ls1.t; //│ h = param0; //│ t = param1; -//│ tmp38 = f11(t); -//│ return h + tmp38 +//│ tmp40 = f11(t); +//│ return h + tmp40 //│ } else if (ls1 instanceof Nil1.class) { //│ return 2 //│ } else { @@ -353,14 +358,14 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f21 = function f2(ls2) { -//│ let param0, param1, h, t, tmp38; +//│ let param0, param1, h, t, tmp40; //│ if (ls2 instanceof Cons1.class) { //│ param0 = ls2.h; //│ param1 = ls2.t; //│ h = param0; //│ t = param1; -//│ tmp38 = f21(t); -//│ return h + tmp38 +//│ tmp40 = f21(t); +//│ return h + tmp40 //│ } else if (ls2 instanceof Nil1.class) { //│ return 3 //│ } else { @@ -368,14 +373,14 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f31 = function f3(ls3) { -//│ let param0, param1, h, t, tmp38; +//│ let param0, param1, h, t, tmp40; //│ if (ls3 instanceof Cons1.class) { //│ param0 = ls3.h; //│ param1 = ls3.t; //│ h = param0; //│ t = param1; -//│ tmp38 = f31(t); -//│ return h + tmp38 +//│ tmp40 = f31(t); +//│ return h + tmp40 //│ } else if (ls3 instanceof Nil1.class) { //│ return 4 //│ } else { @@ -383,46 +388,191 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f41 = function f4(ls4) { -//│ let param0, param1, h, t, tmp38; +//│ let param0, param1, h, t, tmp40; //│ if (ls4 instanceof Cons1.class) { //│ param0 = ls4.h; //│ param1 = ls4.t; //│ h = param0; //│ t = param1; -//│ tmp38 = f41(t); -//│ return h + tmp38 +//│ tmp40 = f41(t); +//│ return h + tmp40 //│ } else if (ls4 instanceof Nil1.class) { //│ return 5 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp28 = to(4); -//│ tmp29 = f11(tmp28); -//│ tmp30 = to(5); -//│ tmp31 = f21(tmp30); -//│ tmp32 = tmp29 + tmp31; -//│ tmp33 = to(6); -//│ tmp34 = f31(tmp33); -//│ tmp35 = tmp32 + tmp34; -//│ tmp36 = to(7); -//│ tmp37 = f41(tmp36); -//│ tmp35 + tmp37 +//│ tmp30 = to(4); +//│ tmp31 = f11(tmp30); +//│ tmp32 = to(5); +//│ tmp33 = f21(tmp32); +//│ tmp34 = tmp31 + tmp33; +//│ tmp35 = to(6); +//│ tmp36 = f31(tmp35); +//│ tmp37 = tmp34 + tmp36; +//│ tmp38 = to(7); +//│ tmp39 = f41(tmp38); +//│ tmp37 + tmp39 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1154 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1155 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1156 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1157 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1150 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1151 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1152 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1153 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let f11, to, f31, f41, f21, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; +//│ to_duplicated = function to_duplicated(n) { +//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp41 = n - 1; +//│ m = tmp41; +//│ tmp40 = to_duplicated(m); +//│ _deforest_Cons_h = n; +//│ _deforest_Cons_t = tmp40; +//│ return () => { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = f11(t); +//│ return h + tmp42 +//│ } +//│ } else { +//│ return () => { +//│ return 2 +//│ } +//│ } +//│ }; +//│ to_duplicated1 = function to_duplicated(n) { +//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp41 = n - 1; +//│ m = tmp41; +//│ tmp40 = to_duplicated1(m); +//│ _deforest_Cons_h = n; +//│ _deforest_Cons_t = tmp40; +//│ return () => { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = f21(t); +//│ return h + tmp42 +//│ } +//│ } else { +//│ return () => { +//│ return 3 +//│ } +//│ } +//│ }; +//│ to_duplicated2 = function to_duplicated(n) { +//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp41 = n - 1; +//│ m = tmp41; +//│ tmp40 = to_duplicated2(m); +//│ _deforest_Cons_h = n; +//│ _deforest_Cons_t = tmp40; +//│ return () => { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = f31(t); +//│ return h + tmp42 +//│ } +//│ } else { +//│ return () => { +//│ return 4 +//│ } +//│ } +//│ }; +//│ to_duplicated3 = function to_duplicated(n) { +//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp41 = n - 1; +//│ m = tmp41; +//│ tmp40 = to_duplicated3(m); +//│ _deforest_Cons_h = n; +//│ _deforest_Cons_t = tmp40; +//│ return () => { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = f41(t); +//│ return h + tmp42 +//│ } +//│ } else { +//│ return () => { +//│ return 5 +//│ } +//│ } +//│ }; +//│ to = function to(n) { +//│ let scrut, m, tmp40, tmp41; +//│ scrut = n > 0; +//│ if (scrut === true) { +//│ tmp40 = n - 1; +//│ m = tmp40; +//│ tmp41 = to(m); +//│ return Cons1(n, tmp41) +//│ } else { +//│ return Nil1 +//│ } +//│ }; +//│ f11 = function f1(ls1) { +//│ return runtime.safeCall(ls1()) +//│ }; +//│ f21 = function f2(ls2) { +//│ return runtime.safeCall(ls2()) +//│ }; +//│ f31 = function f3(ls3) { +//│ return runtime.safeCall(ls3()) +//│ }; +//│ f41 = function f4(ls4) { +//│ return runtime.safeCall(ls4()) +//│ }; +//│ tmp30 = to_duplicated(4); +//│ tmp31 = f11(tmp30); +//│ tmp32 = to_duplicated1(5); +//│ tmp33 = f21(tmp32); +//│ tmp34 = tmp31 + tmp33; +//│ tmp35 = to_duplicated2(6); +//│ tmp36 = f31(tmp35); +//│ tmp37 = tmp34 + tmp36; +//│ tmp38 = to_duplicated3(7); +//│ tmp39 = f41(tmp38); +//│ tmp37 + tmp39 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 88 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1196 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1197 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1198 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1199 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1215 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1216 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1217 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1218 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 88 +//│ 8 fusion opportunities: +//│ Cons --match--> `if ls1 is ...` +//│ Cons --match--> `if ls2 is ...` +//│ Cons --match--> `if ls3 is ...` +//│ Cons --match--> `if ls4 is ...` +//│ Nil --match--> `if ls1 is ...` +//│ Nil --match--> `if ls2 is ...` +//│ Nil --match--> `if ls3 is ...` +//│ Nil --match--> `if ls4 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforestDup @@ -446,6 +596,7 @@ f1(to(4)) :deforestDup +:sjs fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) @@ -454,14 +605,110 @@ fun double(x) = x * 2 fun test() = map(map(1 :: 2 :: Nil, succ), double) test() +//│ JS (unsanitized): +//│ let succ, test2, double1, map; +//│ map = function map(ls, f) { +//│ let param0, param1, h, t, tmp52, tmp53; +//│ if (ls instanceof Nil1.class) { +//│ return Nil1 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h = param0; +//│ t = param1; +//│ tmp52 = runtime.safeCall(f(h)); +//│ tmp53 = map(t, f); +//│ return Cons1(tmp52, tmp53) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ succ = function succ(x) { +//│ return x + 1 +//│ }; +//│ double1 = function (x) { +//│ return x * 2 +//│ }; +//│ test2 = function test() { +//│ let tmp52, tmp53, tmp54; +//│ tmp52 = Cons1(2, Nil1); +//│ tmp53 = Cons1(1, tmp52); +//│ tmp54 = map(tmp53, succ); +//│ return map(tmp54, double1) +//│ }; +//│ test2() +//│ duplication chances: +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1317 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ==== JS (deforested): ==== +//│ let succ, test2, double1, map, map_duplicated, match_ls_branch_Cons; +//│ match_ls_branch_Cons = function match_ls_branch_Cons(f, _deforest_Cons_h, _deforest_Cons_t) { +//│ let param1, tmp52, h, t, tmp53, param0, _deforest_Cons_t1, _deforest_Cons_h1; +//│ param0 = _deforest_Cons_h; +//│ param1 = _deforest_Cons_t; +//│ h = param0; +//│ t = param1; +//│ tmp53 = runtime.safeCall(f(h)); +//│ tmp52 = map_duplicated(t, f); +//│ _deforest_Cons_h1 = tmp53; +//│ _deforest_Cons_t1 = tmp52; +//│ return (f6) => { +//│ let param01, param11, h1, t1, tmp54, tmp55; +//│ param01 = _deforest_Cons_h1; +//│ param11 = _deforest_Cons_t1; +//│ h1 = param01; +//│ t1 = param11; +//│ tmp54 = runtime.safeCall(f6(h1)); +//│ tmp55 = map(t1, f6); +//│ return Cons1(tmp54, tmp55) +//│ } +//│ }; +//│ map_duplicated = function map_duplicated(ls, f) { +//│ return runtime.safeCall(ls(f)) +//│ }; +//│ map = function map(ls, f) { +//│ return runtime.safeCall(ls(f)) +//│ }; +//│ succ = function succ(x) { +//│ return x + 1 +//│ }; +//│ double1 = function (x) { +//│ return x * 2 +//│ }; +//│ test2 = function test() { +//│ let tmp52, tmp53, tmp54, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1; +//│ _deforest_Cons_h_tmp = 2; +//│ _deforest_Cons_t_tmp = (f) => { +//│ return (f6) => { +//│ return Nil1 +//│ } +//│ }; +//│ tmp52 = (f) => { +//│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp) +//│ }; +//│ _deforest_Cons_h_tmp1 = 1; +//│ _deforest_Cons_t_tmp1 = tmp52; +//│ tmp53 = (f) => { +//│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1) +//│ }; +//│ tmp54 = map_duplicated(tmp53, succ); +//│ return map(tmp54, double1) +//│ }; +//│ test2() +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(4, Cons(6, Nil)) //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1281 +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1352 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(4, Cons(6, Nil)) -//│ 1 fusion opportunities: +//│ 5 fusion opportunities: +//│ Cons --match--> `if ls is ...` //│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -475,10 +722,10 @@ fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1339 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1339 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1340 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1340 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1427 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1427 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1428 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1428 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 8 @@ -505,7 +752,7 @@ f1(one) + f2(one) + f3(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1395 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1482 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -526,8 +773,8 @@ fun f2(a) = if a is A(i) then i + 1 f1(id(A(1))) + f2(id(A(2))) //│ = 4 //│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1437 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1438 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1523 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1524 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 @@ -564,8 +811,8 @@ fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1494 -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1495 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1579 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1580 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -575,3 +822,32 @@ f1(wrap(4)) + f2(wrap(5)) //│ Nil --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + +:deforestDup +fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) +fun f1(ls, acc) = if ls is + Nil then acc + h :: t then f1(t, acc + h) +fun f2(ls, acc) = if ls is + Nil then acc + h :: t then f2(t, acc + h + 1) +f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) +//│ = 51 +//│ duplication chances: +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1664 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1664 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1665 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 51 +//│ 5 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index f1bd83e053..e93d3abfcc 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -1,7 +1,9 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class A(x) @@ -37,10 +39,13 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@941 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@942 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@938 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@939 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 10 +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -61,9 +66,12 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@992 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1005 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 9 +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -85,11 +93,14 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1053 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1053 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1054 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1066 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1066 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1067 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 5 +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -107,7 +118,39 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1096 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1097 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1109 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1110 +//│ ========= +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 4 +//│ 0 fusion opportunity +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + +// TODO: forget to not dup recursive calls +:deforestDup +fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) +fun f1(ls, acc) = if ls is + Nil then acc + h :: t then f1(t, acc + h) +fun f2(ls, acc) = if ls is + Nil then acc + h :: t then f2(t, acc + h + 1) +f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) +//│ = 51 +//│ duplication chances: +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1184 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1184 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1185 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 51 +//│ 5 fusion opportunities: +//│ Cons --match--> `if ls is ...` +//│ Cons --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ Nil --match--> `if ls is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 702cdd77e8ee256d6c9fddc97428b06890d7cdbe Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 13 May 2025 14:58:42 +0800 Subject: [PATCH 243/303] update tests --- .../test/mlscript/deforest/def-dup/simple.mls | 369 +++++++++--------- .../test/mlscript/deforest/def-dup/todo.mls | 66 +--- 2 files changed, 195 insertions(+), 240 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index be547200c8..3a596132f1 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -1,9 +1,7 @@ :js :deforest -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity data class A(x) @@ -22,10 +20,7 @@ fun test() = a + b test() + test() //│ = 4 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 4 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity :sjs :deforestDup @@ -55,17 +50,17 @@ test() //│ } //│ }; //│ test1 = function test() { -//│ let tmp4, tmp5, tmp6, tmp7; -//│ tmp4 = p(1); -//│ tmp5 = c1(tmp4); -//│ tmp6 = p(2); -//│ tmp7 = c2(tmp6); -//│ return tmp5 + tmp7 +//│ let tmp2, tmp3, tmp4, tmp5; +//│ tmp2 = p(1); +//│ tmp3 = c1(tmp2); +//│ tmp4 = p(2); +//│ tmp5 = c2(tmp4); +//│ return tmp3 + tmp5 //│ }; //│ test1() //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@929 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@930 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@931 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@932 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== @@ -94,19 +89,19 @@ test() //│ return runtime.safeCall(x()) //│ }; //│ test1 = function test() { -//│ let tmp4, tmp5, tmp6, tmp7; -//│ tmp4 = p_duplicated(1); -//│ tmp5 = c1(tmp4); -//│ tmp6 = p_duplicated1(2); -//│ tmp7 = c2(tmp6); -//│ return tmp5 + tmp7 +//│ let tmp2, tmp3, tmp4, tmp5; +//│ tmp2 = p_duplicated(1); +//│ tmp3 = c1(tmp2); +//│ tmp4 = p_duplicated1(2); +//│ tmp5 = c2(tmp4); +//│ return tmp3 + tmp5 //│ }; //│ test1() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@944 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@945 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@947 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@948 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 3 @@ -126,7 +121,7 @@ fun f4(a4) = if a4 is A(aa) then aa fun f5(a5) = if a5 is A(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ JS (unsanitized): -//│ let f1, f5, f3, p1, f4, f2, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; +//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; //│ p1 = function p(x) { //│ return A1(x) //│ }; @@ -180,30 +175,30 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp4 = p1(1); -//│ tmp5 = f1(tmp4); -//│ tmp6 = p1(2); -//│ tmp7 = f2(tmp6); -//│ tmp8 = tmp5 + tmp7; -//│ tmp9 = p1(3); -//│ tmp10 = f3(tmp9); -//│ tmp11 = tmp8 + tmp10; -//│ tmp12 = p1(4); -//│ tmp13 = f4(tmp12); -//│ tmp14 = tmp11 + tmp13; -//│ tmp15 = p1(5); -//│ tmp16 = f5(tmp15); -//│ tmp14 + tmp16 +//│ tmp2 = p1(1); +//│ tmp3 = f1(tmp2); +//│ tmp4 = p1(2); +//│ tmp5 = f2(tmp4); +//│ tmp6 = tmp3 + tmp5; +//│ tmp7 = p1(3); +//│ tmp8 = f3(tmp7); +//│ tmp9 = tmp6 + tmp8; +//│ tmp10 = p1(4); +//│ tmp11 = f4(tmp10); +//│ tmp12 = tmp9 + tmp11; +//│ tmp13 = p1(5); +//│ tmp14 = f5(tmp13); +//│ tmp12 + tmp14 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1013 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1014 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1015 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1016 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1017 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f1, f5, f3, p1, f4, f2, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; +//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; //│ p_duplicated2 = function p_duplicated(x) { //│ let _deforest_A_x; //│ _deforest_A_x = x; @@ -272,28 +267,28 @@ f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ f5 = function f5(a5) { //│ return runtime.safeCall(a5()) //│ }; -//│ tmp4 = p_duplicated2(1); -//│ tmp5 = f1(tmp4); -//│ tmp6 = p_duplicated3(2); -//│ tmp7 = f2(tmp6); -//│ tmp8 = tmp5 + tmp7; -//│ tmp9 = p_duplicated4(3); -//│ tmp10 = f3(tmp9); -//│ tmp11 = tmp8 + tmp10; -//│ tmp12 = p_duplicated5(4); -//│ tmp13 = f4(tmp12); -//│ tmp14 = tmp11 + tmp13; -//│ tmp15 = p_duplicated6(5); -//│ tmp16 = f5(tmp15); -//│ tmp14 + tmp16 +//│ tmp2 = p_duplicated2(1); +//│ tmp3 = f1(tmp2); +//│ tmp4 = p_duplicated3(2); +//│ tmp5 = f2(tmp4); +//│ tmp6 = tmp3 + tmp5; +//│ tmp7 = p_duplicated4(3); +//│ tmp8 = f3(tmp7); +//│ tmp9 = tmp6 + tmp8; +//│ tmp10 = p_duplicated5(4); +//│ tmp11 = f4(tmp10); +//│ tmp12 = tmp9 + tmp11; +//│ tmp13 = p_duplicated6(5); +//│ tmp14 = f5(tmp13); +//│ tmp12 + tmp14 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1055 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1056 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1057 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1058 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1059 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1059 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1060 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1061 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1062 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1063 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 15 @@ -329,28 +324,28 @@ fun f4(ls4) = if ls4 is Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ JS (unsanitized): -//│ let f11, to, f31, f41, f21, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39; +//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; //│ to = function to(n) { -//│ let scrut, m, tmp40, tmp41; +//│ let scrut, m, tmp38, tmp39; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp40 = n - 1; -//│ m = tmp40; -//│ tmp41 = to(m); -//│ return Cons1(n, tmp41) +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) //│ } else { //│ return Nil1 //│ } //│ }; //│ f11 = function f1(ls1) { -//│ let param0, param1, h, t, tmp40; +//│ let param0, param1, h, t, tmp38; //│ if (ls1 instanceof Cons1.class) { //│ param0 = ls1.h; //│ param1 = ls1.t; //│ h = param0; //│ t = param1; -//│ tmp40 = f11(t); -//│ return h + tmp40 +//│ tmp38 = f11(t); +//│ return h + tmp38 //│ } else if (ls1 instanceof Nil1.class) { //│ return 2 //│ } else { @@ -358,14 +353,14 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f21 = function f2(ls2) { -//│ let param0, param1, h, t, tmp40; +//│ let param0, param1, h, t, tmp38; //│ if (ls2 instanceof Cons1.class) { //│ param0 = ls2.h; //│ param1 = ls2.t; //│ h = param0; //│ t = param1; -//│ tmp40 = f21(t); -//│ return h + tmp40 +//│ tmp38 = f21(t); +//│ return h + tmp38 //│ } else if (ls2 instanceof Nil1.class) { //│ return 3 //│ } else { @@ -373,14 +368,14 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f31 = function f3(ls3) { -//│ let param0, param1, h, t, tmp40; +//│ let param0, param1, h, t, tmp38; //│ if (ls3 instanceof Cons1.class) { //│ param0 = ls3.h; //│ param1 = ls3.t; //│ h = param0; //│ t = param1; -//│ tmp40 = f31(t); -//│ return h + tmp40 +//│ tmp38 = f31(t); +//│ return h + tmp38 //│ } else if (ls3 instanceof Nil1.class) { //│ return 4 //│ } else { @@ -388,57 +383,57 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ f41 = function f4(ls4) { -//│ let param0, param1, h, t, tmp40; +//│ let param0, param1, h, t, tmp38; //│ if (ls4 instanceof Cons1.class) { //│ param0 = ls4.h; //│ param1 = ls4.t; //│ h = param0; //│ t = param1; -//│ tmp40 = f41(t); -//│ return h + tmp40 +//│ tmp38 = f41(t); +//│ return h + tmp38 //│ } else if (ls4 instanceof Nil1.class) { //│ return 5 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp30 = to(4); -//│ tmp31 = f11(tmp30); -//│ tmp32 = to(5); -//│ tmp33 = f21(tmp32); -//│ tmp34 = tmp31 + tmp33; -//│ tmp35 = to(6); -//│ tmp36 = f31(tmp35); -//│ tmp37 = tmp34 + tmp36; -//│ tmp38 = to(7); -//│ tmp39 = f41(tmp38); -//│ tmp37 + tmp39 +//│ tmp28 = to(4); +//│ tmp29 = f11(tmp28); +//│ tmp30 = to(5); +//│ tmp31 = f21(tmp30); +//│ tmp32 = tmp29 + tmp31; +//│ tmp33 = to(6); +//│ tmp34 = f31(tmp33); +//│ tmp35 = tmp32 + tmp34; +//│ tmp36 = to(7); +//│ tmp37 = f41(tmp36); +//│ tmp35 + tmp37 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1150 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1151 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1152 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1153 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1154 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1155 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1156 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1157 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== -//│ let f11, to, f31, f41, f21, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; +//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; //│ to_duplicated = function to_duplicated(n) { -//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp41 = n - 1; -//│ m = tmp41; -//│ tmp40 = to_duplicated(m); +//│ tmp39 = n - 1; +//│ m = tmp39; +//│ tmp38 = to_duplicated(m); //│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp40; +//│ _deforest_Cons_t = tmp38; //│ return () => { -//│ let param0, param1, h, t, tmp42; +//│ let param0, param1, h, t, tmp40; //│ param0 = _deforest_Cons_h; //│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; -//│ tmp42 = f11(t); -//│ return h + tmp42 +//│ tmp40 = f11(t); +//│ return h + tmp40 //│ } //│ } else { //│ return () => { @@ -447,22 +442,22 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ to_duplicated1 = function to_duplicated(n) { -//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp41 = n - 1; -//│ m = tmp41; -//│ tmp40 = to_duplicated1(m); +//│ tmp39 = n - 1; +//│ m = tmp39; +//│ tmp38 = to_duplicated1(m); //│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp40; +//│ _deforest_Cons_t = tmp38; //│ return () => { -//│ let param0, param1, h, t, tmp42; +//│ let param0, param1, h, t, tmp40; //│ param0 = _deforest_Cons_h; //│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; -//│ tmp42 = f21(t); -//│ return h + tmp42 +//│ tmp40 = f21(t); +//│ return h + tmp40 //│ } //│ } else { //│ return () => { @@ -471,22 +466,22 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ to_duplicated2 = function to_duplicated(n) { -//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp41 = n - 1; -//│ m = tmp41; -//│ tmp40 = to_duplicated2(m); +//│ tmp39 = n - 1; +//│ m = tmp39; +//│ tmp38 = to_duplicated2(m); //│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp40; +//│ _deforest_Cons_t = tmp38; //│ return () => { -//│ let param0, param1, h, t, tmp42; +//│ let param0, param1, h, t, tmp40; //│ param0 = _deforest_Cons_h; //│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; -//│ tmp42 = f31(t); -//│ return h + tmp42 +//│ tmp40 = f31(t); +//│ return h + tmp40 //│ } //│ } else { //│ return () => { @@ -495,22 +490,22 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ to_duplicated3 = function to_duplicated(n) { -//│ let tmp40, m, tmp41, scrut, _deforest_Cons_t, _deforest_Cons_h; +//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp41 = n - 1; -//│ m = tmp41; -//│ tmp40 = to_duplicated3(m); +//│ tmp39 = n - 1; +//│ m = tmp39; +//│ tmp38 = to_duplicated3(m); //│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp40; +//│ _deforest_Cons_t = tmp38; //│ return () => { -//│ let param0, param1, h, t, tmp42; +//│ let param0, param1, h, t, tmp40; //│ param0 = _deforest_Cons_h; //│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; -//│ tmp42 = f41(t); -//│ return h + tmp42 +//│ tmp40 = f41(t); +//│ return h + tmp40 //│ } //│ } else { //│ return () => { @@ -519,13 +514,13 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ } //│ }; //│ to = function to(n) { -//│ let scrut, m, tmp40, tmp41; +//│ let scrut, m, tmp38, tmp39; //│ scrut = n > 0; //│ if (scrut === true) { -//│ tmp40 = n - 1; -//│ m = tmp40; -//│ tmp41 = to(m); -//│ return Cons1(n, tmp41) +//│ tmp38 = n - 1; +//│ m = tmp38; +//│ tmp39 = to(m); +//│ return Cons1(n, tmp39) //│ } else { //│ return Nil1 //│ } @@ -542,24 +537,24 @@ f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ f41 = function f4(ls4) { //│ return runtime.safeCall(ls4()) //│ }; -//│ tmp30 = to_duplicated(4); -//│ tmp31 = f11(tmp30); -//│ tmp32 = to_duplicated1(5); -//│ tmp33 = f21(tmp32); -//│ tmp34 = tmp31 + tmp33; -//│ tmp35 = to_duplicated2(6); -//│ tmp36 = f31(tmp35); -//│ tmp37 = tmp34 + tmp36; -//│ tmp38 = to_duplicated3(7); -//│ tmp39 = f41(tmp38); -//│ tmp37 + tmp39 +//│ tmp28 = to_duplicated(4); +//│ tmp29 = f11(tmp28); +//│ tmp30 = to_duplicated1(5); +//│ tmp31 = f21(tmp30); +//│ tmp32 = tmp29 + tmp31; +//│ tmp33 = to_duplicated2(6); +//│ tmp34 = f31(tmp33); +//│ tmp35 = tmp32 + tmp34; +//│ tmp36 = to_duplicated3(7); +//│ tmp37 = f41(tmp36); +//│ tmp35 + tmp37 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 88 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1215 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1216 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1217 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1218 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1220 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1221 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1222 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1223 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 88 @@ -608,7 +603,7 @@ test() //│ JS (unsanitized): //│ let succ, test2, double1, map; //│ map = function map(ls, f) { -//│ let param0, param1, h, t, tmp52, tmp53; +//│ let param0, param1, h, t, tmp50, tmp51; //│ if (ls instanceof Nil1.class) { //│ return Nil1 //│ } else if (ls instanceof Cons1.class) { @@ -616,9 +611,9 @@ test() //│ param1 = ls.t; //│ h = param0; //│ t = param1; -//│ tmp52 = runtime.safeCall(f(h)); -//│ tmp53 = map(t, f); -//│ return Cons1(tmp52, tmp53) +//│ tmp50 = runtime.safeCall(f(h)); +//│ tmp51 = map(t, f); +//│ return Cons1(tmp50, tmp51) //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -630,38 +625,38 @@ test() //│ return x * 2 //│ }; //│ test2 = function test() { -//│ let tmp52, tmp53, tmp54; -//│ tmp52 = Cons1(2, Nil1); -//│ tmp53 = Cons1(1, tmp52); -//│ tmp54 = map(tmp53, succ); -//│ return map(tmp54, double1) +//│ let tmp50, tmp51, tmp52; +//│ tmp50 = Cons1(2, Nil1); +//│ tmp51 = Cons1(1, tmp50); +//│ tmp52 = map(tmp51, succ); +//│ return map(tmp52, double1) //│ }; //│ test2() //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1317 +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1323 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ==== JS (deforested): ==== //│ let succ, test2, double1, map, map_duplicated, match_ls_branch_Cons; //│ match_ls_branch_Cons = function match_ls_branch_Cons(f, _deforest_Cons_h, _deforest_Cons_t) { -//│ let param1, tmp52, h, t, tmp53, param0, _deforest_Cons_t1, _deforest_Cons_h1; +//│ let param0, t, tmp50, param1, h, tmp51, _deforest_Cons_t1, _deforest_Cons_h1; //│ param0 = _deforest_Cons_h; //│ param1 = _deforest_Cons_t; //│ h = param0; //│ t = param1; -//│ tmp53 = runtime.safeCall(f(h)); -//│ tmp52 = map_duplicated(t, f); -//│ _deforest_Cons_h1 = tmp53; -//│ _deforest_Cons_t1 = tmp52; +//│ tmp51 = runtime.safeCall(f(h)); +//│ tmp50 = map_duplicated(t, f); +//│ _deforest_Cons_h1 = tmp51; +//│ _deforest_Cons_t1 = tmp50; //│ return (f6) => { -//│ let param01, param11, h1, t1, tmp54, tmp55; +//│ let param01, param11, h1, t1, tmp52, tmp53; //│ param01 = _deforest_Cons_h1; //│ param11 = _deforest_Cons_t1; //│ h1 = param01; //│ t1 = param11; -//│ tmp54 = runtime.safeCall(f6(h1)); -//│ tmp55 = map(t1, f6); -//│ return Cons1(tmp54, tmp55) +//│ tmp52 = runtime.safeCall(f6(h1)); +//│ tmp53 = map(t1, f6); +//│ return Cons1(tmp52, tmp53) //│ } //│ }; //│ map_duplicated = function map_duplicated(ls, f) { @@ -677,29 +672,29 @@ test() //│ return x * 2 //│ }; //│ test2 = function test() { -//│ let tmp52, tmp53, tmp54, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1; +//│ let tmp50, tmp51, tmp52, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1; //│ _deforest_Cons_h_tmp = 2; //│ _deforest_Cons_t_tmp = (f) => { //│ return (f6) => { //│ return Nil1 //│ } //│ }; -//│ tmp52 = (f) => { +//│ tmp50 = (f) => { //│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp) //│ }; //│ _deforest_Cons_h_tmp1 = 1; -//│ _deforest_Cons_t_tmp1 = tmp52; -//│ tmp53 = (f) => { +//│ _deforest_Cons_t_tmp1 = tmp50; +//│ tmp51 = (f) => { //│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1) //│ }; -//│ tmp54 = map_duplicated(tmp53, succ); -//│ return map(tmp54, double1) +//│ tmp52 = map_duplicated(tmp51, succ); +//│ return map(tmp52, double1) //│ }; //│ test2() //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(4, Cons(6, Nil)) //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1352 +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1359 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = Cons(4, Cons(6, Nil)) @@ -722,10 +717,10 @@ fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1427 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1427 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1428 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1428 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1435 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1435 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1436 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1436 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 8 @@ -752,7 +747,7 @@ f1(one) + f2(one) + f3(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1482 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1491 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -773,8 +768,8 @@ fun f2(a) = if a is A(i) then i + 1 f1(id(A(1))) + f2(id(A(2))) //│ = 4 //│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1523 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1524 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1533 +//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1534 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 4 @@ -811,8 +806,8 @@ fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1579 -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1580 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1590 +//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1591 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 10 @@ -838,9 +833,9 @@ fun f2(ls, acc) = if ls is f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ = 51 //│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1664 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1664 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1665 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1676 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1676 +//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1677 //│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ = 51 diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index e93d3abfcc..5ccfea99ea 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -1,9 +1,7 @@ :js :deforest -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity data class A(x) @@ -39,13 +37,10 @@ fun badWrap(n) = f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 //│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@938 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@939 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@941 +//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@942 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 10 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -66,12 +61,9 @@ f1(one) + f2(one) + f2(two) //│ one = A(1) //│ two = A(2) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1005 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1008 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 9 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -93,14 +85,11 @@ fun test() = test() //│ = 5 //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1066 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1066 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1067 +//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1069 +//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1069 +//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1070 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 5 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity @@ -118,39 +107,10 @@ f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) //│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1109 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1110 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1112 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1113 //│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 4 -//│ 0 fusion opportunity -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ No fusion opportunity - -// TODO: forget to not dup recursive calls -:deforestDup -fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) -fun f1(ls, acc) = if ls is - Nil then acc - h :: t then f1(t, acc + h) -fun f2(ls, acc) = if ls is - Nil then acc - h :: t then f2(t, acc + h + 1) -f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) -//│ = 51 -//│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1184 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1184 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1185 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ = 51 -//│ 5 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From db1e49afb0a2e1d8985407e12c4b2bb2a9f8bcb0 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 13 May 2025 16:48:34 +0800 Subject: [PATCH 244/303] previous method wip --- .../scala/hkmc2/codegen/Deforestation.scala | 15 ++++++++------- .../test/mlscript/deforest/def-dup/simple.mls | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 381127dec3..30edbae899 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -761,7 +761,7 @@ class Deforest(using TL, Raise, Elaborator.State): case (ctorCallId, CtorDest(callResVars = vs, _)) => vs.map(ctorCallId -> _) .groupBy(_._2) - def duplicatable(v: StratVarState): Opt[ResultId -> Symbol -> Dtor] = + def duplicatable(v: StratVarState): Opt[ResultId -> Symbol -> Dtor -> Bool] = val callSiteId -> calleeSym = v.callResOf.get // if this is the only call site, no reason to duplicate if callInfo.getAllCallSites(calleeSym).size == 1 then N @@ -777,14 +777,14 @@ class Deforest(using TL, Raise, Elaborator.State): val dtor = dtors.head.asInstanceOf[Dtor] // it is a potential duplicate chance if the call res of a function // is consumed by the body of the same function - if dtor.inDef.fold(false)(_ is calleeSym) then S(callSiteId -> calleeSym -> dtor) + if dtor.inDef.fold(false)(_ is calleeSym) then S(callSiteId -> calleeSym -> dtor -> true) else // if all ctors flowing into this call res does not have a clash, no dup if callResToCtorsCallsFlowingIntoThem(v).forall: (ctorCallId, fnCallSite) => val CtorDest(matches, sels, noCons, _) = ctorDests.get(ctorCallId).get !noCons && matches.size == 1 then N - else S(callSiteId -> calleeSym -> dtor) + else S(callSiteId -> calleeSym -> dtor -> false) val allDuplicatableCallSites = ctorDests.ctorDests .flatMap: @@ -795,15 +795,16 @@ class Deforest(using TL, Raise, Elaborator.State): allDuplicatableCallSites .groupBy: - case _ -> calleeSym -> dtor => calleeSym -> dtor + case _ -> calleeSym -> dtor -> isRecursive => calleeSym -> dtor .values .toList - .sortBy(x => callInfo.getCallSiteStableId(x.head._1._1)) + .sortBy(x => callInfo.getCallSiteStableId(x.head._1._1._1)) .flatMap: setOfCallSites => - val sharedCalleeSym = setOfCallSites.head._1._2 + // setOfCallSites.groupBy(_._2) + val sharedCalleeSym = setOfCallSites.head._1._1._2 val newCalleeSym = new BlockMemberSymbol(sharedCalleeSym.nme + "_duplicated", Nil, true) setOfCallSites.map: - case callSiteId -> calleeSym -> _ => + case callSiteId -> calleeSym -> _ -> _ => assert(calleeSym is sharedCalleeSym) callSiteId -> newCalleeSym diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 3a596132f1..a038fd3f24 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -846,3 +846,22 @@ f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ Nil --match--> `if ls is ...` //│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// TODO: don't group two maps together +:deforestDup +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +fun succ(x) = x + 1 +fun double(x) = x * 2 +fun triple(x) = x * 3 +fun test() = + map(map(map(1 :: 2 :: Nil, succ), double), triple) +test() +//│ = Cons(12, Cons(18, Nil)) +//│ duplication chances: +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1750 +//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:double)))) <-- dup --> member:map_duplicated@1750 +//│ ========= +//│ No fusion opportunity From 83b575964b012b7584f8134304b6fa1958fffdc8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 15 May 2025 17:49:41 +0800 Subject: [PATCH 245/303] save some tests --- .../test/mlscript/deforest/def-dup/simple.mls | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index a038fd3f24..018d3ab919 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -848,20 +848,42 @@ f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// TODO: don't group two maps together +// // TODO: don't group two maps together +// :deforestDup +// fun map(ls, f) = if ls is +// Nil then Nil +// h :: t then f(h) :: map(t, f) +// fun succ(x) = x + 1 +// fun double(x) = x * 2 +// fun triple(x) = x * 3 +// fun test() = +// map(map(map(1 :: 2 :: Nil, succ), double), triple) +// test() +// //│ = Cons(12, Cons(18, Nil)) +// //│ duplication chances: +// //│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1750 +// //│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:double)))) <-- dup --> member:map_duplicated@1750 +// //│ ========= +// //│ No fusion opportunity + + + :deforestDup -fun map(ls, f) = if ls is - Nil then Nil - h :: t then f(h) :: map(t, f) -fun succ(x) = x + 1 -fun double(x) = x * 2 -fun triple(x) = x * 3 +fun p(x) = A(x) +fun f1(a1) = if a1 is A(a) then a + 1 +fun f2(a2) = if a2 is A(a) then a + 2 +fun wrap(a) = a fun test() = - map(map(map(1 :: 2 :: Nil, succ), double), triple) + f1(wrap(p(2))) + f2(p(1)) test() -//│ = Cons(12, Cons(18, Nil)) +//│ = 6 //│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1750 -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:double)))) <-- dup --> member:map_duplicated@1750 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1745 +//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1746 //│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ = 6 +//│ 2 fusion opportunities: +//│ A --match--> `if a1 is ...` +//│ A --match--> `if a2 is ...` +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From f491f7c7c472e43c0ab67e98e4f4274520537d18 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 27 May 2025 13:05:56 +0800 Subject: [PATCH 246/303] wip: polymorphism --- .../scala/hkmc2/codegen/Deforestation.scala | 93 +++++++++++++------ 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 30edbae899..d2d00eae8c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -18,7 +18,7 @@ sealed abstract class ProdStrat sealed abstract class ConsStrat -class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val callResOf: Opt[ResultId -> Symbol]): +class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val callResOf: Opt[ResultId -> Symbol], val inDef: Opt[BlockMemberSymbol]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) @@ -29,9 +29,9 @@ object StratVarState: // None: not representing the parameter type or return type of a function // Some(Left): parameter type // Some(right): return type - def freshVar(nme: String = "", callResOf: Opt[ResultId -> Symbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: Uid.StratVar.State) = + def freshVar(nme: String = "", callResOf: Opt[ResultId -> Symbol], inDef: Opt[BlockMemberSymbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: Uid.StratVar.State) = val newId = vuid.nextUid - val s = StratVarState(newId, nme, funRetOrArg, callResOf) + val s = StratVarState(newId, nme, funRetOrArg, callResOf, inDef) val p = s.asProdStrat val c = s.asConsStrat p -> c @@ -63,7 +63,40 @@ extension (i: ResultId) case _ => N case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId)(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat -case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat +case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat: + def instantiate(funSym: BlockMemberSymbol)(using d: Deforest): ProdFun = + val constr = d.inDefConstraints.get(funSym) + constr match + case None => this + case Some(constrLs) => + val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def duplicateVarState(s: StratVarState) = + stratVarMap.getOrElseUpdate.curried(s): + StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s + def duplicateProdStrat(s: ProdStrat): ProdStrat = s match + case c@Ctor(ctor, args, expr) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr)(c.inDef) + case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) + case p@ProdVar(s) => + if s.inDef === S(funSym) then + duplicateVarState(s).asProdStrat + else p + case NoProd => NoProd + def duplicateConsStrat(s: ConsStrat): ConsStrat = s match + case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) + case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) + case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) + case c@ConsVar(s) => + if s.inDef === S(funSym) then + duplicateVarState(s).asConsStrat + else c + case NoCons => NoCons + + val newProdFun = duplicateProdStrat(this).asInstanceOf[ProdFun] + constrLs.foreach: + case p -> c => + d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) + newProdFun + case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat @@ -293,7 +326,7 @@ extension (b: Block) class Deforest(using TL, Raise, Elaborator.State): - given Uid.StratVar.State = Uid.StratVar.State() + given stratVarUidState: Uid.StratVar.State = Uid.StratVar.State() given Deforest = this import StratVarState.freshVar @@ -394,28 +427,29 @@ class Deforest(using TL, Raise, Elaborator.State): def init(p: Block) = if store.isEmpty then object FreshVarForAllVars extends BlockTraverser: - var funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N + var inDef: Opt[BlockMemberSymbol] = N + def funRetOrArg = inDef.map(s => L(s)) override def applySymbol(s: Symbol): Unit = s match case b: BlockMemberSymbol => - store += s -> freshVar(s.nme, N)._1 + store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 b.trmImplTree.foreach: t => if t.k is syntax.Fun then usedFunSym += b - case _: TempSymbol => store += s -> freshVar(s.nme, N)._1 - case _: VarSymbol => store += s -> freshVar(s.nme, N, funRetOrArg)._1 - case _: TermSymbol => store += s -> freshVar(s.nme, N)._1 + case _: TempSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 + case _: VarSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 + case _: TermSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 case _ => () override def applyFunDefn(fun: FunDefn): Unit = funSymsWithDefn += fun.sym - funRetOrArg = S(L(fun.sym)) + inDef = S(fun.sym) super.applyFunDefn(fun) - funRetOrArg = N + inDef = N FreshVarForAllVars.applyBlock(p) // `NoProd` to block fusion for those functions that are imported from elsewhere usedFunSym.diff(funSymsWithDefn).foreach: funSymsWithoutDefn => - constrain(NoProd, store(funSymsWithoutDefn).asConsStrat) + constrain(NoProd, store(funSymsWithoutDefn).asConsStrat)(using N) def getStratOfSym(s: Symbol) = @@ -439,8 +473,13 @@ class Deforest(using TL, Raise, Elaborator.State): def getClsFields(s: ClassSymbol) = s.tree.clsParams - - def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c + val inDefConstraints = mutable.Map.empty[BlockMemberSymbol, Ls[ProdStrat -> ConsStrat]] + def constrain(p: ProdStrat, c: ConsStrat)(using inDef: Opt[BlockMemberSymbol]) = + inDef.foreach: i => + inDefConstraints.updateWith(i): + case None => Some((p -> c) :: Nil) + case Some(value) => Some((p -> c) :: value) + constraints ::= p -> c object callInfo: val callerToCallSitesInside = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] @@ -502,7 +541,7 @@ class Deforest(using TL, Raise, Elaborator.State): val dfltRes = dflt.map(processBlock) rest match case End(msg) => - val matchRes = freshVar("", N) + val matchRes = freshVar("", N, inDef) armsRes.appendedAll(dfltRes).foreach: r => constrain(r, matchRes._2) matchRes._1 @@ -524,7 +563,7 @@ class Deforest(using TL, Raise, Elaborator.State): // TODO: handle `restParam` and mutiple param list case ParamList(flags, params, N) :: Nil => params val funStrat = constrFun(param, body)(using inArm, matching, S(sym)) - constrain(funStrat, funSymStratVar.asConsStrat) + constrain(funStrat, funSymStratVar.asConsStrat)(using S(sym)) funSymStratVar case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") @@ -534,7 +573,7 @@ class Deforest(using TL, Raise, Elaborator.State): // default else branches do not block fusion... case Throw(exc) => processResult(exc) - freshVar("throw", N)._1 + freshVar("throw", N, inDef)._1 def constrFun(params: Ls[Param], body: Block)(using inArm: Map[ProdVar, ClsOrModSymbol], @@ -546,7 +585,7 @@ class Deforest(using TL, Raise, Elaborator.State): case Param(sym = sym, _) => sym val paramStrats = paramSyms.map(symToStrat.apply) // symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef.map(L.apply)) + val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef, inDef.map(L.apply)) constrain(processBlock(body), res._2) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) @@ -562,16 +601,17 @@ class Deforest(using TL, Raise, Elaborator.State): s.symbol.map(_.asCls) match case None => val pStrat = processResult(p) - val tpeVar = freshVar("", N) + val tpeVar = freshVar("", N, inDef) constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) - val appRes = freshVar("", N) // unknown function symbol + val appRes = freshVar("", N, inDef) // unknown function symbol constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) appRes._1 case Some(None) => val funSym = s.symbol.get val appRes = freshVar( "call_" + funSym.nme + "_res", - s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)) // if `f` has a blockMemberSymbol, then record it + s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)), // if `f` has a blockMemberSymbol, then record it + inDef ) for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) @@ -587,14 +627,15 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => // then it is a function val appRes = freshVar( "call_" + l.nme + "_res", - l.asBlkMember.map(c.uid -> _) // if `f` has a blockMemberSymbol, then record it + l.asBlkMember.map(c.uid -> _), // if `f` has a blockMemberSymbol, then record it + inDef ) for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) - val appRes = freshVar("call_lam_res", N) + val appRes = freshVar("call_lam_res", N, inDef) constrain(funTpe, ConsFun(argsTpe, appRes._2)) appRes._1 @@ -613,13 +654,13 @@ class Deforest(using TL, Raise, Elaborator.State): val pStrat = processResult(p) pStrat match case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => - val tpeVar = freshVar("", N) + val tpeVar = freshVar("", N, inDef) val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) constrain(pStrat, selStrat) tpeVar._1 case _ => - val tpeVar = freshVar("", N) + val tpeVar = freshVar("", N, inDef) constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) tpeVar._1 From 14d8a641e6588ced8e714c5b08e5acb361082e5a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 30 May 2025 13:47:11 +0800 Subject: [PATCH 247/303] move instantiate function --- .../scala/hkmc2/codegen/Deforestation.scala | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index d2d00eae8c..f320e51a3e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -14,7 +14,47 @@ type StratVar type StratVarId = Uid[StratVar] type ClsOrModSymbol = ClassLikeSymbol -sealed abstract class ProdStrat +sealed abstract class ProdStrat: + def instantiate(referSite: ResultId)(using d: Deforest): ProdStrat = + val funSym = referSite.getFunCallBlkMemSym.orElse: + referSite.getResult match + case sel: Select => sel.symbol.flatMap(_.asBlkMember) + case Value.Ref(l) => l.asBlkMember + case _ => N + funSym match + case None => this + case Some(funSym) => + val constr = d.inDefConstraints.get(funSym) + constr match + case None => this + case Some(constrLs) => + val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def duplicateVarState(s: StratVarState) = + stratVarMap.getOrElseUpdate.curried(s): + StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s + def duplicateProdStrat(s: ProdStrat): ProdStrat = s match + case c@Ctor(ctor, args, expr) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr)(c.inDef) + case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) + case p@ProdVar(s) => + if s.inDef === S(funSym) then + duplicateVarState(s).asProdStrat + else p + case NoProd => NoProd + def duplicateConsStrat(s: ConsStrat): ConsStrat = s match + case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) + case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) + case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) + case c@ConsVar(s) => + if s.inDef === S(funSym) then + duplicateVarState(s).asConsStrat + else c + case NoCons => NoCons + + val newProd = duplicateProdStrat(this) + constrLs.foreach: + case p -> c => + d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) + newProd sealed abstract class ConsStrat @@ -63,39 +103,7 @@ extension (i: ResultId) case _ => N case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId)(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat -case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat: - def instantiate(funSym: BlockMemberSymbol)(using d: Deforest): ProdFun = - val constr = d.inDefConstraints.get(funSym) - constr match - case None => this - case Some(constrLs) => - val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] - def duplicateVarState(s: StratVarState) = - stratVarMap.getOrElseUpdate.curried(s): - StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s - def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr)(c.inDef) - case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) - case p@ProdVar(s) => - if s.inDef === S(funSym) then - duplicateVarState(s).asProdStrat - else p - case NoProd => NoProd - def duplicateConsStrat(s: ConsStrat): ConsStrat = s match - case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) - case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) - case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) - case c@ConsVar(s) => - if s.inDef === S(funSym) then - duplicateVarState(s).asConsStrat - else c - case NoCons => NoCons - - val newProdFun = duplicateProdStrat(this).asInstanceOf[ProdFun] - constrLs.foreach: - case p -> c => - d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) - newProdFun +case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) case object NoProd extends ProdStrat From d4891371937bb9c8f04acb6e406f7669ec89af30 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:02:53 +0800 Subject: [PATCH 248/303] ctor keep track of instantiate id --- .../scala/hkmc2/codegen/Deforestation.scala | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index f320e51a3e..eb3faaf805 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -33,7 +33,7 @@ sealed abstract class ProdStrat: stratVarMap.getOrElseUpdate.curried(s): StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr)(c.inDef) + case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) case p@ProdVar(s) => if s.inDef === S(funSym) then @@ -102,7 +102,11 @@ extension (i: ResultId) case _ => N case _ => N -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId)(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat +// instantiateId: +// - None: not top level and not from an instantiation +// - Some(Nil): top level +// - Some(ls): non-top-level +case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId, instantiationId: Opt[Ls[ResultId]])(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) @@ -481,7 +485,11 @@ class Deforest(using TL, Raise, Elaborator.State): def getClsFields(s: ClassSymbol) = s.tree.clsParams - val inDefConstraints = mutable.Map.empty[BlockMemberSymbol, Ls[ProdStrat -> ConsStrat]] + object inDefConstraints: + val store = mutable.Map.empty[BlockMemberSymbol, Ls[ProdStrat -> ConsStrat]] + def get = store.get + def updateWith = store.updateWith + def constrain(p: ProdStrat, c: ConsStrat)(using inDef: Opt[BlockMemberSymbol]) = inDef.foreach: i => inDefConstraints.updateWith(i): @@ -626,12 +634,12 @@ class Deforest(using TL, Raise, Elaborator.State): appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid, N)(inDef) case Value.Ref(l) => l.asCls match case Some(s) => val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid)(inDef) + Ctor(s, clsFields.zip(argsTpe).toMap, c.uid, N)(inDef) case _ => // then it is a function val appRes = freshVar( "call_" + l.nme + "_res", @@ -657,7 +665,7 @@ class Deforest(using TL, Raise, Elaborator.State): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => - Ctor(s.asObj.get, Map.empty, sel.uid)(inDef) + Ctor(s.asObj.get, Map.empty, sel.uid, N)(inDef) case _ => val pStrat = processResult(p) pStrat match @@ -674,7 +682,7 @@ class Deforest(using TL, Raise, Elaborator.State): case v@Value.Ref(l) => l.asObj match case None => symToStrat.getStratOfSym(l) - case Some(m) => Ctor(m, Map.empty, v.uid)(inDef) + case Some(m) => Ctor(m, Map.empty, v.uid, N)(inDef) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") case Value.Lit(lit) => NoProd @@ -735,15 +743,15 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (Ctor(ctor, args, expr), dtorStrat@Dtor(scrut)) => + case (Ctor(ctor, args, expr, _), dtorStrat@Dtor(scrut)) => ctorDests.update(expr, dtorStrat.expr) dtorSources.update(scrut, expr) - case (Ctor(ctor, args, expr), selDtor@FieldSel(field, consVar)) => + case (Ctor(ctor, args, expr, _), selDtor@FieldSel(field, consVar)) => ctorDests.update(expr, selDtor) dtorSources.update(selDtor.expr, expr) args.find(a => a._1.id == field).map: p => handle(p._2 -> consVar) - case (Ctor(ctor, args, _), ConsFun(l, r)) => () // ignore + case (Ctor(ctor, args, _, _), ConsFun(l, r)) => () // ignore case (p: ProdVar, _) => upperBounds += p.uid -> (cons :: upperBounds(p.uid)) @@ -752,7 +760,7 @@ class Deforest(using TL, Raise, Elaborator.State): case (l: ProdVar, sel@FieldSel(field, consVar)) => sel.updateFilter(l, sel.filter(p)) handle(l -> cons) - case (Ctor(ctor, args, _), sel@FieldSel(field, consVar)) => + case (Ctor(ctor, args, _, _), sel@FieldSel(field, consVar)) => if sel.filter.get(p).forall(_.contains(ctor)) then handle(l -> cons) else @@ -766,14 +774,14 @@ class Deforest(using TL, Raise, Elaborator.State): lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) upperBounds(c.uid).foreach: u => (prod, u) match - case (Ctor(ctor, args, _), sel@FieldSel(field, consVar)) => + case (Ctor(ctor, args, _, _), sel@FieldSel(field, consVar)) => if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then handle(prod -> u) else () case (_: ProdVar, _) => die case _ => handle(prod -> u) - case (Ctor(ctor, args, expr), NoCons) => + case (Ctor(ctor, args, expr, _), NoCons) => ctorDests.update(expr, NoCons) args.valuesIterator.foreach(a => handle(a, NoCons)) case (ProdFun(l, r), Dtor(cls)) => () // ignore From abc03336e0397cb2a6859f90bc266f933aa4de1d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:24:04 +0800 Subject: [PATCH 249/303] wip: preparation traversal to store function bodies --- .../scala/hkmc2/codegen/Deforestation.scala | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index eb3faaf805..37bfc536b7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -322,7 +322,16 @@ class GetCtorsTraverser(using Deforest) extends BlockTraverser: case Instantiate(cls, args) => args.foreach(applyResult) case _ => () + +// TODO: move more traversal of blocks that are irrelevant +// to constraint generation to this place +class DeforestPrepareTraverser(using d: Deforest) extends BlockTraverser: + override def applyFunDefn(fun: FunDefn): Unit = + val fDef@FunDefn(_, sym, params, body) = fun + d.funSymToFunDef += sym -> fDef + + extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) @@ -346,6 +355,13 @@ class Deforest(using TL, Raise, Elaborator.State): val resultIdToResult = mutable.Map.empty[ResultId, Result] val funSymToFunDef = mutable.Map.empty[BlockMemberSymbol, FunDefn] + // def instantiate(referSite: ResultId): ProdStrat = + // val funSym = referSite.getFunCallBlkMemSym.orElse: + // referSite.getResult match + // case sel: Select => sel.symbol.flatMap(_.asBlkMember) + // case Value.Ref(l) => l.asBlkMember + // case _ => N + def apply(p: Program, duplicate: Bool = false, output: String => Unit): Opt[Program] -> String -> Int = val mainBlk = p.main @@ -354,6 +370,9 @@ class Deforest(using TL, Raise, Elaborator.State): // allocate type vars for defined symbols in the blocks symToStrat.init(mainBlk) + val prepare = new DeforestPrepareTraverser + prepare.applyBlock(mainBlk) + try processBlock(mainBlk) catch @@ -464,7 +483,7 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(NoProd, store(funSymsWithoutDefn).asConsStrat)(using N) - def getStratOfSym(s: Symbol) = + def getStratOfSym(s: Symbol, refSite: ResultId) = s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd @@ -476,7 +495,13 @@ class Deforest(using TL, Raise, Elaborator.State): // it means that this constructor is passed around like a function, // which we can't fuse for now case _ if s.asCls.isDefined => NoProd - case _: BlockMemberSymbol => store(s) + case s: BlockMemberSymbol => + val res = store(s) + if s.trmImplTree.fold(false)(_.k is syntax.Fun) then + res.instantiate(refSite) + else + res + case _: LocalSymbol => store(s) def +=(e: Symbol -> ProdVar) = store += e def addAll(es: Iterable[Symbol -> ProdVar]) = store.addAll(es) @@ -489,6 +514,16 @@ class Deforest(using TL, Raise, Elaborator.State): val store = mutable.Map.empty[BlockMemberSymbol, Ls[ProdStrat -> ConsStrat]] def get = store.get def updateWith = store.updateWith + def getOrUpdate(sym: BlockMemberSymbol) = + store.getOrElse.curried(sym): + val fDef@FunDefn(_, _, params, body) = funSymToFunDef(sym) + val funSymStratVar = symToStrat(sym) + val param = params match + // TODO: handle `restParam` and mutiple param list + case ParamList(flags, params, N) :: Nil => params + val funStrat = constrFun(param, body)(using Map.empty, mutable.LinkedHashMap.empty, S(sym)) + constrain(funStrat, funSymStratVar.asConsStrat)(using S(sym)) + store.get(sym).get def constrain(p: ProdStrat, c: ConsStrat)(using inDef: Opt[BlockMemberSymbol]) = inDef.foreach: i => @@ -572,8 +607,8 @@ class Deforest(using TL, Raise, Elaborator.State): processBlock(rest) case Define(defn, rest) => defn match - case fDef@FunDefn(_, sym, params, body) => - funSymToFunDef += sym -> fDef + case fDef@FunDefn(_, sym, params, body) if !inDefConstraints.store.contains(sym) => + // funSymToFunDef += sym -> fDef val funSymStratVar = symToStrat(sym) val param = params match // TODO: handle `restParam` and mutiple param list @@ -630,7 +665,7 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(funSym), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(funSym, r.uid), ConsFun(argsTpe, appRes._2)) appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) @@ -647,7 +682,7 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(l), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(l, r.uid), ConsFun(argsTpe, appRes._2)) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) @@ -681,7 +716,7 @@ class Deforest(using TL, Raise, Elaborator.State): tpeVar._1 case v@Value.Ref(l) => l.asObj match - case None => symToStrat.getStratOfSym(l) + case None => symToStrat.getStratOfSym(l, r.uid) case Some(m) => Ctor(m, Map.empty, v.uid, N)(inDef) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") From de255e81a3beb88aea64c7921f5c9771b7028a17 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:02:16 +0800 Subject: [PATCH 250/303] allow multiple creation of Dtor strat since now strats can be duplicated --- .../main/scala/hkmc2/codegen/Deforestation.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 37bfc536b7..3f0e939dd7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -117,10 +117,19 @@ case object NoProd extends ProdStrat class Dtor(val expr: Match, val outterMatch: Option[ResultId], val inDef: Option[BlockMemberSymbol])(using d: Deforest) extends ConsStrat: d.matchScrutToMatchBlock.updateWith(expr.scrut.uid): case None => Some(expr) - case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") + case Some(v) => + // lastWords(s"should only update once (uid: ${expr.scrut.uid})") + assert(v is expr) + Some(expr) d.matchScrutToParentMatchScrut.updateWith(expr.scrut.uid): case None => Some(outterMatch) - case Some(_) => lastWords(s"should only update once (uid: ${expr.scrut.uid})") + case Some(v) => + // lastWords(s"should only update once (uid: ${expr.scrut.uid})") + v match + case None => assert(outterMatch is None) + case Some(value) => assert(outterMatch.get is value) + Some(v) + object Dtor: def unapply(d: Dtor)(using Deforest): Opt[ResultId] = S(d.expr.scrut.uid) From 5a73860e0f70abcd0f2e6e9fc9b224223cf8a5b3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 3 Jun 2025 20:15:02 +0800 Subject: [PATCH 251/303] update instantiate --- .../scala/hkmc2/codegen/Deforestation.scala | 95 ++++++++++--------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 3f0e939dd7..23f12f0922 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -14,48 +14,7 @@ type StratVar type StratVarId = Uid[StratVar] type ClsOrModSymbol = ClassLikeSymbol -sealed abstract class ProdStrat: - def instantiate(referSite: ResultId)(using d: Deforest): ProdStrat = - val funSym = referSite.getFunCallBlkMemSym.orElse: - referSite.getResult match - case sel: Select => sel.symbol.flatMap(_.asBlkMember) - case Value.Ref(l) => l.asBlkMember - case _ => N - funSym match - case None => this - case Some(funSym) => - val constr = d.inDefConstraints.get(funSym) - constr match - case None => this - case Some(constrLs) => - val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] - def duplicateVarState(s: StratVarState) = - stratVarMap.getOrElseUpdate.curried(s): - StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s - def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) - case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) - case p@ProdVar(s) => - if s.inDef === S(funSym) then - duplicateVarState(s).asProdStrat - else p - case NoProd => NoProd - def duplicateConsStrat(s: ConsStrat): ConsStrat = s match - case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) - case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) - case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) - case c@ConsVar(s) => - if s.inDef === S(funSym) then - duplicateVarState(s).asConsStrat - else c - case NoCons => NoCons - - val newProd = duplicateProdStrat(this) - constrLs.foreach: - case p -> c => - d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) - newProd - +sealed abstract class ProdStrat sealed abstract class ConsStrat class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val callResOf: Opt[ResultId -> Symbol], val inDef: Opt[BlockMemberSymbol]): @@ -108,8 +67,49 @@ extension (i: ResultId) // - Some(ls): non-top-level case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId, instantiationId: Opt[Ls[ResultId]])(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat - -case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s) + +case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): + def instantiate(referSite: ResultId)(using d: Deforest): ProdStrat = + val funSym = referSite.getFunCallBlkMemSym.orElse: + referSite.getResult match + case sel: Select => sel.symbol.flatMap(_.asBlkMember) + case Value.Ref(l) => l.asBlkMember + case _ => N + funSym match + case None => this + case Some(funSym) => + val constr = d.inDefConstraints.get(funSym) + constr match + case None => this + case Some(constrLs) => + val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def duplicateVarState(s: StratVarState) = + stratVarMap.getOrElseUpdate.curried(s): + StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s + def duplicateProdStrat(s: ProdStrat): ProdStrat = s match + case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) + case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) + case p@ProdVar(s) => + if s.inDef === S(funSym) || (p.s is this.s) then + duplicateVarState(s).asProdStrat + else p + case NoProd => NoProd + def duplicateConsStrat(s: ConsStrat): ConsStrat = s match + case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) + case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) + case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) + case c@ConsVar(s) => + if s.inDef === S(funSym) || (c.s is this.s) then + duplicateVarState(s).asConsStrat + else c + case NoCons => NoCons + + val newProd = duplicateProdStrat(this) + constrLs.foreach: + case p -> c => + d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) + newProd + case object NoProd extends ProdStrat @@ -414,8 +414,11 @@ class Deforest(using TL, Raise, Elaborator.State): // DefDupTODO: def dup: change later else - // tl.log("-----------------------------------------") - // upperBounds.foreach: (v, u) => + // upperBounds.foreach: + // case (v, u) => v + tl.log("-----------------------------------------") + constraints.foreach: + case (p, c) => tl.log(s"${p} --> $c") tl.log("-----------------------------------------") ctorDests.ctorDests.foreach: From 1dfb5276d9bb34a2d02caf7ba0929bb93bbee3fd Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:02:43 +0800 Subject: [PATCH 252/303] do not add unnecessary constraints to the work list --- .../scala/hkmc2/codegen/Deforestation.scala | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 23f12f0922..532a7130f7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -82,6 +82,7 @@ case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): constr match case None => this case Some(constrLs) => + // println(s"instantiate: $funSym @ ${referSite.getResult} : ${constrLs}") val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] def duplicateVarState(s: StratVarState) = stratVarMap.getOrElseUpdate.curried(s): @@ -383,7 +384,7 @@ class Deforest(using TL, Raise, Elaborator.State): prepare.applyBlock(mainBlk) try - processBlock(mainBlk) + processBlock(mainBlk)(using true) catch case NotDeforestableException(msg) => // return None if deforestation is not applicable @@ -492,7 +493,7 @@ class Deforest(using TL, Raise, Elaborator.State): FreshVarForAllVars.applyBlock(p) // `NoProd` to block fusion for those functions that are imported from elsewhere usedFunSym.diff(funSymsWithDefn).foreach: funSymsWithoutDefn => - constrain(NoProd, store(funSymsWithoutDefn).asConsStrat)(using N) + constrain(NoProd, store(funSymsWithoutDefn).asConsStrat, true)(using N) def getStratOfSym(s: Symbol, refSite: ResultId) = @@ -534,15 +535,16 @@ class Deforest(using TL, Raise, Elaborator.State): // TODO: handle `restParam` and mutiple param list case ParamList(flags, params, N) :: Nil => params val funStrat = constrFun(param, body)(using Map.empty, mutable.LinkedHashMap.empty, S(sym)) - constrain(funStrat, funSymStratVar.asConsStrat)(using S(sym)) + constrain(funStrat, funSymStratVar.asConsStrat, false)(using S(sym)) store.get(sym).get - def constrain(p: ProdStrat, c: ConsStrat)(using inDef: Opt[BlockMemberSymbol]) = + def constrain(p: ProdStrat, c: ConsStrat, addToConstrList: Bool)(using inDef: Opt[BlockMemberSymbol]) = inDef.foreach: i => inDefConstraints.updateWith(i): case None => Some((p -> c) :: Nil) case Some(value) => Some((p -> c) :: value) - constraints ::= p -> c + if addToConstrList then + constraints ::= p -> c object callInfo: val callerToCallSitesInside = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] @@ -583,18 +585,20 @@ class Deforest(using TL, Raise, Elaborator.State): def processBlock(b: Block)(using + addToConstrList: Bool, inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol], inDef: Opt[BlockMemberSymbol] = N ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1), inDef)) + constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1), inDef), addToConstrList) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: case (Case.Cls(s, _), body) => - processBlock(body)( - using inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), + processBlock(body)(using + addToConstrList, + inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), matching + (scrut.uid -> s), inDef ) @@ -606,13 +610,13 @@ class Deforest(using TL, Raise, Elaborator.State): case End(msg) => val matchRes = freshVar("", N, inDef) armsRes.appendedAll(dfltRes).foreach: r => - constrain(r, matchRes._2) + constrain(r, matchRes._2, addToConstrList) matchRes._1 case _ => processBlock(rest) case Return(res, implct) => processResult(res) case Assign(lhs, rhs, rest) => - constrain(processResult(rhs), symToStrat(lhs).asConsStrat) + constrain(processResult(rhs), symToStrat(lhs).asConsStrat, addToConstrList) processBlock(rest) case Begin(sub, rest) => processBlock(sub) @@ -626,7 +630,7 @@ class Deforest(using TL, Raise, Elaborator.State): // TODO: handle `restParam` and mutiple param list case ParamList(flags, params, N) :: Nil => params val funStrat = constrFun(param, body)(using inArm, matching, S(sym)) - constrain(funStrat, funSymStratVar.asConsStrat)(using S(sym)) + constrain(funStrat, funSymStratVar.asConsStrat, false)(using S(sym)) funSymStratVar case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") @@ -649,10 +653,11 @@ class Deforest(using TL, Raise, Elaborator.State): val paramStrats = paramSyms.map(symToStrat.apply) // symToStrat.addAll(paramSyms.zip(paramStrats)) val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef, inDef.map(L.apply)) - constrain(processBlock(body), res._2) + constrain(processBlock(body)(using false, inArm, matching, inDef), res._2, false) ProdFun(paramStrats.map(s => s.asConsStrat), res._1) def processResult(r: Result)(using + addToConstrList: Bool, inArm: Map[ProdVar, ClsOrModSymbol], matching: LinkedHashMap[ResultId, ClsOrModSymbol], inDef: Opt[BlockMemberSymbol] @@ -665,9 +670,9 @@ class Deforest(using TL, Raise, Elaborator.State): case None => val pStrat = processResult(p) val tpeVar = freshVar("", N, inDef) - constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching), addToConstrList) val appRes = freshVar("", N, inDef) // unknown function symbol - constrain(tpeVar._1, ConsFun(argsTpe, appRes._2)) + constrain(tpeVar._1, ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case Some(None) => val funSym = s.symbol.get @@ -677,7 +682,7 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(funSym, r.uid), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(funSym, r.uid), ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) @@ -694,12 +699,12 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(l, r.uid), ConsFun(argsTpe, appRes._2)) + constrain(symToStrat.getStratOfSym(l, r.uid), ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) val appRes = freshVar("call_lam_res", N, inDef) - constrain(funTpe, ConsFun(argsTpe, appRes._2)) + constrain(funTpe, ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") @@ -720,11 +725,11 @@ class Deforest(using TL, Raise, Elaborator.State): val tpeVar = freshVar("", N, inDef) val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) - constrain(pStrat, selStrat) + constrain(pStrat, selStrat, addToConstrList) tpeVar._1 case _ => val tpeVar = freshVar("", N, inDef) - constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching)) + constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching), addToConstrList) tpeVar._1 case v@Value.Ref(l) => l.asObj match From 2f281f49c6d1c9670b66735e7a6d70db4d6e4f6d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 4 Jun 2025 18:10:35 +0800 Subject: [PATCH 253/303] also include constraints from instantiating other functions into the set of constraints generated by the current function --- .../scala/hkmc2/codegen/Deforestation.scala | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 532a7130f7..7376c5e3e1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -69,7 +69,7 @@ case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: Re case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): - def instantiate(referSite: ResultId)(using d: Deforest): ProdStrat = + def instantiate(referSite: ResultId, inDef: Opt[BlockMemberSymbol])(using d: Deforest): ProdStrat = val funSym = referSite.getFunCallBlkMemSym.orElse: referSite.getResult match case sel: Select => sel.symbol.flatMap(_.asBlkMember) @@ -78,38 +78,43 @@ case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): funSym match case None => this case Some(funSym) => - val constr = d.inDefConstraints.get(funSym) - constr match - case None => this - case Some(constrLs) => - // println(s"instantiate: $funSym @ ${referSite.getResult} : ${constrLs}") - val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] - def duplicateVarState(s: StratVarState) = - stratVarMap.getOrElseUpdate.curried(s): - StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s - def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) - case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) - case p@ProdVar(s) => - if s.inDef === S(funSym) || (p.s is this.s) then - duplicateVarState(s).asProdStrat - else p - case NoProd => NoProd - def duplicateConsStrat(s: ConsStrat): ConsStrat = s match - case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) - case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) - case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) - case c@ConsVar(s) => - if s.inDef === S(funSym) || (c.s is this.s) then - duplicateVarState(s).asConsStrat - else c - case NoCons => NoCons - - val newProd = duplicateProdStrat(this) - constrLs.foreach: - case p -> c => - d.constraints ::= (duplicateProdStrat(p) -> duplicateConsStrat(c)) - newProd + val constrLs = d.inDefConstraints.getOrUpdate(funSym) + // constr match + // case None => this + // case Some(constrLs) => + println(s"instantiate: $funSym @ ${referSite.getResult} : ${constrLs}") + val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def duplicateVarState(s: StratVarState) = + stratVarMap.getOrElseUpdate.curried(s): + StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s + def duplicateProdStrat(s: ProdStrat): ProdStrat = s match + case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) + case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) + case p@ProdVar(s) => + if s.inDef === S(funSym) || (p.s is this.s) then + duplicateVarState(s).asProdStrat + else p + case NoProd => NoProd + def duplicateConsStrat(s: ConsStrat): ConsStrat = s match + case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) + case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) + case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) + case c@ConsVar(s) => + if s.inDef === S(funSym) || (c.s is this.s) then + duplicateVarState(s).asConsStrat + else c + case NoCons => NoCons + + val newProd = duplicateProdStrat(this) + constrLs.foreach: + case p -> c => + val constr = duplicateProdStrat(p) -> duplicateConsStrat(c) + d.constraints ::= constr + inDef.foreach: inFunDef => + d.inDefConstraints.updateWith(inFunDef): + case S(ls) => S(constr :: ls) + case N => S(constr :: Nil) + newProd case object NoProd extends ProdStrat @@ -496,7 +501,7 @@ class Deforest(using TL, Raise, Elaborator.State): constrain(NoProd, store(funSymsWithoutDefn).asConsStrat, true)(using N) - def getStratOfSym(s: Symbol, refSite: ResultId) = + def getStratOfSym(s: Symbol, refSite: ResultId, inDef: Option[BlockMemberSymbol]) = s match case _: BuiltinSymbol => NoProd case _: TopLevelSymbol => NoProd @@ -511,7 +516,7 @@ class Deforest(using TL, Raise, Elaborator.State): case s: BlockMemberSymbol => val res = store(s) if s.trmImplTree.fold(false)(_.k is syntax.Fun) then - res.instantiate(refSite) + res.instantiate(refSite, inDef) else res @@ -682,7 +687,7 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(funSym, r.uid), ConsFun(argsTpe, appRes._2), addToConstrList) + constrain(symToStrat.getStratOfSym(funSym, r.uid, inDef), ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case Some(Some(s)) => val clsFields = getClsFields(s) @@ -699,7 +704,7 @@ class Deforest(using TL, Raise, Elaborator.State): inDef ) for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(l, r.uid), ConsFun(argsTpe, appRes._2), addToConstrList) + constrain(symToStrat.getStratOfSym(l, r.uid, inDef), ConsFun(argsTpe, appRes._2), addToConstrList) appRes._1 case lam@Value.Lam(params, body) => val funTpe = processResult(lam) @@ -733,7 +738,7 @@ class Deforest(using TL, Raise, Elaborator.State): tpeVar._1 case v@Value.Ref(l) => l.asObj match - case None => symToStrat.getStratOfSym(l, r.uid) + case None => symToStrat.getStratOfSym(l, r.uid, inDef) case Some(m) => Ctor(m, Map.empty, v.uid, N)(inDef) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") From ecb284287d08060b7e037f1d47a5fd4ab3505c99 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:23:08 +0800 Subject: [PATCH 254/303] wip: instantiate accumulate call path for ctor --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 7376c5e3e1..bedb95d47e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -88,7 +88,13 @@ case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): stratVarMap.getOrElseUpdate.curried(s): StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr, instId) => Ctor(ctor, args.view.mapValues(duplicateProdStrat).toMap, expr, instId.map(referSite :: _))(c.inDef) + case c@Ctor(ctor, args, expr, instId) => Ctor( + ctor, + args.view.mapValues(duplicateProdStrat).toMap, + expr, + instId.fold(Some(referSite :: Nil))(l => Some(referSite :: l)) + // instId.map(referSite :: _) + )(c.inDef) case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) case p@ProdVar(s) => if s.inDef === S(funSym) || (p.s is this.s) then From e4a4d789f74acb5d64fb1c0631505cb4927dc403 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:01:37 +0800 Subject: [PATCH 255/303] wip: ctor dests differentiate between instantiations --- .../scala/hkmc2/codegen/Deforestation.scala | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index bedb95d47e..3700409b9d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -365,7 +365,13 @@ extension (b: Block) def willBeNonEndTailBlock(using d: Deforest): Bool = WillBeNonEndTailBlockTraverser().analyze(b) - + + +type CtorExprAndInstId = ResultId -> Opt[Ls[ResultId]] +extension (c: CtorExprAndInstId) + def getResult(using Deforest): Result = c._1.getResult + def getClsSymOfUid(using Deforest): ClassLikeSymbol = c._1.getClsSymOfUid + class Deforest(using TL, Raise, Elaborator.State): given stratVarUidState: Uid.StratVar.State = Uid.StratVar.State() @@ -758,22 +764,22 @@ class Deforest(using TL, Raise, Elaborator.State): val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool, callResVars: Ls[StratVarState]) - case class DtorSource(ctors: Set[ResultId], noProd: Bool) + case class DtorSource(ctors: Set[CtorExprAndInstId], noProd: Bool) object ctorDests: - val ctorDests = mutable.LinkedHashMap.empty[ResultId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false, Nil)) - def update(ctor: ResultId, m: Match) = ctorDests.updateWith(ctor): + val ctorDests = mutable.LinkedHashMap.empty[CtorExprAndInstId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false, Nil)) + def update(ctor: CtorExprAndInstId, m: Match) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons, vars)) case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false, Nil)) - def update(ctor: ResultId, s: FieldSel) = ctorDests.updateWith(ctor): + def update(ctor: CtorExprAndInstId, s: FieldSel) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, s :: sels, noCons, vars)) case None => Some(CtorDest(Map.empty, s :: Nil, false, Nil)) - def update(ctor: ResultId, n: NoCons.type) = ctorDests.updateWith(ctor): + def update(ctor: CtorExprAndInstId, n: NoCons.type) = ctorDests.updateWith(ctor): case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, sels, true, vars)) case None => Some(CtorDest(Map.empty, Nil, true, Nil)) - def update(ctor: ResultId, v: StratVarState) = ctorDests.updateWith(ctor): + def update(ctor: CtorExprAndInstId, v: StratVarState) = ctorDests.updateWith(ctor): case Some(dests) => Some(dests.copy(callResVars = v :: dests.callResVars)) case None => Some(CtorDest(Map.empty, Nil, false, v :: Nil)) - def get(ctor: ResultId) = ctorDests.get(ctor) + def get(ctor: CtorExprAndInstId) = ctorDests.get(ctor) object dtorSources: val dtorSources = mutable.Map.empty[DtorExpr, DtorSource].withDefaultValue(DtorSource(Set.empty, false)) @@ -781,7 +787,7 @@ class Deforest(using TL, Raise, Elaborator.State): case s: Select => DtorExpr.Sel(i) case r: Value.Ref => DtorExpr.Match(i) case r => lastWords(s"try to get dtor expr from ResultId, but get $r") - def update(dtor: ResultId, ctor: ResultId) = + def update(dtor: ResultId, ctor: CtorExprAndInstId) = val dtorExpr = getDtorExprOfResultId(dtor) dtorSources.updateWith(dtorExpr): case None => Some(DtorSource(Set(ctor), false)) @@ -806,12 +812,12 @@ class Deforest(using TL, Raise, Elaborator.State): cache += c (prod, cons) match - case (Ctor(ctor, args, expr, _), dtorStrat@Dtor(scrut)) => - ctorDests.update(expr, dtorStrat.expr) - dtorSources.update(scrut, expr) - case (Ctor(ctor, args, expr, _), selDtor@FieldSel(field, consVar)) => - ctorDests.update(expr, selDtor) - dtorSources.update(selDtor.expr, expr) + case (Ctor(ctor, args, expr, instId), dtorStrat@Dtor(scrut)) => + ctorDests.update(expr -> instId, dtorStrat.expr) + dtorSources.update(scrut, expr -> instId) + case (Ctor(ctor, args, expr, instId), selDtor@FieldSel(field, consVar)) => + ctorDests.update(expr -> instId, selDtor) + dtorSources.update(selDtor.expr, expr -> instId) args.find(a => a._1.id == field).map: p => handle(p._2 -> consVar) case (Ctor(ctor, args, _, _), ConsFun(l, r)) => () // ignore @@ -831,7 +837,7 @@ class Deforest(using TL, Raise, Elaborator.State): case _ => handle(l -> cons) case (_, c: ConsVar) => prod -> c match - case Ctor(expr = e, _) -> _ if c.s.callResOf.isDefined => ctorDests.update(e, c.s) + case Ctor(expr = e, instantiationId = instId, _) -> _ if c.s.callResOf.isDefined => ctorDests.update(e -> instId, c.s) case _ => () lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) @@ -844,8 +850,8 @@ class Deforest(using TL, Raise, Elaborator.State): () case (_: ProdVar, _) => die case _ => handle(prod -> u) - case (Ctor(ctor, args, expr, _), NoCons) => - ctorDests.update(expr, NoCons) + case (Ctor(ctor, args, expr, instId), NoCons) => + ctorDests.update(expr -> instId, NoCons) args.valuesIterator.foreach(a => handle(a, NoCons)) case (ProdFun(l, r), Dtor(cls)) => () // ignore case (ProdFun(l, r), FieldSel(field, consVar)) => () // ignore @@ -877,7 +883,7 @@ class Deforest(using TL, Raise, Elaborator.State): lazy val findDefDupChances = val callResToCtorsCallsFlowingIntoThem = ctorDests.ctorDests - .flatMap[ResultId -> StratVarState]: + .flatMap[CtorExprAndInstId -> StratVarState]: case (ctorCallId, CtorDest(callResVars = vs, _)) => vs.map(ctorCallId -> _) .groupBy(_._2) @@ -937,7 +943,7 @@ class Deforest(using TL, Raise, Elaborator.State): val ctorToDtor = ctorDests.ctorDests val dtorToCtor = dtorSources.dtorSources - def removeCtor(rm: ResultId): Unit = + def removeCtor(rm: CtorExprAndInstId): Unit = for CtorDest(mat, sels, _, _) <- ctorToDtor.remove(rm) do for s <- mat.keys do removeDtor(DtorExpr.Match(s)) for s <- sels do removeDtor(DtorExpr.Sel(s.expr)) @@ -992,7 +998,7 @@ class Deforest(using TL, Raise, Elaborator.State): for (c, CtorDest(matches, sels, _, _)) <- ctorToDtor m <- matches.values - x <- findCycle(c, m) + x <- findCycle(c._1, m) do removeCtor(x) ctorToDtor -> dtorToCtor From 588c155f782a05a7049c771046d8ababa67a46ba Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:50:29 +0800 Subject: [PATCH 256/303] correctly update indef --- .../scala/hkmc2/codegen/Deforestation.scala | 121 +++++++++++++----- 1 file changed, 90 insertions(+), 31 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 3700409b9d..6e06656100 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -86,7 +86,8 @@ case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] def duplicateVarState(s: StratVarState) = stratVarMap.getOrElseUpdate.curried(s): - StratVarState.freshVar(s.name, s.callResOf, s.inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s + // update `inDef` because the new strat var is considered to be generated inside the new definition + StratVarState.freshVar(s.name, s.callResOf, inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s def duplicateProdStrat(s: ProdStrat): ProdStrat = s match case c@Ctor(ctor, args, expr, instId) => Ctor( ctor, @@ -969,40 +970,98 @@ class Deforest(using TL, Raise, Elaborator.State): dtorToCtor.filter(_._2.noProd).keys.foreach(removeDtor) // remove cycle: - def getCtorInArm(ctor: ResultId, dtor: Match) = - val ctorSym = getClsSymOfUid(ctor) - val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - val traverser = GetCtorsTraverser() - traverser.applyBlock(arm) - traverser.ctors + // def getCtorInArm(ctor: ResultId, dtor: Match) = + // val ctorSym = getClsSymOfUid(ctor) + // val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get + // val traverser = GetCtorsTraverser() + // traverser.applyBlock(arm) + // traverser.ctors - def findCycle(ctor: ResultId, dtor: Match): Ls[ResultId] = - val cache = mutable.Set(ctor) - def go(ctorAndMatches: Ls[ResultId -> Match]): Ls[ResultId] = - var newCtorsAndNewMatches: Ls[ResultId -> Match] = Nil - for - (c, m) <- ctorAndMatches - c <- getCtorInArm(c, m) - CtorDest(matches, sels, _, _) <- ctorToDtor.get(c) - m <- matches.values.headOption - do newCtorsAndNewMatches = (c -> m) :: newCtorsAndNewMatches - val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) - if newCtorsAndNewMatches.isEmpty then - Nil - else if cycled.nonEmpty then - cycled.map(_._1) - else - go(newCtorsAndNewMatches) - go(Ls(ctor -> dtor)) + // def findCycle(ctor: ResultId, dtor: Match): Ls[ResultId] = + // val cache = mutable.Set(ctor) + // def go(ctorAndMatches: Ls[ResultId -> Match]): Ls[ResultId] = + // var newCtorsAndNewMatches: Ls[ResultId -> Match] = Nil + // for + // (c, m) <- ctorAndMatches + // c <- getCtorInArm(c, m) + // CtorDest(matches, sels, _, _) <- ctorToDtor.get(c) + // m <- matches.values.headOption + // do newCtorsAndNewMatches = (c -> m) :: newCtorsAndNewMatches + // val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) + // if newCtorsAndNewMatches.isEmpty then + // Nil + // else if cycled.nonEmpty then + // cycled.map(_._1) + // else + // go(newCtorsAndNewMatches) + // go(Ls(ctor -> dtor)) - for - (c, CtorDest(matches, sels, _, _)) <- ctorToDtor - m <- matches.values - x <- findCycle(c._1, m) - do removeCtor(x) + // for + // (c, CtorDest(matches, sels, _, _)) <- ctorToDtor + // m <- matches.values + // x <- findCycle(c._1, m) + // do removeCtor(x) ctorToDtor -> dtorToCtor + lazy val filteredCtorDests2: Map[CtorExprAndInstId, CtorFinalDest] = + val res = mutable.Map.empty[CtorExprAndInstId, CtorFinalDest] + + // we need only one CtorFinalDest per arm for each pat mat expr + val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] + + resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false, _)) => + val filteredDtor = { + if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) + else if dtors.size == 0 && sels.size > 1 then + lastWords("more than one consumer") + else if dtors.size > 1 then + lastWords("more than one consumer") + else if dtors.size == 1 then + val currentCtorCls = getClsSymOfUid(ctor) + val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult + handledMatches.getOrElseUpdate( + scrutRef.uid -> currentCtorCls, + if sels.forall{ s => s.expr.getResult match + case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) + case _ => false + } then + val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] + val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] + + dtors.head._2.arms.foreach: + case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => + val c = cOrMod.asCls.get + // if this arm is used more than once, should be var symbol because the arm body will be + // extracted to a function, otherwise just temp symbol + val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 + val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } + + selsInArms.foreach: fs => + assert(getClsFields(c).map(_.id).contains(fs.field)) + fieldNameToSymToBeReplaced.updateWith(fs.field): + case Some(v) => Some(v) + case None => Some(if varSymInsteadOfTempSym + then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) + else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) + val sym = fieldNameToSymToBeReplaced(fs.field) + + selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) + case _ => () + CtorFinalDest.Match( + dtors.head._1, + dtors.head._2, + sels.map(_.expr), + fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap + ) + else + lastWords("more than one consumer") + ) + else die + } + res.updateWith(ctor){_ => Some(filteredDtor)} + } + res.toMap lazy val filteredCtorDests: Map[ResultId, CtorFinalDest] = @@ -1060,7 +1119,7 @@ class Deforest(using TL, Raise, Elaborator.State): ) else die } - res.updateWith(ctor){_ => Some(filteredDtor)} + res.updateWith(ctor._1){_ => Some(filteredDtor)} } res.toMap From 080c25e10016e952ae885f9c68737e3750884a07 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 5 Jun 2025 20:16:10 +0800 Subject: [PATCH 257/303] only solve constraints from top-level --- .../src/main/scala/hkmc2/codegen/Deforestation.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala index 6e06656100..e28e37da87 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala @@ -116,11 +116,12 @@ case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): constrLs.foreach: case p -> c => val constr = duplicateProdStrat(p) -> duplicateConsStrat(c) - d.constraints ::= constr - inDef.foreach: inFunDef => - d.inDefConstraints.updateWith(inFunDef): - case S(ls) => S(constr :: ls) - case N => S(constr :: Nil) + inDef match + case None => d.constraints ::= constr + case Some(inFunDef) => + d.inDefConstraints.updateWith(inFunDef): + case S(ls) => S(constr :: ls) + case N => S(constr :: Nil) newProd case object NoProd extends ProdStrat From 156132257d46afbe6eebcff18f14654481607a82 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:22:27 +0800 Subject: [PATCH 258/303] refactoring... --- hkmc2/shared/src/main/scala/hkmc2/Uid.scala | 1 + .../src/main/scala/hkmc2/codegen/Block.scala | 7 + .../hkmc2/codegen/deforest/Analyze.scala | 216 ++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala index 1b9ea27b05..14d86f3010 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala @@ -14,6 +14,7 @@ object Uid: def reset = curUid = -1 object Symbol extends Handler[semantics.Symbol] object StratVar extends Handler[codegen.StratVar] + object StratVarNew extends Handler[codegen.deforest.StratVarState] extension [T] (x: Uid[T]) def <=(rhs: Uid[T]) = x <= rhs diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index f1b2540e72..9a2852cf6a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -473,6 +473,13 @@ sealed abstract class Result extends AutoLocated: case S(r) => assert(this is r); S(this) uidValue + def uid = + import Result.* + val uidValue = ResultId(System.identityHashCode(this)) + uidValue + + + // type Local = LocalSymbol type Local = Symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala new file mode 100644 index 0000000000..18957292fe --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -0,0 +1,216 @@ +package hkmc2 +package codegen +package deforest + +import semantics.* +import semantics.Elaborator.State +import syntax.{Literal, Tree} +import utils.* +import mlscript.utils.*, shorthands.* +import scala.collection.mutable +import scala.collection.mutable.LinkedHashMap +import Result.ResultId + +final case class NotDeforestableException(msg: String) extends Exception(msg) + +type StratVarId = Uid[StratVarState] + +class StratVarState(val uid: StratVarId, val name: Str, val generatedForDef: Opt[BlockMemberSymbol]): + lazy val asProdStrat = ProdVar(this) + lazy val asConsStrat = ConsVar(this) + override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" +object StratVarState: + def freshVar(nme: String, generatedForDef: Opt[BlockMemberSymbol])(using vuid: Uid.StratVarNew.State) = + val newId = vuid.nextUid + StratVarState(newId, nme, generatedForDef) + +// TODO: examine what info is really needed in the ctors +sealed abstract class ProdStrat +case class ProdVar(s: StratVarState) extends ProdStrat: + lazy val asProdStrat = ProdVar(this.s) + lazy val asConsStrat = ConsVar(this.s) +case class ProdFun(params: Ls[ConsStrat], res: ProdStrat) extends ProdStrat +case object NoProd extends ProdStrat +class Ctor( + val exprId: ResultId, + val instantiationId: Opt[Ls[ResultId]], + val ctor: ClassLikeSymbol, + val args: Ls[TermSymbol -> ProdStrat]) extends ProdStrat + +sealed abstract class ConsStrat +case class ConsVar(s: StratVarState) extends ConsStrat +case class ConsFun(params: Ls[ProdStrat], res: ConsStrat) extends ConsStrat +case object NoCons extends ConsStrat +class FieldSel( + val exprId: ResultId, + val field: Tree.Ident, + val consVar: ConsVar) extends ConsStrat: + val filter = mutable.Map.empty[ProdVar, Ls[ClassLikeSymbol]].withDefaultValue(Nil) + def updateFilter(p: ProdVar, c: Ls[ClassLikeSymbol]) = + filter += p -> (c ::: filter(p)) + +class Dtor( + val exprId: ResultId, + val instantiationId: Opt[Ls[ResultId]], + val outterMatch: Option[ResultId], + val inDef: Option[BlockMemberSymbol]) extends ConsStrat + +class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]): + def instantiate(referSite: ResultId): ProdVar = ??? // TODO: + + +class ConstraintsAndCacheHitCollector: + var constraints: Ls[ProdStrat -> ConsStrat] = Nil + var cacheHit: Ls[BlockMemberSymbol] = Nil + def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c + def constrain(cs: Ls[ProdStrat -> ConsStrat]) = constraints :::= cs + def hit(s: BlockMemberSymbol) = cacheHit ::= s + def hit(ss: Ls[BlockMemberSymbol]) = cacheHit :::= ss + +class DeforestPreAnalyzer(b: Block, cc: ConstraintsAndCacheHitCollector)(using TL, Raise) extends BlockTraverser: + given stratVarUidState: Uid.StratVarNew.State = new Uid.StratVarNew.State + import StratVarState.freshVar + + val resultIdToResult = mutable.Map.empty[ResultId, Result] + val funSymToFun = mutable.Map.empty[BlockMemberSymbol, FunDefn] + val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] + val matchScrutToParentMatchScruts = mutable.Map.empty[ResultId, Ls[ResultId]] + val matchScrutInFunDef = mutable.Map.empty[ResultId, Opt[BlockMemberSymbol]] + val selsToMatchingArms = mutable.Map.empty[ResultId, Ls[ResultId -> Opt[ClassLikeSymbol]]] + val symToStratVar = mutable.Map.empty[Symbol, ProdVar] + val usedFunSyms = mutable.Set.empty[BlockMemberSymbol] + lazy val definedFunSyms = funSymToFun.keySet + def getProdVarForSym(s: Symbol) = symToStratVar(s) + def getFunDefnForSym(s: BlockMemberSymbol) = funSymToFun.get(s) + + private var inMatchScrutsArms: Ls[ResultId -> Opt[ClassLikeSymbol]] = Nil + private def inMatchScruts = inMatchScrutsArms.unzip._1 + private var inFunDef: Opt[BlockMemberSymbol] = N + override def applyFunDefn(fun: FunDefn): Unit = + funSymToFun += fun.sym -> fun + inFunDef match + case N => inFunDef = S(fun.sym) + case S(value) => throw NotDeforestableException("not expecting nested function definitions") + super.applyFunDefn(fun) + inFunDef = N + + override def applySymbol(s: Symbol): Unit = symToStratVar.updateWith(s): + case N => S(freshVar(s.nme, inFunDef).asProdStrat) + case S(x) => S(x) + + override def applyResult(r: Result): Unit = + resultIdToResult += r.uid -> r + super.applyResult(r) + + override def applyPath(p: Path): Unit = + resultIdToResult += p.uid -> p + p match + case s@Select(path, nme) => selsToMatchingArms += s.uid -> inMatchScrutsArms + case _ => () + super.applyPath(p) + + override def applyValue(v: Value): Unit = + resultIdToResult += v.uid -> v + v match + case Value.Ref(l) => l.asBlkMember.foreach: b => + b.trmTree.foreach: t => + if t.k is syntax.Fun then usedFunSyms += b + case _ => () + super.applyValue(v) + + override def applyBlock(b: Block): Unit = b match + case m@Match(scrut, arms, dflt, rest) => + matchScrutToMatchBlock += scrut.uid -> m + matchScrutInFunDef += scrut.uid -> inFunDef + matchScrutToParentMatchScruts += scrut.uid -> inMatchScruts + applyPath(scrut) + + val prev = inMatchScrutsArms + arms.foreach: arm => + val cse = arm._1 match + case Case.Cls(cls, path) => S(cls) + case _ => N + inMatchScrutsArms = (scrut.uid -> cse) :: inMatchScrutsArms + applyCase(arm._1); applySubBlock(arm._2) + inMatchScrutsArms = prev + + inMatchScrutsArms = (scrut.uid -> N) :: inMatchScrutsArms + dflt.foreach(applySubBlock) + inMatchScrutsArms = prev + + applySubBlock(rest) + case _ => super.applyBlock(b) + + applyBlock(b) + // TODO: for used but not defined fun syms, constrain them with NoProd + // TODO: for defined but not fun syms, constrain them with NoCons + +class DeforestAnalyzer(preAnalyzer: DeforestPreAnalyzer)(using TL, Raise, Elaborator.State): + given stratVarUidState: Uid.StratVarNew.State = preAnalyzer.stratVarUidState + given DeforestAnalyzer = this + import StratVarState.freshVar + + + object FunSymToProdStrat: + val store = mutable.Map.empty[BlockMemberSymbol, ProdStratScheme] + def getOrUpdate(s: BlockMemberSymbol)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector): ProdVar | ProdStratScheme = + store.get(s) match + case None => processingDefs.filter(_ is s) match + case Nil => preAnalyzer.getFunDefnForSym(s) match + // not a fun defined in the current block, just return its prodvar + case None => preAnalyzer.getProdVarForSym(s) + case Some(funDefn) => + // TODO: start processing this function, if the cache hit contains the currently processing defs functions + // then: 1. the function belongs to the same recursion group and need to share the constraints 2. return the prodvar + // else: update the store with the correct type scheme and return the type scheme + val cc = processFunDefn(funDefn, processingDefs) + if cc.cacheHit.exists(x => processingDefs.contains(x)) then + // for all the cache hits, + // if the latest hit position in the processing is the head, + // then it means a complete recursive group (which is all the symbols in the cachehit) is discovered, + // otherwise we need to further accumulate the constraints and cachehits (those that are on the path to the hit should also be added to the cachehit set) + // until we find the complete recursive group. + if cc.cacheHit.exists(x => processingDefs.tail.contains(x)) then + ??? // FIXME: + else // a complete recursive function group is found + // FIXME: this is wrong: some other callers of the currently processed fun may call even further up, + // so here we may be still creating prod strat schemes pre-maturally + val sortedRecGroup = cc.cacheHit.toList.sortBy(x => x.uid) + sortedRecGroup.foreach: s => + val stratScheme = ProdStratScheme(preAnalyzer.getProdVarForSym(s).s, cc.constraints) + store.updateWith(s): + case N => S(stratScheme) + case S(_) => die + store(s) + else + val stratScheme = ProdStratScheme(preAnalyzer.getProdVarForSym(s).s, cc.constraints) + store.updateWith(s): + case N => S(stratScheme) + case S(_) => die + stratScheme + case sym :: Nil => + cc.hit(sym) + preAnalyzer.getProdVarForSym(sym) + case _ => die + case Some(scheme) => scheme + + + def processFunDefn(defn: FunDefn, processingDefs: Ls[BlockMemberSymbol]): ConstraintsAndCacheHitCollector = + val thisFunVar = preAnalyzer.getProdVarForSym(defn.sym) + val paramSyms = defn.params.head.params.map: // TODO: handle multiple param list and the `restParam` + case Param(sym = sym, _) => preAnalyzer.getProdVarForSym(sym).asConsStrat + val cc = new ConstraintsAndCacheHitCollector + val bodyStrat = processBlock(defn.body)(using defn.sym :: processingDefs, cc) + val res = freshVar(s"${defn.sym.nme}_res", S(defn.sym)) + cc.constrain(bodyStrat, res.asConsStrat) + cc.constrain(ProdFun(paramSyms, res.asProdStrat), thisFunVar.asConsStrat) + cc + + + // returns the cache-hit set of function symbols + def processBlock(b: Block)(using + processingDefs: Ls[BlockMemberSymbol], + cc: ConstraintsAndCacheHitCollector + ): ProdStrat = ??? + + \ No newline at end of file From ec3deae137e3c658ed24a9b0e524c89943c4c776 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 9 Jun 2025 20:49:14 +0800 Subject: [PATCH 259/303] wip: collector --- .../hkmc2/codegen/deforest/Analyze.scala | 307 ++++++++++++++---- 1 file changed, 243 insertions(+), 64 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 18957292fe..f5bc9bf159 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -50,24 +50,48 @@ class FieldSel( filter += p -> (c ::: filter(p)) class Dtor( - val exprId: ResultId, - val instantiationId: Opt[Ls[ResultId]], - val outterMatch: Option[ResultId], - val inDef: Option[BlockMemberSymbol]) extends ConsStrat + val scrutExprId: ResultId, + val instantiationId: Opt[Ls[ResultId]]) extends ConsStrat class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]): - def instantiate(referSite: ResultId): ProdVar = ??? // TODO: - + def instantiate(referSite: ResultId)(using d: DeforestConstraintsCollector, cc: d.ConstraintsAndCacheHitCollector): ProdVar = + val instantiatingFunSym = d.preAnalyzer.resultIdToResult(referSite) match + case Value.Ref(l) => l.asBlkMember.get + case s: Select => s.symbol.flatMap(_.asBlkMember).get + case _ => die + val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def duplicateVarState(s: StratVarState) = + if s.generatedForDef.fold(false)(_ is instantiatingFunSym) then + stratVarMap.getOrElseUpdate.curried(s): + StratVarState.freshVar(s.name, cc.forFun)(using d.stratVarUidState) + else + s + def duplicateProdStrat(s: ProdStrat): ProdStrat = s match + case ProdVar(s) => duplicateVarState(s).asProdStrat + case ProdFun(params, res) => ProdFun(params.map(duplicateConsStrat), duplicateProdStrat(res)) + case NoProd => NoProd + case c: Ctor => new Ctor( + c.exprId, + S(c.instantiationId.fold(referSite :: Nil)(l => referSite :: l)), + c.ctor, + c.args.map((a, b) => a -> duplicateProdStrat(b)) + ) + def duplicateConsStrat(c: ConsStrat): ConsStrat = c match + case ConsVar(s) => duplicateVarState(s).asConsStrat + case ConsFun(params, res) => ConsFun(params.map(duplicateProdStrat), duplicateConsStrat(res)) + case NoCons => NoCons + case fSel: FieldSel => + val res = new FieldSel(fSel.exprId, fSel.field, duplicateVarState(fSel.consVar.s).asConsStrat) + fSel.filter.foreach: (p, ls) => + res.updateFilter(duplicateVarState(p.s).asProdStrat, ls) + res + case dtor: Dtor => new Dtor(dtor.scrutExprId, S(dtor.instantiationId.fold(referSite :: Nil)(l => referSite :: l))) + val newProd = duplicateVarState(s).asProdStrat + constraints.foreach: (p, c) => + cc.constrain(duplicateProdStrat(p), duplicateConsStrat(c)) + newProd -class ConstraintsAndCacheHitCollector: - var constraints: Ls[ProdStrat -> ConsStrat] = Nil - var cacheHit: Ls[BlockMemberSymbol] = Nil - def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c - def constrain(cs: Ls[ProdStrat -> ConsStrat]) = constraints :::= cs - def hit(s: BlockMemberSymbol) = cacheHit ::= s - def hit(ss: Ls[BlockMemberSymbol]) = cacheHit :::= ss - -class DeforestPreAnalyzer(b: Block, cc: ConstraintsAndCacheHitCollector)(using TL, Raise) extends BlockTraverser: +class DeforestPreAnalyzer(b: Block) extends BlockTraverser: given stratVarUidState: Uid.StratVarNew.State = new Uid.StratVarNew.State import StratVarState.freshVar @@ -142,75 +166,230 @@ class DeforestPreAnalyzer(b: Block, cc: ConstraintsAndCacheHitCollector)(using T case _ => super.applyBlock(b) applyBlock(b) - // TODO: for used but not defined fun syms, constrain them with NoProd - // TODO: for defined but not fun syms, constrain them with NoCons + -class DeforestAnalyzer(preAnalyzer: DeforestPreAnalyzer)(using TL, Raise, Elaborator.State): +class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): given stratVarUidState: Uid.StratVarNew.State = preAnalyzer.stratVarUidState - given DeforestAnalyzer = this import StratVarState.freshVar - - object FunSymToProdStrat: + class ConstraintsAndCacheHitCollector(val forFun: Opt[BlockMemberSymbol]): + var constraints: Ls[ProdStrat -> ConsStrat] = Nil + var cacheHit: Ls[BlockMemberSymbol] = Nil // TODO: a better name to say it actually get the symbol of funs in a recursive group + private def check(p: ProdStrat): Unit = p match + case ProdVar(s) => check(s) + case _ => () + private def check(c: ConsStrat): Unit = c match + case ConsVar(s) => check(s) + case _ => () + private def check(s: StratVarState): Unit = (s.generatedForDef, forFun) match + case (N, N) => () + case (S(s1), S(s2)) => assert(s1 is s2) + case _ => die + def constrain(p: ProdStrat, c: ConsStrat) = + check(p) + check(c) + constraints ::= p -> c + def constrain(cs: Ls[ProdStrat -> ConsStrat]) = + cs.foreach: (p, c) => + check(p) + check(c) + constraints :::= cs + def hit(s: BlockMemberSymbol) = cacheHit ::= s + def hit(ss: Ls[BlockMemberSymbol]) = cacheHit :::= ss + + object funSymToProdStratScheme: val store = mutable.Map.empty[BlockMemberSymbol, ProdStratScheme] def getOrUpdate(s: BlockMemberSymbol)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector): ProdVar | ProdStratScheme = - store.get(s) match - case None => processingDefs.filter(_ is s) match - case Nil => preAnalyzer.getFunDefnForSym(s) match - // not a fun defined in the current block, just return its prodvar - case None => preAnalyzer.getProdVarForSym(s) - case Some(funDefn) => - // TODO: start processing this function, if the cache hit contains the currently processing defs functions - // then: 1. the function belongs to the same recursion group and need to share the constraints 2. return the prodvar - // else: update the store with the correct type scheme and return the type scheme - val cc = processFunDefn(funDefn, processingDefs) - if cc.cacheHit.exists(x => processingDefs.contains(x)) then - // for all the cache hits, - // if the latest hit position in the processing is the head, - // then it means a complete recursive group (which is all the symbols in the cachehit) is discovered, - // otherwise we need to further accumulate the constraints and cachehits (those that are on the path to the hit should also be added to the cachehit set) - // until we find the complete recursive group. - if cc.cacheHit.exists(x => processingDefs.tail.contains(x)) then - ??? // FIXME: - else // a complete recursive function group is found - // FIXME: this is wrong: some other callers of the currently processed fun may call even further up, - // so here we may be still creating prod strat schemes pre-maturally - val sortedRecGroup = cc.cacheHit.toList.sortBy(x => x.uid) - sortedRecGroup.foreach: s => - val stratScheme = ProdStratScheme(preAnalyzer.getProdVarForSym(s).s, cc.constraints) - store.updateWith(s): - case N => S(stratScheme) - case S(_) => die - store(s) + preAnalyzer.getFunDefnForSym(s) match + // not a fun defined in the current block, just return its prodvar + case None => preAnalyzer.getProdVarForSym(s) + case Some(funDefn) => store.get(s) match + case Some(scheme) => scheme + case None => processingDefs.filter(_ is s) match + case sym :: Nil => + cc.hit(sym) + processingDefs.headOption.foreach: h => + cc.hit(h) + preAnalyzer.getProdVarForSym(sym) + case Nil => + // start processing this function, if the cache hit contains the currently processing defs functions + // then: 1. the referred function belongs to the same recursion group and need to share the constraints 2. return the prodvar + // else: we found a new recursive group, for each member of the group, update the store with the correct type scheme and return the type scheme + val newcc = processFunDefn(funDefn, processingDefs) + if newcc.cacheHit.exists(x => processingDefs.contains(x)) then + cc.hit(newcc.cacheHit) + cc.constrain(newcc.constraints) + processingDefs.headOption.foreach: h => + cc.hit(h) + preAnalyzer.getProdVarForSym(s) else - val stratScheme = ProdStratScheme(preAnalyzer.getProdVarForSym(s).s, cc.constraints) - store.updateWith(s): - case N => S(stratScheme) - case S(_) => die - stratScheme - case sym :: Nil => - cc.hit(sym) - preAnalyzer.getProdVarForSym(sym) - case _ => die - case Some(scheme) => scheme - + (s :: newcc.cacheHit).foreach: f => + store.getOrElseUpdate(f, ProdStratScheme(preAnalyzer.getProdVarForSym(f).s, newcc.constraints)) + store(s) + case _ => die // die if something occurs twice in the processing list + + def processTopLevel(b: Block): Ls[ProdStrat -> ConsStrat] = + val cc = new ConstraintsAndCacheHitCollector(N) + val strat = processBlock(b)(using Nil, cc) + cc.constrain(strat, NoCons) + // TODO: for used but not defined fun syms, constrain them with NoProd + // TODO: for defined but not fun syms, constrain them with NoCons + cc.constraints def processFunDefn(defn: FunDefn, processingDefs: Ls[BlockMemberSymbol]): ConstraintsAndCacheHitCollector = val thisFunVar = preAnalyzer.getProdVarForSym(defn.sym) val paramSyms = defn.params.head.params.map: // TODO: handle multiple param list and the `restParam` case Param(sym = sym, _) => preAnalyzer.getProdVarForSym(sym).asConsStrat - val cc = new ConstraintsAndCacheHitCollector + val cc = new ConstraintsAndCacheHitCollector(S(defn.sym)) val bodyStrat = processBlock(defn.body)(using defn.sym :: processingDefs, cc) val res = freshVar(s"${defn.sym.nme}_res", S(defn.sym)) cc.constrain(bodyStrat, res.asConsStrat) cc.constrain(ProdFun(paramSyms, res.asProdStrat), thisFunVar.asConsStrat) cc - // returns the cache-hit set of function symbols def processBlock(b: Block)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector - ): ProdStrat = ??? + ): ProdStrat = b match + case m@Match(scrut, arms, dflt, rest) => + val scrutStrat = processResult(scrut) + cc.constrain(scrutStrat, new Dtor(scrut.uid, N)) + val armsRes = + if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then + arms.map: + case (Case.Cls(clsSym, _), body) => processBlock(body) // TODO: check + else + arms.map: + case (_, armBody) => processBlock(armBody) + val dfltRes = dflt.map(processBlock) + rest match + case End(msg) => + val matchRes = freshVar("", processingDefs.headOption) + armsRes.appendedAll(dfltRes).foreach: r => + cc.constrain(r, matchRes.asConsStrat) + matchRes.asProdStrat + case _ => processBlock(rest) + + case Return(res, implct) => processResult(res) + case Assign(lhs, rhs, rest) => + cc.constrain(processResult(rhs), preAnalyzer.getProdVarForSym(lhs).asConsStrat) + processBlock(rest) + case Begin(sub, rest) => + processBlock(sub) + processBlock(rest) + case Define(defn, rest) => + defn match + case f: FunDefn => () // skip as toplevel fundefs are processed when needed + case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") + case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") + processBlock(rest) + case End(msg) => NoProd + // make it a type var instead of `NoProd` so that things like `throw match error` in + // default else branches do not block fusion... + case Throw(exc) => + processResult(exc) + freshVar("throw", processingDefs.headOption).asProdStrat + + + def processResult(r: Result)(using + processingDefs: Ls[BlockMemberSymbol], + cc: ConstraintsAndCacheHitCollector + ): ProdStrat = + val generatedForDef = processingDefs.headOption + def handleCallLike(f: Path, args: Ls[Path], c: Result): ProdStrat = + val argsTpe = args.map(processResult) + f match + case s@Select(p, nme) => + s.symbol.map(_.asCls) match + case None => + val pStrat = processResult(p) + val tpeVar = freshVar("", generatedForDef) + cc.constrain(pStrat, new FieldSel(s.uid, nme, tpeVar.asConsStrat)) + val appRes = freshVar("", generatedForDef) // unknown function symbol + cc.constrain(tpeVar.asProdStrat, ConsFun(argsTpe, appRes.asConsStrat)) + appRes.asProdStrat + case Some(None) => + val funSym = s.symbol.get + val appRes = freshVar("call_" + funSym.nme + "_res", generatedForDef) + funSym.asBlkMember match + case None => + cc.constrain(preAnalyzer.getProdVarForSym(funSym), ConsFun(argsTpe, appRes.asConsStrat)) + case Some(s) => + funSymToProdStratScheme.getOrUpdate(s) match + case v: ProdVar => cc.constrain(v, ConsFun(argsTpe, appRes.asConsStrat)) + case t: ProdStratScheme => + val instantiated = t.instantiate(f.uid)(using this, cc) + cc.constrain(instantiated, ConsFun(argsTpe, appRes.asConsStrat)) + appRes.asProdStrat + case Some(Some(s)) => + val clsFields = s.tree.clsParams + new Ctor(c.uid, N, s, clsFields.zip(argsTpe)) + case Value.Ref(funSym) => + funSym.asCls match + case Some(s) => + val clsFields = s.tree.clsParams + new Ctor(c.uid, N, s, clsFields.zip(argsTpe)) + case _ => // then it is a function + val appRes = freshVar("call_" + funSym.nme + "_res", generatedForDef) + funSym.asBlkMember match + case None => + cc.constrain(preAnalyzer.getProdVarForSym(funSym), ConsFun(argsTpe, appRes.asConsStrat)) + case Some(s) => + funSymToProdStratScheme.getOrUpdate(s) match + case v: ProdVar => cc.constrain(v, ConsFun(argsTpe, appRes.asConsStrat)) + case t: ProdStratScheme => + val instantiated = t.instantiate(f.uid)(using this, cc) + cc.constrain(instantiated, ConsFun(argsTpe, appRes.asConsStrat)) + appRes.asProdStrat + case lam@Value.Lam(params, body) => + val funTpe = processResult(lam) + val appRes = freshVar("call_lam_res", generatedForDef) + cc.constrain(funTpe, ConsFun(argsTpe, appRes.asConsStrat)) + appRes.asProdStrat + case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") + case Value.Lit(lit) => ??? + case Value.Arr(elems) => ??? + r match + case c@Call(f, args) => handleCallLike(f, args.map {case Arg(false, value) => value}, c) + case i@Instantiate(cls, args) => handleCallLike(cls, args, i) + case sel@Select(p, nme) => sel.symbol match + case Some(s) if s.asObj.isDefined => new Ctor(sel.uid, N, s.asObj.get, Nil) + case _ => + val pStrat = processResult(p) + pStrat match + case ProdVar(pStratVar) => + val inMatchingArm = preAnalyzer.selsToMatchingArms(sel.uid).flatMap: + case (scrutUid, S(inArm)) => + preAnalyzer.matchScrutToMatchBlock(scrutUid).scrut match + case Value.Ref(l) => + S(preAnalyzer.getProdVarForSym(l) -> inArm) + case _ => N + case _ => N + val tpeVar = freshVar("sel_res", generatedForDef) + val selStrat = new FieldSel(sel.uid, nme, tpeVar.asConsStrat) + inMatchingArm.foreach: (p, c) => + selStrat.updateFilter(p, c :: Nil) + cc.constrain(pStrat, selStrat) + tpeVar.asProdStrat + case _ => + val tpeVar = freshVar("sel_res", generatedForDef) + cc.constrain(pStrat, new FieldSel(sel.uid, nme, tpeVar.asConsStrat)) + tpeVar.asProdStrat + + case v@Value.Ref(l) => l.asObj match + case None => preAnalyzer.getProdVarForSym(l) + case Some(m) => new Ctor(v.uid, N, m, Nil) + + case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") + case Value.Lit(lit) => NoProd + case Value.Lam(ParamList(_, params, N), body) => + val paramSyms = params.map: // TODO: handle multiple param list and the `restParam` + case Param(sym = sym, _) => preAnalyzer.getProdVarForSym(sym).asConsStrat + val bodyStrat = processBlock(body) + val res = freshVar(s"lam_res", generatedForDef) + cc.constrain(bodyStrat, res.asConsStrat) + ProdFun(paramSyms, res.asProdStrat) + case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") - \ No newline at end of file +class DeforestConstrainSolver() \ No newline at end of file From 84f95908e08c0f06d021e09a541124ef1e9b8a82 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:16:04 +0800 Subject: [PATCH 260/303] wip: resolve clashes --- .../hkmc2/codegen/deforest/Analyze.scala | 215 +++++++++++++++++- 1 file changed, 208 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index f5bc9bf159..9d3d340bf2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -23,12 +23,15 @@ object StratVarState: def freshVar(nme: String, generatedForDef: Opt[BlockMemberSymbol])(using vuid: Uid.StratVarNew.State) = val newId = vuid.nextUid StratVarState(newId, nme, generatedForDef) +trait StratVar(s: StratVarState): + this: ProdVar | ConsVar => + def asProdStrat = s.asProdStrat + def asConsStrat = s.asConsStrat + def uid = s.uid // TODO: examine what info is really needed in the ctors sealed abstract class ProdStrat -case class ProdVar(s: StratVarState) extends ProdStrat: - lazy val asProdStrat = ProdVar(this.s) - lazy val asConsStrat = ConsVar(this.s) +case class ProdVar(s: StratVarState) extends ProdStrat with StratVar(s) case class ProdFun(params: Ls[ConsStrat], res: ProdStrat) extends ProdStrat case object NoProd extends ProdStrat class Ctor( @@ -38,7 +41,7 @@ class Ctor( val args: Ls[TermSymbol -> ProdStrat]) extends ProdStrat sealed abstract class ConsStrat -case class ConsVar(s: StratVarState) extends ConsStrat +case class ConsVar(s: StratVarState) extends ConsStrat with StratVar(s) case class ConsFun(params: Ls[ProdStrat], res: ConsStrat) extends ConsStrat case object NoCons extends ConsStrat class FieldSel( @@ -106,6 +109,10 @@ class DeforestPreAnalyzer(b: Block) extends BlockTraverser: lazy val definedFunSyms = funSymToFun.keySet def getProdVarForSym(s: Symbol) = symToStratVar(s) def getFunDefnForSym(s: BlockMemberSymbol) = funSymToFun.get(s) + def getCtorSymFromCtorLikeExprId(id: ResultId): Opt[ClassLikeSymbol] = + resultIdToResult(id).getCtorSymFromCtorLikeExpr + def getMatchFromMatchScrutExprId(scrutExprId: ResultId): Opt[Match] = + matchScrutToMatchBlock.get(scrutExprId) private var inMatchScrutsArms: Ls[ResultId -> Opt[ClassLikeSymbol]] = Nil private def inMatchScruts = inMatchScrutsArms.unzip._1 @@ -166,8 +173,8 @@ class DeforestPreAnalyzer(b: Block) extends BlockTraverser: case _ => super.applyBlock(b) applyBlock(b) - - + + class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): given stratVarUidState: Uid.StratVarNew.State = preAnalyzer.stratVarUidState import StratVarState.freshVar @@ -392,4 +399,198 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): ProdFun(paramSyms, res.asProdStrat) case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") -class DeforestConstrainSolver() \ No newline at end of file +class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: Ls[ProdStrat -> ConsStrat]): + val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) + val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) + object ctorDests: + val store = mutable.LinkedHashMap.empty[Ctor, Ls[Dtor | FieldSel] -> Bool] + def update(c: Ctor, d: Dtor | FieldSel | NoCons.type) = d match + case NoCons => store.updateWith(c): + case S(l -> _) => S(l, true) + case N => S(Nil, true) + case d: (Dtor | FieldSel) => store.updateWith(c): + case S(l -> b) => S(d :: l, b) + case N => S(d :: Nil, false) + def get(c: Ctor) = store.get(c).map(l => l._1.distinct -> l._2) + object dtorSources: + val store = mutable.Map.empty[Dtor | FieldSel, Ls[Ctor] -> Bool] + def update(d: Dtor | FieldSel, c: Ctor | NoProd.type) = c match + case NoProd => store.updateWith(d): + case S(l -> _) => S(l, true) + case N => S(Nil, true) + case c: Ctor => store.updateWith(d): + case S(l -> b) => S(c :: l, b) + case N => S(c :: Nil, false) + + private def handle(constraint: ProdStrat -> ConsStrat)(using cache: mutable.Set[ProdStrat -> ConsStrat]): Unit = + val prod = constraint._1 + val cons = constraint._2 + val proceed = cache.add(constraint) + + if proceed then constraint match + case (c: Ctor, d: Dtor) => + ctorDests.update(c, d) + dtorSources.update(d, c) + case (c: Ctor, d: FieldSel) => + ctorDests.update(c, d) + dtorSources.update(d, c) + c.args.find(a => a._1.id == d.field).map: p => + handle(p._2 -> d.consVar) + case (c: Ctor, d: ConsFun) => () // ignore, TODO: maybe a warning? + case (p: ProdVar, _) => + upperBounds += p.uid -> (cons :: upperBounds(p.uid)) + lowerBounds(p.uid).foreach: l => + (l, cons) match + case (l: ProdVar, sel: FieldSel) => + sel.updateFilter(l, sel.filter(p)) + handle(l -> cons) + case (c: Ctor, sel: FieldSel) => + if sel.filter.get(p).forall(_.contains(c.ctor)) then + handle(l -> cons) + case _ => handle(l -> cons) + case (_, c: ConsVar) => + lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) + upperBounds(c.uid).foreach: u => + (prod, u) match + case (ctor: Ctor, sel: FieldSel) => + if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then + handle(prod -> u) + case (_: ProdVar, _) => die + case _ => handle(prod -> u) + case (ctor: Ctor, NoCons) => + ctorDests.update(ctor, NoCons) + ctor.args.foreach(a => handle(a._2, NoCons)) + case (ProdFun(l, r), _: Dtor) => () // ignore + case (ProdFun(l, r), _: FieldSel) => () // ignore + case (ProdFun(lp, rp), ConsFun(lc, rc)) => + lc.zip(lp).foreach(handle) + handle(rp, rc) + case (ProdFun(l, r), NoCons) => + l.foreach(a => handle(NoProd, a)) + handle(r, NoCons) + case (NoProd, d: Dtor) => dtorSources.update(d, NoProd) + case (NoProd, fSel: FieldSel) => dtorSources.update(fSel, NoProd) + case (NoProd, ConsFun(l, r)) => + l.foreach(a => handle(a, NoCons)) + handle(NoProd, r) + case (NoProd, NoCons) => () + + locally: + given mutable.Set[ProdStrat -> ConsStrat] = mutable.Set.empty + constraints.foreach(handle) + + val resolveClashes = + val ctorToDtor = ctorDests.store.clone() + val dtorToCtor = dtorSources.store.clone() + def removeCtor(rm: (Ctor | ResultId)): Unit = rm match + case rm: Ctor => + for + (dtors, _) <- ctorToDtor.remove(rm) + dtor <- dtors + do removeDtor(dtor) + case rmId: ResultId => + for rm <- ctorToDtor.keySet.filter(x => x.exprId is rmId) do removeCtor(rm) + def removeDtor(rm: Dtor | FieldSel) = + for (ctors, _) <- dtorToCtor.remove(rm) + x <- ctors do removeCtor(x) + + // remove clashes + for + (rm, dtors -> noCons) <- ctorToDtor + (mats, sels) = dtors.partitionMap: + case d: Dtor => L(d) + case s: FieldSel => R(s) + if noCons || !locally: + mats.size == 0 && sels.size == 1 || + mats.size == 1 && locally: + val matScrutExprId = mats.head.scrutExprId + val matScrutSym = preAnalyzer.resultIdToResult(matScrutExprId).asInstanceOf[Value.Ref].l + sels.forall: s => + val selExprId = s.exprId + preAnalyzer.resultIdToResult(selExprId) match + case Select(Value.Ref(l), _) => + preAnalyzer.selsToMatchingArms(selExprId).exists(_._1 === matScrutExprId) && (l is matScrutSym) + case _ => false + do removeCtor(rm) + for + case (rm, _ -> true) <- dtorToCtor + do removeDtor(rm) + + // remove cycle + def getCtorInArm(ctorExprId: ResultId , dtorScrutExprId: ResultId) = + val ctorSym = preAnalyzer.getCtorSymFromCtorLikeExprId(ctorExprId).get + val dtor = preAnalyzer.getMatchFromMatchScrutExprId(dtorScrutExprId).get + val arm = + dtor.arms.find: + case (Case.Cls(c1, _) -> body) => c1 is ctorSym + .map(_._2).orElse(dtor.dflt).get + val traverser = new GetCtorsTraverser() + traverser.applyBlock(arm) + traverser.ctors + def findCycle(c: Ctor, d: Dtor): Ls[ResultId] = + val cache = mutable.Set(c.exprId) + def go(ctorAndMatchesScrutExprIds: Ls[ResultId -> ResultId]): Ls[ResultId] = + val newCtorsAndNewMatches = for + (c, m) <- ctorAndMatchesScrutExprIds + c <- getCtorInArm(c, m) + (_, ds -> noCons) <- ctorToDtor.filter(x => x._1.exprId is c) + _ = assert(!noCons) + (mats, _) = ds.partitionMap: + case d: Dtor => L(d) + case s: FieldSel => R(s) + m <- mats.headOption + yield c -> m.scrutExprId + val cycled = newCtorsAndNewMatches.filter: + c => !cache.add(c._1) + if newCtorsAndNewMatches.isEmpty then + Nil + else if cycled.nonEmpty then + cycled.map(_._1) + else + go(newCtorsAndNewMatches) + c.instantiationId -> d.instantiationId match + case S(id1) -> S(id2) if id1 == id2 => + go(Ls(c.exprId -> d.scrutExprId)) + case _ => Nil + for + (ctor, dtors -> noCons) <- ctorToDtor + (mats, sels) = dtors.partitionMap: + case d: Dtor => L(d) + case s: FieldSel => R(s) + _ = assert(!noCons && mats.size <= 1) + dtor <- mats + rm <- findCycle(ctor, dtor) + do removeCtor(rm) + + ctorToDtor -> dtorToCtor + + + +class GetCtorsTraverser() extends BlockTraverser: + var ctors = Set.empty[ResultId] + override def applyResult(r: Result): Unit = r match + case Call(f, args) => + if f.asClsSymbol.isDefined then ctors += r.uid + args.foreach: + case Arg(false, v) => applyResult(v) + case Instantiate(cls, args) => + if cls.asClsSymbol.isDefined then ctors += r.uid + args.foreach(applyResult) + case p: Path => if p.asObjSymbol.isDefined then ctors += r.uid + +extension (p: Path) + def asClsSymbol = p match + case s: Select => s.symbol.flatMap(_.asCls) + case Value.Ref(l) => l.asCls + case _ => N + def asObjSymbol = p match + case s: Select => s.symbol.flatMap(_.asObj) + case Value.Ref(l) => l.asObj + case _ => N + +extension (r: Result) + def getCtorSymFromCtorLikeExpr = r match + case Call(f, _) => f.asClsSymbol + case Instantiate(cls, _) => cls.asClsSymbol + case p: Path => p.asObjSymbol + From ac4ef44a0c224f7c93bfd07015c4052893155a10 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:57:58 +0800 Subject: [PATCH 261/303] fix --- .../hkmc2/codegen/deforest/Analyze.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 9d3d340bf2..34e9c65e07 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -98,6 +98,7 @@ class DeforestPreAnalyzer(b: Block) extends BlockTraverser: given stratVarUidState: Uid.StratVarNew.State = new Uid.StratVarNew.State import StratVarState.freshVar + val noProdStratVar = freshVar("primitive", N).asProdStrat val resultIdToResult = mutable.Map.empty[ResultId, Result] val funSymToFun = mutable.Map.empty[BlockMemberSymbol, FunDefn] val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] @@ -107,7 +108,10 @@ class DeforestPreAnalyzer(b: Block) extends BlockTraverser: val symToStratVar = mutable.Map.empty[Symbol, ProdVar] val usedFunSyms = mutable.Set.empty[BlockMemberSymbol] lazy val definedFunSyms = funSymToFun.keySet - def getProdVarForSym(s: Symbol) = symToStratVar(s) + def getProdVarForSym(s: Symbol) = s match + case _: (BuiltinSymbol | TopLevelSymbol) => noProdStratVar + case _ if s.asCls.isDefined => noProdStratVar + case _ => symToStratVar(s) def getFunDefnForSym(s: BlockMemberSymbol) = funSymToFun.get(s) def getCtorSymFromCtorLikeExprId(id: ResultId): Opt[ClassLikeSymbol] = resultIdToResult(id).getCtorSymFromCtorLikeExpr @@ -125,9 +129,11 @@ class DeforestPreAnalyzer(b: Block) extends BlockTraverser: super.applyFunDefn(fun) inFunDef = N - override def applySymbol(s: Symbol): Unit = symToStratVar.updateWith(s): - case N => S(freshVar(s.nme, inFunDef).asProdStrat) - case S(x) => S(x) + override def applySymbol(s: Symbol): Unit = s match + case s: (BlockMemberSymbol | TempSymbol | VarSymbol | TermSymbol) => symToStratVar.updateWith(s): + case N => S(freshVar(s.nme, inFunDef).asProdStrat) + case S(x) => S(x) + case _ => () override def applyResult(r: Result): Unit = resultIdToResult += r.uid -> r @@ -189,9 +195,8 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case ConsVar(s) => check(s) case _ => () private def check(s: StratVarState): Unit = (s.generatedForDef, forFun) match - case (N, N) => () case (S(s1), S(s2)) => assert(s1 is s2) - case _ => die + case _ => () def constrain(p: ProdStrat, c: ConsStrat) = check(p) check(c) @@ -239,6 +244,8 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val cc = new ConstraintsAndCacheHitCollector(N) val strat = processBlock(b)(using Nil, cc) cc.constrain(strat, NoCons) + cc.constrain(preAnalyzer.noProdStratVar, NoCons) + cc.constrain(NoProd, preAnalyzer.noProdStratVar.asConsStrat) // TODO: for used but not defined fun syms, constrain them with NoProd // TODO: for defined but not fun syms, constrain them with NoCons cc.constraints From 0cc725daa4fa65377a1614883949f69876471ea4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 14:21:48 +0800 Subject: [PATCH 262/303] fix: no check when collecting constraints, and inst duplicate things in the same group --- .../hkmc2/codegen/deforest/Analyze.scala | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 34e9c65e07..a7cfe2f5a7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -62,9 +62,10 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) case Value.Ref(l) => l.asBlkMember.get case s: Select => s.symbol.flatMap(_.asBlkMember).get case _ => die + val instantiatingRecursiveGroup = d.funSymToProdStratScheme.recursiveGroups(instantiatingFunSym) val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] def duplicateVarState(s: StratVarState) = - if s.generatedForDef.fold(false)(_ is instantiatingFunSym) then + if s.generatedForDef.fold(false)(instantiatingRecursiveGroup.contains) then stratVarMap.getOrElseUpdate.curried(s): StratVarState.freshVar(s.name, cc.forFun)(using d.stratVarUidState) else @@ -188,29 +189,14 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): class ConstraintsAndCacheHitCollector(val forFun: Opt[BlockMemberSymbol]): var constraints: Ls[ProdStrat -> ConsStrat] = Nil var cacheHit: Ls[BlockMemberSymbol] = Nil // TODO: a better name to say it actually get the symbol of funs in a recursive group - private def check(p: ProdStrat): Unit = p match - case ProdVar(s) => check(s) - case _ => () - private def check(c: ConsStrat): Unit = c match - case ConsVar(s) => check(s) - case _ => () - private def check(s: StratVarState): Unit = (s.generatedForDef, forFun) match - case (S(s1), S(s2)) => assert(s1 is s2) - case _ => () - def constrain(p: ProdStrat, c: ConsStrat) = - check(p) - check(c) - constraints ::= p -> c - def constrain(cs: Ls[ProdStrat -> ConsStrat]) = - cs.foreach: (p, c) => - check(p) - check(c) - constraints :::= cs + def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c + def constrain(cs: Ls[ProdStrat -> ConsStrat]) = constraints :::= cs def hit(s: BlockMemberSymbol) = cacheHit ::= s def hit(ss: Ls[BlockMemberSymbol]) = cacheHit :::= ss object funSymToProdStratScheme: val store = mutable.Map.empty[BlockMemberSymbol, ProdStratScheme] + val recursiveGroups = mutable.Map.empty[BlockMemberSymbol, Ls[BlockMemberSymbol]] def getOrUpdate(s: BlockMemberSymbol)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector): ProdVar | ProdStratScheme = preAnalyzer.getFunDefnForSym(s) match // not a fun defined in the current block, just return its prodvar @@ -235,8 +221,14 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): cc.hit(h) preAnalyzer.getProdVarForSym(s) else - (s :: newcc.cacheHit).foreach: f => - store.getOrElseUpdate(f, ProdStratScheme(preAnalyzer.getProdVarForSym(f).s, newcc.constraints)) + val recursiveGroupMembers = (s :: newcc.cacheHit).distinct + recursiveGroupMembers.foreach: f => + store.updateWith(f): + case N => S(ProdStratScheme(preAnalyzer.getProdVarForSym(f).s, newcc.constraints)) + case S(_) => die // this means the scc is not computed corrrectely + recursiveGroups.updateWith(f): + case N => S(recursiveGroupMembers) + case S(_) => die // this means the scc is not computed corrrectely store(s) case _ => die // die if something occurs twice in the processing list From 1988f3260cf32a827cb7dec659036d766479cf25 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 14:35:23 +0800 Subject: [PATCH 263/303] some new tests --- .../mlscript/deforest/def-dup/new-simple.mls | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls new file mode 100644 index 0000000000..7e7c058c32 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -0,0 +1,48 @@ +:js + +data class A(x) +data class (::) Cons(h, t) +object Nil + + + +// :sjs +// :deforest +// fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) +// fun f1(ls1, acc) = if ls1 is +// Nil then acc +// h :: t then f1(t, acc + h) +// fun f2(ls2, acc) = if ls2 is +// Nil then acc +// hh :: tt then f2(tt, acc + hh + 1) +// f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) + +// :sjs +// :deforest +// fun id(x) = x +// fun f1(a) = if a is A then 1 +// fun f2(a) = if a is A then 2 +// f1(id(A(1))) + f2(id(A(2))) + + + +// :sjs +// :deforest +// fun p(x) = A(x) +// fun wrap1(x) = p(x) +// fun wrap(x) = wrap1(x) +// fun f1(a1) = if a1 is A then 1 +// fun f2(a2) = if a2 is A then 2 +// f1(wrap(1)) + f2(wrap(2)) + +// :sjs +// :deforest +// fun fffff(x) = if x == 0 then Nil else x :: fffff1(x - 1) +// fun fffff1(x) = if x == 0 then Nil else x :: fffff(x - 1) +// fun cons(a) = if a is +// Nil then 0 +// h :: t then h + cons(t) +// fun cons1(a1) = if a1 is +// Nil then 1 +// h :: t then h + cons1(t) +// cons(fffff(0)) + cons1(fffff(1)) From 852cdc5015ee4205f7fe8af93b98f64bf0608088 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:21:06 +0800 Subject: [PATCH 264/303] wip --- .../shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index a7cfe2f5a7..f26f9c49a1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -3,8 +3,7 @@ package codegen package deforest import semantics.* -import semantics.Elaborator.State -import syntax.{Literal, Tree} +import syntax.Tree import utils.* import mlscript.utils.*, shorthands.* import scala.collection.mutable From b48f3d4bb95c0a079fb93719c84e7d1931387dbc Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:33:31 +0800 Subject: [PATCH 265/303] fix --- .../scala/hkmc2/codegen/deforest/Analyze.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index f26f9c49a1..d7c8983aae 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -486,8 +486,8 @@ class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: (dtors, _) <- ctorToDtor.remove(rm) dtor <- dtors do removeDtor(dtor) - case rmId: ResultId => - for rm <- ctorToDtor.keySet.filter(x => x.exprId is rmId) do removeCtor(rm) + case _ => + for rm <- ctorToDtor.keySet.filter(x => x.exprId == rm) do removeCtor(rm) def removeDtor(rm: Dtor | FieldSel) = for (ctors, _) <- dtorToCtor.remove(rm) x <- ctors do removeCtor(x) @@ -522,8 +522,7 @@ class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: dtor.arms.find: case (Case.Cls(c1, _) -> body) => c1 is ctorSym .map(_._2).orElse(dtor.dflt).get - val traverser = new GetCtorsTraverser() - traverser.applyBlock(arm) + val traverser = new GetCtorsTraverser(arm) traverser.ctors def findCycle(c: Ctor, d: Dtor): Ls[ResultId] = val cache = mutable.Set(c.exprId) @@ -531,15 +530,15 @@ class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: val newCtorsAndNewMatches = for (c, m) <- ctorAndMatchesScrutExprIds c <- getCtorInArm(c, m) - (_, ds -> noCons) <- ctorToDtor.filter(x => x._1.exprId is c) + (_, ds -> noCons) <- ctorToDtor.filter(x => x._1.exprId == c) _ = assert(!noCons) (mats, _) = ds.partitionMap: case d: Dtor => L(d) case s: FieldSel => R(s) m <- mats.headOption yield c -> m.scrutExprId - val cycled = newCtorsAndNewMatches.filter: - c => !cache.add(c._1) + val cycled = newCtorsAndNewMatches.filter: c => + !cache.add(c._1) if newCtorsAndNewMatches.isEmpty then Nil else if cycled.nonEmpty then @@ -564,7 +563,7 @@ class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: -class GetCtorsTraverser() extends BlockTraverser: +class GetCtorsTraverser(b: Block) extends BlockTraverser: var ctors = Set.empty[ResultId] override def applyResult(r: Result): Unit = r match case Call(f, args) => @@ -575,6 +574,7 @@ class GetCtorsTraverser() extends BlockTraverser: if cls.asClsSymbol.isDefined then ctors += r.uid args.foreach(applyResult) case p: Path => if p.asObjSymbol.isDefined then ctors += r.uid + applyBlock(b) extension (p: Path) def asClsSymbol = p match From 208af14ef88aa1af50c0d60fa4f42d741ec0644d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:58:01 +0800 Subject: [PATCH 266/303] small improvements --- .../main/scala/hkmc2/codegen/deforest/Analyze.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index d7c8983aae..8d11789186 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -94,7 +94,7 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) cc.constrain(duplicateProdStrat(p), duplicateConsStrat(c)) newProd -class DeforestPreAnalyzer(b: Block) extends BlockTraverser: +class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: given stratVarUidState: Uid.StratVarNew.State = new Uid.StratVarNew.State import StratVarState.freshVar @@ -185,6 +185,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): given stratVarUidState: Uid.StratVarNew.State = preAnalyzer.stratVarUidState import StratVarState.freshVar + val constraints = processTopLevel(preAnalyzer.b) class ConstraintsAndCacheHitCollector(val forFun: Opt[BlockMemberSymbol]): var constraints: Ls[ProdStrat -> ConsStrat] = Nil var cacheHit: Ls[BlockMemberSymbol] = Nil // TODO: a better name to say it actually get the symbol of funs in a recursive group @@ -252,7 +253,6 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): cc.constrain(ProdFun(paramSyms, res.asProdStrat), thisFunVar.asConsStrat) cc - // returns the cache-hit set of function symbols def processBlock(b: Block)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector @@ -296,7 +296,6 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): processResult(exc) freshVar("throw", processingDefs.headOption).asProdStrat - def processResult(r: Result)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector @@ -396,8 +395,11 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): cc.constrain(bodyStrat, res.asConsStrat) ProdFun(paramSyms, res.asProdStrat) case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") - -class DeforestConstrainSolver(val preAnalyzer: DeforestPreAnalyzer, constraints: Ls[ProdStrat -> ConsStrat]): + + +class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): + val preAnalyzer = collector.preAnalyzer + val constraints = collector.constraints val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) object ctorDests: From 5155e91d817ece9b02d6043a779642b0b88add57 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:33:36 +0800 Subject: [PATCH 267/303] fix selection filter --- .../hkmc2/codegen/deforest/Analyze.scala | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 8d11789186..5e81c2cee3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -358,7 +358,15 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case c@Call(f, args) => handleCallLike(f, args.map {case Arg(false, value) => value}, c) case i@Instantiate(cls, args) => handleCallLike(cls, args, i) case sel@Select(p, nme) => sel.symbol match - case Some(s) if s.asObj.isDefined => new Ctor(sel.uid, N, s.asObj.get, Nil) + case Some(s) if s.asObj.isDefined => + new Ctor(sel.uid, N, s.asObj.get, Nil) + case Some(s) if s.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && + preAnalyzer.definedFunSyms.contains(s.asBlkMember.get) => + funSymToProdStratScheme.getOrUpdate(s.asBlkMember.get) match + case v: ProdVar => v + case t: ProdStratScheme => + val instantiated = t.instantiate(sel.uid)(using this, cc) + instantiated case _ => val pStrat = processResult(p) pStrat match @@ -382,7 +390,16 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): tpeVar.asProdStrat case v@Value.Ref(l) => l.asObj match - case None => preAnalyzer.getProdVarForSym(l) + case None => + if l.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && + preAnalyzer.definedFunSyms.contains(l.asBlkMember.get) then + funSymToProdStratScheme.getOrUpdate(l.asBlkMember.get) match + case v: ProdVar => v + case t: ProdStratScheme => + val instantiated = t.instantiate(v.uid)(using this, cc) + instantiated + else + preAnalyzer.getProdVarForSym(l) case Some(m) => new Ctor(v.uid, N, m, Nil) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") @@ -453,7 +470,7 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): upperBounds(c.uid).foreach: u => (prod, u) match case (ctor: Ctor, sel: FieldSel) => - if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then + if sel.filter.get(c.asProdStrat).forall(_.contains(ctor.ctor)) then handle(prod -> u) case (_: ProdVar, _) => die case _ => handle(prod -> u) From 6a7392bf97a44fec2e398cccd18f70d1d93539d7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 20:51:02 +0800 Subject: [PATCH 268/303] stable resultid when needed --- .../scala/hkmc2/codegen/deforest/Analyze.scala | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 5e81c2cee3..51ab40a1c1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -57,7 +57,7 @@ class Dtor( class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]): def instantiate(referSite: ResultId)(using d: DeforestConstraintsCollector, cc: d.ConstraintsAndCacheHitCollector): ProdVar = - val instantiatingFunSym = d.preAnalyzer.resultIdToResult(referSite) match + val instantiatingFunSym = d.preAnalyzer.getResult(referSite) match case Value.Ref(l) => l.asBlkMember.get case s: Select => s.symbol.flatMap(_.asBlkMember).get case _ => die @@ -117,7 +117,14 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: resultIdToResult(id).getCtorSymFromCtorLikeExpr def getMatchFromMatchScrutExprId(scrutExprId: ResultId): Opt[Match] = matchScrutToMatchBlock.get(scrutExprId) - + def getResult(id: ResultId) = resultIdToResult(id) + def getStableResultId(id: ResultId) = resultIdToStableId.getOrElseUpdate.curried(id): + val prev = stableResuldIt + stableResuldIt += 1 + prev + + private val resultIdToStableId = mutable.Map.empty[ResultId, Int] + private var stableResuldIt = 0 private var inMatchScrutsArms: Ls[ResultId -> Opt[ClassLikeSymbol]] = Nil private def inMatchScruts = inMatchScrutsArms.unzip._1 private var inFunDef: Opt[BlockMemberSymbol] = N @@ -521,10 +528,10 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): mats.size == 0 && sels.size == 1 || mats.size == 1 && locally: val matScrutExprId = mats.head.scrutExprId - val matScrutSym = preAnalyzer.resultIdToResult(matScrutExprId).asInstanceOf[Value.Ref].l + val matScrutSym = preAnalyzer.getResult(matScrutExprId).asInstanceOf[Value.Ref].l sels.forall: s => val selExprId = s.exprId - preAnalyzer.resultIdToResult(selExprId) match + preAnalyzer.getResult(selExprId) match case Select(Value.Ref(l), _) => preAnalyzer.selsToMatchingArms(selExprId).exists(_._1 === matScrutExprId) && (l is matScrutSym) case _ => false From 7548db47f3a3e4aac66be634edefebfbeb2fc576 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 10 Jun 2025 21:56:22 +0800 Subject: [PATCH 269/303] tests --- .../mlscript/deforest/def-dup/new-simple.mls | 250 +++++++++++++++--- 1 file changed, 210 insertions(+), 40 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index 7e7c058c32..b98146a5bb 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -1,48 +1,218 @@ :js data class A(x) +data class B(x) data class (::) Cons(h, t) object Nil -// :sjs -// :deforest -// fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) -// fun f1(ls1, acc) = if ls1 is -// Nil then acc -// h :: t then f1(t, acc + h) -// fun f2(ls2, acc) = if ls2 is -// Nil then acc -// hh :: tt then f2(tt, acc + hh + 1) -// f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) - -// :sjs -// :deforest -// fun id(x) = x -// fun f1(a) = if a is A then 1 -// fun f2(a) = if a is A then 2 -// f1(id(A(1))) + f2(id(A(2))) - - - -// :sjs -// :deforest -// fun p(x) = A(x) -// fun wrap1(x) = p(x) -// fun wrap(x) = wrap1(x) -// fun f1(a1) = if a1 is A then 1 -// fun f2(a2) = if a2 is A then 2 -// f1(wrap(1)) + f2(wrap(2)) - -// :sjs -// :deforest -// fun fffff(x) = if x == 0 then Nil else x :: fffff1(x - 1) -// fun fffff1(x) = if x == 0 then Nil else x :: fffff(x - 1) -// fun cons(a) = if a is -// Nil then 0 -// h :: t then h + cons(t) -// fun cons1(a1) = if a1 is -// Nil then 1 -// h :: t then h + cons1(t) -// cons(fffff(0)) + cons1(fffff(1)) +:deforest +fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) +fun f1(ls1, acc) = if ls1 is + Nil then acc + h :: t then f1(t, acc + h) +fun f2(ls2, acc) = if ls2 is + Nil then acc + hh :: tt then f2(tt, acc + hh + 1) +f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) +//│ = 51 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@None --> +//│ Ref(ls1)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(3)) --> +//│ Ref(ls1)@Some(List(1)) +//│ Ref(member:Nil)@4@None --> +//│ Ref(ls2)@Some(List(5)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(6)) --> +//│ Ref(ls2)@Some(List(5)) +//│ Ref(member:Nil)@7@None --> +//│ Ref(ls1)@Some(List(8)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> +//│ Ref(ls1)@Some(List(8)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun id(x) = x +fun f1(a1) = if a1 is A then 1 +fun f2(a2) = if a2 is A then 2 +f1(id(A(1))) + f2(id(A(2))) +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@None --> +//│ Ref(a2)@Some(List(1)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@None --> +//│ Ref(a1)@Some(List(3)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun p(x) = A(x) +fun wrap1(x) = p(x) +fun wrap(x) = wrap1(x) +fun f1(a1) = if a1 is A then 1 +fun f2(a2) = if a2 is A then 2 +f1(wrap(1)) + f2(wrap(2)) +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1, 2, 3)) --> +//│ Ref(a2)@Some(List(4)) +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> +//│ Ref(a1)@Some(List(6)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun f(x) = if x == 0 then Nil else x :: g(x - 1) +fun g(y) = if y == 0 then Nil else y :: f(y - 1) +fun cons(a) = if a is + Nil then 0 + h :: t then h + cons(t) +fun cons1(a1) = if a1 is + Nil then 1 + h :: t then h + cons1(t) +cons(f(0)) + cons1(f(1)) +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Ref(member:Nil)@4@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Ref(member:Nil)@0@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Ref(member:Nil)@4@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) +//│ = Cons(4, Cons(6, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@Some(List(1)) --> +//│ Ref(ls)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> +//│ Ref(ls)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@None --> +//│ Ref(ls)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@None --> +//│ Ref(ls)@Some(List(1)) +//│ Ref(member:Nil)@6@None --> +//│ Ref(ls)@Some(List(1)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection +:deforest +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +map(1 :: 2 :: Nil, x => x + 1) +//│ = Cons(2, Cons(3, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@None --> +//│ Ref(ls)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@None --> +//│ Ref(ls)@Some(List(1)) +//│ Ref(member:Nil)@3@None --> +//│ Ref(ls)@Some(List(1)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for cyclic strategies removal +:deforest +fun f(x) = if x is + A(_) then f(A(2)) + else 0 +f(Nil) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection filter +:deforest +fun c1(x) = if x is + h :: t then + if t is + Nil then 0 +c1(1 :: Nil) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@None --> +//│ Ref(x)@Some(List(1)) +//│ Ref(member:Nil)@2@None --> +//│ Ref(t)@Some(List(1)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection filter +:deforest +fun f(x) = if x is + A then g1(x.x) + B then g2(x.x) +fun g1(y1) = if y1 is + A then 0 +fun g2(y2) = if y2 is + A then 1 +fun apply(f) = + let inner = A(0) + f(A(inner)) +apply(f) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@Some(List(1)) --> +//│ Ref(x)@Some(List(2)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@Some(List(1)) --> +//│ Ref(y1)@Some(List(2, 4)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +:sjs +:deforest +fun inner(y, z) = if y is + B then z +fun dtor(x) = if x is + A then inner(B(4), x.x) +dtor(A(B(0))) +//│ JS (unsanitized): +//│ let dtor, inner, tmp27, tmp28; +//│ inner = function inner(y, z) { +//│ if (y instanceof B1.class) { +//│ return z +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ dtor = function dtor(x) { +//│ let tmp29; +//│ if (x instanceof A1.class) { +//│ tmp29 = B1(4); +//│ return inner(tmp29, x.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp27 = B1(0); +//│ tmp28 = A1(tmp27); +//│ dtor(tmp28) +//│ = B(0) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@Some(List(1)) --> +//│ Ref(y)@Some(List(1, 2)) +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@None --> +//│ Ref(x)@Some(List(1)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 677b9f0b0d5cc17e5db51d0ae2f6e97468e8a4f6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:04:02 +0800 Subject: [PATCH 270/303] distinguish instantiation id: Some(Nil) vs None --- .../hkmc2/codegen/deforest/Analyze.scala | 32 +++++++++---- .../mlscript/deforest/def-dup/new-simple.mls | 46 +++++++++++++------ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 51ab40a1c1..8f89d3164a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -122,6 +122,15 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: val prev = stableResuldIt stableResuldIt += 1 prev + def getReferredFunSym(id: ResultId) = + def chk(s: BlockMemberSymbol) = + assert(s.trmImplTree.exists(_.k is syntax.Fun)) + s + resultIdToResult(id) match + case s: Select => chk(s.symbol.get.asBlkMember.get) + case Value.Ref(l) => chk(l.asBlkMember.get) + case _ => die + private val resultIdToStableId = mutable.Map.empty[ResultId, Int] private var stableResuldIt = 0 @@ -266,7 +275,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): ): ProdStrat = b match case m@Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) - cc.constrain(scrutStrat, new Dtor(scrut.uid, N)) + cc.constrain(scrutStrat, new Dtor(scrut.uid, cc.forFun.fold(S(Nil))(_ => N))) val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: @@ -277,7 +286,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val dfltRes = dflt.map(processBlock) rest match case End(msg) => - val matchRes = freshVar("", processingDefs.headOption) + val matchRes = freshVar("", cc.forFun) armsRes.appendedAll(dfltRes).foreach: r => cc.constrain(r, matchRes.asConsStrat) matchRes.asProdStrat @@ -301,13 +310,14 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): // default else branches do not block fusion... case Throw(exc) => processResult(exc) - freshVar("throw", processingDefs.headOption).asProdStrat + freshVar("throw", cc.forFun).asProdStrat def processResult(r: Result)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector ): ProdStrat = - val generatedForDef = processingDefs.headOption + val generatedForDef = cc.forFun + val instantiationId = cc.forFun.fold(S(Nil))(_ => N) def handleCallLike(f: Path, args: Ls[Path], c: Result): ProdStrat = val argsTpe = args.map(processResult) f match @@ -335,12 +345,12 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): appRes.asProdStrat case Some(Some(s)) => val clsFields = s.tree.clsParams - new Ctor(c.uid, N, s, clsFields.zip(argsTpe)) + new Ctor(c.uid, instantiationId, s, clsFields.zip(argsTpe)) case Value.Ref(funSym) => funSym.asCls match case Some(s) => val clsFields = s.tree.clsParams - new Ctor(c.uid, N, s, clsFields.zip(argsTpe)) + new Ctor(c.uid, instantiationId, s, clsFields.zip(argsTpe)) case _ => // then it is a function val appRes = freshVar("call_" + funSym.nme + "_res", generatedForDef) funSym.asBlkMember match @@ -366,7 +376,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case i@Instantiate(cls, args) => handleCallLike(cls, args, i) case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => - new Ctor(sel.uid, N, s.asObj.get, Nil) + new Ctor(sel.uid, instantiationId, s.asObj.get, Nil) case Some(s) if s.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && preAnalyzer.definedFunSyms.contains(s.asBlkMember.get) => funSymToProdStratScheme.getOrUpdate(s.asBlkMember.get) match @@ -407,7 +417,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): instantiated else preAnalyzer.getProdVarForSym(l) - case Some(m) => new Ctor(v.uid, N, m, Nil) + case Some(m) => new Ctor(v.uid, instantiationId, m, Nil) case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") case Value.Lit(lit) => NoProd @@ -450,7 +460,9 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): val prod = constraint._1 val cons = constraint._2 val proceed = cache.add(constraint) - + assert: + (!prod.isInstanceOf[Ctor] || prod.asInstanceOf[Ctor].instantiationId.isDefined) && + (!cons.isInstanceOf[Dtor] || cons.asInstanceOf[Dtor].instantiationId.isDefined) if proceed then constraint match case (c: Ctor, d: Dtor) => ctorDests.update(c, d) @@ -504,7 +516,7 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): constraints.foreach(handle) val resolveClashes = - val ctorToDtor = ctorDests.store.clone() + val ctorToDtor = ctorDests.store.clone() // TODO: clone is only helpful for debugging val dtorToCtor = dtorSources.store.clone() def removeCtor(rm: (Ctor | ResultId)): Unit = rm match case rm: Ctor => diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index b98146a5bb..739a401194 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -18,15 +18,15 @@ fun f2(ls2, acc) = if ls2 is f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ = 51 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@None --> +//│ Ref(member:Nil)@0@Some(List()) --> //│ Ref(ls1)@Some(List(1)) //│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(3)) --> //│ Ref(ls1)@Some(List(1)) -//│ Ref(member:Nil)@4@None --> +//│ Ref(member:Nil)@4@Some(List()) --> //│ Ref(ls2)@Some(List(5)) //│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(6)) --> //│ Ref(ls2)@Some(List(5)) -//│ Ref(member:Nil)@7@None --> +//│ Ref(member:Nil)@7@Some(List()) --> //│ Ref(ls1)@Some(List(8)) //│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> //│ Ref(ls1)@Some(List(8)) @@ -40,9 +40,9 @@ fun f2(a2) = if a2 is A then 2 f1(id(A(1))) + f2(id(A(2))) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@None --> +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@Some(List()) --> //│ Ref(a2)@Some(List(1)) -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@None --> +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@Some(List()) --> //│ Ref(a1)@Some(List(3)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -105,11 +105,11 @@ map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) //│ Ref(ls)@Some(List(2)) //│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> //│ Ref(ls)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@None --> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@Some(List()) --> //│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@None --> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@Some(List()) --> //│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@6@None --> +//│ Ref(member:Nil)@6@Some(List()) --> //│ Ref(ls)@Some(List(1)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -122,11 +122,11 @@ fun map(ls, f) = if ls is map(1 :: 2 :: Nil, x => x + 1) //│ = Cons(2, Cons(3, Nil)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@None --> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@Some(List()) --> //│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@None --> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@Some(List()) --> //│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@3@None --> +//│ Ref(member:Nil)@3@Some(List()) --> //│ Ref(ls)@Some(List(1)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -151,9 +151,9 @@ fun c1(x) = if x is c1(1 :: Nil) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@None --> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@Some(List()) --> //│ Ref(x)@Some(List(1)) -//│ Ref(member:Nil)@2@None --> +//│ Ref(member:Nil)@2@Some(List()) --> //│ Ref(t)@Some(List(1)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -213,6 +213,24 @@ dtor(A(B(0))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@Some(List(1)) --> //│ Ref(y)@Some(List(1, 2)) -//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@None --> +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@Some(List()) --> //│ Ref(x)@Some(List(1)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun localFuse(x) = if A(x) is + A(y) then B(y) +if localFuse(3) is + B(a) then a +localFuse(4) +//│ = B(4) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1)) --> +//│ Ref($scrut)@Some(List(1)) +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(2)) --> +//│ Ref($scrut)@Some(List(2)) +//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@Some(List(2)) --> +//│ Ref($scrut)@Some(List()) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + From 7ae110f3bf77edfac36fab211630c9b3bb92b986 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 11 Jun 2025 20:53:35 +0800 Subject: [PATCH 271/303] wip --- .../hkmc2/codegen/deforest/Analyze.scala | 5 +- .../hkmc2/codegen/deforest/Rewrite.scala | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 8f89d3164a..0cb589ce76 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -7,7 +7,6 @@ import syntax.Tree import utils.* import mlscript.utils.*, shorthands.* import scala.collection.mutable -import scala.collection.mutable.LinkedHashMap import Result.ResultId final case class NotDeforestableException(msg: String) extends Exception(msg) @@ -405,7 +404,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val tpeVar = freshVar("sel_res", generatedForDef) cc.constrain(pStrat, new FieldSel(sel.uid, nme, tpeVar.asConsStrat)) tpeVar.asProdStrat - + case v@Value.Ref(l) => l.asObj match case None => if l.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && @@ -447,7 +446,7 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): case N => S(d :: Nil, false) def get(c: Ctor) = store.get(c).map(l => l._1.distinct -> l._2) object dtorSources: - val store = mutable.Map.empty[Dtor | FieldSel, Ls[Ctor] -> Bool] + val store = mutable.LinkedHashMap.empty[Dtor | FieldSel, Ls[Ctor] -> Bool] def update(d: Dtor | FieldSel, c: Ctor | NoProd.type) = c match case NoProd => store.updateWith(d): case S(l -> _) => S(l, true) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala new file mode 100644 index 0000000000..3b13bf06f3 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -0,0 +1,114 @@ +package hkmc2 +package codegen +package deforest + + +import semantics.* +import semantics.Elaborator.State +import syntax.Tree +import utils.* +import mlscript.utils.*, shorthands.* +import scala.collection.mutable +import Result.ResultId + +type InstantiationId = Ls[ResultId] +type CtorId = ResultId -> InstantiationId + +enum FinalDest: + case Match(val scrutExprId: ResultId, val instantiationId: InstantiationId, val arm: ClassLikeSymbol, val selsInArm: Ls[ResultId]) + case Sel(val s: ResultId) + +class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator.State): + val preAnalyzer = sol.preAnalyzer + + val instIdToMappingFromOldToNewSyms = mutable.Map.empty[Ls[ResultId], Map[BlockMemberSymbol, BlockMemberSymbol]] + // need to be ordered because we need to traverse it to generate new fun def bodies + val newSymToInstIdAndOldSym = mutable.LinkedHashMap.empty[BlockMemberSymbol, Ls[ResultId] -> BlockMemberSymbol] + locally: + for + (ctor, _) <- sol.resolveClashes._1 + instantiationId = ctor.instantiationId.get + case instId@(invokedReferSite :: _) <- instantiationId.scanRight(Nil)(_ :: _) + do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): + val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) + val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) + val newSymSuffix = instId.map(preAnalyzer.getStableResultId).mkString("_") + val mapping = recursiveGroupFunSym.map: old => + val newSymbol = BlockMemberSymbol(old.nme + s"_$newSymSuffix", Nil, old.nameIsMeaningful) + newSymToInstIdAndOldSym.updateWith(newSymbol): + case N => S(instId -> old) + case S(_) => die + old -> newSymbol + mapping.toMap + // TODO: can merge all ids that *only* have dtor to one id, but also need to consider all other + // referred functions along the way + for + case (dtor: Dtor, _) <- sol.resolveClashes._2 + instantiationId = dtor.instantiationId.get + case instId@(invokedReferSite :: _) <- instantiationId.scanRight(Nil)(_ :: _) + do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): + val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) + val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) + val newSymSuffix = instId.map(preAnalyzer.getStableResultId).mkString("_") + val mapping = recursiveGroupFunSym.map: old => + val newSymbol = BlockMemberSymbol(old.nme + s"_$newSymSuffix", Nil, old.nameIsMeaningful) + newSymToInstIdAndOldSym.updateWith(newSymbol): + case N => S(instId -> old) + case S(_) => die + old -> newSymbol + mapping.toMap + + val finalDestToCtorIds = mutable.Map.empty[FinalDest, Set[CtorId]] + val ctorIdToFinalDest: Map[CtorId, FinalDest] = sol.resolveClashes._1 + .map: + case (c: Ctor, dtors -> noCons) => + assert(!noCons) + val (dtor, sel) = dtors.partitionMap: + case d: Dtor => L(d) + case s: FieldSel => R(s) + val ctorExprId = c.exprId -> c.instantiationId.get + val ctorFinalDest = + if dtor.isEmpty then + assert(sel.size == 1) + FinalDest.Sel(sel.head.exprId) + else + assert(dtor.size == 1) + val dtorScrutExprId = dtor.head.scrutExprId + val distinctSels = sel.map(_.exprId).distinct // need to preserve order + distinctSels.foreach: s => + assert(preAnalyzer.selsToMatchingArms(s).exists(_._1 == dtorScrutExprId)) + FinalDest.Match( + dtorScrutExprId, + dtor.head.instantiationId.get, + preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get, + distinctSels + ) + finalDestToCtorIds.updateWith(ctorFinalDest): + case N => S(Set(ctorExprId)) + case S(s) => S(s + ctorExprId) + ctorExprId -> ctorFinalDest + .toMap + val rewritingMatchIds -> rewritingSelIds = finalDestToCtorIds.keySet.partitionMap: + case FinalDest.Match(scrutExprId = s, instantiationId = id, _) => L(s -> id) + case FinalDest.Sel(s) => R(s) + + + +class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elaborator.State): + val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Block] = + rewritePrepare.finalDestToCtorIds.collect: + case (FinalDest.Match(scrutExprId, instantiationId, arm, selsInArm) -> ctorIds) => ??? + ??? + + + + // val duplicatedFuns = newSymToInstIdAndOldSym.foldRight(sol.preAnalyzer.b): + // case (newSym -> instId, acc) => + // 1. find original fundefs + // 2. transform fun body under the specific instantiation id + // 3. accumulate the definition + // ??? + class Transformer(instId: InstantiationId) extends BlockTransformer(new SymbolSubst): + override def applyBlock(b: Block): Block = ??? + override def applyResult2(r: Result)(k: Result => Block): Block = ??? + def apply(b: Block) = applyBlock(b) \ No newline at end of file From 6210ef2f4d7784ce21cf29eca54c142f65dadeb4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:30:05 +0800 Subject: [PATCH 272/303] more on cycle detection --- .../src/main/scala/hkmc2/codegen/deforest/Analyze.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 0cb589ce76..13b28df566 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -559,7 +559,13 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): dtor.arms.find: case (Case.Cls(c1, _) -> body) => c1 is ctorSym .map(_._2).orElse(dtor.dflt).get - val traverser = new GetCtorsTraverser(arm) + val armAndMatchRest = + preAnalyzer + .matchScrutToParentMatchScruts(dtorScrutExprId) + .foldLeft(Begin(arm, dtor.rest)): (acc, x) => + val newRest = preAnalyzer.getMatchFromMatchScrutExprId(x).get.rest + Begin(acc, newRest) + val traverser = new GetCtorsTraverser(armAndMatchRest) traverser.ctors def findCycle(c: Ctor, d: Dtor): Ls[ResultId] = val cache = mutable.Set(c.exprId) From c1acf12294ee5592909bdd65cd405e217a7daace Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:06:28 +0800 Subject: [PATCH 273/303] wip --- .../hkmc2/codegen/deforest/Rewrite.scala | 265 +++++++++++++++++- 1 file changed, 253 insertions(+), 12 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 3b13bf06f3..1c86ca1ca1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -12,10 +12,15 @@ import scala.collection.mutable import Result.ResultId type InstantiationId = Ls[ResultId] +extension (instId: InstantiationId) + def makeSuffix(preAnalyzer: DeforestPreAnalyzer) = + instId.map(preAnalyzer.getStableResultId).mkString("_") + type CtorId = ResultId -> InstantiationId +type MatchId = ResultId -> InstantiationId enum FinalDest: - case Match(val scrutExprId: ResultId, val instantiationId: InstantiationId, val arm: ClassLikeSymbol, val selsInArm: Ls[ResultId]) + case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol], val selsInArm: Ls[ResultId]) case Sel(val s: ResultId) class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator.State): @@ -32,7 +37,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) - val newSymSuffix = instId.map(preAnalyzer.getStableResultId).mkString("_") + val newSymSuffix = instId.makeSuffix(preAnalyzer) val mapping = recursiveGroupFunSym.map: old => val newSymbol = BlockMemberSymbol(old.nme + s"_$newSymSuffix", Nil, old.nameIsMeaningful) newSymToInstIdAndOldSym.updateWith(newSymbol): @@ -49,7 +54,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) - val newSymSuffix = instId.map(preAnalyzer.getStableResultId).mkString("_") + val newSymSuffix = instId.makeSuffix(preAnalyzer) val mapping = recursiveGroupFunSym.map: old => val newSymbol = BlockMemberSymbol(old.nme + s"_$newSymSuffix", Nil, old.nameIsMeaningful) newSymToInstIdAndOldSym.updateWith(newSymbol): @@ -58,7 +63,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. old -> newSymbol mapping.toMap - val finalDestToCtorIds = mutable.Map.empty[FinalDest, Set[CtorId]] + val finalDestToCtorIds = mutable.LinkedHashMap.empty[FinalDest, Set[CtorId]] val ctorIdToFinalDest: Map[CtorId, FinalDest] = sol.resolveClashes._1 .map: case (c: Ctor, dtors -> noCons) => @@ -77,10 +82,16 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val distinctSels = sel.map(_.exprId).distinct // need to preserve order distinctSels.foreach: s => assert(preAnalyzer.selsToMatchingArms(s).exists(_._1 == dtorScrutExprId)) + val ctorCls = preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get + val whichArm = preAnalyzer.matchScrutToMatchBlock(dtorScrutExprId).arms + .flatMap: x => + x._1 match + case Case.Cls(cls, _) => if (cls is ctorCls) then S(cls) else N + case _ => die + .headOption FinalDest.Match( - dtorScrutExprId, - dtor.head.instantiationId.get, - preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get, + dtorScrutExprId -> dtor.head.instantiationId.get, + whichArm, distinctSels ) finalDestToCtorIds.updateWith(ctorFinalDest): @@ -89,15 +100,151 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. ctorExprId -> ctorFinalDest .toMap val rewritingMatchIds -> rewritingSelIds = finalDestToCtorIds.keySet.partitionMap: - case FinalDest.Match(scrutExprId = s, instantiationId = id, _) => L(s -> id) + case FinalDest.Match(matchId, _, _) => L(matchId) case FinalDest.Sel(s) => R(s) + + val fusingMatchIdToMatchRestFunSymbols = mutable.LinkedHashMap.empty[MatchId, BlockMemberSymbol] + locally: + // the order of traversal is deterministic because rewritingMatchIds is from + // the keySet of linkedHashMap + for + case (FinalDest.Match(matchId, _, _), _) <- finalDestToCtorIds + numOfMatchingArms = finalDestToCtorIds.keySet.count: + case FinalDest.Match(matId, _, _) => matId == matchId + case _ => false + if numOfMatchingArms > 1 + do + fusingMatchIdToMatchRestFunSymbols.updateWith(matchId): + case N => + val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme + S(BlockMemberSymbol( + s"match_${scrutName}_rest_${matchId._2.makeSuffix(preAnalyzer)}", + Nil)) + case S(x) => S(x) + + val finalDestToSymbolsToReplaceSelInArms = mutable.Map.empty[ + FinalDest, + Map[ResultId, (TempSymbol | VarSymbol)] -> Map[Tree.Ident, (TempSymbol | VarSymbol)]] + val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] + locally: + def updateSelReplacementMaps(matchArmDest: FinalDest.Match, useVarSym: Bool) = + val FinalDest.Match(_, cls, selsInArm) = matchArmDest + cls match + case N => () + case Some(cls) => + val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, (TempSymbol | VarSymbol)] + val selExprIdToNewSymbol = mutable.Map.empty[ResultId, (TempSymbol | VarSymbol)] + for selId <- selsInArm do + val selName = preAnalyzer.getResult(selId).asInstanceOf[Select].name + val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): + if useVarSym then VarSymbol(Tree.Ident(s"_deforest_${cls.nme}_${selName}")) + else TempSymbol(N, s"_deforest_${cls.nme}_${selName}") + selExprIdToNewSymbol += selId -> sym + finalDestToSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) + finalDestToCtorIds.foreach: + case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die + case (matchArmDest@FinalDest.Match(matchId, cls, _)) -> ctorIds => + val needArmSymAndVarSym = ctorIds.size > 1 + updateSelReplacementMaps(matchArmDest, needArmSymAndVarSym) + if needArmSymAndVarSym then + val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme + val armName = cls.fold("default")(_.nme) + val funSym = BlockMemberSymbol( + s"match_${scrutName}_arm_${armName}_${matchId._2.makeSuffix(preAnalyzer)}", + Nil) + finalDestToMatchArmFunSymbols += matchArmDest -> funSym + case _ => () + + val alwaysNonFreeVars = + sol.preAnalyzer.b.definedVars ++ + newSymToInstIdAndOldSym.keySet ++ + fusingMatchIdToMatchRestFunSymbols.values ++ + finalDestToMatchArmFunSymbols.values + + State.globalThisSymbol + + State.runtimeSymbol class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elaborator.State): - val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Block] = + val preAnalyzer = rewritePrepare.preAnalyzer + + object matchRestOfFusingMatches: + // from match scrut expr id to either a function def with a set of args that should be applied + val store = mutable.Map.empty[MatchId, Either[FunDefn -> Ls[Symbol], Block]] + def getAllFunDefs = + rewritePrepare.fusingMatchIdToMatchRestFunSymbols.foldRight(identity: Block => Block): + case matchId -> _ -> k => store(matchId) match + case Left(fdef -> _) => r => Define(fdef, k(r)) + case Right(b) => + assert(b.isInstanceOf[End]) + k + + // returns the block of match rest, or a `Return` block that calls the function extracted + // from the match rest + def getOrElseUpdate(matchId: MatchId): Block = + store.get(matchId) match + case S(R(blk)) => die // blk + case S(L(fdef -> args)) => + Return( + Call(Value.Ref(fdef.sym), args.map(a => Arg(false, Value.Ref(a))))(true, false), + false) + case N => + val scrutExprId -> instantiationId = matchId + val parentMatchesScrutExprIds -> thePossiblyFusingOne = preAnalyzer + .matchScrutToParentMatchScruts(scrutExprId) + .span: x => + !rewritePrepare.rewritingMatchIds.contains(x -> instantiationId) + val transform = new Transform(instantiationId) + val transformedCurrentMatchRest = + transform(preAnalyzer.matchScrutToMatchBlock(scrutExprId).rest) + val withAllParentMatchesRests = + parentMatchesScrutExprIds.foldLeft[Block](transformedCurrentMatchRest): (acc, p) => + val parentMatchRest = transform(preAnalyzer.matchScrutToMatchBlock(p).rest) + val isEnd = parentMatchRest.isInstanceOf[End] + if isEnd then acc else Begin(acc, parentMatchRest) + val withTheRestOfPossiblyFusingMatch = thePossiblyFusingOne.headOption + .fold(withAllParentMatchesRests): scrutExprIdOfTheFusingOne => + Begin(withAllParentMatchesRests, getOrElseUpdate(scrutExprIdOfTheFusingOne, instantiationId)) + .flattened + + val noNeedToBuild = + withTheRestOfPossiblyFusingMatch.isInstanceOf[End] || + rewritePrepare.fusingMatchIdToMatchRestFunSymbols.get(matchId).isEmpty + // no need to build a new function for empty rest, or if the rest is only going to be used once + if noNeedToBuild then + store.updateWith(matchId): + case S(_) => die + case N => S(R(withTheRestOfPossiblyFusingMatch)) + withTheRestOfPossiblyFusingMatch + else // build a new function for the rest of this fusing match + val sym = rewritePrepare.fusingMatchIdToMatchRestFunSymbols(matchId) + val freeVars = withTheRestOfPossiblyFusingMatch + .sortedFvsForTransformedBlocks(rewritePrepare.alwaysNonFreeVars) + val newSymbols = freeVars.map(s => VarSymbol(Tree.Ident(s.nme))) + val newFunDef = FunDefn(N, sym, newSymbols.asParamList :: Nil, + withTheRestOfPossiblyFusingMatch.replaceSymbols(freeVars.zip(newSymbols).toMap)) + store.updateWith(matchId): + case S(_) => die + case N => S(L(newFunDef -> freeVars)) + Return( + Call(Value.Ref(sym), freeVars.map(a => Arg(false, Value.Ref(a))))(true, false), + false) + + object matchArmsOfFusingMatches: + val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn -> Ls[Symbol], Block]] + + // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms + def getOrElseUpdate(dest: FinalDest.Match): Value.Lam = ??? + + + + val matchArmBodyFunDefns = mutable.Map.empty[FinalDest.Match, FunDefn] + val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Result] = rewritePrepare.finalDestToCtorIds.collect: - case (FinalDest.Match(scrutExprId, instantiationId, arm, selsInArm) -> ctorIds) => ??? + case matchArmDest@FinalDest.Match(matchId, arm, selsInArm) -> ctorIds => + if ctorIds.size < 1 then die + else if ctorIds.size == 1 then ??? + else ??? ??? @@ -108,7 +255,101 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // 2. transform fun body under the specific instantiation id // 3. accumulate the definition // ??? - class Transformer(instId: InstantiationId) extends BlockTransformer(new SymbolSubst): + // object transformers: + // val store = mutable.Map.empty[InstantiationId, Transform] + // def apply(instId: InstantiationId) = + // store.getOrElseUpdate(instId, new Transform(instId)) + + private val uselessSymbolSubst = new SymbolSubst + class Transform(instId: InstantiationId) extends BlockTransformer(uselessSymbolSubst): override def applyBlock(b: Block): Block = ??? override def applyResult2(r: Result)(k: Result => Block): Block = ??? - def apply(b: Block) = applyBlock(b) \ No newline at end of file + def apply(b: Block) = applyBlock(b) + + + +// Compute free vars for a block, without considering deforestation. +// Used on blocks after the deforestation transformation. +// This means that for matches we don't need to consider the extra +// free vars that may be introduced by deforestation: +// 1. the free vars from the `rest` of the their parent matches +// 2. the free vars caused by the substitution of selections of scrutinees of their parent matches +class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: + val ctx = mutable.Set.from(alwaysDefined) + val result = mutable.Set.empty[Symbol] + + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + applyPath(scrut) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + val realArm = Begin(a, rest) + applyBlock(realArm) + + case Assign(lhs, rhs, rest) => + applyResult(rhs) + ctx += lhs + applyBlock(rest) + ctx -= lhs + case Begin(sub, rest) => applyBlock(b.flattened) + case Define(defn, rest) => defn match + case FunDefn(owner, sym, params, body) => + val paramSymbols = params.flatMap: + case ParamList(_, params, restParam) => (params ++ restParam).map: + case Param(sym = sym, _) => sym + ctx += sym + ctx ++= paramSymbols + applyBlock(body) + ctx --= paramSymbols + applyBlock(rest) + ctx -= sym + case ValDefn(owner, k, sym, rhs) => + ctx += sym + applyPath(rhs) + applyBlock(rest) + ctx -= sym + case c: ClsLikeDefn => ??? // not supported + + case _ => super.applyBlock(b) + + override def applyValue(v: Value): Unit = v match + case Value.Ref(l) => l match + // builtin symbols and toplevel symbols are always in scope + case _: (BuiltinSymbol | TopLevelSymbol) => () + // NOTE: assume all class definitions are in the toplevel + case b: BlockMemberSymbol if b.asClsLike.isDefined => () + case _ => if !ctx.contains(l) then result += l + case _ => super.applyValue(v) + + override def applyLam(l: Value.Lam): Unit = + val paramSymbols = l.params.params.map(p => p.sym) + ctx ++= paramSymbols + applyBlock(l.body) + ctx --= paramSymbols + + def analyze(b: Block) = + applyBlock(b) + result.toList.sortBy(_.uid) + + +class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): + override def applyValue(v: Value): Value = v match + case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) + case _ => super.applyValue(v) + + +extension (b: Block) + def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = + new ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) + def sortedFvsForTransformedBlocks(alwaysDefined: Set[Symbol]) = + new FreeVarTraverser(alwaysDefined).analyze(b) + +extension (ps: Ls[VarSymbol]) + def asParamList = ParamList( + ParamListFlags.empty, + ps.map(s => Param(FldFlags.empty, s, N, Modulefulness.none)), + N) + + + From 8c424eb25faed57b66c110a26024115bfd46f627 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 13 Jun 2025 17:31:37 +0800 Subject: [PATCH 274/303] selections also have instantiation ids now --- .../hkmc2/codegen/deforest/Analyze.scala | 35 +++++++---- .../hkmc2/codegen/deforest/Rewrite.scala | 60 ++++++++++++------- .../mlscript/deforest/def-dup/new-simple.mls | 26 ++++++++ 3 files changed, 87 insertions(+), 34 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 13b28df566..b4050fa7cb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -12,6 +12,7 @@ import Result.ResultId final case class NotDeforestableException(msg: String) extends Exception(msg) type StratVarId = Uid[StratVarState] +type InstantiationId = Ls[ResultId] class StratVarState(val uid: StratVarId, val name: Str, val generatedForDef: Opt[BlockMemberSymbol]): lazy val asProdStrat = ProdVar(this) @@ -34,7 +35,7 @@ case class ProdFun(params: Ls[ConsStrat], res: ProdStrat) extends ProdStrat case object NoProd extends ProdStrat class Ctor( val exprId: ResultId, - val instantiationId: Opt[Ls[ResultId]], + val instantiationId: Opt[InstantiationId], val ctor: ClassLikeSymbol, val args: Ls[TermSymbol -> ProdStrat]) extends ProdStrat @@ -44,6 +45,7 @@ case class ConsFun(params: Ls[ProdStrat], res: ConsStrat) extends ConsStrat case object NoCons extends ConsStrat class FieldSel( val exprId: ResultId, + val instantiationId: Opt[InstantiationId], val field: Tree.Ident, val consVar: ConsVar) extends ConsStrat: val filter = mutable.Map.empty[ProdVar, Ls[ClassLikeSymbol]].withDefaultValue(Nil) @@ -52,7 +54,7 @@ class FieldSel( class Dtor( val scrutExprId: ResultId, - val instantiationId: Opt[Ls[ResultId]]) extends ConsStrat + val instantiationId: Opt[InstantiationId]) extends ConsStrat class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]): def instantiate(referSite: ResultId)(using d: DeforestConstraintsCollector, cc: d.ConstraintsAndCacheHitCollector): ProdVar = @@ -62,6 +64,8 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) case _ => die val instantiatingRecursiveGroup = d.funSymToProdStratScheme.recursiveGroups(instantiatingFunSym) val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] + def updateInstantiationId(instId: Opt[InstantiationId]) = + S(instId.fold(referSite :: Nil)(l => referSite :: l)) def duplicateVarState(s: StratVarState) = if s.generatedForDef.fold(false)(instantiatingRecursiveGroup.contains) then stratVarMap.getOrElseUpdate.curried(s): @@ -74,7 +78,7 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) case NoProd => NoProd case c: Ctor => new Ctor( c.exprId, - S(c.instantiationId.fold(referSite :: Nil)(l => referSite :: l)), + updateInstantiationId(c.instantiationId), c.ctor, c.args.map((a, b) => a -> duplicateProdStrat(b)) ) @@ -83,11 +87,15 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) case ConsFun(params, res) => ConsFun(params.map(duplicateProdStrat), duplicateConsStrat(res)) case NoCons => NoCons case fSel: FieldSel => - val res = new FieldSel(fSel.exprId, fSel.field, duplicateVarState(fSel.consVar.s).asConsStrat) + val res = new FieldSel( + fSel.exprId, + updateInstantiationId(fSel.instantiationId), + fSel.field, + duplicateVarState(fSel.consVar.s).asConsStrat) fSel.filter.foreach: (p, ls) => res.updateFilter(duplicateVarState(p.s).asProdStrat, ls) res - case dtor: Dtor => new Dtor(dtor.scrutExprId, S(dtor.instantiationId.fold(referSite :: Nil)(l => referSite :: l))) + case dtor: Dtor => new Dtor(dtor.scrutExprId, updateInstantiationId(dtor.instantiationId)) val newProd = duplicateVarState(s).asProdStrat constraints.foreach: (p, c) => cc.constrain(duplicateProdStrat(p), duplicateConsStrat(c)) @@ -103,7 +111,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] val matchScrutToParentMatchScruts = mutable.Map.empty[ResultId, Ls[ResultId]] val matchScrutInFunDef = mutable.Map.empty[ResultId, Opt[BlockMemberSymbol]] - val selsToMatchingArms = mutable.Map.empty[ResultId, Ls[ResultId -> Opt[ClassLikeSymbol]]] + val selsToMatchingArmsContainingIt = mutable.Map.empty[ResultId, Ls[ResultId -> Opt[ClassLikeSymbol]]] val symToStratVar = mutable.Map.empty[Symbol, ProdVar] val usedFunSyms = mutable.Set.empty[BlockMemberSymbol] lazy val definedFunSyms = funSymToFun.keySet @@ -157,7 +165,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: override def applyPath(p: Path): Unit = resultIdToResult += p.uid -> p p match - case s@Select(path, nme) => selsToMatchingArms += s.uid -> inMatchScrutsArms + case s@Select(path, nme) => selsToMatchingArmsContainingIt += s.uid -> inMatchScrutsArms case _ => () super.applyPath(p) @@ -325,7 +333,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case None => val pStrat = processResult(p) val tpeVar = freshVar("", generatedForDef) - cc.constrain(pStrat, new FieldSel(s.uid, nme, tpeVar.asConsStrat)) + cc.constrain(pStrat, new FieldSel(s.uid, instantiationId, nme, tpeVar.asConsStrat)) val appRes = freshVar("", generatedForDef) // unknown function symbol cc.constrain(tpeVar.asProdStrat, ConsFun(argsTpe, appRes.asConsStrat)) appRes.asProdStrat @@ -387,7 +395,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val pStrat = processResult(p) pStrat match case ProdVar(pStratVar) => - val inMatchingArm = preAnalyzer.selsToMatchingArms(sel.uid).flatMap: + val inMatchingArm = preAnalyzer.selsToMatchingArmsContainingIt(sel.uid).flatMap: case (scrutUid, S(inArm)) => preAnalyzer.matchScrutToMatchBlock(scrutUid).scrut match case Value.Ref(l) => @@ -395,14 +403,14 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case _ => N case _ => N val tpeVar = freshVar("sel_res", generatedForDef) - val selStrat = new FieldSel(sel.uid, nme, tpeVar.asConsStrat) + val selStrat = new FieldSel(sel.uid, instantiationId, nme, tpeVar.asConsStrat) inMatchingArm.foreach: (p, c) => selStrat.updateFilter(p, c :: Nil) cc.constrain(pStrat, selStrat) tpeVar.asProdStrat case _ => val tpeVar = freshVar("sel_res", generatedForDef) - cc.constrain(pStrat, new FieldSel(sel.uid, nme, tpeVar.asConsStrat)) + cc.constrain(pStrat, new FieldSel(sel.uid, instantiationId, nme, tpeVar.asConsStrat)) tpeVar.asProdStrat case v@Value.Ref(l) => l.asObj match @@ -539,12 +547,15 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): mats.size == 0 && sels.size == 1 || mats.size == 1 && locally: val matScrutExprId = mats.head.scrutExprId + val matExprInstantiationId = mats.head.instantiationId.get val matScrutSym = preAnalyzer.getResult(matScrutExprId).asInstanceOf[Value.Ref].l sels.forall: s => val selExprId = s.exprId preAnalyzer.getResult(selExprId) match case Select(Value.Ref(l), _) => - preAnalyzer.selsToMatchingArms(selExprId).exists(_._1 === matScrutExprId) && (l is matScrutSym) + preAnalyzer.selsToMatchingArmsContainingIt(selExprId).exists(_._1 === matScrutExprId) && + (l is matScrutSym) && + s.instantiationId.get == matExprInstantiationId case _ => false do removeCtor(rm) for diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 1c86ca1ca1..88150baadf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -11,24 +11,22 @@ import mlscript.utils.*, shorthands.* import scala.collection.mutable import Result.ResultId -type InstantiationId = Ls[ResultId] -extension (instId: InstantiationId) - def makeSuffix(preAnalyzer: DeforestPreAnalyzer) = - instId.map(preAnalyzer.getStableResultId).mkString("_") type CtorId = ResultId -> InstantiationId type MatchId = ResultId -> InstantiationId +type SelId = ResultId -> InstantiationId enum FinalDest: - case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol], val selsInArm: Ls[ResultId]) - case Sel(val s: ResultId) + case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol], val selsInArm: Ls[SelId]) + case Sel(val s: SelId) + class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator.State): val preAnalyzer = sol.preAnalyzer - val instIdToMappingFromOldToNewSyms = mutable.Map.empty[Ls[ResultId], Map[BlockMemberSymbol, BlockMemberSymbol]] + val instIdToMappingFromOldToNewSyms = mutable.Map.empty[InstantiationId, Map[BlockMemberSymbol, BlockMemberSymbol]] // need to be ordered because we need to traverse it to generate new fun def bodies - val newSymToInstIdAndOldSym = mutable.LinkedHashMap.empty[BlockMemberSymbol, Ls[ResultId] -> BlockMemberSymbol] + val newSymToInstIdAndOldSym = mutable.LinkedHashMap.empty[BlockMemberSymbol, InstantiationId -> BlockMemberSymbol] locally: for (ctor, _) <- sol.resolveClashes._1 @@ -48,8 +46,10 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. // TODO: can merge all ids that *only* have dtor to one id, but also need to consider all other // referred functions along the way for - case (dtor: Dtor, _) <- sol.resolveClashes._2 - instantiationId = dtor.instantiationId.get + (dtorOrSel, _) <- sol.resolveClashes._2 + instantiationId = dtorOrSel match + case f: FieldSel => f.instantiationId.get + case d: Dtor => d.instantiationId.get case instId@(invokedReferSite :: _) <- instantiationId.scanRight(Nil)(_ :: _) do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) @@ -75,22 +75,30 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val ctorFinalDest = if dtor.isEmpty then assert(sel.size == 1) - FinalDest.Sel(sel.head.exprId) + FinalDest.Sel(sel.head.exprId -> sel.head.instantiationId.get) else assert(dtor.size == 1) - val dtorScrutExprId = dtor.head.scrutExprId - val distinctSels = sel.map(_.exprId).distinct // need to preserve order - distinctSels.foreach: s => - assert(preAnalyzer.selsToMatchingArms(s).exists(_._1 == dtorScrutExprId)) + val dtorScrutExprId = dtor.head.scrutExprId -> dtor.head.instantiationId.get + val distinctSels = sel + .map: s => + assert(s.instantiationId.get == dtorScrutExprId._2) + s.exprId -> s.instantiationId.get + .distinct // need to preserve order + assert: + distinctSels.forall: s => + preAnalyzer.selsToMatchingArmsContainingIt(s._1).exists: armInfo => + armInfo._1 == dtorScrutExprId._1 && + armInfo._2.fold(true): clsSym => + clsSym is c.ctor val ctorCls = preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get - val whichArm = preAnalyzer.matchScrutToMatchBlock(dtorScrutExprId).arms + val whichArm = preAnalyzer.matchScrutToMatchBlock(dtorScrutExprId._1).arms .flatMap: x => x._1 match case Case.Cls(cls, _) => if (cls is ctorCls) then S(cls) else N case _ => die .headOption FinalDest.Match( - dtorScrutExprId -> dtor.head.instantiationId.get, + dtorScrutExprId, whichArm, distinctSels ) @@ -124,7 +132,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val finalDestToSymbolsToReplaceSelInArms = mutable.Map.empty[ FinalDest, - Map[ResultId, (TempSymbol | VarSymbol)] -> Map[Tree.Ident, (TempSymbol | VarSymbol)]] + Map[SelId, (TempSymbol | VarSymbol)] -> Map[Tree.Ident, (TempSymbol | VarSymbol)]] val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] locally: def updateSelReplacementMaps(matchArmDest: FinalDest.Match, useVarSym: Bool) = @@ -133,9 +141,9 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. case N => () case Some(cls) => val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, (TempSymbol | VarSymbol)] - val selExprIdToNewSymbol = mutable.Map.empty[ResultId, (TempSymbol | VarSymbol)] + val selExprIdToNewSymbol = mutable.Map.empty[SelId, (TempSymbol | VarSymbol)] for selId <- selsInArm do - val selName = preAnalyzer.getResult(selId).asInstanceOf[Select].name + val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): if useVarSym then VarSymbol(Tree.Ident(s"_deforest_${cls.nme}_${selName}")) else TempSymbol(N, s"_deforest_${cls.nme}_${selName}") @@ -162,6 +170,12 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. finalDestToMatchArmFunSymbols.values + State.globalThisSymbol + State.runtimeSymbol + + // val sel + + // object freeVarsOfMatchesConsideringDeforestation: + // val store = mutable.Map.empty[MatchId, Ls[Symbol]] + // val matchIdToSymbolsToReplacedInAll @@ -229,7 +243,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora Return( Call(Value.Ref(sym), freeVars.map(a => Arg(false, Value.Ref(a))))(true, false), false) - + object matchArmsOfFusingMatches: val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn -> Ls[Symbol], Block]] @@ -351,5 +365,7 @@ extension (ps: Ls[VarSymbol]) ps.map(s => Param(FldFlags.empty, s, N, Modulefulness.none)), N) - +extension (instId: InstantiationId) + def makeSuffix(preAnalyzer: DeforestPreAnalyzer) = + instId.map(preAnalyzer.getStableResultId).mkString("_") diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index 739a401194..f89d6fd76f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -234,3 +234,29 @@ localFuse(4) //│ Ref($scrut)@Some(List()) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// cyclic strategy, shouldn't fuse +:deforest +fun f(x) = + let res = if x is + A(x) then x + if res > 0 then f(A(res - 1)) else 0 +f(A(4)) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +// shouldn't have any fusion due to strategy clash +:deforest +fun g(x) = if x is + A(a) then a +fun f(x) = x.x +let o = A(1) +g(o) + f(o) + o.x +//│ = 3 +//│ o = A(1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From f8ecafcbe8b3ea05038054b37b1af82e08baceb3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 13 Jun 2025 21:55:15 +0800 Subject: [PATCH 275/303] free vars traversers --- .../hkmc2/codegen/deforest/Rewrite.scala | 97 ++++++++++++++++--- 1 file changed, 83 insertions(+), 14 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 88150baadf..6f627b1f01 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -22,6 +22,7 @@ enum FinalDest: class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator.State): + drwp => val preAnalyzer = sol.preAnalyzer val instIdToMappingFromOldToNewSyms = mutable.Map.empty[InstantiationId, Map[BlockMemberSymbol, BlockMemberSymbol]] @@ -133,10 +134,13 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val finalDestToSymbolsToReplaceSelInArms = mutable.Map.empty[ FinalDest, Map[SelId, (TempSymbol | VarSymbol)] -> Map[Tree.Ident, (TempSymbol | VarSymbol)]] + val fusingMatchIdToSymbolsToReplacedInAllBranches = mutable.Map.empty[ + MatchId, + Map[SelId, (TempSymbol | VarSymbol)]] val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] locally: def updateSelReplacementMaps(matchArmDest: FinalDest.Match, useVarSym: Bool) = - val FinalDest.Match(_, cls, selsInArm) = matchArmDest + val FinalDest.Match(matchId, cls, selsInArm) = matchArmDest cls match case N => () case Some(cls) => @@ -144,11 +148,14 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val selExprIdToNewSymbol = mutable.Map.empty[SelId, (TempSymbol | VarSymbol)] for selId <- selsInArm do val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name + val symName = s"_deforest_${cls.nme}_${selName}_${selId._2.makeSuffix(preAnalyzer)}" val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): - if useVarSym then VarSymbol(Tree.Ident(s"_deforest_${cls.nme}_${selName}")) - else TempSymbol(N, s"_deforest_${cls.nme}_${selName}") + if useVarSym then VarSymbol(Tree.Ident(symName)) else TempSymbol(N, symName) selExprIdToNewSymbol += selId -> sym finalDestToSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) + fusingMatchIdToSymbolsToReplacedInAllBranches.updateWith(matchId): + case N => S(selExprIdToNewSymbol.toMap) + case S(x) => S(x ++ selExprIdToNewSymbol.toMap) finalDestToCtorIds.foreach: case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die case (matchArmDest@FinalDest.Match(matchId, cls, _)) -> ctorIds => @@ -171,11 +178,13 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. State.globalThisSymbol + State.runtimeSymbol - // val sel + val selIdsToSymbolsToReplace = + finalDestToSymbolsToReplaceSelInArms.values.flatMap(_._1).toMap - // object freeVarsOfMatchesConsideringDeforestation: - // val store = mutable.Map.empty[MatchId, Ls[Symbol]] - // val matchIdToSymbolsToReplacedInAll + object freeVarsOfOriginalMatchesConsideringDeforestation: + val store = mutable.Map.empty[MatchId, Ls[Symbol]] + def apply(m: MatchId) = store.getOrElseUpdate.curried(m): + new FreeVarTraverserForMatchConsideringDeforestation(m, drwp).freeVars @@ -288,9 +297,9 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // free vars that may be introduced by deforestation: // 1. the free vars from the `rest` of the their parent matches // 2. the free vars caused by the substitution of selections of scrutinees of their parent matches -class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: - val ctx = mutable.Set.from(alwaysDefined) - val result = mutable.Set.empty[Symbol] +class FreeVarTraverser(val blk: Block, alwaysDefined: Set[Symbol]) extends BlockTraverser: + protected val ctx = mutable.Set.from(alwaysDefined) + protected val result = mutable.Set.empty[Symbol] override def applyBlock(b: Block): Unit = b match case Match(scrut, arms, dflt, rest) => @@ -342,9 +351,69 @@ class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: applyBlock(l.body) ctx --= paramSymbols - def analyze(b: Block) = - applyBlock(b) - result.toList.sortBy(_.uid) + protected def analyze: Unit = applyBlock(blk) + + analyze + + val freeVars = result.toList.sortBy(_.uid) + +// Used on match blocks before deforestation transformation +// Compute free vars considering new vars introduced by deforestation +class FreeVarTraverserForMatchConsideringDeforestation( + matchId: MatchId, + drwp: DeforestRewritePrepare +) extends FreeVarTraverser(drwp.preAnalyzer.matchScrutToMatchBlock(matchId._1), drwp.alwaysNonFreeVars): + val instantiationId = matchId._2 + val preAnalyzer = drwp.preAnalyzer + + val selsReplacementByCurrentMatch = + drwp.fusingMatchIdToSymbolsToReplacedInAllBranches(matchId) + val selsReplacementNotForThisMatch = + drwp.selIdsToSymbolsToReplace -- + selsReplacementByCurrentMatch.keySet + val currentMatchScrutSymbol = blk.asInstanceOf[Match].scrut.asInstanceOf[Value.Ref].l + + + override def applyBlock(b: Block): Unit = b match + // a nested match + case m@Match(scrut, _, _, _) => + result ++= drwp.freeVarsOfOriginalMatchesConsideringDeforestation(scrut.uid -> instantiationId) + + // sub-matches' scruts (which are not included in freeVarsOfNonTransformedMatches) + // are also free vars + val Value.Ref(l) = scrut: @unchecked + if !ctx(l) then result += l + + // free vars in nested-matches reported by freeVarsOfNonTransformedMatches may also contain + // spurious ones: those that are going to be substitued by the current match, + // and those that are in the ctx + result --= selsReplacementByCurrentMatch.values + result --= ctx + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Unit = p match + case p@Select(qual, name) => selsReplacementNotForThisMatch.get(p.uid -> instantiationId) match + case None => qual match + // if it is the scrut of current match and the computation containing + // this selection is moved, then the selection will be replaced and there will be no free vars + case Value.Ref(l) if l == currentMatchScrutSymbol => () + case _ => super.applyPath(p) + case Some(s) => result += s + case _ => super.applyPath(p) + + override def analyze: Unit = + val Match(_, arms, dflt, rest) = blk: @unchecked + val parentMatchRest = drwp.preAnalyzer + .matchScrutToParentMatchScruts(matchId._1) + .foldRight[Block](End("")): (p, acc) => + Begin(preAnalyzer.matchScrutToMatchBlock(p).rest, acc) + (arms.map(_._2) ++ dflt).foreach: a => + // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. + // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. + // Also take care of the `rest`s of its parent match blocks. + val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened + applyBlock(realArm) + class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): @@ -357,7 +426,7 @@ extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = new ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) def sortedFvsForTransformedBlocks(alwaysDefined: Set[Symbol]) = - new FreeVarTraverser(alwaysDefined).analyze(b) + new FreeVarTraverser(b, alwaysDefined).freeVars extension (ps: Ls[VarSymbol]) def asParamList = ParamList( From 7d78d05ccafad4d03cc371efdec5229f63b7fc48 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 13 Jun 2025 22:03:33 +0800 Subject: [PATCH 276/303] more lastWords instead of ??? --- .../src/main/scala/hkmc2/codegen/deforest/Analyze.scala | 4 ++-- .../src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index b4050fa7cb..efc905526f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -376,8 +376,8 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): cc.constrain(funTpe, ConsFun(argsTpe, appRes.asConsStrat)) appRes.asProdStrat case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") - case Value.Lit(lit) => ??? - case Value.Arr(elems) => ??? + case Value.Lit(lit) => lastWords(s"try to call literal $lit") + case Value.Arr(elems) => lastWords(s"try to call array $elems") r match case c@Call(f, args) => handleCallLike(f, args.map {case Arg(false, value) => value}, c) case i@Instantiate(cls, args) => handleCallLike(cls, args, i) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 6f627b1f01..ec12254c6e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -332,7 +332,7 @@ class FreeVarTraverser(val blk: Block, alwaysDefined: Set[Symbol]) extends Block applyPath(rhs) applyBlock(rest) ctx -= sym - case c: ClsLikeDefn => ??? // not supported + case c: ClsLikeDefn => die // not supported case _ => super.applyBlock(b) From 4702a5d0250545642472fd4d2340d5d5e3c085ae Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 14 Jun 2025 01:52:44 +0800 Subject: [PATCH 277/303] also precompute tmp vars for the fields of fusing ctors --- .../hkmc2/codegen/deforest/Rewrite.scala | 102 +++++++++++------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index ec12254c6e..c962003915 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -16,8 +16,14 @@ type CtorId = ResultId -> InstantiationId type MatchId = ResultId -> InstantiationId type SelId = ResultId -> InstantiationId +// TODO: maybe better design this to not use secondary param list for case class enum FinalDest: - case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol], val selsInArm: Ls[SelId]) + // A match arm is considered a final destination. + // `selsInArm` totally depends on `matchId` and `arm`, while + // `tmpSymbolForASpecificCtorId` depends on the ctor that reaches this destination. + // This field is in the second param list because we want maps from `FinalDest`s + // to only consider `matchId` and `arm` (i.e. the match arm). + case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol])(val selsInArm: Ls[SelId], val tmpSymbolForASpecificCtorId: Ls[TempSymbol]) case Sel(val s: SelId) @@ -98,18 +104,19 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. case Case.Cls(cls, _) => if (cls is ctorCls) then S(cls) else N case _ => die .headOption - FinalDest.Match( - dtorScrutExprId, - whichArm, - distinctSels - ) + val tmpSymbolsForFieldsOfCtor = ctorCls.asCls + .map: cls => + cls.tree.clsParams.map: fieldName => + TempSymbol(N, fieldName.name) + .getOrElse(Nil) + FinalDest.Match(dtorScrutExprId, whichArm)(distinctSels, tmpSymbolsForFieldsOfCtor) finalDestToCtorIds.updateWith(ctorFinalDest): case N => S(Set(ctorExprId)) case S(s) => S(s + ctorExprId) ctorExprId -> ctorFinalDest .toMap val rewritingMatchIds -> rewritingSelIds = finalDestToCtorIds.keySet.partitionMap: - case FinalDest.Match(matchId, _, _) => L(matchId) + case FinalDest.Match(matchId, _) => L(matchId) case FinalDest.Sel(s) => R(s) val fusingMatchIdToMatchRestFunSymbols = mutable.LinkedHashMap.empty[MatchId, BlockMemberSymbol] @@ -117,9 +124,9 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. // the order of traversal is deterministic because rewritingMatchIds is from // the keySet of linkedHashMap for - case (FinalDest.Match(matchId, _, _), _) <- finalDestToCtorIds + case (FinalDest.Match(matchId, _), _) <- finalDestToCtorIds numOfMatchingArms = finalDestToCtorIds.keySet.count: - case FinalDest.Match(matId, _, _) => matId == matchId + case FinalDest.Match(matId, _) => matId == matchId case _ => false if numOfMatchingArms > 1 do @@ -131,44 +138,40 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. Nil)) case S(x) => S(x) + // if a key doesn't exist, it means the final dest is only used once val finalDestToSymbolsToReplaceSelInArms = mutable.Map.empty[ FinalDest, - Map[SelId, (TempSymbol | VarSymbol)] -> Map[Tree.Ident, (TempSymbol | VarSymbol)]] + Map[SelId, VarSymbol] -> Map[Tree.Ident, VarSymbol]] + // if a key doesn't exist, it means the matchId doesn't have any arm that is used more than once val fusingMatchIdToSymbolsToReplacedInAllBranches = mutable.Map.empty[ MatchId, - Map[SelId, (TempSymbol | VarSymbol)]] + Map[SelId, VarSymbol]] + // if a key doesn't exist, it means the final dest is only used once val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] - locally: - def updateSelReplacementMaps(matchArmDest: FinalDest.Match, useVarSym: Bool) = - val FinalDest.Match(matchId, cls, selsInArm) = matchArmDest - cls match - case N => () - case Some(cls) => - val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, (TempSymbol | VarSymbol)] - val selExprIdToNewSymbol = mutable.Map.empty[SelId, (TempSymbol | VarSymbol)] - for selId <- selsInArm do + + finalDestToCtorIds.foreach: + case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die + case (matchArmDest@FinalDest.Match(matchId, cls)) -> ctorIds if ctorIds.size > 1 => + for cls <- cls do + val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, VarSymbol] + val selExprIdToNewSymbol = mutable.Map.empty[SelId, VarSymbol] + for selId <- matchArmDest.selsInArm do val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name val symName = s"_deforest_${cls.nme}_${selName}_${selId._2.makeSuffix(preAnalyzer)}" val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): - if useVarSym then VarSymbol(Tree.Ident(symName)) else TempSymbol(N, symName) + VarSymbol(Tree.Ident(symName)) selExprIdToNewSymbol += selId -> sym finalDestToSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) fusingMatchIdToSymbolsToReplacedInAllBranches.updateWith(matchId): case N => S(selExprIdToNewSymbol.toMap) case S(x) => S(x ++ selExprIdToNewSymbol.toMap) - finalDestToCtorIds.foreach: - case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die - case (matchArmDest@FinalDest.Match(matchId, cls, _)) -> ctorIds => - val needArmSymAndVarSym = ctorIds.size > 1 - updateSelReplacementMaps(matchArmDest, needArmSymAndVarSym) - if needArmSymAndVarSym then - val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme - val armName = cls.fold("default")(_.nme) - val funSym = BlockMemberSymbol( - s"match_${scrutName}_arm_${armName}_${matchId._2.makeSuffix(preAnalyzer)}", - Nil) - finalDestToMatchArmFunSymbols += matchArmDest -> funSym - case _ => () + val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme + val armName = cls.fold("default")(_.nme) + val funSym = BlockMemberSymbol( + s"match_${scrutName}_arm_${armName}_${matchId._2.makeSuffix(preAnalyzer)}", + Nil) + finalDestToMatchArmFunSymbols += matchArmDest -> funSym + case _ => () val alwaysNonFreeVars = sol.preAnalyzer.b.definedVars ++ @@ -256,15 +259,34 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora object matchArmsOfFusingMatches: val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn -> Ls[Symbol], Block]] - // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms - def getOrElseUpdate(dest: FinalDest.Match): Value.Lam = ??? + // return a lambda, which either calls the extracted arm function + // or contains the computations in matching arms + def getOrElseUpdate(dest: FinalDest.Match): Value.Lam = + val freeVarsAndTheirSyms = rewritePrepare + .freeVarsOfOriginalMatchesConsideringDeforestation(dest.matchId) + .map: x => + x -> VarSymbol(Tree.Ident(x.nme)) + val armFunOrBlk = store.updateWith(dest): + case S(R(_)) => die + case S(f@L(_)) => S(f) + case N => S(???) + armFunOrBlk.get match + case Left(fdefn -> syms) => + Value.Lam.apply.curried(freeVarsAndTheirSyms.unzip._2.asParamList): + Return( + Call(Value.Ref(fdefn.sym), freeVarsAndTheirSyms.unzip._2.asArgsList ::: Nil)(true, false), // FIXME: not nil! + false) + .asInstanceOf[Value.Lam] + // ??? + case Right(value) => ??? + val matchArmBodyFunDefns = mutable.Map.empty[FinalDest.Match, FunDefn] val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Result] = rewritePrepare.finalDestToCtorIds.collect: - case matchArmDest@FinalDest.Match(matchId, arm, selsInArm) -> ctorIds => + case matchArmDest@FinalDest.Match(matchId, arm) -> ctorIds => if ctorIds.size < 1 then die else if ctorIds.size == 1 then ??? else ??? @@ -367,7 +389,7 @@ class FreeVarTraverserForMatchConsideringDeforestation( val preAnalyzer = drwp.preAnalyzer val selsReplacementByCurrentMatch = - drwp.fusingMatchIdToSymbolsToReplacedInAllBranches(matchId) + drwp.fusingMatchIdToSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) val selsReplacementNotForThisMatch = drwp.selIdsToSymbolsToReplace -- selsReplacementByCurrentMatch.keySet @@ -434,6 +456,10 @@ extension (ps: Ls[VarSymbol]) ps.map(s => Param(FldFlags.empty, s, N, Modulefulness.none)), N) +extension (ss: Ls[Symbol]) + def asArgsList = ss.map: s => + Arg(false, Value.Ref(s)) + extension (instId: InstantiationId) def makeSuffix(preAnalyzer: DeforestPreAnalyzer) = instId.map(preAnalyzer.getStableResultId).mkString("_") From 341730c4175a965ea318bda96710514a73355a78 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 01:05:52 +0800 Subject: [PATCH 278/303] match arms and tests --- .../hkmc2/codegen/deforest/Rewrite.scala | 156 +++++++++++------- .../mlscript/deforest/def-dup/new-simple.mls | 48 ++++++ 2 files changed, 145 insertions(+), 59 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index c962003915..ccd493ffa4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -25,6 +25,10 @@ enum FinalDest: // to only consider `matchId` and `arm` (i.e. the match arm). case Match(val matchId: MatchId, val arm: Opt[ClassLikeSymbol])(val selsInArm: Ls[SelId], val tmpSymbolForASpecificCtorId: Ls[TempSymbol]) case Sel(val s: SelId) + def toString(pre: DeforestPreAnalyzer): String = this match + case Match(matchId, arm) => pre.resultIdToResult(matchId._1).toString() + "@" + matchId._2.makeSuffix(pre) + "@" + arm.fold("dflt")(_.nme) + case Sel(s) => pre.resultIdToResult(s._1).toString() + "@" + s._2.makeSuffix(pre) + class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator.State): @@ -71,50 +75,52 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. mapping.toMap val finalDestToCtorIds = mutable.LinkedHashMap.empty[FinalDest, Set[CtorId]] - val ctorIdToFinalDest: Map[CtorId, FinalDest] = sol.resolveClashes._1 - .map: - case (c: Ctor, dtors -> noCons) => - assert(!noCons) - val (dtor, sel) = dtors.partitionMap: - case d: Dtor => L(d) - case s: FieldSel => R(s) - val ctorExprId = c.exprId -> c.instantiationId.get - val ctorFinalDest = - if dtor.isEmpty then - assert(sel.size == 1) - FinalDest.Sel(sel.head.exprId -> sel.head.instantiationId.get) - else - assert(dtor.size == 1) - val dtorScrutExprId = dtor.head.scrutExprId -> dtor.head.instantiationId.get - val distinctSels = sel - .map: s => - assert(s.instantiationId.get == dtorScrutExprId._2) - s.exprId -> s.instantiationId.get - .distinct // need to preserve order - assert: - distinctSels.forall: s => - preAnalyzer.selsToMatchingArmsContainingIt(s._1).exists: armInfo => - armInfo._1 == dtorScrutExprId._1 && - armInfo._2.fold(true): clsSym => - clsSym is c.ctor - val ctorCls = preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get - val whichArm = preAnalyzer.matchScrutToMatchBlock(dtorScrutExprId._1).arms - .flatMap: x => - x._1 match - case Case.Cls(cls, _) => if (cls is ctorCls) then S(cls) else N - case _ => die - .headOption - val tmpSymbolsForFieldsOfCtor = ctorCls.asCls - .map: cls => - cls.tree.clsParams.map: fieldName => - TempSymbol(N, fieldName.name) - .getOrElse(Nil) - FinalDest.Match(dtorScrutExprId, whichArm)(distinctSels, tmpSymbolsForFieldsOfCtor) - finalDestToCtorIds.updateWith(ctorFinalDest): - case N => S(Set(ctorExprId)) - case S(s) => S(s + ctorExprId) - ctorExprId -> ctorFinalDest - .toMap + val ctorIdToFinalDest = mutable.LinkedHashMap.empty[CtorId, FinalDest] + sol.resolveClashes._1.foreach: + case (c: Ctor, dtors -> noCons) => + assert(!noCons) + val (dtor, sel) = dtors.partitionMap: + case d: Dtor => L(d) + case s: FieldSel => R(s) + val ctorExprId = c.exprId -> c.instantiationId.get + val ctorFinalDest = + if dtor.isEmpty then + assert(sel.size == 1) + FinalDest.Sel(sel.head.exprId -> sel.head.instantiationId.get) + else + assert(dtor.size == 1) + val dtorScrutExprId = dtor.head.scrutExprId -> dtor.head.instantiationId.get + val distinctSels = sel + .map: s => + assert(s.instantiationId.get == dtorScrutExprId._2) + s.exprId -> s.instantiationId.get + .distinct // need to preserve order + assert: + distinctSels.forall: s => + preAnalyzer.selsToMatchingArmsContainingIt(s._1).exists: armInfo => + armInfo._1 == dtorScrutExprId._1 && + armInfo._2.fold(true): clsSym => + clsSym is c.ctor + val ctorCls = preAnalyzer.getCtorSymFromCtorLikeExprId(c.exprId).get + val whichArm = preAnalyzer.matchScrutToMatchBlock(dtorScrutExprId._1).arms + .flatMap: x => + x._1 match + case Case.Cls(cls, _) => if (cls is ctorCls) then S(cls) else N + case _ => die + .headOption + val tmpSymbolsForFieldsOfCtor = ctorCls.asCls + .map: cls => + cls.tree.clsParams.map: fieldName => + TempSymbol(N, fieldName.name) + .getOrElse(Nil) + FinalDest.Match(dtorScrutExprId, whichArm)(distinctSels, tmpSymbolsForFieldsOfCtor) + finalDestToCtorIds.updateWith(ctorFinalDest): + case N => S(Set(ctorExprId)) + case S(s) => S(s + ctorExprId) + ctorIdToFinalDest.updateWith(ctorExprId): + case N => S(ctorFinalDest) + case S(_) => die + val rewritingMatchIds -> rewritingSelIds = finalDestToCtorIds.keySet.partitionMap: case FinalDest.Match(matchId, _) => L(matchId) case FinalDest.Sel(s) => R(s) @@ -257,28 +263,60 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora false) object matchArmsOfFusingMatches: - val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn -> Ls[Symbol], Block]] + val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn, Block]] // return a lambda, which either calls the extracted arm function // or contains the computations in matching arms - def getOrElseUpdate(dest: FinalDest.Match): Value.Lam = - val freeVarsAndTheirSyms = rewritePrepare - .freeVarsOfOriginalMatchesConsideringDeforestation(dest.matchId) - .map: x => - x -> VarSymbol(Tree.Ident(x.nme)) + def getOrElseUpdate(ctorId: CtorId): Value.Lam = + val dest = rewritePrepare.ctorIdToFinalDest(ctorId).asInstanceOf[FinalDest.Match] + val freeVarsInTheMatch = + rewritePrepare.freeVarsOfOriginalMatchesConsideringDeforestation(dest.matchId) val armFunOrBlk = store.updateWith(dest): case S(R(_)) => die case S(f@L(_)) => S(f) - case N => S(???) + case N => S: + val transformedRest = matchRestOfFusingMatches.getOrElseUpdate(dest.matchId) + val originalMatchArmBody = + val matchExpr = preAnalyzer.matchScrutToMatchBlock(dest.matchId._1) + dest.arm.fold(matchExpr.dflt.get): armCls => + matchExpr.arms.find(a => a._1.asInstanceOf[Case.Cls].cls is armCls).get._2 + // this rewrittenBody here already has its selection replaced with + // the pre-computed var symbols (if the arm is used multiple times) + // or the temp symbols (if the arm is used only once) + val rewrittenBody = Begin(Transform(dest.matchId._2)(originalMatchArmBody), transformedRest).flattened + rewritePrepare.finalDestToMatchArmFunSymbols.get(dest) match + case N => R(rewrittenBody) + case Some(funSym) => + val freeVarSymForFunDef = freeVarsInTheMatch.map: x => + VarSymbol(Tree.Ident(x.nme)) + val funBody = rewrittenBody.replaceSymbols(freeVarsInTheMatch.zip(freeVarSymForFunDef).toMap).mapTail: + case Return(res, implct) => Return(res, false) + case t => t + val varSymbolsThatReplacedSelections = + // the order is the same as class ctor param + preAnalyzer.getCtorSymFromCtorLikeExprId(ctorId._1).get.asCls.fold(Nil): c => + c.tree.clsParams.map: p => + rewritePrepare.finalDestToSymbolsToReplaceSelInArms(dest)._2(Tree.Ident(p.name)) + L(FunDefn( + N, + funSym, + (freeVarSymForFunDef ::: varSymbolsThatReplacedSelections).asParamList :: Nil, + funBody)) + val symsForArmFreeVarsInLam = freeVarsInTheMatch.map: x => + VarSymbol(Tree.Ident(x.nme)) armFunOrBlk.get match - case Left(fdefn -> syms) => - Value.Lam.apply.curried(freeVarsAndTheirSyms.unzip._2.asParamList): - Return( - Call(Value.Ref(fdefn.sym), freeVarsAndTheirSyms.unzip._2.asArgsList ::: Nil)(true, false), // FIXME: not nil! - false) - .asInstanceOf[Value.Lam] - // ??? - case Right(value) => ??? + case L(fdefn) => Value.Lam( + symsForArmFreeVarsInLam.asParamList, + Return( + Call( + Value.Ref(fdefn.sym), + symsForArmFreeVarsInLam.asArgsList ::: dest.tmpSymbolForASpecificCtorId.asArgsList)(true, false), + false)) + case R(b) => Value.Lam( + symsForArmFreeVarsInLam.asParamList, + b.replaceSymbols(freeVarsInTheMatch.zip(symsForArmFreeVarsInLam).toMap).mapTail: + case Return(res, implct) => Return(res, false) + case t => t) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index f89d6fd76f..66a1f69d5a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -30,6 +30,13 @@ f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ Ref(ls1)@Some(List(8)) //│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> //│ Ref(ls1)@Some(List(8)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@ --> Ref(ls1)@1@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@3 --> Ref(ls1)@1@Cons +//│ Ref(member:Nil)@4@ --> Ref(ls2)@5@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@6 --> Ref(ls2)@5@Cons +//│ Ref(member:Nil)@7@ --> Ref(ls1)@8@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@9 --> Ref(ls1)@8@Cons //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -44,6 +51,9 @@ f1(id(A(1))) + f2(id(A(2))) //│ Ref(a2)@Some(List(1)) //│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@Some(List()) --> //│ Ref(a1)@Some(List(3)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@ --> Ref(a2)@1@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@ --> Ref(a1)@3@A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -60,6 +70,9 @@ f1(wrap(1)) + f2(wrap(2)) //│ Ref(a2)@Some(List(4)) //│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> //│ Ref(a1)@Some(List(6)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@1_2_3 --> Ref(a2)@4@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@5_2_3 --> Ref(a1)@6@A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -91,6 +104,15 @@ cons(f(0)) + cons1(f(1)) //│ Ref(a)@Some(List(7)) //│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(6)) --> //│ Ref(a)@Some(List(7)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@1 --> Ref(a1)@2@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@1 --> Ref(a1)@2@Cons +//│ Ref(member:Nil)@4@1 --> Ref(a1)@2@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@1 --> Ref(a1)@2@Cons +//│ Ref(member:Nil)@0@6 --> Ref(a)@7@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@6 --> Ref(a)@7@Cons +//│ Ref(member:Nil)@4@6 --> Ref(a)@7@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@6 --> Ref(a)@7@Cons //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -111,6 +133,12 @@ map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) //│ Ref(ls)@Some(List(1)) //│ Ref(member:Nil)@6@Some(List()) --> //│ Ref(ls)@Some(List(1)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@1 --> Ref(ls)@2@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@1 --> Ref(ls)@2@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@ --> Ref(ls)@1@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@ --> Ref(ls)@1@Cons +//│ Ref(member:Nil)@6@ --> Ref(ls)@1@Nil //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -128,6 +156,10 @@ map(1 :: 2 :: Nil, x => x + 1) //│ Ref(ls)@Some(List(1)) //│ Ref(member:Nil)@3@Some(List()) --> //│ Ref(ls)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@ --> Ref(ls)@1@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@ --> Ref(ls)@1@Cons +//│ Ref(member:Nil)@3@ --> Ref(ls)@1@Nil //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -139,6 +171,7 @@ fun f(x) = if x is f(Nil) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -155,6 +188,9 @@ c1(1 :: Nil) //│ Ref(x)@Some(List(1)) //│ Ref(member:Nil)@2@Some(List()) --> //│ Ref(t)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@ --> Ref(x)@1@Cons +//│ Ref(member:Nil)@2@ --> Ref(t)@1@Nil //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -177,6 +213,9 @@ apply(f) //│ Ref(x)@Some(List(2)) //│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@Some(List(1)) --> //│ Ref(y1)@Some(List(2, 4)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@1 --> Ref(x)@2@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@1 --> Ref(y1)@2_4@A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -215,6 +254,9 @@ dtor(A(B(0))) //│ Ref(y)@Some(List(1, 2)) //│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@Some(List()) --> //│ Ref(x)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@1 --> Ref(y)@1_2@B +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@ --> Ref(x)@1@A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -232,6 +274,10 @@ localFuse(4) //│ Ref($scrut)@Some(List(2)) //│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@Some(List(2)) --> //│ Ref($scrut)@Some(List()) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@1 --> Ref($scrut)@1@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@2 --> Ref($scrut)@2@A +//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@2 --> Ref($scrut)@@B //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -245,6 +291,7 @@ fun f(x) = f(A(4)) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -259,4 +306,5 @@ g(o) + f(o) + o.x //│ = 3 //│ o = A(1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From f15be70f346cf79b25f9a5544df673d0794b6af4 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 01:06:40 +0800 Subject: [PATCH 279/303] transform --- .../hkmc2/codegen/deforest/Rewrite.scala | 164 ++++++++++++++++-- 1 file changed, 147 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index ccd493ffa4..1cd5393d9d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -125,6 +125,8 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. case FinalDest.Match(matchId, _) => L(matchId) case FinalDest.Sel(s) => R(s) + // not all symbols are used because some of the match rests are just `End`s, so no need to + // create a new function for them. val fusingMatchIdToMatchRestFunSymbols = mutable.LinkedHashMap.empty[MatchId, BlockMemberSymbol] locally: // the order of traversal is deterministic because rewritingMatchIds is from @@ -154,7 +156,6 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. Map[SelId, VarSymbol]] // if a key doesn't exist, it means the final dest is only used once val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] - finalDestToCtorIds.foreach: case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die case (matchArmDest@FinalDest.Match(matchId, cls)) -> ctorIds if ctorIds.size > 1 => @@ -187,8 +188,18 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. State.globalThisSymbol + State.runtimeSymbol - val selIdsToSymbolsToReplace = - finalDestToSymbolsToReplaceSelInArms.values.flatMap(_._1).toMap + val selIdsInArmToSymbolsToReplace = mutable.Map.empty[SelId, TempSymbol | VarSymbol] + locally: + for case (c, mat: FinalDest.Match) <- ctorIdToFinalDest do + fusingMatchIdToSymbolsToReplacedInAllBranches.get(mat.matchId) match + case Some(v) => selIdsInArmToSymbolsToReplace.addAll(v) + case N => selIdsInArmToSymbolsToReplace.addAll: + mat.selsInArm.map: selId => + val ctorFieldNames = + preAnalyzer.getCtorSymFromCtorLikeExprId(c._1).flatMap(_.asCls).get.tree.clsParams + val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name + val idx = ctorFieldNames.indexWhere(_.id == selName) + selId -> mat.tmpSymbolForASpecificCtorId(idx) object freeVarsOfOriginalMatchesConsideringDeforestation: val store = mutable.Map.empty[MatchId, Ls[Symbol]] @@ -296,7 +307,9 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // the order is the same as class ctor param preAnalyzer.getCtorSymFromCtorLikeExprId(ctorId._1).get.asCls.fold(Nil): c => c.tree.clsParams.map: p => - rewritePrepare.finalDestToSymbolsToReplaceSelInArms(dest)._2(Tree.Ident(p.name)) + rewritePrepare + .finalDestToSymbolsToReplaceSelInArms(dest)._2 + .getOrElse(Tree.Ident(p.name), VarSymbol(Tree.Ident(s"_unused_${p.name}"))) L(FunDefn( N, funSym, @@ -321,14 +334,14 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora - val matchArmBodyFunDefns = mutable.Map.empty[FinalDest.Match, FunDefn] - val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Result] = - rewritePrepare.finalDestToCtorIds.collect: - case matchArmDest@FinalDest.Match(matchId, arm) -> ctorIds => - if ctorIds.size < 1 then die - else if ctorIds.size == 1 then ??? - else ??? - ??? + // val matchArmBodyFunDefns = mutable.Map.empty[FinalDest.Match, FunDefn] + // val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Result] = + // rewritePrepare.finalDestToCtorIds.collect: + // case matchArmDest@FinalDest.Match(matchId, arm) -> ctorIds => + // if ctorIds.size < 1 then die + // else if ctorIds.size == 1 then ??? + // else ??? + // ??? @@ -344,9 +357,100 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // store.getOrElseUpdate(instId, new Transform(instId)) private val uselessSymbolSubst = new SymbolSubst + // rewrite ctor and dtors + // replace selections in match arms + // replace refer site symbols to their new symbols class Transform(instId: InstantiationId) extends BlockTransformer(uselessSymbolSubst): - override def applyBlock(b: Block): Block = ??? - override def applyResult2(r: Result)(k: Result => Block): Block = ??? + extension (resId: ResultId) + def withInstId = resId -> instId + override def applyBlock(b: Block): Block = b match + case mat@Match(scrut, arms, dflt, rest) => + if rewritePrepare.rewritingMatchIds.contains(scrut.uid.withInstId) then + // since all fusing matches will be considered to be in the tail position, + // if any of the parent `rest`s has explicit return, the rewritten match will have explicit return + val oneOfParentMatchRestHasExplicitRet = + preAnalyzer.matchScrutToParentMatchScruts(scrut.uid).foldRight(false): (pid, acc) => + acc || preAnalyzer.matchScrutToMatchBlock(pid).rest.hasExplicitRet + val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet + val freeVars = rewritePrepare.freeVarsOfOriginalMatchesConsideringDeforestation(scrut.uid.withInstId) + Return(Call(scrut, freeVars.asArgsList)(false, false), !needExplicitRet) + else + val allArmWillBeNonEnd = + dflt.fold(false)(_.willBeNonEndTailBlock(instId, rewritePrepare)) && + arms.forall: + case (_, body) => body.willBeNonEndTailBlock(instId, rewritePrepare) + if allArmWillBeNonEnd then + super.applyBlock(Match(scrut, arms, dflt, End(""))) + else + super.applyBlock(b) + case _ => super.applyBlock(b) + + override def applyResult(r: Result): Result = r match + case _: Call => + // calls to fusing contructors are handled in `applyResult2` + // here we only handle calls to non-fusing constructors and functions + assert(!rewritePrepare.ctorIdToFinalDest.isDefinedAt(r.uid.withInstId)) + super.applyResult(r) + case _ => super.applyResult(r) + + override def applyResult2(r: Result)(k: Result => Block): Block = + def handleCallLike(f: Path, ctorResId: ResultId)(args: Ls[Path]) = + val c = preAnalyzer.getCtorSymFromCtorLikeExprId(ctorResId).get.asCls.get + rewritePrepare.ctorIdToFinalDest(ctorResId.withInstId) match + case matchDest@FinalDest.Match(matchId, whichArm) => + // use pre-determined symbols + val tempSymbolsForFields = matchDest.tmpSymbolForASpecificCtorId + tempSymbolsForFields + .zip(args) + .foldRight(k(matchArmsOfFusingMatches.getOrElseUpdate(ctorResId.withInstId))): + case (tmpSym, arg) -> rest => + applyResult2(arg): r => + Assign(tmpSym, r, rest) + case FinalDest.Sel(s) => + val selFieldName = preAnalyzer.getResult(s._1).asInstanceOf[Select].name + val idx = c.tree.clsParams.indexWhere(_.id == selFieldName) + k(args(idx)) + r match + case call@Call(f, args) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(call.uid.withInstId) => + handleCallLike(f, call.uid): + args.map: + case Arg(false, value) => value + case ins@Instantiate(cls, args) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(ins.uid.withInstId) => + handleCallLike(cls, ins.uid)(args) + case _ => super.applyResult2(r)(k) + + override def applyPath(p: Path): Path = p match + // a selection which is a consumer on its own + case s: Select if rewritePrepare.rewritingSelIds(s.uid.withInstId) => applyPath(p) + // a selection inside a fusing match that needs to be replaced by pre-computed symbols + case s: Select if rewritePrepare.selIdsInArmToSymbolsToReplace.get(s.uid.withInstId).isDefined => + Value.Ref(rewritePrepare.selIdsInArmToSymbolsToReplace(s.uid.withInstId)) + case s@Select(p, nme) => s.symbol.flatMap(_.asObj) match + // a fusing object constructor + case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(s.uid.withInstId) => + matchArmsOfFusingMatches.getOrElseUpdate(s.uid.withInstId) + case _ => s.symbol.flatMap(_.asBlkMember) match + case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => + val newInstId = s.uid :: instId + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => + Value.Ref(m(blk)) + case _ => super.applyPath(s) + case v: Value => applyValue(v) + case _ => super.applyPath(p) + + override def applyValue(v: Value): Value = v match + case r@Value.Ref(l) => l.asObj match + case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(r.uid.withInstId) => + matchArmsOfFusingMatches.getOrElseUpdate(r.uid.withInstId) + case None => l.asBlkMember match + case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => + val newInstId = r.uid :: instId + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => + Value.Ref(m(blk)) + case _ => super.applyValue(v) + case _ => super.applyValue(v) + case _ => super.applyValue(v) + def apply(b: Block) = applyBlock(b) @@ -429,7 +533,7 @@ class FreeVarTraverserForMatchConsideringDeforestation( val selsReplacementByCurrentMatch = drwp.fusingMatchIdToSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) val selsReplacementNotForThisMatch = - drwp.selIdsToSymbolsToReplace -- + drwp.selIdsInArmToSymbolsToReplace.toMap -- selsReplacementByCurrentMatch.keySet val currentMatchScrutSymbol = blk.asInstanceOf[Match].scrut.asInstanceOf[Value.Ref].l @@ -474,19 +578,45 @@ class FreeVarTraverserForMatchConsideringDeforestation( val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened applyBlock(realArm) - - class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): override def applyValue(v: Value): Value = v match case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) case _ => super.applyValue(v) +class HasExplicitRetTraverser(b: Block) extends BlockTraverserShallow: + var result = false + override def applyBlock(b: Block): Unit = b match + case Return(_, imp) => result = !imp + case _ => super.applyBlock(b) + + applyBlock(b) + +class WillBeNonEndTailBlockTraverser(b: Block, instId: InstantiationId, drwp: DeforestRewritePrepare) extends BlockTraverserShallow: + var result = false + override def applyBlock(b: Block): Unit = b match + case Match(scrut, arms, dflt, rest) => + result = + drwp.rewritingMatchIds.contains(scrut.uid -> instId) || + rest.willBeNonEndTailBlock(instId, drwp) || + locally: + dflt.fold(true)(_.willBeNonEndTailBlock(instId, drwp)) && + arms.forall: + case (_, b) => b.willBeNonEndTailBlock(instId, drwp) + case _: End => () + case _: BlockTail => result = true + case _ => super.applyBlock(b) + + applyBlock(b) extension (b: Block) def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = new ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) def sortedFvsForTransformedBlocks(alwaysDefined: Set[Symbol]) = new FreeVarTraverser(b, alwaysDefined).freeVars + def hasExplicitRet = + new HasExplicitRetTraverser(b).result + def willBeNonEndTailBlock(instId: InstantiationId, drwp: DeforestRewritePrepare) = + new WillBeNonEndTailBlockTraverser(b, instId, drwp).result extension (ps: Ls[VarSymbol]) def asParamList = ParamList( From 96ba053e63c4d4184ec17ba256c98b1f6b03574a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 02:18:39 +0800 Subject: [PATCH 280/303] make new functions --- .../hkmc2/codegen/deforest/Rewrite.scala | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 1cd5393d9d..35d6c9f269 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -211,6 +211,28 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elaborator.State): val preAnalyzer = rewritePrepare.preAnalyzer + def apply() = + rewritePrepare.newSymToInstIdAndOldSym.foldRight(Transform(Nil)(preAnalyzer.b)): + case (newSym -> (instId -> oldSym), acc) => + // 1. find original fundefs + // 2. transform fun body under the specific instantiation id + // 3. accumulate the definition + val FunDefn(_, _, param, body) = preAnalyzer.getFunDefnForSym(oldSym).get + val oldToNewParam = mutable.Map.empty[VarSymbol, VarSymbol] + val newParam = param.map: + case ParamList(flags, params, restParam) => + def makeNewParam(p: Param) = + val Param(flags, sym, sign, modulefulness) = p + val newSym = VarSymbol(sym.id) + oldToNewParam += sym -> newSym + Param(flags, newSym, sign, modulefulness) + val newParams = params.map(makeNewParam) + val newRestParam = restParam.map(makeNewParam) + ParamList(flags, newParams, newRestParam) + val newBody = Transform(instId)(body).replaceSymbols(oldToNewParam.toMap) + val newFunDefn = FunDefn(N, newSym, newParam, newBody) + Define(newFunDefn, acc) + object matchRestOfFusingMatches: // from match scrut expr id to either a function def with a set of args that should be applied val store = mutable.Map.empty[MatchId, Either[FunDefn -> Ls[Symbol], Block]] @@ -330,31 +352,6 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora b.replaceSymbols(freeVarsInTheMatch.zip(symsForArmFreeVarsInLam).toMap).mapTail: case Return(res, implct) => Return(res, false) case t => t) - - - - - // val matchArmBodyFunDefns = mutable.Map.empty[FinalDest.Match, FunDefn] - // val finalDestToMatchArmBody: Map[FinalDest.Match, Ls[TempSymbol] => Result] = - // rewritePrepare.finalDestToCtorIds.collect: - // case matchArmDest@FinalDest.Match(matchId, arm) -> ctorIds => - // if ctorIds.size < 1 then die - // else if ctorIds.size == 1 then ??? - // else ??? - // ??? - - - - // val duplicatedFuns = newSymToInstIdAndOldSym.foldRight(sol.preAnalyzer.b): - // case (newSym -> instId, acc) => - // 1. find original fundefs - // 2. transform fun body under the specific instantiation id - // 3. accumulate the definition - // ??? - // object transformers: - // val store = mutable.Map.empty[InstantiationId, Transform] - // def apply(instId: InstantiationId) = - // store.getOrElseUpdate(instId, new Transform(instId)) private val uselessSymbolSubst = new SymbolSubst // rewrite ctor and dtors @@ -451,6 +448,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case _ => super.applyValue(v) case _ => super.applyValue(v) + override def applyFunDefn(fun: FunDefn): FunDefn = fun def apply(b: Block) = applyBlock(b) From 26e8ecbb8c608dcb2765164fbecbd74ed372e05f Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 16:27:43 +0800 Subject: [PATCH 281/303] some fixes to free vars --- .../hkmc2/codegen/deforest/Rewrite.scala | 73 ++++++++++++------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 35d6c9f269..237b7d4546 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -133,7 +133,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. // the keySet of linkedHashMap for case (FinalDest.Match(matchId, _), _) <- finalDestToCtorIds - numOfMatchingArms = finalDestToCtorIds.keySet.count: + numOfMatchingArms = finalDestToCtorIds.keys.count: case FinalDest.Match(matId, _) => matId == matchId case _ => false if numOfMatchingArms > 1 @@ -151,7 +151,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. FinalDest, Map[SelId, VarSymbol] -> Map[Tree.Ident, VarSymbol]] // if a key doesn't exist, it means the matchId doesn't have any arm that is used more than once - val fusingMatchIdToSymbolsToReplacedInAllBranches = mutable.Map.empty[ + val fusingMatchIdToVarSymbolsToReplacedInAllBranches = mutable.Map.empty[ MatchId, Map[SelId, VarSymbol]] // if a key doesn't exist, it means the final dest is only used once @@ -169,7 +169,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. VarSymbol(Tree.Ident(symName)) selExprIdToNewSymbol += selId -> sym finalDestToSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) - fusingMatchIdToSymbolsToReplacedInAllBranches.updateWith(matchId): + fusingMatchIdToVarSymbolsToReplacedInAllBranches.updateWith(matchId): case N => S(selExprIdToNewSymbol.toMap) case S(x) => S(x ++ selExprIdToNewSymbol.toMap) val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme @@ -188,18 +188,30 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. State.globalThisSymbol + State.runtimeSymbol + // if a key doesn't exist, it means the all the arms of this matchId are used more than once + val fusingMatchIdToTmpSymbolsToReplacedInAllBranches = mutable.Map.empty[ + MatchId, + Map[SelId, TempSymbol]] val selIdsInArmToSymbolsToReplace = mutable.Map.empty[SelId, TempSymbol | VarSymbol] locally: + def matDestToTempSymbolMap(mat: FinalDest.Match) = + mat.arm.fold(Nil): c => + mat.selsInArm.map: selId => + val ctorFieldNames = c.asCls.get.tree.clsParams + val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name + val idx = ctorFieldNames.indexWhere(_.id == selName) + selId -> mat.tmpSymbolForASpecificCtorId(idx) for case (c, mat: FinalDest.Match) <- ctorIdToFinalDest do - fusingMatchIdToSymbolsToReplacedInAllBranches.get(mat.matchId) match - case Some(v) => selIdsInArmToSymbolsToReplace.addAll(v) - case N => selIdsInArmToSymbolsToReplace.addAll: - mat.selsInArm.map: selId => - val ctorFieldNames = - preAnalyzer.getCtorSymFromCtorLikeExprId(c._1).flatMap(_.asCls).get.tree.clsParams - val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name - val idx = ctorFieldNames.indexWhere(_.id == selName) - selId -> mat.tmpSymbolForASpecificCtorId(idx) + if finalDestToCtorIds(mat).size == 1 then + val selsToTmpSyms = matDestToTempSymbolMap(mat) + fusingMatchIdToTmpSymbolsToReplacedInAllBranches.updateWith(mat.matchId): + case N => S(selsToTmpSyms.toMap) + case S(m) => S(m ++ selsToTmpSyms) + selIdsInArmToSymbolsToReplace.addAll(selsToTmpSyms) + else + val selsToVarSyms = finalDestToSymbolsToReplaceSelInArms(mat)._1 + selIdsInArmToSymbolsToReplace.addAll(selsToVarSyms) + object freeVarsOfOriginalMatchesConsideringDeforestation: val store = mutable.Map.empty[MatchId, Ls[Symbol]] @@ -212,7 +224,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora val preAnalyzer = rewritePrepare.preAnalyzer def apply() = - rewritePrepare.newSymToInstIdAndOldSym.foldRight(Transform(Nil)(preAnalyzer.b)): + val withDuplicatedDefs = rewritePrepare.newSymToInstIdAndOldSym.foldRight(Transform(Nil)(preAnalyzer.b)): case (newSym -> (instId -> oldSym), acc) => // 1. find original fundefs // 2. transform fun body under the specific instantiation id @@ -232,6 +244,9 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora val newBody = Transform(instId)(body).replaceSymbols(oldToNewParam.toMap) val newFunDefn = FunDefn(N, newSym, newParam, newBody) Define(newFunDefn, acc) + matchRestOfFusingMatches.getAllFunDefs: + matchArmsOfFusingMatches.getAllFunDefs: + withDuplicatedDefs object matchRestOfFusingMatches: // from match scrut expr id to either a function def with a set of args that should be applied @@ -248,7 +263,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // from the match rest def getOrElseUpdate(matchId: MatchId): Block = store.get(matchId) match - case S(R(blk)) => die // blk + case S(R(blk)) => if blk.isInstanceOf[End] then blk else die case S(L(fdef -> args)) => Return( Call(Value.Ref(fdef.sym), args.map(a => Arg(false, Value.Ref(a))))(true, false), @@ -297,7 +312,14 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora object matchArmsOfFusingMatches: val store = mutable.Map.empty[FinalDest.Match, Either[FunDefn, Block]] - + def getAllFunDefs = + rewritePrepare.finalDestToCtorIds.keys.foldRight(identity: Block => Block): + case (m: FinalDest.Match, acc) => + store.get(m) match + case S(L(fdefn)) => rest => Define(fdefn, acc(rest)) + case _ => acc + case (_, acc) => acc + // return a lambda, which either calls the extracted arm function // or contains the computations in matching arms def getOrElseUpdate(ctorId: CtorId): Value.Lam = @@ -429,7 +451,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case _ => s.symbol.flatMap(_.asBlkMember) match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => val newInstId = s.uid :: instId - rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => // FIXME: correctly rename recursive groups Value.Ref(m(blk)) case _ => super.applyPath(s) case v: Value => applyValue(v) @@ -442,7 +464,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case None => l.asBlkMember match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => val newInstId = r.uid :: instId - rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => // FIXME: correctly rename recursive groups Value.Ref(m(blk)) case _ => super.applyValue(v) case _ => super.applyValue(v) @@ -513,11 +535,9 @@ class FreeVarTraverser(val blk: Block, alwaysDefined: Set[Symbol]) extends Block applyBlock(l.body) ctx --= paramSymbols - protected def analyze: Unit = applyBlock(blk) - - analyze - - val freeVars = result.toList.sortBy(_.uid) + lazy val freeVars = + applyBlock(blk) + result.toList.sortBy(_.uid) // Used on match blocks before deforestation transformation // Compute free vars considering new vars introduced by deforestation @@ -529,7 +549,9 @@ class FreeVarTraverserForMatchConsideringDeforestation( val preAnalyzer = drwp.preAnalyzer val selsReplacementByCurrentMatch = - drwp.fusingMatchIdToSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) + drwp.fusingMatchIdToVarSymbolsToReplacedInAllBranches.getOrElse[Map[SelId, VarSymbol | TempSymbol]](matchId, Map.empty) ++ + drwp.fusingMatchIdToTmpSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) + println(selsReplacementByCurrentMatch.size) val selsReplacementNotForThisMatch = drwp.selIdsInArmToSymbolsToReplace.toMap -- selsReplacementByCurrentMatch.keySet @@ -558,12 +580,12 @@ class FreeVarTraverserForMatchConsideringDeforestation( case None => qual match // if it is the scrut of current match and the computation containing // this selection is moved, then the selection will be replaced and there will be no free vars - case Value.Ref(l) if l == currentMatchScrutSymbol => () + case Value.Ref(l) if l is currentMatchScrutSymbol => () case _ => super.applyPath(p) case Some(s) => result += s case _ => super.applyPath(p) - override def analyze: Unit = + override lazy val freeVars: List[Symbol] = val Match(_, arms, dflt, rest) = blk: @unchecked val parentMatchRest = drwp.preAnalyzer .matchScrutToParentMatchScruts(matchId._1) @@ -575,6 +597,7 @@ class FreeVarTraverserForMatchConsideringDeforestation( // Also take care of the `rest`s of its parent match blocks. val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened applyBlock(realArm) + result.toList.sortBy(_.uid) class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): override def applyValue(v: Value): Value = v match From 705b0a3ccfcf7b0a76c6d8f6cf023766a0986500 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 16:52:36 +0800 Subject: [PATCH 282/303] better instantiation id suffix --- .../shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 237b7d4546..38c147d65e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -551,7 +551,6 @@ class FreeVarTraverserForMatchConsideringDeforestation( val selsReplacementByCurrentMatch = drwp.fusingMatchIdToVarSymbolsToReplacedInAllBranches.getOrElse[Map[SelId, VarSymbol | TempSymbol]](matchId, Map.empty) ++ drwp.fusingMatchIdToTmpSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) - println(selsReplacementByCurrentMatch.size) val selsReplacementNotForThisMatch = drwp.selIdsInArmToSymbolsToReplace.toMap -- selsReplacementByCurrentMatch.keySet @@ -651,5 +650,5 @@ extension (ss: Ls[Symbol]) extension (instId: InstantiationId) def makeSuffix(preAnalyzer: DeforestPreAnalyzer) = - instId.map(preAnalyzer.getStableResultId).mkString("_") + "inst_" + instId.map(preAnalyzer.getStableResultId).mkString("_") + "_tsni" From 7409fe791595cfbfc73e16bdedfb165d28d8cddb Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sun, 15 Jun 2025 17:12:00 +0800 Subject: [PATCH 283/303] better logic --- .../hkmc2/codegen/deforest/Rewrite.scala | 96 ++++++++++--------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 38c147d65e..f9169b5139 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -147,52 +147,20 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. case S(x) => S(x) // if a key doesn't exist, it means the final dest is only used once - val finalDestToSymbolsToReplaceSelInArms = mutable.Map.empty[ + val finalDestToVarSymbolsToReplaceSelInArms = mutable.Map.empty[ FinalDest, Map[SelId, VarSymbol] -> Map[Tree.Ident, VarSymbol]] // if a key doesn't exist, it means the matchId doesn't have any arm that is used more than once val fusingMatchIdToVarSymbolsToReplacedInAllBranches = mutable.Map.empty[ MatchId, Map[SelId, VarSymbol]] - // if a key doesn't exist, it means the final dest is only used once - val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] - finalDestToCtorIds.foreach: - case (_: FinalDest.Match) -> ctorIds if ctorIds.size < 1 => die - case (matchArmDest@FinalDest.Match(matchId, cls)) -> ctorIds if ctorIds.size > 1 => - for cls <- cls do - val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, VarSymbol] - val selExprIdToNewSymbol = mutable.Map.empty[SelId, VarSymbol] - for selId <- matchArmDest.selsInArm do - val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name - val symName = s"_deforest_${cls.nme}_${selName}_${selId._2.makeSuffix(preAnalyzer)}" - val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): - VarSymbol(Tree.Ident(symName)) - selExprIdToNewSymbol += selId -> sym - finalDestToSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) - fusingMatchIdToVarSymbolsToReplacedInAllBranches.updateWith(matchId): - case N => S(selExprIdToNewSymbol.toMap) - case S(x) => S(x ++ selExprIdToNewSymbol.toMap) - val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme - val armName = cls.fold("default")(_.nme) - val funSym = BlockMemberSymbol( - s"match_${scrutName}_arm_${armName}_${matchId._2.makeSuffix(preAnalyzer)}", - Nil) - finalDestToMatchArmFunSymbols += matchArmDest -> funSym - case _ => () - - val alwaysNonFreeVars = - sol.preAnalyzer.b.definedVars ++ - newSymToInstIdAndOldSym.keySet ++ - fusingMatchIdToMatchRestFunSymbols.values ++ - finalDestToMatchArmFunSymbols.values + - State.globalThisSymbol + - State.runtimeSymbol - // if a key doesn't exist, it means the all the arms of this matchId are used more than once val fusingMatchIdToTmpSymbolsToReplacedInAllBranches = mutable.Map.empty[ MatchId, Map[SelId, TempSymbol]] - val selIdsInArmToSymbolsToReplace = mutable.Map.empty[SelId, TempSymbol | VarSymbol] + // if a key doesn't exist, it means the final dest is only used once + val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] + val selIdsInAllArmsToSymbolsToReplace = mutable.Map.empty[SelId, TempSymbol | VarSymbol] locally: def matDestToTempSymbolMap(mat: FinalDest.Match) = mat.arm.fold(Nil): c => @@ -201,16 +169,50 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name val idx = ctorFieldNames.indexWhere(_.id == selName) selId -> mat.tmpSymbolForASpecificCtorId(idx) - for case (c, mat: FinalDest.Match) <- ctorIdToFinalDest do - if finalDestToCtorIds(mat).size == 1 then - val selsToTmpSyms = matDestToTempSymbolMap(mat) - fusingMatchIdToTmpSymbolsToReplacedInAllBranches.updateWith(mat.matchId): + for case (c, matchArmDest@FinalDest.Match(matchId, cls)) <- ctorIdToFinalDest do + val numOfCtors = finalDestToCtorIds(matchArmDest).size + if numOfCtors < 1 then + die + else if numOfCtors == 1 then + val selsToTmpSyms = matDestToTempSymbolMap(matchArmDest) + fusingMatchIdToTmpSymbolsToReplacedInAllBranches.updateWith(matchId): case N => S(selsToTmpSyms.toMap) case S(m) => S(m ++ selsToTmpSyms) - selIdsInArmToSymbolsToReplace.addAll(selsToTmpSyms) + selIdsInAllArmsToSymbolsToReplace.addAll(selsToTmpSyms) else - val selsToVarSyms = finalDestToSymbolsToReplaceSelInArms(mat)._1 - selIdsInArmToSymbolsToReplace.addAll(selsToVarSyms) + finalDestToMatchArmFunSymbols.getOrElseUpdate.curried(matchArmDest): + for cls <- cls do + val selNameToNewSymbol = mutable.Map.empty[Tree.Ident, VarSymbol] + val selExprIdToNewSymbol = mutable.Map.empty[SelId, VarSymbol] + for selId <- matchArmDest.selsInArm do + val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name + val symName = s"_deforest_${cls.nme}_${selName}_${selId._2.makeSuffix(preAnalyzer)}" + val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): + VarSymbol(Tree.Ident(symName)) + selExprIdToNewSymbol += selId -> sym + finalDestToVarSymbolsToReplaceSelInArms += matchArmDest -> (selExprIdToNewSymbol.toMap -> selNameToNewSymbol.toMap) + fusingMatchIdToVarSymbolsToReplacedInAllBranches.updateWith(matchId): + case N => S(selExprIdToNewSymbol.toMap) + case S(x) => S(x ++ selExprIdToNewSymbol.toMap) + selIdsInAllArmsToSymbolsToReplace.addAll(selExprIdToNewSymbol) + val scrutName = preAnalyzer.getResult(matchId._1).asInstanceOf[Value.Ref].l.nme + val armName = cls.fold("default")(_.nme) + val funSym = BlockMemberSymbol( + s"match_${scrutName}_arm_${armName}_${matchId._2.makeSuffix(preAnalyzer)}", + Nil) + funSym + + + val alwaysNonFreeVars = + sol.preAnalyzer.b.definedVars ++ + newSymToInstIdAndOldSym.keySet ++ + fusingMatchIdToMatchRestFunSymbols.values ++ + finalDestToMatchArmFunSymbols.values + + State.globalThisSymbol + + State.runtimeSymbol + + + object freeVarsOfOriginalMatchesConsideringDeforestation: @@ -352,7 +354,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora preAnalyzer.getCtorSymFromCtorLikeExprId(ctorId._1).get.asCls.fold(Nil): c => c.tree.clsParams.map: p => rewritePrepare - .finalDestToSymbolsToReplaceSelInArms(dest)._2 + .finalDestToVarSymbolsToReplaceSelInArms(dest)._2 .getOrElse(Tree.Ident(p.name), VarSymbol(Tree.Ident(s"_unused_${p.name}"))) L(FunDefn( N, @@ -442,8 +444,8 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // a selection which is a consumer on its own case s: Select if rewritePrepare.rewritingSelIds(s.uid.withInstId) => applyPath(p) // a selection inside a fusing match that needs to be replaced by pre-computed symbols - case s: Select if rewritePrepare.selIdsInArmToSymbolsToReplace.get(s.uid.withInstId).isDefined => - Value.Ref(rewritePrepare.selIdsInArmToSymbolsToReplace(s.uid.withInstId)) + case s: Select if rewritePrepare.selIdsInAllArmsToSymbolsToReplace.get(s.uid.withInstId).isDefined => + Value.Ref(rewritePrepare.selIdsInAllArmsToSymbolsToReplace(s.uid.withInstId)) case s@Select(p, nme) => s.symbol.flatMap(_.asObj) match // a fusing object constructor case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(s.uid.withInstId) => @@ -552,7 +554,7 @@ class FreeVarTraverserForMatchConsideringDeforestation( drwp.fusingMatchIdToVarSymbolsToReplacedInAllBranches.getOrElse[Map[SelId, VarSymbol | TempSymbol]](matchId, Map.empty) ++ drwp.fusingMatchIdToTmpSymbolsToReplacedInAllBranches.getOrElse(matchId, Map.empty) val selsReplacementNotForThisMatch = - drwp.selIdsInArmToSymbolsToReplace.toMap -- + drwp.selIdsInAllArmsToSymbolsToReplace.toMap -- selsReplacementByCurrentMatch.keySet val currentMatchScrutSymbol = blk.asInstanceOf[Match].scrut.asInstanceOf[Value.Ref].l From 53d915c1bcdd08027f85387157e3d1be3d3fcfe8 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:04:11 +0800 Subject: [PATCH 284/303] correct instantiatino id in rewrite --- .../src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index f9169b5139..fa00d4768b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -42,7 +42,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. for (ctor, _) <- sol.resolveClashes._1 instantiationId = ctor.instantiationId.get - case instId@(invokedReferSite :: _) <- instantiationId.scanRight(Nil)(_ :: _) + case instId@(_ :+ invokedReferSite) <- instantiationId.scanLeft(Nil)(_ :+ _) do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) @@ -452,7 +452,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora matchArmsOfFusingMatches.getOrElseUpdate(s.uid.withInstId) case _ => s.symbol.flatMap(_.asBlkMember) match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => - val newInstId = s.uid :: instId + val newInstId = instId :+ s.uid rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => // FIXME: correctly rename recursive groups Value.Ref(m(blk)) case _ => super.applyPath(s) @@ -465,7 +465,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora matchArmsOfFusingMatches.getOrElseUpdate(r.uid.withInstId) case None => l.asBlkMember match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => - val newInstId = r.uid :: instId + val newInstId = instId :+ r.uid rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => // FIXME: correctly rename recursive groups Value.Ref(m(blk)) case _ => super.applyValue(v) From 41de79bbc6a7a79c8239a07c7f2fe0feab9508d6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 02:02:28 +0800 Subject: [PATCH 285/303] fix instantiation id for recursive calls; more tests --- .../hkmc2/codegen/deforest/Rewrite.scala | 16 +- .../deforest/def-dup/new-dup-simple.mls | 1059 +++++++++++++++++ .../mlscript/deforest/def-dup/new-simple.mls | 432 +++---- 3 files changed, 1244 insertions(+), 263 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index fa00d4768b..695c230c30 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -61,7 +61,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. instantiationId = dtorOrSel match case f: FieldSel => f.instantiationId.get case d: Dtor => d.instantiationId.get - case instId@(invokedReferSite :: _) <- instantiationId.scanRight(Nil)(_ :: _) + case instId@(_ :+ invokedReferSite) <- instantiationId.scanLeft(Nil)(_ :+ _) do instIdToMappingFromOldToNewSyms.getOrElseUpdate.curried(instId): val invokedFunSym = preAnalyzer.getReferredFunSym(invokedReferSite) val recursiveGroupFunSym = sol.collector.funSymToProdStratScheme.recursiveGroups(invokedFunSym) @@ -452,8 +452,11 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora matchArmsOfFusingMatches.getOrElseUpdate(s.uid.withInstId) case _ => s.symbol.flatMap(_.asBlkMember) match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => - val newInstId = instId :+ s.uid - rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => // FIXME: correctly rename recursive groups + val inTheSameRecursiveGroup = instId.lastOption.fold(false): currentReferSite => + val currentSym = preAnalyzer.getReferredFunSym(currentReferSite) + rewritePrepare.sol.collector.funSymToProdStratScheme.recursiveGroups(currentSym).contains(blk) + val newInstId = if inTheSameRecursiveGroup then instId else instId :+ s.uid + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyPath(s)): m => Value.Ref(m(blk)) case _ => super.applyPath(s) case v: Value => applyValue(v) @@ -465,8 +468,11 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora matchArmsOfFusingMatches.getOrElseUpdate(r.uid.withInstId) case None => l.asBlkMember match case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => - val newInstId = instId :+ r.uid - rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => // FIXME: correctly rename recursive groups + val inTheSameRecursiveGroup = instId.lastOption.fold(false): currentReferSite => + val currentSym = preAnalyzer.getReferredFunSym(currentReferSite) + rewritePrepare.sol.collector.funSymToProdStratScheme.recursiveGroups(currentSym).contains(blk) + val newInstId = if inTheSameRecursiveGroup then instId else instId :+ r.uid + rewritePrepare.instIdToMappingFromOldToNewSyms.get(newInstId).fold(super.applyValue(v)): m => Value.Ref(m(blk)) case _ => super.applyValue(v) case _ => super.applyValue(v) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls new file mode 100644 index 0000000000..2f580ec4b0 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls @@ -0,0 +1,1059 @@ +:js + +data class A(x) +data class B(x) +data class (::) Cons(h, t) +object Nil + + + +:deforest +fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) +fun f1(ls1, acc) = if ls1 is + Nil then acc + h :: t then f1(t, acc + h) +fun f2(ls2, acc) = if ls2 is + Nil then acc + hh :: tt then f2(tt, acc + hh + 1) +f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) +//│ = 51 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@Some(List()) --> +//│ Ref(ls1)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(3)) --> +//│ Ref(ls1)@Some(List(1)) +//│ Ref(member:Nil)@4@Some(List()) --> +//│ Ref(ls2)@Some(List(5)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(6)) --> +//│ Ref(ls2)@Some(List(5)) +//│ Ref(member:Nil)@7@Some(List()) --> +//│ Ref(ls1)@Some(List(8)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> +//│ Ref(ls1)@Some(List(8)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@inst__tsni --> Ref(ls1)@inst_1_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_3_tsni --> Ref(ls1)@inst_1_tsni@Cons +//│ Ref(member:Nil)@4@inst__tsni --> Ref(ls2)@inst_5_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_6_tsni --> Ref(ls2)@inst_5_tsni@Cons +//│ Ref(member:Nil)@7@inst__tsni --> Ref(ls1)@inst_8_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_9_tsni --> Ref(ls1)@inst_8_tsni@Cons +//│ ==== JS (deforested): ==== +//│ to_inst_3_tsni = function to_inst_3_tsni(n, acc) { +//│ let scrut, tmp14, tmp15, h, t; +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return acc +//│ } else { +//│ tmp14 = n - 1; +//│ h = n; +//│ t = acc; +//│ tmp15 = (acc1) => { +//│ let param0, param1, h1, t1, tmp16; +//│ param0 = h; +//│ param1 = t; +//│ h1 = param0; +//│ t1 = param1; +//│ tmp16 = acc1 + h1; +//│ return f1_inst_1_tsni(t1, tmp16) +//│ }; +//│ return to_inst_3_tsni(tmp14, tmp15) +//│ } +//│ }; +//│ to_inst_6_tsni = function to_inst_6_tsni(n, acc) { +//│ let scrut, tmp14, tmp15, h, t; +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return acc +//│ } else { +//│ tmp14 = n - 1; +//│ h = n; +//│ t = acc; +//│ tmp15 = (acc1) => { +//│ let param0, param1, hh, tt, tmp16, tmp17; +//│ param0 = h; +//│ param1 = t; +//│ hh = param0; +//│ tt = param1; +//│ tmp16 = acc1 + hh; +//│ tmp17 = tmp16 + 1; +//│ return f2_inst_5_tsni(tt, tmp17) +//│ }; +//│ return to_inst_6_tsni(tmp14, tmp15) +//│ } +//│ }; +//│ to_inst_9_tsni = function to_inst_9_tsni(n, acc) { +//│ let scrut, tmp14, tmp15, h, t; +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return acc +//│ } else { +//│ tmp14 = n - 1; +//│ h = n; +//│ t = acc; +//│ tmp15 = (acc1) => { +//│ let param0, param1, h1, t1, tmp16; +//│ param0 = h; +//│ param1 = t; +//│ h1 = param0; +//│ t1 = param1; +//│ tmp16 = acc1 + h1; +//│ return f1_inst_8_tsni(t1, tmp16) +//│ }; +//│ return to_inst_9_tsni(tmp14, tmp15) +//│ } +//│ }; +//│ f1_inst_1_tsni = function f1_inst_1_tsni(ls1, acc) { +//│ return runtime.safeCall(ls1(acc)) +//│ }; +//│ f2_inst_5_tsni = function f2_inst_5_tsni(ls2, acc) { +//│ return runtime.safeCall(ls2(acc)) +//│ }; +//│ f1_inst_8_tsni = function f1_inst_8_tsni(ls1, acc) { +//│ return runtime.safeCall(ls1(acc)) +//│ }; +//│ to = function to(n, acc) { +//│ let scrut, tmp14, tmp15; +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return acc +//│ } else { +//│ tmp14 = n - 1; +//│ tmp15 = Cons1(n, acc); +//│ return to(tmp14, tmp15) +//│ } +//│ }; +//│ f1 = function f1(ls1, acc) { +//│ let param0, param1, h, t, tmp14; +//│ if (ls1 instanceof Nil1.class) { +//│ return acc +//│ } else if (ls1 instanceof Cons1.class) { +//│ param0 = ls1.h; +//│ param1 = ls1.t; +//│ h = param0; +//│ t = param1; +//│ tmp14 = acc + h; +//│ return f1(t, tmp14) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f2 = function f2(ls2, acc) { +//│ let param0, param1, hh, tt, tmp14, tmp15; +//│ if (ls2 instanceof Nil1.class) { +//│ return acc +//│ } else if (ls2 instanceof Cons1.class) { +//│ param0 = ls2.h; +//│ param1 = ls2.t; +//│ hh = param0; +//│ tt = param1; +//│ tmp14 = acc + hh; +//│ tmp15 = tmp14 + 1; +//│ return f2(tt, tmp15) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp7 = to_inst_9_tsni(4, (acc) => { +//│ return acc +//│ }); +//│ tmp8 = f1_inst_8_tsni(tmp7, 0); +//│ tmp9 = to_inst_6_tsni(5, (acc) => { +//│ return acc +//│ }); +//│ tmp10 = f2_inst_5_tsni(tmp9, 0); +//│ tmp11 = tmp8 + tmp10; +//│ tmp12 = to_inst_3_tsni(6, (acc) => { +//│ return acc +//│ }); +//│ tmp13 = f1_inst_1_tsni(tmp12, 0); +//│ block$res_deforest = tmp11 + tmp13; +//│ undefined +//│ -------- executing ---------- +//│ = 51 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun id(x) = x +fun f1(a1) = if a1 is A then 1 +fun f2(a2) = if a2 is A then 2 +f1(id(A(1))) + f2(id(A(2))) +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@Some(List()) --> +//│ Ref(a2)@Some(List(1)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@Some(List()) --> +//│ Ref(a1)@Some(List(3)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@inst__tsni --> Ref(a2)@inst_1_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@inst__tsni --> Ref(a1)@inst_3_tsni@A +//│ ==== JS (deforested): ==== +//│ f2_inst_1_tsni = function f2_inst_1_tsni(a2) { +//│ return runtime.safeCall(a2()) +//│ }; +//│ f1_inst_3_tsni = function f1_inst_3_tsni(a1) { +//│ return runtime.safeCall(a1()) +//│ }; +//│ id = function id(x2) { +//│ return x2 +//│ }; +//│ f11 = function f1(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f21 = function f2(a2) { +//│ if (a2 instanceof A1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ x1 = 1; +//│ tmp20 = () => { +//│ return 1 +//│ }; +//│ tmp21 = id(tmp20); +//│ tmp22 = f1_inst_3_tsni(tmp21); +//│ x = 2; +//│ tmp23 = () => { +//│ return 2 +//│ }; +//│ tmp24 = id(tmp23); +//│ tmp25 = f2_inst_1_tsni(tmp24); +//│ block$res_deforest1 = tmp22 + tmp25; +//│ undefined +//│ -------- executing ---------- +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun p(x) = A(x) +fun wrap1(x) = p(x) +fun wrap(x) = wrap1(x) +fun f1(a1) = if a1 is A then 1 +fun f2(a2) = if a2 is A then 2 +f1(wrap(1)) + f2(wrap(2)) +//│ = 3 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1, 2, 3)) --> +//│ Ref(a2)@Some(List(4)) +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> +//│ Ref(a1)@Some(List(6)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_2_3_tsni --> Ref(a2)@inst_4_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_5_2_3_tsni --> Ref(a1)@inst_6_tsni@A +//│ ==== JS (deforested): ==== +//│ wrap_inst_1_tsni = function wrap_inst_1_tsni(x2) { +//│ return wrap1_inst_1_2_tsni(x2) +//│ }; +//│ wrap1_inst_1_2_tsni = function wrap1_inst_1_2_tsni(x2) { +//│ return p_inst_1_2_3_tsni(x2) +//│ }; +//│ p_inst_1_2_3_tsni = function p_inst_1_2_3_tsni(x2) { +//│ let x3; +//│ x3 = x2; +//│ return () => { +//│ return 2 +//│ } +//│ }; +//│ wrap_inst_5_tsni = function wrap_inst_5_tsni(x2) { +//│ return wrap1_inst_5_2_tsni(x2) +//│ }; +//│ wrap1_inst_5_2_tsni = function wrap1_inst_5_2_tsni(x2) { +//│ return p_inst_5_2_3_tsni(x2) +//│ }; +//│ p_inst_5_2_3_tsni = function p_inst_5_2_3_tsni(x2) { +//│ let x3; +//│ x3 = x2; +//│ return () => { +//│ return 1 +//│ } +//│ }; +//│ f2_inst_4_tsni = function f2_inst_4_tsni(a2) { +//│ return runtime.safeCall(a2()) +//│ }; +//│ f1_inst_6_tsni = function f1_inst_6_tsni(a1) { +//│ return runtime.safeCall(a1()) +//│ }; +//│ p = function p(x2) { +//│ return A1(x2) +//│ }; +//│ wrap1 = function wrap1(x2) { +//│ return p(x2) +//│ }; +//│ wrap = function wrap(x2) { +//│ return wrap1(x2) +//│ }; +//│ f12 = function f1(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f22 = function f2(a2) { +//│ if (a2 instanceof A1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp30 = wrap_inst_5_tsni(1); +//│ tmp31 = f1_inst_6_tsni(tmp30); +//│ tmp32 = wrap_inst_1_tsni(2); +//│ tmp33 = f2_inst_4_tsni(tmp32); +//│ block$res_deforest2 = tmp31 + tmp33; +//│ undefined +//│ -------- executing ---------- +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun f(x) = if x == 0 then Nil else x :: g(x - 1) +fun g(y) = if y == 0 then Nil else y :: f(y - 1) +fun cons(a) = if a is + Nil then 0 + h :: t then h + cons(t) +fun cons1(a1) = if a1 is + Nil then 1 + h :: t then h + cons1(t) +cons(f(0)) + cons1(f(1)) +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Ref(member:Nil)@4@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(1)) --> +//│ Ref(a1)@Some(List(2)) +//│ Ref(member:Nil)@0@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Ref(member:Nil)@4@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(6)) --> +//│ Ref(a)@Some(List(7)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@inst_1_tsni --> Ref(a1)@inst_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@inst_1_tsni --> Ref(a1)@inst_2_tsni@Cons +//│ Ref(member:Nil)@4@inst_1_tsni --> Ref(a1)@inst_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@inst_1_tsni --> Ref(a1)@inst_2_tsni@Cons +//│ Ref(member:Nil)@0@inst_6_tsni --> Ref(a)@inst_7_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@inst_6_tsni --> Ref(a)@inst_7_tsni@Cons +//│ Ref(member:Nil)@4@inst_6_tsni --> Ref(a)@inst_7_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@inst_6_tsni --> Ref(a)@inst_7_tsni@Cons +//│ ==== JS (deforested): ==== +//│ match_a1_arm_Nil_inst_2_tsni = function match_a1_arm_Nil_inst_2_tsni() { +//│ return 1 +//│ }; +//│ match_a1_arm_Cons_inst_2_tsni = function match_a1_arm_Cons_inst_2_tsni(_deforest_Cons_Ident$_h$__inst_2_tsni, _deforest_Cons_Ident$_t$__inst_2_tsni) { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_Ident$_h$__inst_2_tsni; +//│ param1 = _deforest_Cons_Ident$_t$__inst_2_tsni; +//│ h = param0; +//│ t = param1; +//│ tmp42 = cons1_inst_2_tsni(t); +//│ return h + tmp42 +//│ }; +//│ match_a_arm_Nil_inst_7_tsni = function match_a_arm_Nil_inst_7_tsni() { +//│ return 0 +//│ }; +//│ match_a_arm_Cons_inst_7_tsni = function match_a_arm_Cons_inst_7_tsni(_deforest_Cons_Ident$_h$__inst_7_tsni, _deforest_Cons_Ident$_t$__inst_7_tsni) { +//│ let param0, param1, h, t, tmp42; +//│ param0 = _deforest_Cons_Ident$_h$__inst_7_tsni; +//│ param1 = _deforest_Cons_Ident$_t$__inst_7_tsni; +//│ h = param0; +//│ t = param1; +//│ tmp42 = cons_inst_7_tsni(t); +//│ return h + tmp42 +//│ }; +//│ f_inst_1_tsni = function f_inst_1_tsni(x2) { +//│ let scrut, tmp42, tmp43, h, t; +//│ scrut = x2 == 0; +//│ if (scrut === true) { +//│ return () => { +//│ return match_a1_arm_Nil_inst_2_tsni() +//│ } +//│ } else { +//│ tmp42 = x2 - 1; +//│ tmp43 = g_inst_1_tsni(tmp42); +//│ h = x2; +//│ t = tmp43; +//│ return () => { +//│ return match_a1_arm_Cons_inst_2_tsni(h, t) +//│ } +//│ } +//│ }; +//│ g_inst_1_tsni = function g_inst_1_tsni(y) { +//│ let scrut, tmp42, tmp43, h, t; +//│ scrut = y == 0; +//│ if (scrut === true) { +//│ return () => { +//│ return match_a1_arm_Nil_inst_2_tsni() +//│ } +//│ } else { +//│ tmp42 = y - 1; +//│ tmp43 = f_inst_1_tsni(tmp42); +//│ h = y; +//│ t = tmp43; +//│ return () => { +//│ return match_a1_arm_Cons_inst_2_tsni(h, t) +//│ } +//│ } +//│ }; +//│ f_inst_6_tsni = function f_inst_6_tsni(x2) { +//│ let scrut, tmp42, tmp43, h, t; +//│ scrut = x2 == 0; +//│ if (scrut === true) { +//│ return () => { +//│ return match_a_arm_Nil_inst_7_tsni() +//│ } +//│ } else { +//│ tmp42 = x2 - 1; +//│ tmp43 = g_inst_6_tsni(tmp42); +//│ h = x2; +//│ t = tmp43; +//│ return () => { +//│ return match_a_arm_Cons_inst_7_tsni(h, t) +//│ } +//│ } +//│ }; +//│ g_inst_6_tsni = function g_inst_6_tsni(y) { +//│ let scrut, tmp42, tmp43, h, t; +//│ scrut = y == 0; +//│ if (scrut === true) { +//│ return () => { +//│ return match_a_arm_Nil_inst_7_tsni() +//│ } +//│ } else { +//│ tmp42 = y - 1; +//│ tmp43 = f_inst_6_tsni(tmp42); +//│ h = y; +//│ t = tmp43; +//│ return () => { +//│ return match_a_arm_Cons_inst_7_tsni(h, t) +//│ } +//│ } +//│ }; +//│ cons1_inst_2_tsni = function cons1_inst_2_tsni(a1) { +//│ return runtime.safeCall(a1()) +//│ }; +//│ cons_inst_7_tsni = function cons_inst_7_tsni(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ f = function f(x2) { +//│ let scrut, tmp42, tmp43; +//│ scrut = x2 == 0; +//│ if (scrut === true) { +//│ return Nil1 +//│ } else { +//│ tmp42 = x2 - 1; +//│ tmp43 = g(tmp42); +//│ return Cons1(x2, tmp43) +//│ } +//│ }; +//│ g = function g(y) { +//│ let scrut, tmp42, tmp43; +//│ scrut = y == 0; +//│ if (scrut === true) { +//│ return Nil1 +//│ } else { +//│ tmp42 = y - 1; +//│ tmp43 = f(tmp42); +//│ return Cons1(y, tmp43) +//│ } +//│ }; +//│ cons = function cons(a) { +//│ let param0, param1, h, t, tmp42; +//│ if (a instanceof Nil1.class) { +//│ return 0 +//│ } else if (a instanceof Cons1.class) { +//│ param0 = a.h; +//│ param1 = a.t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = cons(t); +//│ return h + tmp42 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ cons1 = function cons1(a1) { +//│ let param0, param1, h, t, tmp42; +//│ if (a1 instanceof Nil1.class) { +//│ return 1 +//│ } else if (a1 instanceof Cons1.class) { +//│ param0 = a1.h; +//│ param1 = a1.t; +//│ h = param0; +//│ t = param1; +//│ tmp42 = cons1(t); +//│ return h + tmp42 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp38 = f_inst_6_tsni(0); +//│ tmp39 = cons_inst_7_tsni(tmp38); +//│ tmp40 = f_inst_1_tsni(1); +//│ tmp41 = cons1_inst_2_tsni(tmp40); +//│ block$res_deforest3 = tmp39 + tmp41; +//│ undefined +//│ -------- executing ---------- +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) +//│ = Cons(4, Cons(6, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Ref(member:Nil)@0@Some(List(1)) --> +//│ Ref(ls)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> +//│ Ref(ls)@Some(List(2)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ Ref(member:Nil)@6@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ ------------------------------------ +//│ Ref(member:Nil)@0@inst_1_tsni --> Ref(ls)@inst_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_1_tsni --> Ref(ls)@inst_2_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls)@inst_1_tsni@Nil +//│ ==== JS (deforested): ==== +//│ match_ls_arm_Cons_inst_1_tsni = function match_ls_arm_Cons_inst_1_tsni(f3, _deforest_Cons_Ident$_h$__inst_1_tsni, _deforest_Cons_Ident$_t$__inst_1_tsni) { +//│ let param0, param1, h2, t2, tmp48, tmp49, h3, t3; +//│ param0 = _deforest_Cons_Ident$_h$__inst_1_tsni; +//│ param1 = _deforest_Cons_Ident$_t$__inst_1_tsni; +//│ h2 = param0; +//│ t2 = param1; +//│ tmp48 = runtime.safeCall(f3(h2)); +//│ tmp49 = map_inst_1_tsni(t2, f3); +//│ h3 = tmp48; +//│ t3 = tmp49; +//│ return (f4) => { +//│ param0 = h3; +//│ param1 = t3; +//│ h2 = param0; +//│ t2 = param1; +//│ tmp48 = runtime.safeCall(f4(h2)); +//│ tmp49 = map_inst_2_tsni(t2, f4); +//│ return Cons1(tmp48, tmp49) +//│ } +//│ }; +//│ map_inst_1_tsni = function map_inst_1_tsni(ls, f3) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ map_inst_2_tsni = function map_inst_2_tsni(ls, f3) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ map = function map(ls, f3) { +//│ let param0, param1, h2, t2, tmp48, tmp49; +//│ if (ls instanceof Nil1.class) { +//│ return Nil1 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h2 = param0; +//│ t2 = param1; +//│ tmp48 = runtime.safeCall(f3(h2)); +//│ tmp49 = map(t2, f3); +//│ return Cons1(tmp48, tmp49) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ h1 = 2; +//│ t1 = (f3) => { +//│ return (f4) => { +//│ return Nil1 +//│ } +//│ }; +//│ tmp45 = (f3) => { +//│ return match_ls_arm_Cons_inst_1_tsni(f3, h1, t1) +//│ }; +//│ h = 1; +//│ t = tmp45; +//│ tmp46 = (f3) => { +//│ return match_ls_arm_Cons_inst_1_tsni(f3, h, t) +//│ }; +//│ lambda2 = (undefined, function (x2) { +//│ return x2 + 1 +//│ }); +//│ tmp47 = map_inst_1_tsni(tmp46, lambda2); +//│ lambda3 = (undefined, function (x2) { +//│ return x2 * 2 +//│ }); +//│ block$res_deforest4 = map_inst_2_tsni(tmp47, lambda3); +//│ undefined +//│ -------- executing ---------- +//│ = Cons(4, Cons(6, Nil)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection +:deforest +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +map(1 :: 2 :: Nil, x => x + 1) +//│ = Cons(2, Cons(3, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ Ref(member:Nil)@3@Some(List()) --> +//│ Ref(ls)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@inst__tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@inst__tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst__tsni --> Ref(ls)@inst_1_tsni@Nil +//│ ==== JS (deforested): ==== +//│ match_ls_arm_Cons_inst_1_tsni1 = function match_ls_arm_Cons_inst_1_tsni(f3, _deforest_Cons_Ident$_h$__inst_1_tsni, _deforest_Cons_Ident$_t$__inst_1_tsni) { +//│ let param0, param1, h4, t4, tmp52, tmp53; +//│ param0 = _deforest_Cons_Ident$_h$__inst_1_tsni; +//│ param1 = _deforest_Cons_Ident$_t$__inst_1_tsni; +//│ h4 = param0; +//│ t4 = param1; +//│ tmp52 = runtime.safeCall(f3(h4)); +//│ tmp53 = map_inst_1_tsni1(t4, f3); +//│ return Cons1(tmp52, tmp53) +//│ }; +//│ map_inst_1_tsni1 = function map_inst_1_tsni(ls, f3) { +//│ return runtime.safeCall(ls(f3)) +//│ }; +//│ map1 = function map(ls, f3) { +//│ let param0, param1, h4, t4, tmp52, tmp53; +//│ if (ls instanceof Nil1.class) { +//│ return Nil1 +//│ } else if (ls instanceof Cons1.class) { +//│ param0 = ls.h; +//│ param1 = ls.t; +//│ h4 = param0; +//│ t4 = param1; +//│ tmp52 = runtime.safeCall(f3(h4)); +//│ tmp53 = map1(t4, f3); +//│ return Cons1(tmp52, tmp53) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ h3 = 2; +//│ t3 = (f3) => { +//│ return Nil1 +//│ }; +//│ tmp50 = (f3) => { +//│ return match_ls_arm_Cons_inst_1_tsni1(f3, h3, t3) +//│ }; +//│ h2 = 1; +//│ t2 = tmp50; +//│ tmp51 = (f3) => { +//│ return match_ls_arm_Cons_inst_1_tsni1(f3, h2, t2) +//│ }; +//│ lambda5 = (undefined, function (x2) { +//│ return x2 + 1 +//│ }); +//│ block$res_deforest5 = map_inst_1_tsni1(tmp51, lambda5); +//│ undefined +//│ -------- executing ---------- +//│ = Cons(2, Cons(3, Nil)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for cyclic strategies removal +:deforest +fun f(x) = if x is + A(_) then f(A(2)) + else 0 +f(Nil) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ +//│ ==== JS (deforested): ==== +//│ f3 = function f(x2) { +//│ let param0, tmp52; +//│ if (x2 instanceof A1.class) { +//│ param0 = x2.x; +//│ tmp52 = A1(2); +//│ return f3(tmp52) +//│ } else { +//│ return 0 +//│ } +//│ }; +//│ block$res_deforest6 = f3(Nil1); +//│ undefined +//│ -------- executing ---------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection filter +:deforest +fun c1(x) = if x is + h :: t then + if t is + Nil then 0 +c1(1 :: Nil) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@Some(List()) --> +//│ Ref(x)@Some(List(1)) +//│ Ref(member:Nil)@2@Some(List()) --> +//│ Ref(t)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@inst__tsni --> Ref(x)@inst_1_tsni@Cons +//│ Ref(member:Nil)@2@inst__tsni --> Ref(t)@inst_1_tsni@Nil +//│ ==== JS (deforested): ==== +//│ c1_inst_1_tsni = function c1_inst_1_tsni(x2) { +//│ return runtime.safeCall(x2()) +//│ }; +//│ c1 = function c1(x2) { +//│ let param0, param1, h5, t5; +//│ if (x2 instanceof Cons1.class) { +//│ param0 = x2.h; +//│ param1 = x2.t; +//│ h5 = param0; +//│ t5 = param1; +//│ if (t5 instanceof Nil1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ h4 = 1; +//│ t4 = () => { +//│ return 0 +//│ }; +//│ tmp53 = () => { +//│ let param0, param1, h5, t5; +//│ param0 = h4; +//│ param1 = t4; +//│ h5 = param0; +//│ t5 = param1; +//│ return runtime.safeCall(t5()) +//│ }; +//│ block$res_deforest7 = c1_inst_1_tsni(tmp53); +//│ undefined +//│ -------- executing ---------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// for selection filter +:deforest +fun f(x) = if x is + A then g1(x.x) + B then g2(x.x) +fun g1(y1) = if y1 is + A then 0 +fun g2(y2) = if y2 is + A then 1 +fun apply(f) = + let inner = A(0) + f(A(inner)) +apply(f) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@Some(List(1)) --> +//│ Ref(x)@Some(List(2)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@Some(List(1)) --> +//│ Ref(y1)@Some(List(2, 4)) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@inst_1_tsni --> Ref(x)@inst_2_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@inst_1_tsni --> Ref(y1)@inst_2_4_tsni@A +//│ ==== JS (deforested): ==== +//│ apply_inst_1_tsni = function apply_inst_1_tsni(f5) { +//│ let inner, tmp54, tmp55, x2, x3; +//│ x3 = 0; +//│ tmp54 = () => { +//│ return 0 +//│ }; +//│ inner = tmp54; +//│ x2 = inner; +//│ tmp55 = () => { +//│ return g1_inst_2_4_tsni(x2) +//│ }; +//│ return runtime.safeCall(f5(tmp55)) +//│ }; +//│ f_inst_2_tsni = function f_inst_2_tsni(x2) { +//│ return runtime.safeCall(x2()) +//│ }; +//│ g1_inst_2_4_tsni = function g1_inst_2_4_tsni(y1) { +//│ return runtime.safeCall(y1()) +//│ }; +//│ f4 = function f(x2) { +//│ if (x2 instanceof A1.class) { +//│ return g1(x2.x) +//│ } else if (x2 instanceof B1.class) { +//│ return g2(x2.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ g1 = function g1(y1) { +//│ if (y1 instanceof A1.class) { +//│ return 0 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ g2 = function g2(y2) { +//│ if (y2 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ apply = function apply(f5) { +//│ let inner, tmp54, tmp55; +//│ tmp54 = A1(0); +//│ inner = tmp54; +//│ tmp55 = A1(inner); +//│ return runtime.safeCall(f5(tmp55)) +//│ }; +//│ block$res_deforest8 = apply_inst_1_tsni(f_inst_2_tsni); +//│ undefined +//│ -------- executing ---------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +:sjs +:deforest +fun inner(y, z) = if y is + B then z +fun dtor(x) = if x is + A then inner(B(4), x.x) +dtor(A(B(0))) +//│ JS (unsanitized): +//│ let dtor, inner, tmp54, tmp55; +//│ inner = function inner(y, z) { +//│ if (y instanceof B1.class) { +//│ return z +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ dtor = function dtor(x2) { +//│ let tmp56; +//│ if (x2 instanceof A1.class) { +//│ tmp56 = B1(4); +//│ return inner(tmp56, x2.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp54 = B1(0); +//│ tmp55 = A1(tmp54); +//│ dtor(tmp55) +//│ = B(0) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@Some(List(1)) --> +//│ Ref(y)@Some(List(1, 2)) +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@Some(List()) --> +//│ Ref(x)@Some(List(1)) +//│ ------------------------------------ +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@inst_1_tsni --> Ref(y)@inst_1_2_tsni@B +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ ==== JS (deforested): ==== +//│ dtor_inst_1_tsni = function dtor_inst_1_tsni(x3) { +//│ return runtime.safeCall(x3()) +//│ }; +//│ inner_inst_1_2_tsni = function inner_inst_1_2_tsni(y, z) { +//│ return runtime.safeCall(y(z)) +//│ }; +//│ inner = function inner(y, z) { +//│ if (y instanceof B1.class) { +//│ return z +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ dtor = function dtor(x3) { +//│ let tmp58; +//│ if (x3 instanceof A1.class) { +//│ tmp58 = B1(4); +//│ return inner(tmp58, x3.x) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp56 = B1(0); +//│ x2 = tmp56; +//│ tmp57 = () => { +//│ let tmp58, x3; +//│ x3 = 4; +//│ tmp58 = (z) => { +//│ return z +//│ }; +//│ return inner_inst_1_2_tsni(tmp58, x2) +//│ }; +//│ block$res_deforest9 = dtor_inst_1_tsni(tmp57); +//│ undefined +//│ -------- executing ---------- +//│ = B(0) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +:deforest +fun localFuse(x) = if A(x) is + A(y) then B(y) +if localFuse(3) is + B(a) then a +localFuse(4) +//│ = B(4) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1)) --> +//│ Ref($scrut)@Some(List(1)) +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(2)) --> +//│ Ref($scrut)@Some(List(2)) +//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@Some(List(2)) --> +//│ Ref($scrut)@Some(List()) +//│ ------------------------------------ +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_tsni --> Ref($scrut)@inst_1_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_2_tsni --> Ref($scrut)@inst_2_tsni@A +//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@inst_2_tsni --> Ref($scrut)@inst__tsni@B +//│ ==== JS (deforested): ==== +//│ localFuse_inst_1_tsni = function localFuse_inst_1_tsni(x3) { +//│ let scrut1, x4; +//│ x4 = x3; +//│ scrut1 = () => { +//│ let param01, y; +//│ param01 = x4; +//│ y = param01; +//│ return B1(y) +//│ }; +//│ return runtime.safeCall(scrut1()) +//│ }; +//│ localFuse_inst_2_tsni = function localFuse_inst_2_tsni(x3) { +//│ let scrut1, x4; +//│ x4 = x3; +//│ scrut1 = () => { +//│ let param01, y, x5; +//│ param01 = x4; +//│ y = param01; +//│ x5 = y; +//│ return () => { +//│ let tmp59; +//│ param0 = x5; +//│ a = param0; +//│ tmp59 = a; +//│ return localFuse_inst_1_tsni(4) +//│ } +//│ }; +//│ return runtime.safeCall(scrut1()) +//│ }; +//│ localFuse = function localFuse(x3) { +//│ let scrut1, param01, y; +//│ scrut1 = A1(x3); +//│ if (scrut1 instanceof A1.class) { +//│ param01 = scrut1.x; +//│ y = param01; +//│ return B1(y) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ scrut = localFuse_inst_2_tsni(3); +//│ block$res_deforest10 = runtime.safeCall(scrut()); +//│ undefined +//│ -------- executing ---------- +//│ = B(4) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +// cyclic strategy, shouldn't fuse +:deforest +fun f(x) = + let res = if x is + A(x) then x + if res > 0 then f(A(res - 1)) else 0 +f(A(4)) +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ +//│ ==== JS (deforested): ==== +//│ f5 = function f(x3) { +//│ let res, param01, x4, scrut1, tmp61, tmp62, tmp63; +//│ if (x3 instanceof A1.class) { +//│ param01 = x3.x; +//│ x4 = param01; +//│ tmp61 = x4; +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ res = tmp61; +//│ scrut1 = res > 0; +//│ if (scrut1 === true) { +//│ tmp62 = res - 1; +//│ tmp63 = A1(tmp62); +//│ return f5(tmp63) +//│ } else { +//│ return 0 +//│ } +//│ }; +//│ tmp60 = A1(4); +//│ block$res_deforest11 = f5(tmp60); +//│ undefined +//│ -------- executing ---------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + +// shouldn't have any fusion due to strategy clash +:deforest +fun g(x) = if x is + A(a) then a +fun f(x) = x.x +let o = A(1) +g(o) + f(o) + o.x +//│ = 3 +//│ o = A(1) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ------------------------------------ +//│ ==== JS (deforested): ==== +//│ g3 = function g(x3) { +//│ let param01, a1; +//│ if (x3 instanceof A1.class) { +//│ param01 = x3.x; +//│ a1 = param01; +//│ return a1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ f6 = function f(x3) { +//│ return x3.x +//│ }; +//│ tmp65 = A1(1); +//│ o = tmp65; +//│ tmp66 = g3(o); +//│ tmp67 = f6(o); +//│ tmp68 = tmp66 + tmp67; +//│ block$res_deforest12 = tmp68 + o.x; +//│ undefined +//│ -------- executing ---------- +//│ = 3 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index 66a1f69d5a..36012dee81 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -8,55 +8,76 @@ object Nil :deforest -fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) -fun f1(ls1, acc) = if ls1 is - Nil then acc - h :: t then f1(t, acc + h) -fun f2(ls2, acc) = if ls2 is - Nil then acc - hh :: tt then f2(tt, acc + hh + 1) -f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) -//│ = 51 +fun f(x) = if x is + A(x) then x +f(A(1)) +//│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List()) --> -//│ Ref(ls1)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(3)) --> -//│ Ref(ls1)@Some(List(1)) -//│ Ref(member:Nil)@4@Some(List()) --> -//│ Ref(ls2)@Some(List(5)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(6)) --> -//│ Ref(ls2)@Some(List(5)) -//│ Ref(member:Nil)@7@Some(List()) --> -//│ Ref(ls1)@Some(List(8)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> -//│ Ref(ls1)@Some(List(8)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@Some(List()) --> +//│ Ref(x)@Some(List(1)) //│ ------------------------------------ -//│ Ref(member:Nil)@0@ --> Ref(ls1)@1@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@3 --> Ref(ls1)@1@Cons -//│ Ref(member:Nil)@4@ --> Ref(ls2)@5@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@6 --> Ref(ls2)@5@Cons -//│ Ref(member:Nil)@7@ --> Ref(ls1)@8@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@9 --> Ref(ls1)@8@Cons +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ ==== JS (deforested): ==== +//│ f_inst_1_tsni = function f_inst_1_tsni(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ f = function f(x1) { +//│ let param0, x2; +//│ if (x1 instanceof A1.class) { +//│ param0 = x1.x; +//│ x2 = param0; +//│ return x2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ x = 1; +//│ tmp1 = () => { +//│ let param0, x1; +//│ param0 = x; +//│ x1 = param0; +//│ return x1 +//│ }; +//│ block$res_deforest = f_inst_1_tsni(tmp1); +//│ undefined +//│ -------- executing ---------- +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforest -fun id(x) = x -fun f1(a1) = if a1 is A then 1 -fun f2(a2) = if a2 is A then 2 -f1(id(A(1))) + f2(id(A(2))) -//│ = 3 +fun f(x) = if x is + A then x.x +f(A(1)) +//│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@Some(List()) --> -//│ Ref(a2)@Some(List(1)) -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@Some(List()) --> -//│ Ref(a1)@Some(List(3)) +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@Some(List()) --> +//│ Ref(x)@Some(List(1)) //│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@ --> Ref(a2)@1@A -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@ --> Ref(a1)@3@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ ==== JS (deforested): ==== +//│ f_inst_1_tsni1 = function f_inst_1_tsni(x2) { +//│ return runtime.safeCall(x2()) +//│ }; +//│ f1 = function f(x2) { +//│ if (x2 instanceof A1.class) { +//│ return x2.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ x1 = 1; +//│ tmp3 = () => { +//│ return x1 +//│ }; +//│ block$res_deforest1 = f_inst_1_tsni1(tmp3); +//│ undefined +//│ -------- executing ---------- +//│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + :deforest fun p(x) = A(x) fun wrap1(x) = p(x) @@ -71,240 +92,135 @@ f1(wrap(1)) + f2(wrap(2)) //│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> //│ Ref(a1)@Some(List(6)) //│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@1_2_3 --> Ref(a2)@4@A -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@5_2_3 --> Ref(a1)@6@A -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -:deforest -fun f(x) = if x == 0 then Nil else x :: g(x - 1) -fun g(y) = if y == 0 then Nil else y :: f(y - 1) -fun cons(a) = if a is - Nil then 0 - h :: t then h + cons(t) -fun cons1(a1) = if a1 is - Nil then 1 - h :: t then h + cons1(t) -cons(f(0)) + cons1(f(1)) -//│ = 2 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Ref(member:Nil)@4@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Ref(member:Nil)@0@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Ref(member:Nil)@4@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ ------------------------------------ -//│ Ref(member:Nil)@0@1 --> Ref(a1)@2@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@1 --> Ref(a1)@2@Cons -//│ Ref(member:Nil)@4@1 --> Ref(a1)@2@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@1 --> Ref(a1)@2@Cons -//│ Ref(member:Nil)@0@6 --> Ref(a)@7@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@6 --> Ref(a)@7@Cons -//│ Ref(member:Nil)@4@6 --> Ref(a)@7@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@6 --> Ref(a)@7@Cons -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -:deforest -fun map(ls, f) = if ls is - Nil then Nil - h :: t then f(h) :: map(t, f) -map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) -//│ = Cons(4, Cons(6, Nil)) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List(1)) --> -//│ Ref(ls)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> -//│ Ref(ls)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@6@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ ------------------------------------ -//│ Ref(member:Nil)@0@1 --> Ref(ls)@2@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@1 --> Ref(ls)@2@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@ --> Ref(ls)@1@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@ --> Ref(ls)@1@Cons -//│ Ref(member:Nil)@6@ --> Ref(ls)@1@Nil -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// for selection -:deforest -fun map(ls, f) = if ls is - Nil then Nil - h :: t then f(h) :: map(t, f) -map(1 :: 2 :: Nil, x => x + 1) -//│ = Cons(2, Cons(3, Nil)) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@3@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@ --> Ref(ls)@1@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@ --> Ref(ls)@1@Cons -//│ Ref(member:Nil)@3@ --> Ref(ls)@1@Nil -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// for cyclic strategies removal -:deforest -fun f(x) = if x is - A(_) then f(A(2)) - else 0 -f(Nil) -//│ = 0 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// for selection filter -:deforest -fun c1(x) = if x is - h :: t then - if t is - Nil then 0 -c1(1 :: Nil) -//│ = 0 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ Ref(member:Nil)@2@Some(List()) --> -//│ Ref(t)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@ --> Ref(x)@1@Cons -//│ Ref(member:Nil)@2@ --> Ref(t)@1@Nil -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - -// for selection filter -:deforest -fun f(x) = if x is - A then g1(x.x) - B then g2(x.x) -fun g1(y1) = if y1 is - A then 0 -fun g2(y2) = if y2 is - A then 1 -fun apply(f) = - let inner = A(0) - f(A(inner)) -apply(f) -//│ = 0 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@Some(List(1)) --> -//│ Ref(x)@Some(List(2)) -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@Some(List(1)) --> -//│ Ref(y1)@Some(List(2, 4)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@1 --> Ref(x)@2@A -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@1 --> Ref(y1)@2_4@A -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - -:sjs -:deforest -fun inner(y, z) = if y is - B then z -fun dtor(x) = if x is - A then inner(B(4), x.x) -dtor(A(B(0))) -//│ JS (unsanitized): -//│ let dtor, inner, tmp27, tmp28; -//│ inner = function inner(y, z) { -//│ if (y instanceof B1.class) { -//│ return z +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_2_3_tsni --> Ref(a2)@inst_4_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_5_2_3_tsni --> Ref(a1)@inst_6_tsni@A +//│ ==== JS (deforested): ==== +//│ wrap_inst_1_tsni = function wrap_inst_1_tsni(x2) { +//│ return wrap1_inst_1_2_tsni(x2) +//│ }; +//│ wrap1_inst_1_2_tsni = function wrap1_inst_1_2_tsni(x2) { +//│ return p_inst_1_2_3_tsni(x2) +//│ }; +//│ p_inst_1_2_3_tsni = function p_inst_1_2_3_tsni(x2) { +//│ let x3; +//│ x3 = x2; +//│ return () => { +//│ return 2 +//│ } +//│ }; +//│ wrap_inst_5_tsni = function wrap_inst_5_tsni(x2) { +//│ return wrap1_inst_5_2_tsni(x2) +//│ }; +//│ wrap1_inst_5_2_tsni = function wrap1_inst_5_2_tsni(x2) { +//│ return p_inst_5_2_3_tsni(x2) +//│ }; +//│ p_inst_5_2_3_tsni = function p_inst_5_2_3_tsni(x2) { +//│ let x3; +//│ x3 = x2; +//│ return () => { +//│ return 1 +//│ } +//│ }; +//│ f2_inst_4_tsni = function f2_inst_4_tsni(a2) { +//│ return runtime.safeCall(a2()) +//│ }; +//│ f1_inst_6_tsni = function f1_inst_6_tsni(a1) { +//│ return runtime.safeCall(a1()) +//│ }; +//│ p = function p(x2) { +//│ return A1(x2) +//│ }; +//│ wrap1 = function wrap1(x2) { +//│ return p(x2) +//│ }; +//│ wrap = function wrap(x2) { +//│ return wrap1(x2) +//│ }; +//│ f11 = function f1(a1) { +//│ if (a1 instanceof A1.class) { +//│ return 1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ dtor = function dtor(x) { -//│ let tmp29; -//│ if (x instanceof A1.class) { -//│ tmp29 = B1(4); -//│ return inner(tmp29, x.x) +//│ f2 = function f2(a2) { +//│ if (a2 instanceof A1.class) { +//│ return 2 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp27 = B1(0); -//│ tmp28 = A1(tmp27); -//│ dtor(tmp28) -//│ = B(0) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@Some(List(1)) --> -//│ Ref(y)@Some(List(1, 2)) -//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@1 --> Ref(y)@1_2@B -//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@ --> Ref(x)@1@A +//│ tmp8 = wrap_inst_5_tsni(1); +//│ tmp9 = f1_inst_6_tsni(tmp8); +//│ tmp10 = wrap_inst_1_tsni(2); +//│ tmp11 = f2_inst_4_tsni(tmp10); +//│ block$res_deforest2 = tmp9 + tmp11; +//│ undefined +//│ -------- executing ---------- +//│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforest -fun localFuse(x) = if A(x) is - A(y) then B(y) -if localFuse(3) is - B(a) then a -localFuse(4) -//│ = B(4) +fun p1(x) = p2(x) +fun p2(x) = if x == 0 then A(x) else p1(x - 1) +fun c(a) = if a is A(x) then x +c(p1(3)) +//│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1)) --> -//│ Ref($scrut)@Some(List(1)) -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(2)) --> -//│ Ref($scrut)@Some(List(2)) -//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@Some(List(2)) --> -//│ Ref($scrut)@Some(List()) +//│ Ref(a)@Some(List(2)) //│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@1 --> Ref($scrut)@1@A -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@2 --> Ref($scrut)@2@A -//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@2 --> Ref($scrut)@@B -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - -// cyclic strategy, shouldn't fuse -:deforest -fun f(x) = - let res = if x is - A(x) then x - if res > 0 then f(A(res - 1)) else 0 -f(A(4)) +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_tsni --> Ref(a)@inst_2_tsni@A +//│ ==== JS (deforested): ==== +//│ p1_inst_1_tsni = function p1_inst_1_tsni(x2) { +//│ return p2_inst_1_tsni(x2) +//│ }; +//│ p2_inst_1_tsni = function p2_inst_1_tsni(x2) { +//│ let scrut, tmp14, x3; +//│ scrut = x2 == 0; +//│ if (scrut === true) { +//│ x3 = x2; +//│ return () => { +//│ let param0, x4; +//│ param0 = x3; +//│ x4 = param0; +//│ return x4 +//│ } +//│ } else { +//│ tmp14 = x2 - 1; +//│ return p1_inst_1_tsni(tmp14) +//│ } +//│ }; +//│ c_inst_2_tsni = function c_inst_2_tsni(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ p1 = function p1(x2) { +//│ return p2(x2) +//│ }; +//│ p2 = function p2(x2) { +//│ let scrut, tmp14; +//│ scrut = x2 == 0; +//│ if (scrut === true) { +//│ return A1(x2) +//│ } else { +//│ tmp14 = x2 - 1; +//│ return p1(tmp14) +//│ } +//│ }; +//│ c = function c(a) { +//│ let param0, x2; +//│ if (a instanceof A1.class) { +//│ param0 = a.x; +//│ x2 = param0; +//│ return x2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp13 = p1_inst_1_tsni(3); +//│ block$res_deforest3 = c_inst_2_tsni(tmp13); +//│ undefined +//│ -------- executing ---------- //│ = 0 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - - - -// shouldn't have any fusion due to strategy clash -:deforest -fun g(x) = if x is - A(a) then a -fun f(x) = x.x -let o = A(1) -g(o) + f(o) + o.x -//│ = 3 -//│ o = A(1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From cec7eed03b16a54863bc36b7126dd245ebf545e3 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 02:25:54 +0800 Subject: [PATCH 286/303] minor --- .../src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 695c230c30..3b74ca144c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -159,7 +159,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. MatchId, Map[SelId, TempSymbol]] // if a key doesn't exist, it means the final dest is only used once - val finalDestToMatchArmFunSymbols = mutable.LinkedHashMap.empty[FinalDest, BlockMemberSymbol] + val finalDestToMatchArmFunSymbols = mutable.Map.empty[FinalDest, BlockMemberSymbol] val selIdsInAllArmsToSymbolsToReplace = mutable.Map.empty[SelId, TempSymbol | VarSymbol] locally: def matDestToTempSymbolMap(mat: FinalDest.Match) = @@ -211,10 +211,6 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. State.globalThisSymbol + State.runtimeSymbol - - - - object freeVarsOfOriginalMatchesConsideringDeforestation: val store = mutable.Map.empty[MatchId, Ls[Symbol]] def apply(m: MatchId) = store.getOrElseUpdate.curried(m): From 74afb68eac70f1cdf041124dfd392d876699c06d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:32:00 +0800 Subject: [PATCH 287/303] minor --- .../shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala | 2 +- .../shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index efc905526f..52b5db595a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -553,7 +553,7 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): val selExprId = s.exprId preAnalyzer.getResult(selExprId) match case Select(Value.Ref(l), _) => - preAnalyzer.selsToMatchingArmsContainingIt(selExprId).exists(_._1 === matScrutExprId) && + preAnalyzer.selsToMatchingArmsContainingIt(selExprId).exists(_._1 == matScrutExprId) && (l is matScrutSym) && s.instantiationId.get == matExprInstantiationId case _ => false diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 3b74ca144c..8908db968d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -186,7 +186,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. val selExprIdToNewSymbol = mutable.Map.empty[SelId, VarSymbol] for selId <- matchArmDest.selsInArm do val selName = preAnalyzer.getResult(selId._1).asInstanceOf[Select].name - val symName = s"_deforest_${cls.nme}_${selName}_${selId._2.makeSuffix(preAnalyzer)}" + val symName = s"_deforest_${cls.nme}_${selName.name}_${selId._2.makeSuffix(preAnalyzer)}" val sym = selNameToNewSymbol.getOrElseUpdate.curried(selName): VarSymbol(Tree.Ident(symName)) selExprIdToNewSymbol += selId -> sym From b67ff20f2bd4bbba0c661c287e93f39c3bc4e5fc Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:46:46 +0800 Subject: [PATCH 288/303] vars for outside symbols used in function bodies shouldn't be duplicated --- .../main/scala/hkmc2/codegen/deforest/Analyze.scala | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 52b5db595a..f2442dc093 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -138,23 +138,30 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: case Value.Ref(l) => chk(l.asBlkMember.get) case _ => die - + private val resultIdToStableId = mutable.Map.empty[ResultId, Int] private var stableResuldIt = 0 private var inMatchScrutsArms: Ls[ResultId -> Opt[ClassLikeSymbol]] = Nil private def inMatchScruts = inMatchScrutsArms.unzip._1 private var inFunDef: Opt[BlockMemberSymbol] = N + private var symsDefinedInFun: Opt[Set[Symbol]] = N override def applyFunDefn(fun: FunDefn): Unit = funSymToFun += fun.sym -> fun inFunDef match - case N => inFunDef = S(fun.sym) + case N => + inFunDef = S(fun.sym) + symsDefinedInFun = S(fun.body.definedVars) case S(value) => throw NotDeforestableException("not expecting nested function definitions") super.applyFunDefn(fun) inFunDef = N + symsDefinedInFun = N override def applySymbol(s: Symbol): Unit = s match case s: (BlockMemberSymbol | TempSymbol | VarSymbol | TermSymbol) => symToStratVar.updateWith(s): - case N => S(freshVar(s.nme, inFunDef).asProdStrat) + case N => + val inFunOrNot = inFunDef.fold(false): _ => + symsDefinedInFun.get.contains(s) + S(freshVar(s.nme, if inFunOrNot then inFunDef else N).asProdStrat) case S(x) => S(x) case _ => () From 39cfebc715c72f150610d576de8c2d36d9add364 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 18:59:53 +0800 Subject: [PATCH 289/303] more fix to sym to strat vars; pipeline --- .../hkmc2/codegen/deforest/Analyze.scala | 20 +- .../deforest/def-dup/new-dup-simple.mls | 938 ++---------------- .../mlscript/deforest/def-dup/new-simple.mls | 226 +---- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 100 +- 4 files changed, 183 insertions(+), 1101 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index f2442dc093..7ca7716734 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -144,26 +144,34 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: private var inMatchScrutsArms: Ls[ResultId -> Opt[ClassLikeSymbol]] = Nil private def inMatchScruts = inMatchScrutsArms.unzip._1 private var inFunDef: Opt[BlockMemberSymbol] = N - private var symsDefinedInFun: Opt[Set[Symbol]] = N + private var symsDefinedForFun: Opt[Set[Symbol]] = N override def applyFunDefn(fun: FunDefn): Unit = funSymToFun += fun.sym -> fun inFunDef match case N => inFunDef = S(fun.sym) - symsDefinedInFun = S(fun.body.definedVars) + symsDefinedForFun = S(fun.body.definedVars ++ fun.params.flatMap(_.params.map(_.sym)) + fun.sym) case S(value) => throw NotDeforestableException("not expecting nested function definitions") super.applyFunDefn(fun) inFunDef = N - symsDefinedInFun = N + symsDefinedForFun = N override def applySymbol(s: Symbol): Unit = s match - case s: (BlockMemberSymbol | TempSymbol | VarSymbol | TermSymbol) => symToStratVar.updateWith(s): + case s: BlockMemberSymbol if s.trmImplTree.fold(false)(_.k is syntax.Fun) => symToStratVar.updateWith(s): + case N => S(freshVar(s.nme, S(s)).asProdStrat) + case S(x) => S(x) + // term symbol: variable in patterns so they are always inside the current fundefn (if any) + case s: (TermSymbol | TempSymbol) => symToStratVar.updateWith(s): + case N => S(freshVar(s.nme, inFunDef).asProdStrat) + case S(x) => S(x) + case v: (BlockMemberSymbol | VarSymbol) => symToStratVar.updateWith(s): case N => val inFunOrNot = inFunDef.fold(false): _ => - symsDefinedInFun.get.contains(s) + symsDefinedForFun.get.contains(s) S(freshVar(s.nme, if inFunOrNot then inFunDef else N).asProdStrat) case S(x) => S(x) - case _ => () + case _: (TopLevelSymbol | BuiltinSymbol | ClassLikeSymbol) => () + case _ => die override def applyResult(r: Result): Unit = resultIdToResult += r.uid -> r diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls index 2f580ec4b0..e84e9e9759 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls @@ -18,157 +18,14 @@ fun f2(ls2, acc) = if ls2 is f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ = 51 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List()) --> -//│ Ref(ls1)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(3)) --> -//│ Ref(ls1)@Some(List(1)) -//│ Ref(member:Nil)@4@Some(List()) --> -//│ Ref(ls2)@Some(List(5)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(6)) --> -//│ Ref(ls2)@Some(List(5)) -//│ Ref(member:Nil)@7@Some(List()) --> -//│ Ref(ls1)@Some(List(8)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@Some(List(9)) --> -//│ Ref(ls1)@Some(List(8)) -//│ ------------------------------------ -//│ Ref(member:Nil)@0@inst__tsni --> Ref(ls1)@inst_1_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_3_tsni --> Ref(ls1)@inst_1_tsni@Cons -//│ Ref(member:Nil)@4@inst__tsni --> Ref(ls2)@inst_5_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_6_tsni --> Ref(ls2)@inst_5_tsni@Cons -//│ Ref(member:Nil)@7@inst__tsni --> Ref(ls1)@inst_8_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@2@inst_9_tsni --> Ref(ls1)@inst_8_tsni@Cons -//│ ==== JS (deforested): ==== -//│ to_inst_3_tsni = function to_inst_3_tsni(n, acc) { -//│ let scrut, tmp14, tmp15, h, t; -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return acc -//│ } else { -//│ tmp14 = n - 1; -//│ h = n; -//│ t = acc; -//│ tmp15 = (acc1) => { -//│ let param0, param1, h1, t1, tmp16; -//│ param0 = h; -//│ param1 = t; -//│ h1 = param0; -//│ t1 = param1; -//│ tmp16 = acc1 + h1; -//│ return f1_inst_1_tsni(t1, tmp16) -//│ }; -//│ return to_inst_3_tsni(tmp14, tmp15) -//│ } -//│ }; -//│ to_inst_6_tsni = function to_inst_6_tsni(n, acc) { -//│ let scrut, tmp14, tmp15, h, t; -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return acc -//│ } else { -//│ tmp14 = n - 1; -//│ h = n; -//│ t = acc; -//│ tmp15 = (acc1) => { -//│ let param0, param1, hh, tt, tmp16, tmp17; -//│ param0 = h; -//│ param1 = t; -//│ hh = param0; -//│ tt = param1; -//│ tmp16 = acc1 + hh; -//│ tmp17 = tmp16 + 1; -//│ return f2_inst_5_tsni(tt, tmp17) -//│ }; -//│ return to_inst_6_tsni(tmp14, tmp15) -//│ } -//│ }; -//│ to_inst_9_tsni = function to_inst_9_tsni(n, acc) { -//│ let scrut, tmp14, tmp15, h, t; -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return acc -//│ } else { -//│ tmp14 = n - 1; -//│ h = n; -//│ t = acc; -//│ tmp15 = (acc1) => { -//│ let param0, param1, h1, t1, tmp16; -//│ param0 = h; -//│ param1 = t; -//│ h1 = param0; -//│ t1 = param1; -//│ tmp16 = acc1 + h1; -//│ return f1_inst_8_tsni(t1, tmp16) -//│ }; -//│ return to_inst_9_tsni(tmp14, tmp15) -//│ } -//│ }; -//│ f1_inst_1_tsni = function f1_inst_1_tsni(ls1, acc) { -//│ return runtime.safeCall(ls1(acc)) -//│ }; -//│ f2_inst_5_tsni = function f2_inst_5_tsni(ls2, acc) { -//│ return runtime.safeCall(ls2(acc)) -//│ }; -//│ f1_inst_8_tsni = function f1_inst_8_tsni(ls1, acc) { -//│ return runtime.safeCall(ls1(acc)) -//│ }; -//│ to = function to(n, acc) { -//│ let scrut, tmp14, tmp15; -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return acc -//│ } else { -//│ tmp14 = n - 1; -//│ tmp15 = Cons1(n, acc); -//│ return to(tmp14, tmp15) -//│ } -//│ }; -//│ f1 = function f1(ls1, acc) { -//│ let param0, param1, h, t, tmp14; -//│ if (ls1 instanceof Nil1.class) { -//│ return acc -//│ } else if (ls1 instanceof Cons1.class) { -//│ param0 = ls1.h; -//│ param1 = ls1.t; -//│ h = param0; -//│ t = param1; -//│ tmp14 = acc + h; -//│ return f1(t, tmp14) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f2 = function f2(ls2, acc) { -//│ let param0, param1, hh, tt, tmp14, tmp15; -//│ if (ls2 instanceof Nil1.class) { -//│ return acc -//│ } else if (ls2 instanceof Cons1.class) { -//│ param0 = ls2.h; -//│ param1 = ls2.t; -//│ hh = param0; -//│ tt = param1; -//│ tmp14 = acc + hh; -//│ tmp15 = tmp14 + 1; -//│ return f2(tt, tmp15) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp7 = to_inst_9_tsni(4, (acc) => { -//│ return acc -//│ }); -//│ tmp8 = f1_inst_8_tsni(tmp7, 0); -//│ tmp9 = to_inst_6_tsni(5, (acc) => { -//│ return acc -//│ }); -//│ tmp10 = f2_inst_5_tsni(tmp9, 0); -//│ tmp11 = tmp8 + tmp10; -//│ tmp12 = to_inst_3_tsni(6, (acc) => { -//│ return acc -//│ }); -//│ tmp13 = f1_inst_1_tsni(tmp12, 0); -//│ block$res_deforest = tmp11 + tmp13; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls1)@inst_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_0_tsni --> Ref(ls1)@inst_3_tsni@Cons +//│ Ref(member:Nil)@8@inst__tsni --> Ref(ls2)@inst_4_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_1_tsni --> Ref(ls2)@inst_4_tsni@Cons +//│ Ref(member:Nil)@9@inst__tsni --> Ref(ls1)@inst_5_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_2_tsni --> Ref(ls1)@inst_5_tsni@Cons +//│ -------------- executing ------------- //│ = 51 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -180,52 +37,10 @@ fun f2(a2) = if a2 is A then 2 f1(id(A(1))) + f2(id(A(2))) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@Some(List()) --> -//│ Ref(a2)@Some(List(1)) -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@Some(List()) --> -//│ Ref(a1)@Some(List(3)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@0@inst__tsni --> Ref(a2)@inst_1_tsni@A -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@2@inst__tsni --> Ref(a1)@inst_3_tsni@A -//│ ==== JS (deforested): ==== -//│ f2_inst_1_tsni = function f2_inst_1_tsni(a2) { -//│ return runtime.safeCall(a2()) -//│ }; -//│ f1_inst_3_tsni = function f1_inst_3_tsni(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ id = function id(x2) { -//│ return x2 -//│ }; -//│ f11 = function f1(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f21 = function f2(a2) { -//│ if (a2 instanceof A1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ x1 = 1; -//│ tmp20 = () => { -//│ return 1 -//│ }; -//│ tmp21 = id(tmp20); -//│ tmp22 = f1_inst_3_tsni(tmp21); -//│ x = 2; -//│ tmp23 = () => { -//│ return 2 -//│ }; -//│ tmp24 = id(tmp23); -//│ tmp25 = f2_inst_1_tsni(tmp24); -//│ block$res_deforest1 = tmp22 + tmp25; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(a2)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@3@inst__tsni --> Ref(a1)@inst_1_tsni@A +//│ -------------- executing ------------- //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -239,76 +54,10 @@ fun f2(a2) = if a2 is A then 2 f1(wrap(1)) + f2(wrap(2)) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1, 2, 3)) --> -//│ Ref(a2)@Some(List(4)) -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> -//│ Ref(a1)@Some(List(6)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_2_3_tsni --> Ref(a2)@inst_4_tsni@A -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_5_2_3_tsni --> Ref(a1)@inst_6_tsni@A -//│ ==== JS (deforested): ==== -//│ wrap_inst_1_tsni = function wrap_inst_1_tsni(x2) { -//│ return wrap1_inst_1_2_tsni(x2) -//│ }; -//│ wrap1_inst_1_2_tsni = function wrap1_inst_1_2_tsni(x2) { -//│ return p_inst_1_2_3_tsni(x2) -//│ }; -//│ p_inst_1_2_3_tsni = function p_inst_1_2_3_tsni(x2) { -//│ let x3; -//│ x3 = x2; -//│ return () => { -//│ return 2 -//│ } -//│ }; -//│ wrap_inst_5_tsni = function wrap_inst_5_tsni(x2) { -//│ return wrap1_inst_5_2_tsni(x2) -//│ }; -//│ wrap1_inst_5_2_tsni = function wrap1_inst_5_2_tsni(x2) { -//│ return p_inst_5_2_3_tsni(x2) -//│ }; -//│ p_inst_5_2_3_tsni = function p_inst_5_2_3_tsni(x2) { -//│ let x3; -//│ x3 = x2; -//│ return () => { -//│ return 1 -//│ } -//│ }; -//│ f2_inst_4_tsni = function f2_inst_4_tsni(a2) { -//│ return runtime.safeCall(a2()) -//│ }; -//│ f1_inst_6_tsni = function f1_inst_6_tsni(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ p = function p(x2) { -//│ return A1(x2) -//│ }; -//│ wrap1 = function wrap1(x2) { -//│ return p(x2) -//│ }; -//│ wrap = function wrap(x2) { -//│ return wrap1(x2) -//│ }; -//│ f12 = function f1(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f22 = function f2(a2) { -//│ if (a2 instanceof A1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp30 = wrap_inst_5_tsni(1); -//│ tmp31 = f1_inst_6_tsni(tmp30); -//│ tmp32 = wrap_inst_1_tsni(2); -//│ tmp33 = f2_inst_4_tsni(tmp32); -//│ block$res_deforest2 = tmp31 + tmp33; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@6@inst_0_1_2_tsni --> Ref(a2)@inst_4_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@6@inst_3_1_2_tsni --> Ref(a1)@inst_5_tsni@A +//│ -------------- executing ------------- //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -325,189 +74,16 @@ fun cons1(a1) = if a1 is cons(f(0)) + cons1(f(1)) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Ref(member:Nil)@4@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(1)) --> -//│ Ref(a1)@Some(List(2)) -//│ Ref(member:Nil)@0@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Ref(member:Nil)@4@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@Some(List(6)) --> -//│ Ref(a)@Some(List(7)) -//│ ------------------------------------ -//│ Ref(member:Nil)@0@inst_1_tsni --> Ref(a1)@inst_2_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@inst_1_tsni --> Ref(a1)@inst_2_tsni@Cons -//│ Ref(member:Nil)@4@inst_1_tsni --> Ref(a1)@inst_2_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@inst_1_tsni --> Ref(a1)@inst_2_tsni@Cons -//│ Ref(member:Nil)@0@inst_6_tsni --> Ref(a)@inst_7_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@3@inst_6_tsni --> Ref(a)@inst_7_tsni@Cons -//│ Ref(member:Nil)@4@inst_6_tsni --> Ref(a)@inst_7_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@5@inst_6_tsni --> Ref(a)@inst_7_tsni@Cons -//│ ==== JS (deforested): ==== -//│ match_a1_arm_Nil_inst_2_tsni = function match_a1_arm_Nil_inst_2_tsni() { -//│ return 1 -//│ }; -//│ match_a1_arm_Cons_inst_2_tsni = function match_a1_arm_Cons_inst_2_tsni(_deforest_Cons_Ident$_h$__inst_2_tsni, _deforest_Cons_Ident$_t$__inst_2_tsni) { -//│ let param0, param1, h, t, tmp42; -//│ param0 = _deforest_Cons_Ident$_h$__inst_2_tsni; -//│ param1 = _deforest_Cons_Ident$_t$__inst_2_tsni; -//│ h = param0; -//│ t = param1; -//│ tmp42 = cons1_inst_2_tsni(t); -//│ return h + tmp42 -//│ }; -//│ match_a_arm_Nil_inst_7_tsni = function match_a_arm_Nil_inst_7_tsni() { -//│ return 0 -//│ }; -//│ match_a_arm_Cons_inst_7_tsni = function match_a_arm_Cons_inst_7_tsni(_deforest_Cons_Ident$_h$__inst_7_tsni, _deforest_Cons_Ident$_t$__inst_7_tsni) { -//│ let param0, param1, h, t, tmp42; -//│ param0 = _deforest_Cons_Ident$_h$__inst_7_tsni; -//│ param1 = _deforest_Cons_Ident$_t$__inst_7_tsni; -//│ h = param0; -//│ t = param1; -//│ tmp42 = cons_inst_7_tsni(t); -//│ return h + tmp42 -//│ }; -//│ f_inst_1_tsni = function f_inst_1_tsni(x2) { -//│ let scrut, tmp42, tmp43, h, t; -//│ scrut = x2 == 0; -//│ if (scrut === true) { -//│ return () => { -//│ return match_a1_arm_Nil_inst_2_tsni() -//│ } -//│ } else { -//│ tmp42 = x2 - 1; -//│ tmp43 = g_inst_1_tsni(tmp42); -//│ h = x2; -//│ t = tmp43; -//│ return () => { -//│ return match_a1_arm_Cons_inst_2_tsni(h, t) -//│ } -//│ } -//│ }; -//│ g_inst_1_tsni = function g_inst_1_tsni(y) { -//│ let scrut, tmp42, tmp43, h, t; -//│ scrut = y == 0; -//│ if (scrut === true) { -//│ return () => { -//│ return match_a1_arm_Nil_inst_2_tsni() -//│ } -//│ } else { -//│ tmp42 = y - 1; -//│ tmp43 = f_inst_1_tsni(tmp42); -//│ h = y; -//│ t = tmp43; -//│ return () => { -//│ return match_a1_arm_Cons_inst_2_tsni(h, t) -//│ } -//│ } -//│ }; -//│ f_inst_6_tsni = function f_inst_6_tsni(x2) { -//│ let scrut, tmp42, tmp43, h, t; -//│ scrut = x2 == 0; -//│ if (scrut === true) { -//│ return () => { -//│ return match_a_arm_Nil_inst_7_tsni() -//│ } -//│ } else { -//│ tmp42 = x2 - 1; -//│ tmp43 = g_inst_6_tsni(tmp42); -//│ h = x2; -//│ t = tmp43; -//│ return () => { -//│ return match_a_arm_Cons_inst_7_tsni(h, t) -//│ } -//│ } -//│ }; -//│ g_inst_6_tsni = function g_inst_6_tsni(y) { -//│ let scrut, tmp42, tmp43, h, t; -//│ scrut = y == 0; -//│ if (scrut === true) { -//│ return () => { -//│ return match_a_arm_Nil_inst_7_tsni() -//│ } -//│ } else { -//│ tmp42 = y - 1; -//│ tmp43 = f_inst_6_tsni(tmp42); -//│ h = y; -//│ t = tmp43; -//│ return () => { -//│ return match_a_arm_Cons_inst_7_tsni(h, t) -//│ } -//│ } -//│ }; -//│ cons1_inst_2_tsni = function cons1_inst_2_tsni(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ cons_inst_7_tsni = function cons_inst_7_tsni(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ f = function f(x2) { -//│ let scrut, tmp42, tmp43; -//│ scrut = x2 == 0; -//│ if (scrut === true) { -//│ return Nil1 -//│ } else { -//│ tmp42 = x2 - 1; -//│ tmp43 = g(tmp42); -//│ return Cons1(x2, tmp43) -//│ } -//│ }; -//│ g = function g(y) { -//│ let scrut, tmp42, tmp43; -//│ scrut = y == 0; -//│ if (scrut === true) { -//│ return Nil1 -//│ } else { -//│ tmp42 = y - 1; -//│ tmp43 = f(tmp42); -//│ return Cons1(y, tmp43) -//│ } -//│ }; -//│ cons = function cons(a) { -//│ let param0, param1, h, t, tmp42; -//│ if (a instanceof Nil1.class) { -//│ return 0 -//│ } else if (a instanceof Cons1.class) { -//│ param0 = a.h; -//│ param1 = a.t; -//│ h = param0; -//│ t = param1; -//│ tmp42 = cons(t); -//│ return h + tmp42 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ cons1 = function cons1(a1) { -//│ let param0, param1, h, t, tmp42; -//│ if (a1 instanceof Nil1.class) { -//│ return 1 -//│ } else if (a1 instanceof Cons1.class) { -//│ param0 = a1.h; -//│ param1 = a1.t; -//│ h = param0; -//│ t = param1; -//│ tmp42 = cons1(t); -//│ return h + tmp42 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp38 = f_inst_6_tsni(0); -//│ tmp39 = cons_inst_7_tsni(tmp38); -//│ tmp40 = f_inst_1_tsni(1); -//│ tmp41 = cons1_inst_2_tsni(tmp40); -//│ block$res_deforest3 = tmp39 + tmp41; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@4@inst_0_tsni --> Ref(a1)@inst_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@5@inst_0_tsni --> Ref(a1)@inst_2_tsni@Cons +//│ Ref(member:Nil)@6@inst_0_tsni --> Ref(a1)@inst_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@7@inst_0_tsni --> Ref(a1)@inst_2_tsni@Cons +//│ Ref(member:Nil)@4@inst_1_tsni --> Ref(a)@inst_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@5@inst_1_tsni --> Ref(a)@inst_3_tsni@Cons +//│ Ref(member:Nil)@6@inst_1_tsni --> Ref(a)@inst_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(y)), Arg(false,Ref($tmp))))@7@inst_1_tsni --> Ref(a)@inst_3_tsni@Cons +//│ -------------- executing ------------- //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -519,89 +95,13 @@ fun map(ls, f) = if ls is map(map(1 :: 2 :: Nil, x => x + 1), x => x * 2) //│ = Cons(4, Cons(6, Nil)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Ref(member:Nil)@0@Some(List(1)) --> -//│ Ref(ls)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@Some(List(1)) --> -//│ Ref(ls)@Some(List(2)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@6@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ ------------------------------------ -//│ Ref(member:Nil)@0@inst_1_tsni --> Ref(ls)@inst_2_tsni@Nil -//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_1_tsni --> Ref(ls)@inst_2_tsni@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ls)@inst_1_tsni@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ls)@inst_1_tsni@Cons -//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls)@inst_1_tsni@Nil -//│ ==== JS (deforested): ==== -//│ match_ls_arm_Cons_inst_1_tsni = function match_ls_arm_Cons_inst_1_tsni(f3, _deforest_Cons_Ident$_h$__inst_1_tsni, _deforest_Cons_Ident$_t$__inst_1_tsni) { -//│ let param0, param1, h2, t2, tmp48, tmp49, h3, t3; -//│ param0 = _deforest_Cons_Ident$_h$__inst_1_tsni; -//│ param1 = _deforest_Cons_Ident$_t$__inst_1_tsni; -//│ h2 = param0; -//│ t2 = param1; -//│ tmp48 = runtime.safeCall(f3(h2)); -//│ tmp49 = map_inst_1_tsni(t2, f3); -//│ h3 = tmp48; -//│ t3 = tmp49; -//│ return (f4) => { -//│ param0 = h3; -//│ param1 = t3; -//│ h2 = param0; -//│ t2 = param1; -//│ tmp48 = runtime.safeCall(f4(h2)); -//│ tmp49 = map_inst_2_tsni(t2, f4); -//│ return Cons1(tmp48, tmp49) -//│ } -//│ }; -//│ map_inst_1_tsni = function map_inst_1_tsni(ls, f3) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ map_inst_2_tsni = function map_inst_2_tsni(ls, f3) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ map = function map(ls, f3) { -//│ let param0, param1, h2, t2, tmp48, tmp49; -//│ if (ls instanceof Nil1.class) { -//│ return Nil1 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h2 = param0; -//│ t2 = param1; -//│ tmp48 = runtime.safeCall(f3(h2)); -//│ tmp49 = map(t2, f3); -//│ return Cons1(tmp48, tmp49) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ h1 = 2; -//│ t1 = (f3) => { -//│ return (f4) => { -//│ return Nil1 -//│ } -//│ }; -//│ tmp45 = (f3) => { -//│ return match_ls_arm_Cons_inst_1_tsni(f3, h1, t1) -//│ }; -//│ h = 1; -//│ t = tmp45; -//│ tmp46 = (f3) => { -//│ return match_ls_arm_Cons_inst_1_tsni(f3, h, t) -//│ }; -//│ lambda2 = (undefined, function (x2) { -//│ return x2 + 1 -//│ }); -//│ tmp47 = map_inst_1_tsni(tmp46, lambda2); -//│ lambda3 = (undefined, function (x2) { -//│ return x2 * 2 -//│ }); -//│ block$res_deforest4 = map_inst_2_tsni(tmp47, lambda3); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(4, Cons(6, Nil)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -614,64 +114,11 @@ fun map(ls, f) = if ls is map(1 :: 2 :: Nil, x => x + 1) //│ = Cons(2, Cons(3, Nil)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ Ref(member:Nil)@3@Some(List()) --> -//│ Ref(ls)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@0@inst__tsni --> Ref(ls)@inst_1_tsni@Cons -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@inst__tsni --> Ref(ls)@inst_1_tsni@Cons -//│ Ref(member:Nil)@3@inst__tsni --> Ref(ls)@inst_1_tsni@Nil -//│ ==== JS (deforested): ==== -//│ match_ls_arm_Cons_inst_1_tsni1 = function match_ls_arm_Cons_inst_1_tsni(f3, _deforest_Cons_Ident$_h$__inst_1_tsni, _deforest_Cons_Ident$_t$__inst_1_tsni) { -//│ let param0, param1, h4, t4, tmp52, tmp53; -//│ param0 = _deforest_Cons_Ident$_h$__inst_1_tsni; -//│ param1 = _deforest_Cons_Ident$_t$__inst_1_tsni; -//│ h4 = param0; -//│ t4 = param1; -//│ tmp52 = runtime.safeCall(f3(h4)); -//│ tmp53 = map_inst_1_tsni1(t4, f3); -//│ return Cons1(tmp52, tmp53) -//│ }; -//│ map_inst_1_tsni1 = function map_inst_1_tsni(ls, f3) { -//│ return runtime.safeCall(ls(f3)) -//│ }; -//│ map1 = function map(ls, f3) { -//│ let param0, param1, h4, t4, tmp52, tmp53; -//│ if (ls instanceof Nil1.class) { -//│ return Nil1 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h4 = param0; -//│ t4 = param1; -//│ tmp52 = runtime.safeCall(f3(h4)); -//│ tmp53 = map1(t4, f3); -//│ return Cons1(tmp52, tmp53) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ h3 = 2; -//│ t3 = (f3) => { -//│ return Nil1 -//│ }; -//│ tmp50 = (f3) => { -//│ return match_ls_arm_Cons_inst_1_tsni1(f3, h3, t3) -//│ }; -//│ h2 = 1; -//│ t2 = tmp50; -//│ tmp51 = (f3) => { -//│ return match_ls_arm_Cons_inst_1_tsni1(f3, h2, t2) -//│ }; -//│ lambda5 = (undefined, function (x2) { -//│ return x2 + 1 -//│ }); -//│ block$res_deforest5 = map_inst_1_tsni1(tmp51, lambda5); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@2@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Ref(member:Nil)@3@inst__tsni --> Ref(ls)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(2, Cons(3, Nil)) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -684,21 +131,8 @@ fun f(x) = if x is f(Nil) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ -//│ ==== JS (deforested): ==== -//│ f3 = function f(x2) { -//│ let param0, tmp52; -//│ if (x2 instanceof A1.class) { -//│ param0 = x2.x; -//│ tmp52 = A1(2); -//│ return f3(tmp52) -//│ } else { -//│ return 0 -//│ } -//│ }; -//│ block$res_deforest6 = f3(Nil1); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -712,48 +146,10 @@ fun c1(x) = if x is c1(1 :: Nil) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ Ref(member:Nil)@2@Some(List()) --> -//│ Ref(t)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@0@inst__tsni --> Ref(x)@inst_1_tsni@Cons -//│ Ref(member:Nil)@2@inst__tsni --> Ref(t)@inst_1_tsni@Nil -//│ ==== JS (deforested): ==== -//│ c1_inst_1_tsni = function c1_inst_1_tsni(x2) { -//│ return runtime.safeCall(x2()) -//│ }; -//│ c1 = function c1(x2) { -//│ let param0, param1, h5, t5; -//│ if (x2 instanceof Cons1.class) { -//│ param0 = x2.h; -//│ param1 = x2.t; -//│ h5 = param0; -//│ t5 = param1; -//│ if (t5 instanceof Nil1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ h4 = 1; -//│ t4 = () => { -//│ return 0 -//│ }; -//│ tmp53 = () => { -//│ let param0, param1, h5, t5; -//│ param0 = h4; -//│ param1 = t4; -//│ h5 = param0; -//│ t5 = param1; -//│ return runtime.safeCall(t5()) -//│ }; -//│ block$res_deforest7 = c1_inst_1_tsni(tmp53); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(member:Nil))))@1@inst__tsni --> Ref(x)@inst_0_tsni@Cons +//│ Ref(member:Nil)@2@inst__tsni --> Ref(t)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -773,144 +169,28 @@ fun apply(f) = apply(f) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@Some(List(1)) --> -//│ Ref(x)@Some(List(2)) -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@Some(List(1)) --> -//│ Ref(y1)@Some(List(2, 4)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@0@inst_1_tsni --> Ref(x)@inst_2_tsni@A -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@3@inst_1_tsni --> Ref(y1)@inst_2_4_tsni@A -//│ ==== JS (deforested): ==== -//│ apply_inst_1_tsni = function apply_inst_1_tsni(f5) { -//│ let inner, tmp54, tmp55, x2, x3; -//│ x3 = 0; -//│ tmp54 = () => { -//│ return 0 -//│ }; -//│ inner = tmp54; -//│ x2 = inner; -//│ tmp55 = () => { -//│ return g1_inst_2_4_tsni(x2) -//│ }; -//│ return runtime.safeCall(f5(tmp55)) -//│ }; -//│ f_inst_2_tsni = function f_inst_2_tsni(x2) { -//│ return runtime.safeCall(x2()) -//│ }; -//│ g1_inst_2_4_tsni = function g1_inst_2_4_tsni(y1) { -//│ return runtime.safeCall(y1()) -//│ }; -//│ f4 = function f(x2) { -//│ if (x2 instanceof A1.class) { -//│ return g1(x2.x) -//│ } else if (x2 instanceof B1.class) { -//│ return g2(x2.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ g1 = function g1(y1) { -//│ if (y1 instanceof A1.class) { -//│ return 0 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ g2 = function g2(y2) { -//│ if (y2 instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ apply = function apply(f5) { -//│ let inner, tmp54, tmp55; -//│ tmp54 = A1(0); -//│ inner = tmp54; -//│ tmp55 = A1(inner); -//│ return runtime.safeCall(f5(tmp55)) -//│ }; -//│ block$res_deforest8 = apply_inst_1_tsni(f_inst_2_tsni); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(inner))))@3@inst_0_tsni --> Ref(x)@inst_1_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(0)))))@4@inst_0_tsni --> Ref(y1)@inst_1_2_tsni@A +//│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs + :deforest fun inner(y, z) = if y is B then z fun dtor(x) = if x is A then inner(B(4), x.x) dtor(A(B(0))) -//│ JS (unsanitized): -//│ let dtor, inner, tmp54, tmp55; -//│ inner = function inner(y, z) { -//│ if (y instanceof B1.class) { -//│ return z -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ dtor = function dtor(x2) { -//│ let tmp56; -//│ if (x2 instanceof A1.class) { -//│ tmp56 = B1(4); -//│ return inner(tmp56, x2.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp54 = B1(0); -//│ tmp55 = A1(tmp54); -//│ dtor(tmp55) //│ = B(0) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@Some(List(1)) --> -//│ Ref(y)@Some(List(1, 2)) -//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@0@inst_1_tsni --> Ref(y)@inst_1_2_tsni@B -//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(x)@inst_1_tsni@A -//│ ==== JS (deforested): ==== -//│ dtor_inst_1_tsni = function dtor_inst_1_tsni(x3) { -//│ return runtime.safeCall(x3()) -//│ }; -//│ inner_inst_1_2_tsni = function inner_inst_1_2_tsni(y, z) { -//│ return runtime.safeCall(y(z)) -//│ }; -//│ inner = function inner(y, z) { -//│ if (y instanceof B1.class) { -//│ return z -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ dtor = function dtor(x3) { -//│ let tmp58; -//│ if (x3 instanceof A1.class) { -//│ tmp58 = B1(4); -//│ return inner(tmp58, x3.x) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp56 = B1(0); -//│ x2 = tmp56; -//│ tmp57 = () => { -//│ let tmp58, x3; -//│ x3 = 4; -//│ tmp58 = (z) => { -//│ return z -//│ }; -//│ return inner_inst_1_2_tsni(tmp58, x2) -//│ }; -//│ block$res_deforest9 = dtor_inst_1_tsni(tmp57); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(4)))))@2@inst_0_tsni --> Ref(y)@inst_0_1_tsni@B +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = B(0) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -923,61 +203,11 @@ if localFuse(3) is localFuse(4) //│ = B(4) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1)) --> -//│ Ref($scrut)@Some(List(1)) -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(2)) --> -//│ Ref($scrut)@Some(List(2)) -//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@Some(List(2)) --> -//│ Ref($scrut)@Some(List()) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_tsni --> Ref($scrut)@inst_1_tsni@A -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_2_tsni --> Ref($scrut)@inst_2_tsni@A -//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@inst_2_tsni --> Ref($scrut)@inst__tsni@B -//│ ==== JS (deforested): ==== -//│ localFuse_inst_1_tsni = function localFuse_inst_1_tsni(x3) { -//│ let scrut1, x4; -//│ x4 = x3; -//│ scrut1 = () => { -//│ let param01, y; -//│ param01 = x4; -//│ y = param01; -//│ return B1(y) -//│ }; -//│ return runtime.safeCall(scrut1()) -//│ }; -//│ localFuse_inst_2_tsni = function localFuse_inst_2_tsni(x3) { -//│ let scrut1, x4; -//│ x4 = x3; -//│ scrut1 = () => { -//│ let param01, y, x5; -//│ param01 = x4; -//│ y = param01; -//│ x5 = y; -//│ return () => { -//│ let tmp59; -//│ param0 = x5; -//│ a = param0; -//│ tmp59 = a; -//│ return localFuse_inst_1_tsni(4) -//│ } -//│ }; -//│ return runtime.safeCall(scrut1()) -//│ }; -//│ localFuse = function localFuse(x3) { -//│ let scrut1, param01, y; -//│ scrut1 = A1(x3); -//│ if (scrut1 instanceof A1.class) { -//│ param01 = scrut1.x; -//│ y = param01; -//│ return B1(y) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ scrut = localFuse_inst_2_tsni(3); -//│ block$res_deforest10 = runtime.safeCall(scrut()); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@2@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@2@inst_1_tsni --> Ref($scrut)@inst_1_tsni@A +//│ Call(Ref(member:B),List(Arg(false,Ref(y))))@3@inst_1_tsni --> Ref($scrut)@inst__tsni@B +//│ -------------- executing ------------- //│ = B(4) //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -992,31 +222,8 @@ fun f(x) = f(A(4)) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ -//│ ==== JS (deforested): ==== -//│ f5 = function f(x3) { -//│ let res, param01, x4, scrut1, tmp61, tmp62, tmp63; -//│ if (x3 instanceof A1.class) { -//│ param01 = x3.x; -//│ x4 = param01; -//│ tmp61 = x4; -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ res = tmp61; -//│ scrut1 = res > 0; -//│ if (scrut1 === true) { -//│ tmp62 = res - 1; -//│ tmp63 = A1(tmp62); -//│ return f5(tmp63) -//│ } else { -//│ return 0 -//│ } -//│ }; -//│ tmp60 = A1(4); -//│ block$res_deforest11 = f5(tmp60); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -1032,28 +239,7 @@ g(o) + f(o) + o.x //│ = 3 //│ o = A(1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ------------------------------------ -//│ ==== JS (deforested): ==== -//│ g3 = function g(x3) { -//│ let param01, a1; -//│ if (x3 instanceof A1.class) { -//│ param01 = x3.x; -//│ a1 = param01; -//│ return a1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f6 = function f(x3) { -//│ return x3.x -//│ }; -//│ tmp65 = A1(1); -//│ o = tmp65; -//│ tmp66 = g3(o); -//│ tmp67 = f6(o); -//│ tmp68 = tmp66 + tmp67; -//│ block$res_deforest12 = tmp68 + o.x; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ -------------- executing ------------- //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls index 36012dee81..9745482085 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-simple.mls @@ -13,67 +13,23 @@ fun f(x) = if x is f(A(1)) //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@inst__tsni --> Ref(x)@inst_1_tsni@A -//│ ==== JS (deforested): ==== -//│ f_inst_1_tsni = function f_inst_1_tsni(x1) { -//│ return runtime.safeCall(x1()) -//│ }; -//│ f = function f(x1) { -//│ let param0, x2; -//│ if (x1 instanceof A1.class) { -//│ param0 = x1.x; -//│ x2 = param0; -//│ return x2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ x = 1; -//│ tmp1 = () => { -//│ let param0, x1; -//│ param0 = x; -//│ x1 = param0; -//│ return x1 -//│ }; -//│ block$res_deforest = f_inst_1_tsni(tmp1); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :deforest fun f(x) = if x is - A then x.x + A(a) then x.x + a f(A(1)) -//│ = 1 +//│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@Some(List()) --> -//│ Ref(x)@Some(List(1)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@0@inst__tsni --> Ref(x)@inst_1_tsni@A -//│ ==== JS (deforested): ==== -//│ f_inst_1_tsni1 = function f_inst_1_tsni(x2) { -//│ return runtime.safeCall(x2()) -//│ }; -//│ f1 = function f(x2) { -//│ if (x2 instanceof A1.class) { -//│ return x2.x -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ x1 = 1; -//│ tmp3 = () => { -//│ return x1 -//│ }; -//│ block$res_deforest1 = f_inst_1_tsni1(tmp3); -//│ undefined -//│ -------- executing ---------- -//│ = 1 +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ -------------- executing ------------- +//│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -87,76 +43,10 @@ fun f2(a2) = if a2 is A then 2 f1(wrap(1)) + f2(wrap(2)) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1, 2, 3)) --> -//│ Ref(a2)@Some(List(4)) -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(5, 2, 3)) --> -//│ Ref(a1)@Some(List(6)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_2_3_tsni --> Ref(a2)@inst_4_tsni@A -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_5_2_3_tsni --> Ref(a1)@inst_6_tsni@A -//│ ==== JS (deforested): ==== -//│ wrap_inst_1_tsni = function wrap_inst_1_tsni(x2) { -//│ return wrap1_inst_1_2_tsni(x2) -//│ }; -//│ wrap1_inst_1_2_tsni = function wrap1_inst_1_2_tsni(x2) { -//│ return p_inst_1_2_3_tsni(x2) -//│ }; -//│ p_inst_1_2_3_tsni = function p_inst_1_2_3_tsni(x2) { -//│ let x3; -//│ x3 = x2; -//│ return () => { -//│ return 2 -//│ } -//│ }; -//│ wrap_inst_5_tsni = function wrap_inst_5_tsni(x2) { -//│ return wrap1_inst_5_2_tsni(x2) -//│ }; -//│ wrap1_inst_5_2_tsni = function wrap1_inst_5_2_tsni(x2) { -//│ return p_inst_5_2_3_tsni(x2) -//│ }; -//│ p_inst_5_2_3_tsni = function p_inst_5_2_3_tsni(x2) { -//│ let x3; -//│ x3 = x2; -//│ return () => { -//│ return 1 -//│ } -//│ }; -//│ f2_inst_4_tsni = function f2_inst_4_tsni(a2) { -//│ return runtime.safeCall(a2()) -//│ }; -//│ f1_inst_6_tsni = function f1_inst_6_tsni(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ p = function p(x2) { -//│ return A1(x2) -//│ }; -//│ wrap1 = function wrap1(x2) { -//│ return p(x2) -//│ }; -//│ wrap = function wrap(x2) { -//│ return wrap1(x2) -//│ }; -//│ f11 = function f1(a1) { -//│ if (a1 instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f2 = function f2(a2) { -//│ if (a2 instanceof A1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp8 = wrap_inst_5_tsni(1); -//│ tmp9 = f1_inst_6_tsni(tmp8); -//│ tmp10 = wrap_inst_1_tsni(2); -//│ tmp11 = f2_inst_4_tsni(tmp10); -//│ block$res_deforest2 = tmp9 + tmp11; -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@6@inst_0_1_2_tsni --> Ref(a2)@inst_4_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@6@inst_3_1_2_tsni --> Ref(a1)@inst_5_tsni@A +//│ -------------- executing ------------- //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -168,59 +58,41 @@ fun c(a) = if a is A(x) then x c(p1(3)) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@Some(List(1)) --> -//│ Ref(a)@Some(List(2)) -//│ ------------------------------------ -//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@0@inst_1_tsni --> Ref(a)@inst_2_tsni@A -//│ ==== JS (deforested): ==== -//│ p1_inst_1_tsni = function p1_inst_1_tsni(x2) { -//│ return p2_inst_1_tsni(x2) -//│ }; -//│ p2_inst_1_tsni = function p2_inst_1_tsni(x2) { -//│ let scrut, tmp14, x3; -//│ scrut = x2 == 0; -//│ if (scrut === true) { -//│ x3 = x2; -//│ return () => { -//│ let param0, x4; -//│ param0 = x3; -//│ x4 = param0; -//│ return x4 -//│ } -//│ } else { -//│ tmp14 = x2 - 1; -//│ return p1_inst_1_tsni(tmp14) -//│ } -//│ }; -//│ c_inst_2_tsni = function c_inst_2_tsni(a) { -//│ return runtime.safeCall(a()) -//│ }; -//│ p1 = function p1(x2) { -//│ return p2(x2) -//│ }; -//│ p2 = function p2(x2) { -//│ let scrut, tmp14; -//│ scrut = x2 == 0; -//│ if (scrut === true) { -//│ return A1(x2) -//│ } else { -//│ tmp14 = x2 - 1; -//│ return p1(tmp14) -//│ } -//│ }; -//│ c = function c(a) { -//│ let param0, x2; -//│ if (a instanceof A1.class) { -//│ param0 = a.x; -//│ x2 = param0; -//│ return x2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp13 = p1_inst_1_tsni(3); -//│ block$res_deforest3 = c_inst_2_tsni(tmp13); -//│ undefined -//│ -------- executing ---------- +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@2@inst_0_tsni --> Ref(a)@inst_1_tsni@A +//│ -------------- executing ------------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + +:deforest +fun f(x) = if x is + A(x) then 2 +f(A(2)) + f(A(3)) +//│ = 4 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ -------------- executing ------------- +//│ = 4 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// let binding outside function body +:deforest +let x = Nil +fun f() = + if x is + Nil then 0 +f() +//│ = 0 +//│ x = Nil +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@1@inst__tsni --> Ref(x)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 82af2d8209..2029691abd 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -25,7 +25,6 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val showRepl = NullaryCommand("showRepl") val traceJS = NullaryCommand("traceJS") val deforestFlag = NullaryCommand("deforest") - val deforestDupFlag = NullaryCommand("deforestDup") val deforestInfo = NullaryCommand("deforestInfo") val expect = Command("expect"): ln => ln.trim @@ -92,25 +91,19 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: output(jsStr) if deforestFlag.isSet then - val deforest = new Deforest(using deforestTL) - val deforestRes -> _ -> num = deforest(le, deforestDupFlag.isSet, output.apply) - deforestRes match - case None => () - case Some(_) if num == 0 => output("No fusion opportunity") - case Some(deforestRes) => - output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") - if showLoweredTree.isSet then - output("\n==== deforested tree ====") - output(deforestRes.showAsTree) - output("\n") - - val je = baseScp.nest.givenIn: - jsb.program(deforestRes, N, wd) - output("==== JS (deforested): ====") - val jsStr = je.stripBreaks.mkString(100) - output(jsStr) - output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") - + import codegen.deforest.* + output(">>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>>") + val pre = new DeforestPreAnalyzer(le.main) + val col = new DeforestConstraintsCollector(pre) + val ana = new DeforestConstrainSolver(col) + val rwp = new DeforestRewritePrepare(ana) + val rw = new DeforestRewriter(rwp) + val deforestRes = rw() + val jsStr = baseScp.nest.givenIn: + jsb.program(Program(Nil, deforestRes), N, wd).stripBreaks.mkString(100) + output(jsStr) + output("<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<<") + if js.isSet then given Elaborator.Ctx = curCtx given Raise = @@ -247,27 +240,50 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: handleDefinedValues(nme, sym, expected)(if sym === resSym then r => correctResult = S(r) else _ => ()) if deforestFlag.isSet then + import codegen.deforest.* + output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") val deforestLow = ltl.givenIn: codegen.Lowering() - val lowered0 = deforestLow.program(blk) - val deforest = new Deforest(using deforestTL) - val maybeDeforestRes -> deforestStat -> num = deforest(lowered0, deforestDupFlag.isSet, output.apply) - maybeDeforestRes match - case None => () - case Some(_) if num == 0 => output("No fusion opportunity") - case Some(deforestRes) => - output(">>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>>") - val resSym -> resNme = getResSymAndResNme("block$res_deforest") - val le = assignResultSymForBlock(deforestRes, resSym) - val (preStr, jsStr) = mkJS(le) - executeJS(preStr, jsStr, resNme) - - if silent.isUnset then - handleDefinedValues("", resSym, expect.get): result => - if correctResult.fold(false)(_ != result) then raise: - ErrorReport( - msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, - source = Diagnostic.Source.Runtime) - - output(deforestStat) - output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") + val le = deforestLow.program(blk) + val pre = new DeforestPreAnalyzer(le.main) + val col = new DeforestConstraintsCollector(pre) + if deforestInfo.isSet then + col.constraints.foreach: (p, c) => + output(s"$p --> $c") + val ana = new DeforestConstrainSolver(col) + val rwp = new DeforestRewritePrepare(ana) + output("---------- deforest summary ----------") + rwp.ctorIdToFinalDest.foreach: + case (ctorid -> dest) => + output: + pre.getResult(ctorid._1).toString() + + "@" + + pre.getStableResultId(ctorid._1) + + "@" + + ctorid._2.makeSuffix(pre) + + " --> " + + dest.toString(pre) + val rw = new DeforestRewriter(rwp) + val deforestRes = rw() + val resSym -> resNme = getResSymAndResNme("block$res_deforest") + val deforestRes2 = assignResultSymForBlock(Program(Nil, deforestRes), resSym) + if showLoweredTree.isSet then + output(s"Lowered:") + output(deforestRes2.showAsTree) + if ppLoweredTree.isSet then + output(s"Pretty Lowered:") + output(Printer.mkDocument(deforestRes2)(using summon[Raise], baseScp).toString) + val (preStr, jsStr) = mkJS(deforestRes2) + if showSanitizedJS.isSet then + output("------ deforested sanitized js -------") + output(jsStr) + output("-------------- executing -------------") + executeJS(preStr, jsStr, resNme) + if silent.isUnset then + handleDefinedValues("", resSym, expect.get): result => + if correctResult.fold(false)(_ != result) then raise: + ErrorReport( + msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, + source = Diagnostic.Source.Runtime) + output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") + From 0f4603521561eaec6693ec12f074fef28f9a240b Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 22:50:01 +0800 Subject: [PATCH 290/303] fix the checking when handling match rests --- .../hkmc2/codegen/deforest/Rewrite.scala | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 8908db968d..04058a6c20 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -133,7 +133,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. // the keySet of linkedHashMap for case (FinalDest.Match(matchId, _), _) <- finalDestToCtorIds - numOfMatchingArms = finalDestToCtorIds.keys.count: + numOfMatchingArms = finalDestToCtorIds.keySet.count: case FinalDest.Match(matId, _) => matId == matchId case _ => false if numOfMatchingArms > 1 @@ -146,6 +146,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. Nil)) case S(x) => S(x) + // if a key doesn't exist, it means the final dest is only used once val finalDestToVarSymbolsToReplaceSelInArms = mutable.Map.empty[ FinalDest, @@ -259,9 +260,14 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // returns the block of match rest, or a `Return` block that calls the function extracted // from the match rest - def getOrElseUpdate(matchId: MatchId): Block = - store.get(matchId) match - case S(R(blk)) => if blk.isInstanceOf[End] then blk else die + private val outsideQueryAvailable = mutable.Set.empty[MatchId] + opaque type IsInnerCall = Bool + def getOrElseUpdate(matchId: MatchId)(using inner: IsInnerCall = false): Block = store.get(matchId) match + case S(R(blk)) => + if inner || blk.isInstanceOf[End] || outsideQueryAvailable.remove(matchId) then + blk + else + lastWords(s"match rest $blk was expected to be used only once, but now it's used more than once") case S(L(fdef -> args)) => Return( Call(Value.Ref(fdef.sym), args.map(a => Arg(false, Value.Ref(a))))(true, false), @@ -282,14 +288,16 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora if isEnd then acc else Begin(acc, parentMatchRest) val withTheRestOfPossiblyFusingMatch = thePossiblyFusingOne.headOption .fold(withAllParentMatchesRests): scrutExprIdOfTheFusingOne => - Begin(withAllParentMatchesRests, getOrElseUpdate(scrutExprIdOfTheFusingOne, instantiationId)) + Begin(withAllParentMatchesRests, getOrElseUpdate(scrutExprIdOfTheFusingOne, instantiationId)(using true)) .flattened + // if the rest is empty or only going to be used once, + // then no need to build a function for it val noNeedToBuild = withTheRestOfPossiblyFusingMatch.isInstanceOf[End] || rewritePrepare.fusingMatchIdToMatchRestFunSymbols.get(matchId).isEmpty - // no need to build a new function for empty rest, or if the rest is only going to be used once if noNeedToBuild then + if inner then outsideQueryAvailable += matchId store.updateWith(matchId): case S(_) => die case N => S(R(withTheRestOfPossiblyFusingMatch)) From 477d19bbfd1ecb01da4d41f628cffcfcb4a38ef7 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Mon, 16 Jun 2025 22:53:55 +0800 Subject: [PATCH 291/303] fix fusion of selection --- .../shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 04058a6c20..613c03709d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -446,7 +446,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora override def applyPath(p: Path): Path = p match // a selection which is a consumer on its own - case s: Select if rewritePrepare.rewritingSelIds(s.uid.withInstId) => applyPath(p) + case Select(qual, _) if rewritePrepare.rewritingSelIds(p.uid.withInstId) => applyPath(qual) // a selection inside a fusing match that needs to be replaced by pre-computed symbols case s: Select if rewritePrepare.selIdsInAllArmsToSymbolsToReplace.get(s.uid.withInstId).isDefined => Value.Ref(rewritePrepare.selIdsInAllArmsToSymbolsToReplace(s.uid.withInstId)) From 9854d014d0f8bcf71da308f7f87eed977c134a2a Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:25:37 +0800 Subject: [PATCH 292/303] remove old deforestation --- hkmc2/shared/src/main/scala/hkmc2/Uid.scala | 3 +- .../src/main/scala/hkmc2/codegen/Block.scala | 9 - .../scala/hkmc2/codegen/Deforestation.scala | 1604 ----------------- .../hkmc2/codegen/deforest/Analyze.scala | 6 +- 4 files changed, 4 insertions(+), 1618 deletions(-) delete mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala index 14d86f3010..5af536aa14 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Uid.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Uid.scala @@ -13,8 +13,7 @@ object Uid: curUid def reset = curUid = -1 object Symbol extends Handler[semantics.Symbol] - object StratVar extends Handler[codegen.StratVar] - object StratVarNew extends Handler[codegen.deforest.StratVarState] + object StratVar extends Handler[codegen.deforest.StratVarState] extension [T] (x: Uid[T]) def <=(rhs: Uid[T]) = x <= rhs diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 9a2852cf6a..876621230b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -464,15 +464,6 @@ sealed abstract class Result extends AutoLocated: case DynSelect(qual, fld, arrayIdx) => qual.freeVarsLLIR ++ fld.freeVarsLLIR case Value.Rcd(args) => args.flatMap(arg => arg.idx.fold(Set.empty)(_.freeVarsLLIR) ++ arg.value.freeVarsLLIR).toSet - // for deforestation - def uid(using d: Deforest) = - import Result.* - val uidValue = ResultId(System.identityHashCode(this)) - d.resultIdToResult.updateWith(uidValue): - case N => S(this) - case S(r) => assert(this is r); S(this) - uidValue - def uid = import Result.* val uidValue = ResultId(System.identityHashCode(this)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala deleted file mode 100644 index e28e37da87..0000000000 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala +++ /dev/null @@ -1,1604 +0,0 @@ -package hkmc2 -package codegen - -import semantics.* -import semantics.Elaborator.State -import syntax.{Literal, Tree} -import utils.* -import mlscript.utils.*, shorthands.* -import scala.collection.mutable -import scala.collection.mutable.LinkedHashMap -import Result.ResultId - -type StratVar -type StratVarId = Uid[StratVar] -type ClsOrModSymbol = ClassLikeSymbol - -sealed abstract class ProdStrat -sealed abstract class ConsStrat - -class StratVarState(val uid: StratVarId, val name: Str, val funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]], val callResOf: Opt[ResultId -> Symbol], val inDef: Opt[BlockMemberSymbol]): - lazy val asProdStrat = ProdVar(this) - lazy val asConsStrat = ConsVar(this) - - override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" - -object StratVarState: - // funRetOrArg: - // None: not representing the parameter type or return type of a function - // Some(Left): parameter type - // Some(right): return type - def freshVar(nme: String = "", callResOf: Opt[ResultId -> Symbol], inDef: Opt[BlockMemberSymbol], funRetOrArg: Opt[Either[BlockMemberSymbol, BlockMemberSymbol]] = N)(using vuid: Uid.StratVar.State) = - val newId = vuid.nextUid - val s = StratVarState(newId, nme, funRetOrArg, callResOf, inDef) - val p = s.asProdStrat - val c = s.asConsStrat - p -> c - - -extension (i: ResultId) - def getResult(using d: Deforest) = d.resultIdToResult(i) - def handleCtorIds[A](k: (ResultId, Select | Value.Ref, ClsOrModSymbol, Ls[Arg]) => A)(using Deforest) = - def handleCallLike(f: Path, args: Ls[Arg]) = f match - case s: Select if s.symbol.flatMap(_.asCls).isDefined => - Some(k(i, s, s.symbol.get.asCls.get, args)) - case v: Value.Ref if v.l.asCls.isDefined => - Some(k(i, v, v.l.asCls.get, args)) - case _ => None - i.getResult match - case Call(fun, args) => handleCallLike(fun, args) - case Instantiate(cls, args) => handleCallLike(cls, args.map(Arg(false, _))) - case s: Select if s.symbol.flatMap(_.asObj).isDefined => - Some(k(i, s, s.symbol.get.asObj.get, Nil)) - case v: Value.Ref if v.l.asObj.isDefined => - Some(k(i, v, v.l.asObj.get, Nil)) - case _ => None - def getClsSymOfUid(using Deforest) = i.handleCtorIds((_, _, s, _) => s).get - def getFunCallBlkMemSym(using Deforest) = i.getResult match - case Call(fun, _) => fun match - case s: Select => s.symbol.flatMap(_.asBlkMember) - case v: Value.Ref => v.l.asBlkMember - case _ => N - case _ => N - -// instantiateId: -// - None: not top level and not from an instantiation -// - Some(Nil): top level -// - Some(ls): non-top-level -case class Ctor(ctor: ClsOrModSymbol, args: Map[TermSymbol, ProdStrat], expr: ResultId, instantiationId: Opt[Ls[ResultId]])(val inDef: Opt[BlockMemberSymbol]) extends ProdStrat -case class ProdFun(l: Ls[ConsStrat], r: ProdStrat) extends ProdStrat - -case class ProdVar(s: StratVarState) extends ProdStrat with StratVarTrait(s): - def instantiate(referSite: ResultId, inDef: Opt[BlockMemberSymbol])(using d: Deforest): ProdStrat = - val funSym = referSite.getFunCallBlkMemSym.orElse: - referSite.getResult match - case sel: Select => sel.symbol.flatMap(_.asBlkMember) - case Value.Ref(l) => l.asBlkMember - case _ => N - funSym match - case None => this - case Some(funSym) => - val constrLs = d.inDefConstraints.getOrUpdate(funSym) - // constr match - // case None => this - // case Some(constrLs) => - println(s"instantiate: $funSym @ ${referSite.getResult} : ${constrLs}") - val stratVarMap = mutable.Map.empty[StratVarState, StratVarState] - def duplicateVarState(s: StratVarState) = - stratVarMap.getOrElseUpdate.curried(s): - // update `inDef` because the new strat var is considered to be generated inside the new definition - StratVarState.freshVar(s.name, s.callResOf, inDef, s.funRetOrArg)(using d.stratVarUidState)._1.s - def duplicateProdStrat(s: ProdStrat): ProdStrat = s match - case c@Ctor(ctor, args, expr, instId) => Ctor( - ctor, - args.view.mapValues(duplicateProdStrat).toMap, - expr, - instId.fold(Some(referSite :: Nil))(l => Some(referSite :: l)) - // instId.map(referSite :: _) - )(c.inDef) - case ProdFun(l, r) => ProdFun(l.map(duplicateConsStrat), duplicateProdStrat(r)) - case p@ProdVar(s) => - if s.inDef === S(funSym) || (p.s is this.s) then - duplicateVarState(s).asProdStrat - else p - case NoProd => NoProd - def duplicateConsStrat(s: ConsStrat): ConsStrat = s match - case d: Dtor => Dtor(d.expr, d.outterMatch, d.inDef) - case s@FieldSel(expr, inMatching) => FieldSel(expr, inMatching)(s.expr, s.inMatching) - case ConsFun(l, r) => ConsFun(l.map(duplicateProdStrat), duplicateConsStrat(r)) - case c@ConsVar(s) => - if s.inDef === S(funSym) || (c.s is this.s) then - duplicateVarState(s).asConsStrat - else c - case NoCons => NoCons - - val newProd = duplicateProdStrat(this) - constrLs.foreach: - case p -> c => - val constr = duplicateProdStrat(p) -> duplicateConsStrat(c) - inDef match - case None => d.constraints ::= constr - case Some(inFunDef) => - d.inDefConstraints.updateWith(inFunDef): - case S(ls) => S(constr :: ls) - case N => S(constr :: Nil) - newProd - -case object NoProd extends ProdStrat - - - -class Dtor(val expr: Match, val outterMatch: Option[ResultId], val inDef: Option[BlockMemberSymbol])(using d: Deforest) extends ConsStrat: - d.matchScrutToMatchBlock.updateWith(expr.scrut.uid): - case None => Some(expr) - case Some(v) => - // lastWords(s"should only update once (uid: ${expr.scrut.uid})") - assert(v is expr) - Some(expr) - d.matchScrutToParentMatchScrut.updateWith(expr.scrut.uid): - case None => Some(outterMatch) - case Some(v) => - // lastWords(s"should only update once (uid: ${expr.scrut.uid})") - v match - case None => assert(outterMatch is None) - case Some(value) => assert(outterMatch.get is value) - Some(v) - -object Dtor: - def unapply(d: Dtor)(using Deforest): Opt[ResultId] = S(d.expr.scrut.uid) - - -case class FieldSel(field: Tree.Ident, consVar: ConsVar)(val expr: ResultId, val inMatching: LinkedHashMap[ResultId, ClsOrModSymbol]) extends ConsStrat with FieldSelTrait -case class ConsFun(l: Ls[ProdStrat], r: ConsStrat) extends ConsStrat -case class ConsVar(s: StratVarState) extends ConsStrat with StratVarTrait(s) -case object NoCons extends ConsStrat - - -enum DtorExpr: - case Match(s: ResultId) - case Sel(s: ResultId) - -enum CtorFinalDest: - case Match(scrut: ResultId, expr: codegen.Match, selInArms: Ls[ResultId], selMaps: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol]) - case Sel(s: ResultId) - -trait FieldSelTrait: - this: FieldSel => - val filter = mutable.Map.empty[ProdVar, Ls[ClsOrModSymbol]].withDefaultValue(Nil) - - def updateFilter(p: ProdVar, c: Ls[ClsOrModSymbol]) = - filter += p -> (c ::: filter(p)) - -trait StratVarTrait(stratState: StratVarState): - this: ProdVar | ConsVar => - - lazy val asProdStrat = stratState.asProdStrat - lazy val asConsStrat = stratState.asConsStrat - lazy val uid = stratState.uid - -final case class NotDeforestableException(msg: String) extends Exception(msg) - -// Compute free vars for a block, without considering deforestation. -// Used on blocks after the deforestation transformation. -// This means that for matches we don't need to consider the extra -// free vars that may be introduced by deforestation: -// 1. the free vars from the `rest` of the their parent matches -// 2. the free vars caused by the substitution of selections of scrutinees of their parent matches -class FreeVarTraverser(alwaysDefined: Set[Symbol]) extends BlockTraverser: - val ctx = mutable.Set.from(alwaysDefined) - val result = mutable.Set.empty[Symbol] - - override def applyBlock(b: Block): Unit = b match - case Match(scrut, arms, dflt, rest) => - applyPath(scrut) - (arms.map(_._2) ++ dflt).foreach: a => - // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. - // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - val realArm = Begin(a, rest) - applyBlock(realArm) - - case Assign(lhs, rhs, rest) => - applyResult(rhs) - ctx += lhs - applyBlock(rest) - ctx -= lhs - case Begin(sub, rest) => applyBlock(b.flattened) - case Define(defn, rest) => defn match - case FunDefn(owner, sym, params, body) => - val paramSymbols = params.flatMap: - case ParamList(_, params, restParam) => (params ++ restParam).map: - case Param(sym = sym, _) => sym - ctx += sym - ctx ++= paramSymbols - applyBlock(body) - ctx --= paramSymbols - applyBlock(rest) - ctx -= sym - case ValDefn(owner, k, sym, rhs) => - ctx += sym - applyPath(rhs) - applyBlock(rest) - ctx -= sym - case c: ClsLikeDefn => ??? // not supported - - case _ => super.applyBlock(b) - - override def applyValue(v: Value): Unit = v match - case Value.Ref(l) => l match - // builtin symbols and toplevel symbols are always in scope - case _: (BuiltinSymbol | TopLevelSymbol) => () - // NOTE: assume all class definitions are in the toplevel - case b: BlockMemberSymbol if b.asClsLike.isDefined => () - case _ => if !ctx.contains(l) then result += l - case _ => super.applyValue(v) - - override def applyLam(l: Value.Lam): Unit = - val paramSymbols = l.params.params.map(p => p.sym) - ctx ++= paramSymbols - applyBlock(l.body) - ctx --= paramSymbols - - def analyze(b: Block) = - applyBlock(b) - result.toList.sortBy(_.uid) - -// Compute free vars for a Match block, considering deforestations. Used on non-transformed blocks -// Make use of `freeVarsOfNonTransformedMatches`, which computes the free vars _after transformation_ -// from non-transformed matches (either fusing or un-fusing matches). -// Sources of additional free vars for fusing matches: -// - the free vars from the `rest` of the their parent matches -// - the free vars caused by the substitution of selections of scrutinees of their parent matches -// Otherwise, additional free vars come from fusing matches that are contained in the block, -// and this is handled by freeVarsOfNonTransformedMatches -class DeforestationFreeVarTraverserForMatch( - alwaysDefined: Set[Symbol], - selsToBeReplaced: Map[ResultId, Symbol], - selsReplacementByCurrentMatch: Map[ResultId, Symbol], - currentMatchScrut: Symbol, - dt: DeforestTransformer -) extends FreeVarTraverser(alwaysDefined): - given Deforest = dt.d - override def applyBlock(b: Block): Unit = b match - // a nested match - case m@Match(scrut, arms, dflt, rest) => - result ++= dt.freeVarsOfNonTransformedMatches(scrut.uid, m) - - // sub-matches' scruts (which are not included in freeVarsOfNonTransformedMatches) - // are also free vars - val Value.Ref(l) = scrut - if !ctx(l) then result += l - - // free vars in nested-matches reported by freeVarsOfNonTransformedMatches may also contain - // spurious ones: those that are going to be substitued by the current match, - // and those that are in the ctx - result --= selsReplacementByCurrentMatch.values - result --= ctx - case _ => super.applyBlock(b) - - override def applyPath(p: Path): Unit = p match - case p @ Select(qual, name) => selsToBeReplaced.get(p.uid) match - case None => qual match - // if it is the scrut of current match and the computation containing - // this selection is moved, then the selection will be replaced and there will be no free vars - case Value.Ref(l) if l == currentMatchScrut => () - case _ => super.applyPath(p) - case Some(s) => result += s - case _ => super.applyPath(p) - - override def analyze(m: Block): List[Symbol] = - require(m.isInstanceOf[Match]) - val matchExpr@Match(scrut@Value.Ref(l), arms, dflt, rest) = m - val parentMatchRest = dt.allParentMatches(scrut.uid).foldRight[Block](End("")): (p, acc) => - Begin(dt.d.matchScrutToMatchBlock(p).rest, acc) - (arms.map(_._2) ++ dflt).foreach: a => - // dflt may just be `throw error``, and `rest` may use vars assigned in non default arms. - // So use `flattened` to remove dead code (after `throw error`) and spurious free vars. - // Also take care of the `rest`s of its parent match blocks. - val realArm = Begin(a, Begin(rest, parentMatchRest)).flattened - applyBlock(realArm) - - result.toList.sortBy(_.uid) - - -class WillBeNonEndTailBlockTraverser(using d: Deforest) extends BlockTraverserShallow: - var flag = false - override def applyBlock(b: Block): Unit = b match - case Match(scrut, arms, dflt, rest) => - flag = - d.rewritingMatchConsumers(scrut.uid) || - (arms.forall { case (_, b) => b.willBeNonEndTailBlock } && dflt.fold(true)(_.willBeNonEndTailBlock)) || - rest.willBeNonEndTailBlock - case _: End => () - case _: BlockTail => flag = true - case _ => super.applyBlock(b) - def analyze(b: Block): Bool = - applyBlock(b) - flag - -class ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) extends BlockTransformer(new SymbolSubst()): - override def applyValue(v: Value): Value = v match - case Value.Ref(l) => Value.Ref(freeVarsAndTheirNewSyms.getOrElse(l, l)) - case _ => super.applyValue(v) - -class HasExplicitRetTraverser extends BlockTraverserShallow: - var flag = false - override def applyBlock(b: Block): Unit = b match - case Return(_, imp) => flag = !imp - case _ => super.applyBlock(b) - - def analyze(b: Block) = - flag = false - applyBlock(b) - flag - -class GetCtorsTraverser(using Deforest) extends BlockTraverser: - val ctors = mutable.Set.empty[ResultId] - override def applyResult(r: Result): Unit = - r.uid.handleCtorIds{ (id, f, clsOrMod, args) => - ctors += id - args.foreach { case Arg(_, value) => applyResult(value) } - } match - case Some(_) => () - case None => r match - case Call(_, args) => - args.foreach { case Arg(_, value) => applyResult(value) } - case Instantiate(cls, args) => - args.foreach(applyResult) - case _ => () - -// TODO: move more traversal of blocks that are irrelevant -// to constraint generation to this place -class DeforestPrepareTraverser(using d: Deforest) extends BlockTraverser: - override def applyFunDefn(fun: FunDefn): Unit = - val fDef@FunDefn(_, sym, params, body) = fun - d.funSymToFunDef += sym -> fDef - - - -extension (b: Block) - def replaceSymbols(freeVarsAndTheirNewSyms: Map[Symbol, Symbol]) = - ReplaceLocalSymTransformer(freeVarsAndTheirNewSyms).applyBlock(b) - - def sortedFvsForTransformedBlocks(alwaysDefined: Set[Symbol]) = - FreeVarTraverser(alwaysDefined).analyze(b) - - def hasExplicitRet: Boolean = - HasExplicitRetTraverser().analyze(b) - - def willBeNonEndTailBlock(using d: Deforest): Bool = - WillBeNonEndTailBlockTraverser().analyze(b) - - -type CtorExprAndInstId = ResultId -> Opt[Ls[ResultId]] -extension (c: CtorExprAndInstId) - def getResult(using Deforest): Result = c._1.getResult - def getClsSymOfUid(using Deforest): ClassLikeSymbol = c._1.getClsSymOfUid - -class Deforest(using TL, Raise, Elaborator.State): - - given stratVarUidState: Uid.StratVar.State = Uid.StratVar.State() - given Deforest = this - import StratVarState.freshVar - - - val resultIdToResult = mutable.Map.empty[ResultId, Result] - val funSymToFunDef = mutable.Map.empty[BlockMemberSymbol, FunDefn] - - // def instantiate(referSite: ResultId): ProdStrat = - // val funSym = referSite.getFunCallBlkMemSym.orElse: - // referSite.getResult match - // case sel: Select => sel.symbol.flatMap(_.asBlkMember) - // case Value.Ref(l) => l.asBlkMember - // case _ => N - - def apply(p: Program, duplicate: Bool = false, output: String => Unit): Opt[Program] -> String -> Int = - val mainBlk = p.main - - globallyDefinedVars.init(mainBlk) - - // allocate type vars for defined symbols in the blocks - symToStrat.init(mainBlk) - - val prepare = new DeforestPrepareTraverser - prepare.applyBlock(mainBlk) - - try - processBlock(mainBlk)(using true) - catch - case NotDeforestableException(msg) => - // return None if deforestation is not applicable - return N -> "" -> 0 - - resolveConstraints - - - if duplicate then - // val defDuplicateInfo = findDefDupChances - // DefDupTODO: do not use `output` from difftest here - val numOfChances = findDefDupChances.size - val dupRes = if numOfChances > 0 then - output("duplication chances:") - findDefDupChances.foreach: (r, s) => - output(s"\t${r.getResult} <-- dup --> $s@${s.uid}") - output("=========") - val defDupper = new DefDupTransformer - S(defDupper(p)) -> s"$numOfChances dup chances" -> numOfChances - else N -> "no duplication chance" -> 0 - - val newDeforest = new Deforest - val pAfterDup = if dupRes._2 > 0 then dupRes._1._1.get else p - - // output("\n\n\n\n\nvvvvvvvvvvvvv\n dup done\n^^^^^^^^^^^^^^^^^\n\n\n\n\n\n\n\n") - - newDeforest(pAfterDup, false, output) - - // DefDupTODO: def dup: change later - else - // upperBounds.foreach: - // case (v, u) => v - tl.log("-----------------------------------------") - constraints.foreach: - case (p, c) => tl.log(s"${p} --> $c") - - tl.log("-----------------------------------------") - ctorDests.ctorDests.foreach: - case (ctorExprId, CtorDest(matches, sels, noCons, _)) => tl.log: - val ctorName = ctorExprId.getClsSymOfUid.nme + s"(id:$ctorExprId)" - val matchExprScruts = "if " + matches.map{(s, m) => - m.scrut.asInstanceOf[Value.Ref].l.nme + s"(id:$s)" - }.toList.sorted.mkString(" | ") + " then ... " - val selExpr = sels.map{ - case sel@FieldSel(s, v) => s".${s.name}(id:${sel.expr})" - }.toList.sorted.mkString(" | ") - s"${ctorExprId.getResult}\n\t --- match ---> $matchExprScruts\n\t --- sels ---> $selExpr\n\tNoCons: $noCons" - tl.log("-----------------------------------------") - dtorSources.dtorSources.foreach: - case (d, DtorSource(ctors, noProd)) => - tl.log(s"$d <--- ${ctors.map(c => c.getClsSymOfUid.nme + s"(id:$c)").toList.mkString(" | ")} <--- (NoProd: $noProd)") - tl.log("-----------------------------------------") - filteredCtorDests.foreach: - case (ctorUid, CtorFinalDest.Sel(s)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --sel--> " + s) - case (ctorUid, CtorFinalDest.Match(scrut, _, _, _)) => tl.log(s"${ctorUid.getClsSymOfUid.nme}(id:$ctorUid) --mat--> " + scrut ) - - val fusionStat = filteredCtorDests.map: - case (ctorUid, CtorFinalDest.Sel(s)) => - "\t" + ctorUid.getClsSymOfUid.nme + " --sel--> " + s"`.${resultIdToResult(s).asInstanceOf[Select].name}`" - case (ctorUid, CtorFinalDest.Match(scrut, expr, _, _)) => - "\t" + ctorUid.getClsSymOfUid.nme + " --match--> " + s"`if ${expr.scrut.asInstanceOf[Value.Ref].l.nme} is ...`" - - if filteredCtorDests.nonEmpty then - S(Program(p.imports, rewrite(mainBlk))) -> s"${filteredCtorDests.size} fusion opportunities:\n${fusionStat.toList.sorted.mkString("\n")}" -> filteredCtorDests.size - else - S(p) -> s"0 fusion opportunity" -> 0 - - object globallyDefinedVars: - val store = mutable.Set.from[Symbol](State.globalThisSymbol ::State.runtimeSymbol :: Nil) - - def apply(s: Symbol) = store.contains(s) - - def init(b: Block) = store ++= b.definedVars - - var constraints: Ls[ProdStrat -> ConsStrat] = Nil - - val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] - val matchScrutToParentMatchScrut = mutable.Map.empty[ResultId, Option[ResultId]] - object symToStrat: - val store = mutable.Map.empty[Symbol, ProdVar] - val funSymsWithDefn = mutable.Set.empty[BlockMemberSymbol] - val usedFunSym = mutable.Set.empty[BlockMemberSymbol] - - def init(p: Block) = - if store.isEmpty then - object FreshVarForAllVars extends BlockTraverser: - var inDef: Opt[BlockMemberSymbol] = N - def funRetOrArg = inDef.map(s => L(s)) - - override def applySymbol(s: Symbol): Unit = s match - case b: BlockMemberSymbol => - store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 - b.trmImplTree.foreach: t => - if t.k is syntax.Fun then usedFunSym += b - case _: TempSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 - case _: VarSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 - case _: TermSymbol => store += s -> freshVar(s.nme, N, inDef, funRetOrArg)._1 - case _ => () - - override def applyFunDefn(fun: FunDefn): Unit = - funSymsWithDefn += fun.sym - inDef = S(fun.sym) - super.applyFunDefn(fun) - inDef = N - - FreshVarForAllVars.applyBlock(p) - // `NoProd` to block fusion for those functions that are imported from elsewhere - usedFunSym.diff(funSymsWithDefn).foreach: funSymsWithoutDefn => - constrain(NoProd, store(funSymsWithoutDefn).asConsStrat, true)(using N) - - - def getStratOfSym(s: Symbol, refSite: ResultId, inDef: Option[BlockMemberSymbol]) = - s match - case _: BuiltinSymbol => NoProd - case _: TopLevelSymbol => NoProd - // TODO: cannot fuse intermediate values created by - // calling data constructors passed around like functions, - // like `fun app(ctor) = ctor(1); if app(AA) is AA(x) then x`; - // immediate data constructor calls are handled directly, - // so if this method is called on a ClsLike symbol, - // it means that this constructor is passed around like a function, - // which we can't fuse for now - case _ if s.asCls.isDefined => NoProd - case s: BlockMemberSymbol => - val res = store(s) - if s.trmImplTree.fold(false)(_.k is syntax.Fun) then - res.instantiate(refSite, inDef) - else - res - - case _: LocalSymbol => store(s) - def +=(e: Symbol -> ProdVar) = store += e - def addAll(es: Iterable[Symbol -> ProdVar]) = store.addAll(es) - def apply(s: Symbol) = store(s) - - def getClsFields(s: ClassSymbol) = s.tree.clsParams - - - object inDefConstraints: - val store = mutable.Map.empty[BlockMemberSymbol, Ls[ProdStrat -> ConsStrat]] - def get = store.get - def updateWith = store.updateWith - def getOrUpdate(sym: BlockMemberSymbol) = - store.getOrElse.curried(sym): - val fDef@FunDefn(_, _, params, body) = funSymToFunDef(sym) - val funSymStratVar = symToStrat(sym) - val param = params match - // TODO: handle `restParam` and mutiple param list - case ParamList(flags, params, N) :: Nil => params - val funStrat = constrFun(param, body)(using Map.empty, mutable.LinkedHashMap.empty, S(sym)) - constrain(funStrat, funSymStratVar.asConsStrat, false)(using S(sym)) - store.get(sym).get - - def constrain(p: ProdStrat, c: ConsStrat, addToConstrList: Bool)(using inDef: Opt[BlockMemberSymbol]) = - inDef.foreach: i => - inDefConstraints.updateWith(i): - case None => Some((p -> c) :: Nil) - case Some(value) => Some((p -> c) :: value) - if addToConstrList then - constraints ::= p -> c - - object callInfo: - val callerToCallSitesInside = mutable.Map.empty[Symbol, Ls[ResultId -> Symbol]] - val callSiteInDefInfo = mutable.Map.empty[ResultId, Symbol] - val fnSymToAllCallSites = mutable.Map.empty[Symbol, Ls[ResultId]] - val callSiteIdToCallResVar = mutable.Map.empty[ResultId, StratVarState] - - var callSiteStableId = 0 - val callSiteStableIdStore = mutable.Map.empty[ResultId, Int] - def update(caller: Opt[Symbol], callee: Symbol, callResultId: ResultId, callResVar: StratVarState) = - caller.foreach: caller => - callerToCallSitesInside.updateWith(caller): - case None => Some((callResultId -> callee) :: Nil) - case Some(l) => Some((callResultId -> callee) :: l) - callSiteInDefInfo.updateWith(callResultId): - case None => Some(caller) - case _ => die - fnSymToAllCallSites.updateWith(callee): - case None => Some(callResultId :: Nil) - case Some(l) => Some(callResultId :: l) - callSiteStableIdStore.updateWith(callResultId): - case None => callSiteStableId += 1; Some(callSiteStableId) - case Some(_) => die - callSiteIdToCallResVar.updateWith(callResultId): - case None => Some(callResVar) - case Some(_) => die - - def isObviousRecursiveCall(c: ResultId) = - // tl.log("checking callsite: " + c.getResult.toString() + s"@$c") - val sym = c.getFunCallBlkMemSym.get - callSiteInDefInfo.get(c).fold(false)(_ is sym) - def getAllCallSites(s: Symbol) = fnSymToAllCallSites(s) - - def getCallSiteStableId(callSiteId: ResultId) = - callSiteStableIdStore(callSiteId) - def getCallSiteResultVar(callSiteId: ResultId) = - callSiteIdToCallResVar.get(callSiteId) - - - def processBlock(b: Block)(using - addToConstrList: Bool, - inArm: Map[ProdVar, ClsOrModSymbol] = Map.empty[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol] = LinkedHashMap.empty[ResultId, ClsOrModSymbol], - inDef: Opt[BlockMemberSymbol] = N - ): ProdStrat = b match - case m@Match(scrut, arms, dflt, rest) => - val scrutStrat = processResult(scrut) - constrain(scrutStrat, Dtor(m, matching.lastOption.map(_._1), inDef), addToConstrList) - val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then - arms.map: - case (Case.Cls(s, _), body) => - processBlock(body)(using - addToConstrList, - inArm + (scrutStrat.asInstanceOf[ProdVar] -> s), - matching + (scrut.uid -> s), - inDef - ) - else - arms.map: - case (_, armBody) => processBlock(armBody) - val dfltRes = dflt.map(processBlock) - rest match - case End(msg) => - val matchRes = freshVar("", N, inDef) - armsRes.appendedAll(dfltRes).foreach: r => - constrain(r, matchRes._2, addToConstrList) - matchRes._1 - case _ => processBlock(rest) - - case Return(res, implct) => processResult(res) - case Assign(lhs, rhs, rest) => - constrain(processResult(rhs), symToStrat(lhs).asConsStrat, addToConstrList) - processBlock(rest) - case Begin(sub, rest) => - processBlock(sub) - processBlock(rest) - case Define(defn, rest) => - defn match - case fDef@FunDefn(_, sym, params, body) if !inDefConstraints.store.contains(sym) => - // funSymToFunDef += sym -> fDef - val funSymStratVar = symToStrat(sym) - val param = params match - // TODO: handle `restParam` and mutiple param list - case ParamList(flags, params, N) :: Nil => params - val funStrat = constrFun(param, body)(using inArm, matching, S(sym)) - constrain(funStrat, funSymStratVar.asConsStrat, false)(using S(sym)) - funSymStratVar - case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") - case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") - processBlock(rest) - case End(msg) => NoProd - // make it a type var instead of `NoProd` so that things like `throw match error` in - // default else branches do not block fusion... - case Throw(exc) => - processResult(exc) - freshVar("throw", N, inDef)._1 - - def constrFun(params: Ls[Param], body: Block)(using - inArm: Map[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol], - inDef: Opt[BlockMemberSymbol] - ) = - tl.log(s"constr fun body: $inDef") - val paramSyms = params.map: - case Param(sym = sym, _) => sym - val paramStrats = paramSyms.map(symToStrat.apply) - // symToStrat.addAll(paramSyms.zip(paramStrats)) - val res = freshVar(s"${inDef.fold("")(_.nme + "_")}fun_res", N, inDef, inDef.map(L.apply)) - constrain(processBlock(body)(using false, inArm, matching, inDef), res._2, false) - ProdFun(paramStrats.map(s => s.asConsStrat), res._1) - - def processResult(r: Result)(using - addToConstrList: Bool, - inArm: Map[ProdVar, ClsOrModSymbol], - matching: LinkedHashMap[ResultId, ClsOrModSymbol], - inDef: Opt[BlockMemberSymbol] - ): ProdStrat = tl.trace[ProdStrat](s"========== processing: ${r.toString()} <<<<< in $inDef", _.toString()): - def handleCallLike(f: Path, args: Ls[Path], c: Result) = - val argsTpe = args.map(processResult) - f match - case s@Select(p, nme) => - s.symbol.map(_.asCls) match - case None => - val pStrat = processResult(p) - val tpeVar = freshVar("", N, inDef) - constrain(pStrat, FieldSel(nme, tpeVar._2)(s.uid, matching), addToConstrList) - val appRes = freshVar("", N, inDef) // unknown function symbol - constrain(tpeVar._1, ConsFun(argsTpe, appRes._2), addToConstrList) - appRes._1 - case Some(None) => - val funSym = s.symbol.get - val appRes = freshVar( - "call_" + funSym.nme + "_res", - s.symbol.flatMap(_.asBlkMember.map(c.uid -> _)), // if `f` has a blockMemberSymbol, then record it - inDef - ) - for callee <- s.symbol.flatMap(_.asBlkMember) do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(funSym, r.uid, inDef), ConsFun(argsTpe, appRes._2), addToConstrList) - appRes._1 - case Some(Some(s)) => - val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid, N)(inDef) - case Value.Ref(l) => - l.asCls match - case Some(s) => - val clsFields = getClsFields(s) - Ctor(s, clsFields.zip(argsTpe).toMap, c.uid, N)(inDef) - case _ => // then it is a function - val appRes = freshVar( - "call_" + l.nme + "_res", - l.asBlkMember.map(c.uid -> _), // if `f` has a blockMemberSymbol, then record it - inDef - ) - for callee <- l.asBlkMember do callInfo.update(inDef, callee, c.uid, appRes._1.s) - constrain(symToStrat.getStratOfSym(l, r.uid, inDef), ConsFun(argsTpe, appRes._2), addToConstrList) - appRes._1 - case lam@Value.Lam(params, body) => - val funTpe = processResult(lam) - val appRes = freshVar("call_lam_res", N, inDef) - constrain(funTpe, ConsFun(argsTpe, appRes._2), addToConstrList) - appRes._1 - - case Value.This(sym) => throw NotDeforestableException("No support for `this` as a callee yet") - case Value.Lit(lit) => ??? - case Value.Arr(elems) => ??? - r match - case c@Call(f, args) => handleCallLike(f, args.map {case Arg(false, value) => value}, c) - - case i@Instantiate(cls, args) => handleCallLike(cls, args, i) - - case sel@Select(p, nme) => sel.symbol match - case Some(s) if s.asObj.isDefined => - Ctor(s.asObj.get, Map.empty, sel.uid, N)(inDef) - case _ => - val pStrat = processResult(p) - pStrat match - case ProdVar(pStratVar) if inArm.contains(pStratVar.asProdStrat) => - val tpeVar = freshVar("", N, inDef) - val selStrat = FieldSel(nme, tpeVar._2)(sel.uid, matching) - selStrat.updateFilter(pStratVar.asProdStrat, inArm(pStratVar.asProdStrat) :: Nil) - constrain(pStrat, selStrat, addToConstrList) - tpeVar._1 - case _ => - val tpeVar = freshVar("", N, inDef) - constrain(pStrat, FieldSel(nme, tpeVar._2)(sel.uid, matching), addToConstrList) - tpeVar._1 - - case v@Value.Ref(l) => l.asObj match - case None => symToStrat.getStratOfSym(l, r.uid, inDef) - case Some(m) => Ctor(m, Map.empty, v.uid, N)(inDef) - - case Value.This(sym) => throw NotDeforestableException("No support for `this` yet") - case Value.Lit(lit) => NoProd - case Value.Lam(ParamList(_, params, N), body) => - constrFun(params, body) - case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") - - - val upperBounds = mutable.Map.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) - val lowerBounds = mutable.Map.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) - - case class CtorDest(matches: Map[ResultId, Match], sels: Ls[FieldSel], noCons: Bool, callResVars: Ls[StratVarState]) - case class DtorSource(ctors: Set[CtorExprAndInstId], noProd: Bool) - object ctorDests: - val ctorDests = mutable.LinkedHashMap.empty[CtorExprAndInstId, CtorDest].withDefaultValue(CtorDest(Map.empty, Nil, false, Nil)) - def update(ctor: CtorExprAndInstId, m: Match) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches + (m.scrut.uid -> m), sels, noCons, vars)) - case None => Some(CtorDest(Map(m.scrut.uid -> m), Nil, false, Nil)) - def update(ctor: CtorExprAndInstId, s: FieldSel) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, s :: sels, noCons, vars)) - case None => Some(CtorDest(Map.empty, s :: Nil, false, Nil)) - def update(ctor: CtorExprAndInstId, n: NoCons.type) = ctorDests.updateWith(ctor): - case Some(CtorDest(matches, sels, noCons, vars)) => Some(CtorDest(matches, sels, true, vars)) - case None => Some(CtorDest(Map.empty, Nil, true, Nil)) - def update(ctor: CtorExprAndInstId, v: StratVarState) = ctorDests.updateWith(ctor): - case Some(dests) => Some(dests.copy(callResVars = v :: dests.callResVars)) - case None => Some(CtorDest(Map.empty, Nil, false, v :: Nil)) - def get(ctor: CtorExprAndInstId) = ctorDests.get(ctor) - - object dtorSources: - val dtorSources = mutable.Map.empty[DtorExpr, DtorSource].withDefaultValue(DtorSource(Set.empty, false)) - private def getDtorExprOfResultId(i: ResultId) = i.getResult match - case s: Select => DtorExpr.Sel(i) - case r: Value.Ref => DtorExpr.Match(i) - case r => lastWords(s"try to get dtor expr from ResultId, but get $r") - def update(dtor: ResultId, ctor: CtorExprAndInstId) = - val dtorExpr = getDtorExprOfResultId(dtor) - dtorSources.updateWith(dtorExpr): - case None => Some(DtorSource(Set(ctor), false)) - case Some(DtorSource(ctors, noProd)) => Some(DtorSource(ctors + ctor, noProd)) - def update(dtor: ResultId, noProd: NoProd.type) = - val dtorExpr = getDtorExprOfResultId(dtor) - dtorSources.updateWith(dtorExpr): - case None => Some(DtorSource(Set.empty, true)) - case Some(DtorSource(ctors, noProd)) => Some(DtorSource(ctors, true)) - def get(dtor: ResultId) = dtorSources.get(getDtorExprOfResultId(dtor)) - - - - def resolveConstraints: Unit = - - def handle(c: ProdStrat -> ConsStrat)(using cache: mutable.Set[ProdStrat -> ConsStrat]): Unit = - val prod = c._1 - val cons = c._2 - - if cache(c) then return () - - cache += c - - (prod, cons) match - case (Ctor(ctor, args, expr, instId), dtorStrat@Dtor(scrut)) => - ctorDests.update(expr -> instId, dtorStrat.expr) - dtorSources.update(scrut, expr -> instId) - case (Ctor(ctor, args, expr, instId), selDtor@FieldSel(field, consVar)) => - ctorDests.update(expr -> instId, selDtor) - dtorSources.update(selDtor.expr, expr -> instId) - args.find(a => a._1.id == field).map: p => - handle(p._2 -> consVar) - case (Ctor(ctor, args, _, _), ConsFun(l, r)) => () // ignore - - case (p: ProdVar, _) => - upperBounds += p.uid -> (cons :: upperBounds(p.uid)) - lowerBounds(p.uid).foreach: l => - (l, cons) match - case (l: ProdVar, sel@FieldSel(field, consVar)) => - sel.updateFilter(l, sel.filter(p)) - handle(l -> cons) - case (Ctor(ctor, args, _, _), sel@FieldSel(field, consVar)) => - if sel.filter.get(p).forall(_.contains(ctor)) then - handle(l -> cons) - else - () - case _ => handle(l -> cons) - case (_, c: ConsVar) => - prod -> c match - case Ctor(expr = e, instantiationId = instId, _) -> _ if c.s.callResOf.isDefined => ctorDests.update(e -> instId, c.s) - case _ => () - - lowerBounds += c.uid -> (prod :: lowerBounds(c.uid)) - upperBounds(c.uid).foreach: u => - (prod, u) match - case (Ctor(ctor, args, _, _), sel@FieldSel(field, consVar)) => - if sel.filter.get(c.asProdStrat).forall(_.contains(ctor)) then - handle(prod -> u) - else - () - case (_: ProdVar, _) => die - case _ => handle(prod -> u) - case (Ctor(ctor, args, expr, instId), NoCons) => - ctorDests.update(expr -> instId, NoCons) - args.valuesIterator.foreach(a => handle(a, NoCons)) - case (ProdFun(l, r), Dtor(cls)) => () // ignore - case (ProdFun(l, r), FieldSel(field, consVar)) => () // ignore - case (ProdFun(lp, rp), ConsFun(lc, rc)) => - lc.zip(lp).foreach(handle) - handle(rp, rc) - case (ProdFun(l, r), NoCons) => - l.foreach(a => handle(NoProd, a)) - handle(r, NoCons) - case (NoProd, Dtor(scrut)) => dtorSources.update(scrut, NoProd) - case (NoProd, fSel@FieldSel(field, consVar)) => dtorSources.update(fSel.expr, NoProd) - case (NoProd, ConsFun(l, r)) => - l.foreach(a => handle(a, NoCons)) - handle(NoProd, r) - case (NoProd, NoCons) => () - - constraints.foreach(c => handle(c)(using mutable.Set.empty)) - - - // ======== after resolving constraints ====== - - def allUpperBoundsOf(k: StratVarId) = - def go(k: StratVarId, cache: Set[StratVarId]): Set[ConsStrat] = - upperBounds(k).toSet.flatMap: - case u@ConsVar(s) if !cache.contains(s.uid) => go(s.uid, cache + s.uid) + u - case u => Set(u) - go(k, Set.empty) - - lazy val findDefDupChances = - val callResToCtorsCallsFlowingIntoThem = - ctorDests.ctorDests - .flatMap[CtorExprAndInstId -> StratVarState]: - case (ctorCallId, CtorDest(callResVars = vs, _)) => vs.map(ctorCallId -> _) - .groupBy(_._2) - - def duplicatable(v: StratVarState): Opt[ResultId -> Symbol -> Dtor -> Bool] = - val callSiteId -> calleeSym = v.callResOf.get - // if this is the only call site, no reason to duplicate - if callInfo.getAllCallSites(calleeSym).size == 1 then N - // if this is a obvious recursive call site, do not duplicate - else if callInfo.isObviousRecursiveCall(callSiteId) then N - else - val dtors = allUpperBoundsOf(v.uid).filter(c => c.isInstanceOf[Dtor]) - // if this call result is not consumed by any dtor, do not duplicate - if dtors.size == 0 then N - // if this call result is consumed by multiple dtors, do not duplicate because it's helpless - else if dtors.size > 1 then N - else // if there is only one dtor for the call result... - val dtor = dtors.head.asInstanceOf[Dtor] - // it is a potential duplicate chance if the call res of a function - // is consumed by the body of the same function - if dtor.inDef.fold(false)(_ is calleeSym) then S(callSiteId -> calleeSym -> dtor -> true) - else - // if all ctors flowing into this call res does not have a clash, no dup - if callResToCtorsCallsFlowingIntoThem(v).forall: (ctorCallId, fnCallSite) => - val CtorDest(matches, sels, noCons, _) = ctorDests.get(ctorCallId).get - !noCons && matches.size == 1 - then N - else S(callSiteId -> calleeSym -> dtor -> false) - - val allDuplicatableCallSites = ctorDests.ctorDests - .flatMap: - case _ -> CtorDest(callResVars = vs) => vs - .toList - .distinct - .flatMap(duplicatable) - - allDuplicatableCallSites - .groupBy: - case _ -> calleeSym -> dtor -> isRecursive => calleeSym -> dtor - .values - .toList - .sortBy(x => callInfo.getCallSiteStableId(x.head._1._1._1)) - .flatMap: setOfCallSites => - // setOfCallSites.groupBy(_._2) - val sharedCalleeSym = setOfCallSites.head._1._1._2 - val newCalleeSym = new BlockMemberSymbol(sharedCalleeSym.nme + "_duplicated", Nil, true) - setOfCallSites.map: - case callSiteId -> calleeSym -> _ -> _ => - assert(calleeSym is sharedCalleeSym) - callSiteId -> newCalleeSym - - lazy val defDupMap = findDefDupChances.toMap - - - - - lazy val resolveClashes = - val ctorToDtor = ctorDests.ctorDests - val dtorToCtor = dtorSources.dtorSources - - def removeCtor(rm: CtorExprAndInstId): Unit = - for CtorDest(mat, sels, _, _) <- ctorToDtor.remove(rm) do - for s <- mat.keys do removeDtor(DtorExpr.Match(s)) - for s <- sels do removeDtor(DtorExpr.Sel(s.expr)) - - def removeDtor(rm: DtorExpr) = - for - c <- dtorToCtor.remove(rm) - x <- c.ctors - do - removeCtor(x) - - // remove clashes: - ctorToDtor.filterNot { case _ -> CtorDest(dtors, sels, noCons, _) => - ((dtors.size == 0 && sels.size == 1) - || (dtors.size == 1 && { - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - sels.forall { s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) // need to be in the matching arms, and checking the scrutinee - case _ => false } - })) - && !noCons - }.keys.foreach(removeCtor) - dtorToCtor.filter(_._2.noProd).keys.foreach(removeDtor) - - // remove cycle: - // def getCtorInArm(ctor: ResultId, dtor: Match) = - // val ctorSym = getClsSymOfUid(ctor) - // val arm = dtor.arms.find{ case (Case.Cls(c1, _) -> body) => c1 === ctorSym }.map(_._2).orElse(dtor.dflt).get - // val traverser = GetCtorsTraverser() - // traverser.applyBlock(arm) - // traverser.ctors - - // def findCycle(ctor: ResultId, dtor: Match): Ls[ResultId] = - // val cache = mutable.Set(ctor) - // def go(ctorAndMatches: Ls[ResultId -> Match]): Ls[ResultId] = - // var newCtorsAndNewMatches: Ls[ResultId -> Match] = Nil - // for - // (c, m) <- ctorAndMatches - // c <- getCtorInArm(c, m) - // CtorDest(matches, sels, _, _) <- ctorToDtor.get(c) - // m <- matches.values.headOption - // do newCtorsAndNewMatches = (c -> m) :: newCtorsAndNewMatches - // val cycled = newCtorsAndNewMatches.filter(c => !cache.add(c._1)) - // if newCtorsAndNewMatches.isEmpty then - // Nil - // else if cycled.nonEmpty then - // cycled.map(_._1) - // else - // go(newCtorsAndNewMatches) - // go(Ls(ctor -> dtor)) - - // for - // (c, CtorDest(matches, sels, _, _)) <- ctorToDtor - // m <- matches.values - // x <- findCycle(c._1, m) - // do removeCtor(x) - - ctorToDtor -> dtorToCtor - - lazy val filteredCtorDests2: Map[CtorExprAndInstId, CtorFinalDest] = - val res = mutable.Map.empty[CtorExprAndInstId, CtorFinalDest] - - // we need only one CtorFinalDest per arm for each pat mat expr - val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] - - resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false, _)) => - val filteredDtor = { - if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) - else if dtors.size == 0 && sels.size > 1 then - lastWords("more than one consumer") - else if dtors.size > 1 then - lastWords("more than one consumer") - else if dtors.size == 1 then - val currentCtorCls = getClsSymOfUid(ctor) - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - handledMatches.getOrElseUpdate( - scrutRef.uid -> currentCtorCls, - if sels.forall{ s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) - case _ => false - } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] - - dtors.head._2.arms.foreach: - case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => - val c = cOrMod.asCls.get - // if this arm is used more than once, should be var symbol because the arm body will be - // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } - - selsInArms.foreach: fs => - assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(fs.field): - case Some(v) => Some(v) - case None => Some(if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) - else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) - val sym = fieldNameToSymToBeReplaced(fs.field) - - selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) - case _ => () - CtorFinalDest.Match( - dtors.head._1, - dtors.head._2, - sels.map(_.expr), - fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - ) - else - lastWords("more than one consumer") - ) - else die - } - res.updateWith(ctor){_ => Some(filteredDtor)} - } - res.toMap - - - lazy val filteredCtorDests: Map[ResultId, CtorFinalDest] = - val res = mutable.Map.empty[ResultId, CtorFinalDest] - - // we need only one CtorFinalDest per arm for each pat mat expr - val handledMatches = mutable.Map.empty[ResultId -> ClsOrModSymbol, CtorFinalDest] - - resolveClashes._1.foreach { case (ctor, CtorDest(dtors, sels, false, _)) => - val filteredDtor = { - if dtors.size == 0 && sels.size == 1 then CtorFinalDest.Sel(sels.head.expr) - else if dtors.size == 0 && sels.size > 1 then - lastWords("more than one consumer") - else if dtors.size > 1 then - lastWords("more than one consumer") - else if dtors.size == 1 then - val currentCtorCls = getClsSymOfUid(ctor) - val scrutRef@Value.Ref(scrut) = dtors.head._1.getResult - handledMatches.getOrElseUpdate( - scrutRef.uid -> currentCtorCls, - if sels.forall{ s => s.expr.getResult match - case Select(Value.Ref(l), nme) => (l === scrut) && s.inMatching.contains(scrutRef.uid) - case _ => false - } then - val fieldNameToSymToBeReplaced = mutable.Map.empty[Tree.Ident, Symbol] - val selectionUidsToSymToBeReplaced = mutable.Map.empty[ResultId, Symbol] - - dtors.head._2.arms.foreach: - case (Case.Cls(cOrMod, _), body) if cOrMod.asCls.fold(false)(_ === currentCtorCls) => - val c = cOrMod.asCls.get - // if this arm is used more than once, should be var symbol because the arm body will be - // extracted to a function, otherwise just temp symbol - val varSymInsteadOfTempSym = resolveClashes._2(DtorExpr.Match(dtors.head._1)).ctors.count(getClsSymOfUid(_) === c) > 1 - val selsInArms = sels.filter { fs => fs.inMatching(dtors.head._1) === c } - - selsInArms.foreach: fs => - assert(getClsFields(c).map(_.id).contains(fs.field)) - fieldNameToSymToBeReplaced.updateWith(fs.field): - case Some(v) => Some(v) - case None => Some(if varSymInsteadOfTempSym - then VarSymbol(Tree.Ident(s"_deforest_${c.name}_${fs.field.name}")) - else TempSymbol(N, s"_deforest_${c.name}_${fs.field.name}")) - val sym = fieldNameToSymToBeReplaced(fs.field) - - selectionUidsToSymToBeReplaced.addOne(fs.expr -> sym) - case _ => () - CtorFinalDest.Match( - dtors.head._1, - dtors.head._2, - sels.map(_.expr), - fieldNameToSymToBeReplaced.toMap -> selectionUidsToSymToBeReplaced.toMap - ) - else - lastWords("more than one consumer") - ) - else die - } - res.updateWith(ctor._1){_ => Some(filteredDtor)} - } - res.toMap - - lazy val rewritingSelConsumers = filteredCtorDests.values.collect { - case CtorFinalDest.Sel(s) => s - }.toSet - - lazy val rewritingMatchConsumers = filteredCtorDests.values.collect { - case CtorFinalDest.Match(scrut = s, _) => s - }.toSet - - def rewrite(p: Block) = - val deforestTransformer = DeforestTransformer() - val rest = deforestTransformer.applyBlock(p) - val newDefsRest = deforestTransformer.matchRest.getAllFunDefs - val newDefsArms = deforestTransformer.matchArms.getAllFunDefs - newDefsArms(newDefsRest(rest)) - -// this duplicator needs to deeply copy, because -// the ResultIds (related to ctor ids and match scrut ids) should not be messed up -class DefDuplicator( - oldFunSym: BlockMemberSymbol, - newFunSym: BlockMemberSymbol, - callSiteId: ResultId, - cache: Map[ResultId, BlockMemberSymbol] -)(using val d: Deforest, defDupTransformer: DefDupTransformer, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): - val localSymSubst = mutable.Map.empty[Symbol, Symbol] - - override def applyResult(r: Result): Result = r match - // if this is a callsite that is pre-computed to have a duplication: - case c@Call(f, args) if d.defDupMap.contains(r.uid) => - val newSym = d.defDupMap(r.uid) - defDupTransformer.newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid, cache + (c.uid -> newSym)) - val args2 = args.map(applyArg) - Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) - case c@Call(fun, args) => - val symOfFun = c.uid.getFunCallBlkMemSym - // if this is a call site that is obviously recursive - if symOfFun.fold(false)(_ is oldFunSym) then - Call(Value.Ref(newFunSym), args.map(applyArg))(true, c.mayRaiseEffects) - else - cache.get(c.uid) match - case Some(sym) => Call(Value.Ref(sym), args.map(applyArg))(true, c.mayRaiseEffects) - case None => - val currentCallSiteResVar = d.callInfo.getCallSiteResultVar(c.uid) - val flowsIntoTheDuplicatedDef = currentCallSiteResVar.fold(false): v => - val callerCallSiteStratVar = d.callInfo.getCallSiteResultVar(callSiteId).get - d.allUpperBoundsOf(v.uid).contains(callerCallSiteStratVar.asConsStrat) - symOfFun match - case Some(sym) if flowsIntoTheDuplicatedDef => - // further duplicate this call site - val newSym = new BlockMemberSymbol(sym.nme + "_nested_duplicated", Nil, true) - defDupTransformer.newDefs.makeDefn(sym, newSym, c.uid, cache + (c.uid -> newSym)) - val args2 = args.map(applyArg) - Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) - case _ => Call(applyPath(fun), args.map(applyArg))(c.isMlsFun, c.mayRaiseEffects) - case Instantiate(cls, args) => Instantiate(applyPath(cls), args.map(applyPath)) - case p: Path => applyPath(p) - - override def applyPath(p: Path): Path = p match - case s@Select(p, symbol) => Select(applyPath(p), symbol)(s.symbol) - case DynSelect(qual, fld, arrayIdx) => DynSelect(applyPath(qual), applyPath(fld), arrayIdx) - case v: Value => applyValue(v) - - override def applyValue(v: Value): Value = v match - case Value.Ref(l) => localSymSubst.get(l).fold(Value.Ref(l))(Value.Ref(_)) - case Value.This(sym) => Value.This(sym) - case Value.Lit(lit) => Value.Lit(lit) - case Value.Lam(params, body) => Value.Lam(params, body) - case Value.Arr(elems) => Value.Arr(elems) - case Value.Rcd(elems) => Value.Rcd(elems) - - - override def applyParamList(pl: ParamList): ParamList = - def applyParam(p: Param): Param = - val sym2 = VarSymbol(p.sym.id) - localSymSubst += p.sym -> sym2 - p.copy(sym = sym2) - val params2 = pl.params.map(applyParam) - val rest2 = pl.restParam.map(applyParam) - ParamList(pl.flags, params2, rest2) - - override def applyLocal(sym: Local): Local = - localSymSubst.getOrElse.curried(sym): - // tl.log(s"$sym not subst") - sym - - override def applyFunDefn(fun: FunDefn): FunDefn = - val params2 = fun.params.map(applyParamList) - fun.body.definedVars.foreach: v => - localSymSubst += v -> TempSymbol(N, v.nme) - val body2 = applySubBlock(fun.body) - FunDefn(fun.owner, newFunSym, params2, body2) - - def apply(fun: FunDefn) = - applyFunDefn(fun) - - -class DefDupTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): - self => - object newDefs: - val store = mutable.Map.empty[Symbol, FunDefn] - def makeDefn( - oldS: BlockMemberSymbol, - newS: BlockMemberSymbol, - callSiteId: ResultId, - cache: Map[ResultId, BlockMemberSymbol] - ): Unit = store.getOrElseUpdate.curried(newS): - val originalDefn = d.funSymToFunDef(oldS) - self.givenIn: - DefDuplicator(oldS, newS, callSiteId, cache)(originalDefn) - def apply(b: Block) = - store.values.toList.sortBy(_.sym.uid).foldRight(b)(Define(_, _)) - - override def applyResult(r: Result): Result = r match - case c@Call(f, args) if d.defDupMap.contains(r.uid) => - val newSym = d.defDupMap(r.uid) - newDefs.makeDefn(r.uid.getFunCallBlkMemSym.get, newSym, c.uid, Map(c.uid -> newSym)) - val args2 = args.mapConserve(applyArg) - Call(Value.Ref(newSym), args2)(true, c.mayRaiseEffects) - case _ => super.applyResult(r) - - def apply(p: Program): Program = - val newMainBlock = applyBlock(p.main) - val newMainBlockWithNewDefns = newDefs(newMainBlock) - Program(p.imports, newMainBlockWithNewDefns) - - - -class DeforestTransformer(using val d: Deforest, elabState: Elaborator.State) extends BlockTransformer(new SymbolSubst()): - self => - val nonFreeVars: Set[Symbol] = d.globallyDefinedVars.store.toSet - - val replaceSelInfo: Map[ResultId, Symbol] = - d.filteredCtorDests.values.flatMap { - case CtorFinalDest.Match(_, _, _, selMaps) => - selMaps._2 - case CtorFinalDest.Sel(s) => Nil - }.toMap - - def parentMatchesUptoAFusingOne(scrutId: ResultId) = - def go(scrutId: ResultId): List[ResultId] -> Opt[ResultId] = - d.matchScrutToParentMatchScrut(scrutId).fold(Nil -> N): r => - if d.rewritingMatchConsumers.contains(r) - then Nil -> S(r) - else - val res = go(r) - (r :: res._1) -> res._2 - go(scrutId) - - def allParentMatches(scrutId: ResultId) = - def go(scrutId: ResultId): List[ResultId] = - d.matchScrutToParentMatchScrut(scrutId).fold(Nil)(r => r :: go(r)) - go(scrutId) - - object freeVarsOfNonTransformedMatches: - val store = mutable.Map.empty[ResultId, List[Symbol]] - - private val toBeReplacedForAllBranches = mutable.Map.empty[ResultId, Map[ResultId, Symbol]].withDefaultValue(Map.empty) - d.filteredCtorDests.values.foreach: - case CtorFinalDest.Match(scrut, expr, selInArms, selMaps) => - toBeReplacedForAllBranches += scrut -> (toBeReplacedForAllBranches(scrut) ++ selMaps._2) - case CtorFinalDest.Sel(s) => () - - def apply(scrutExprId: ResultId, m: Match) = store.getOrElseUpdate( - scrutExprId, - locally: - assert(m.scrut.uid === scrutExprId) - val Match(Value.Ref(l), _, _, _) = m - val selReplacementNotForThisSel = replaceSelInfo -- toBeReplacedForAllBranches(scrutExprId).keys - DeforestationFreeVarTraverserForMatch( - nonFreeVars, - selReplacementNotForThisSel, - toBeReplacedForAllBranches(scrutExprId), - l, - self - ).analyze(m) - ) - - object matchArms: - val store = LinkedHashMap.empty[ResultId, Map[ClsOrModSymbol | None.type, FunDefn]].withDefaultValue(Map.empty) - - // return a lambda, which either calls the extracted arm function, or contains the computations in matching arms - def getOrElseUpdate( - scrut: ResultId, - m: Match, - cls: ClsOrModSymbol, - sel: Set[ResultId], - currentUsedCtorArgsToFields: Map[Tree.Ident, Value.Ref], - preComputedSymbols: Map[Tree.Ident, Symbol] -> Map[ResultId, Symbol] = Map.empty -> Map.empty - ) = - assert(scrut === m.scrut.uid) - val freeVarsAndTheirNewSyms = freeVarsOfNonTransformedMatches(scrut, m).map(s => s -> VarSymbol(Tree.Ident(s.nme))) - val (body, isDflt) = m.arms.find{ case (Case.Cls(c1, _) -> _) => c1 === cls }.map(_._2 -> false).orElse(m.dflt.map(_ -> true)).get - store.get(scrut).flatMap(_.get(if isDflt then None else cls)) match - case None => // not registered before, or this branch of this match will only appear once - val rest = m.rest - val makeBody = matchRest.getOrElseUpdate(scrut, rest) match - case N -> rewrittenRest => (bodyBlk: Block) => - Begin(bodyBlk, rewrittenRest).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: - case Return(res, implct) => Return(res, false) - case t => t - case Some(f) -> rewrittenRest => (bodyBlk: Block) => - Begin( - bodyBlk, - Return( - Call( - Value.Ref(f), - rewrittenRest.sortedFvsForTransformedBlocks(nonFreeVars).map(a => Arg(false, Value.Ref(a))))(true, false), - false - ) - ).flattened.replaceSymbols(freeVarsAndTheirNewSyms.toMap).mapTail: - case Return(res, implct) => Return(res, false) - case t => t - - if d.resolveClashes._2(DtorExpr.Match(scrut)).ctors.count{c => - if !isDflt then c.getClsSymOfUid === cls - else m.arms.find{ case (Case.Cls(c1, _), _) => c1 === c.getClsSymOfUid }.isEmpty - } > 1 then - // make a function, and register, and return a lambda calling that function with correct arguments - // arguments for lambda: free vars - // arguments for that function: free vars and pattern vars - - val bodyReplaceSel = applyBlock(body) - - val freeVarsAndTheirNewSymsInLam = freeVarsAndTheirNewSyms.map(s => s._1 -> VarSymbol(s._2.id)) - val funBody = makeBody(bodyReplaceSel) - val funSym = BlockMemberSymbol(s"match_${scrut.getResult.asInstanceOf[Value.Ref].l.nme}_branch_${if isDflt then "dflt" else cls.nme}", Nil) - val newDef = FunDefn( - N, - funSym, - ParamList( - ParamListFlags.empty, - freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N, Modulefulness.none)).toList - ::: preComputedSymbols._1.toList.sortBy(_._1.name).map(v => - Param(FldFlags.empty, v._2.asInstanceOf[VarSymbol], N, Modulefulness.none) - ), - N - ) :: Nil, - funBody - ) - store += (scrut -> (store(scrut) + ((if isDflt then None else cls) -> newDef))) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSymsInLam.map(s => Param(FldFlags.empty, s._2, N, Modulefulness.none)), N), - Return( - Call(Value.Ref(funSym), freeVarsAndTheirNewSymsInLam.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), - false - ) - ) - else - val bodyReplaceSel = applyBlock(body) - val lambdaBody = makeBody(bodyReplaceSel) - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N, Modulefulness.none)).toList, N), - lambdaBody - ) - - case Some(f) => - // return a lambda that calls f with correct arguments - Value.Lam( - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.map(s => Param(FldFlags.empty, s._2, N, Modulefulness.none)), N), - Return( - Call(Value.Ref(f.sym), freeVarsAndTheirNewSyms.map(a => Arg(false, Value.Ref(a._2))) ::: currentUsedCtorArgsToFields.toList.sortBy(_._1.name).map(a => Arg(false, a._2)))(true, false), - false - ) - ) - - def getAllFunDefs: Block => Block = - store.values.flatMap(v => v.values).foldRight(identity: Block => Block): - case (defn, k) => r => Define(defn, k(r)) - - object matchRest: - val store = LinkedHashMap.empty[ResultId, Opt[FunDefn] -> Block] - - def getAllDefined = store.valuesIterator.flatMap(_._1.map(_.sym)) - - // returns the symbol for the rest function (if any), and the rewritten rest block - def getOrElseUpdate(s: ResultId, restBeforeRewriting: Block): Opt[Symbol] -> Block = - store.get(s) match - case Some(f, b) => f.map(_.sym) -> b - case _ => - // return all blocks concat together using `Begin`, and return if all of them are `End` blocks - def concatAllRestBlocksOfMatches(ps: List[ResultId]) = - ps.foldRight[Block](End("")){ (pid, acc) => - val b = d.matchScrutToMatchBlock(pid).rest - val isEnd = b.isInstanceOf[End] - if isEnd then acc else Begin(b, acc) - } - val parentRestInfo = parentMatchesUptoAFusingOne(s) match - case ps -> Some(theFusingOne) => - // return the original rests from unfused parent matches, - // and the function symbol for the `rest` of the fusing parent match (if any) - // and the rewritten `rest` block of that fusing parent match - concatAllRestBlocksOfMatches(ps) -> - getOrElseUpdate(theFusingOne, d.matchScrutToMatchBlock(theFusingOne).rest) - case ps -> None => - // return the original rests from unfused parent matches, and none (meaning that there is no fusing parent match) - concatAllRestBlocksOfMatches(ps) -> None - - // bd: original `rest`s of non-fusing parent matches - val restRewritten = - val nonFlatten = parentRestInfo match - // None: there is no fusing parent match - case bd -> None => applyBlock(Begin(restBeforeRewriting, bd)) - // (Some(s), b): there is a fusing parent match, and its `rest` is extracted into a function with symbol `s`, - // and the transformed `rest` is b - case bd -> (Some(s), b) => Begin( - applyBlock(restBeforeRewriting), - Return(Call(Value.Ref(s), b.sortedFvsForTransformedBlocks(nonFreeVars ++ getAllDefined).map(a => Arg(false, Value.Ref(a))))(true, false), false)) - // (None, b): there is a fusing parent match, and its `rest` is not extracted into a function - case bd -> (None, b) => Begin(applyBlock(Begin(restBeforeRewriting, bd)), b) - nonFlatten.flattened - - // no need to build a new function for empty rest, or if the rest is only going to be used once - if restRewritten.isInstanceOf[End] || (d.resolveClashes._2(DtorExpr.Match(s)).ctors.map(c => c.getClsSymOfUid).size == 1) then - val res = N -> restRewritten - store += s -> res - res - else // build a new function and update the store - val scrutName = s.getResult.asInstanceOf[Value.Ref].l.nme - val sym = BlockMemberSymbol(s"match_${scrutName}_rest", Nil) - val freeVarsAndTheirNewSyms = restRewritten.sortedFvsForTransformedBlocks(nonFreeVars ++ getAllDefined).map(s => s -> VarSymbol(Tree.Ident(s.nme))).toMap - val newFunDef = FunDefn( - N, - sym, - ParamList(ParamListFlags.empty, freeVarsAndTheirNewSyms.values.map(s => Param(FldFlags.empty, s, N, Modulefulness.none)).toList, N) :: Nil, - restRewritten.replaceSymbols(freeVarsAndTheirNewSyms) - ) - store += s -> (Some(newFunDef) -> restRewritten) - Some(sym) -> restRewritten - - def getAllFunDefs: Block => Block = - store.values.foldRight(identity: Block => Block): - case (defn -> _, k) => - r => defn match - case None => k(r) - case Some(defn) => Define(defn, k(r)) - - - override def applyBlock(b: Block): Block = b match - case mat@Match(scrut, arms, dflt, rest) if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } && d.rewritingMatchConsumers.contains(scrut.uid) => - // since all fusing matches will be considered to be in the tail position, - // if any of the parent `rest`s has explicit return, the rewritten match will have explicit return - val oneOfParentMatchRestHasExplicitRet = allParentMatches(scrut.uid).foldRight(false) { (pid, acc) => acc || d.matchScrutToMatchBlock(pid).rest.hasExplicitRet } - val needExplicitRet = rest.hasExplicitRet || arms.exists(_._2.hasExplicitRet) || oneOfParentMatchRestHasExplicitRet - val freeVars = freeVarsOfNonTransformedMatches(scrut.uid, mat).map(v => Arg(false, Value.Ref(v))) - Return(Call(scrut, freeVars)(false, false), !needExplicitRet) - case Match(scrut, arms, dflt, rest) - if - // If all the arms end with non-`End` blocks, then the `rest` of this `Match` will never be executed, - // and we remove the `rest` in this case. This prevents `rest` to use variables that become - // undefined because computation in arms that defines them are moved away. - // One example illustrating the case of "deadcode using never assigned variable causing scope error during JS generation" is as follows: - // The mlscript program is: - // ``` - // fun test(x) = - // let t = if x is - // AA(AA(a)) then a - // t + 5 - // fun f(a) = if a is - // AA then 0 - // let p = AA(AA(10)) - // test(p) + f(p) - // ``` - // After lowering, it is essentially: - // ``` - // fun test(x) = - // if x is AA(param0) then - // if param0 is AA(param1) then - // a = param1 - // tmpRes = a - // else throw "match error" - // else throw "match error" - // t = tmpRes - // return t + 5 - // fun f(a) = if a is AA then 0 - // let p = AA(AA(10)) - // test(p) + f(p) - // ``` - // And after fusion, the program (before the removal of dead code causing scope error) is: - // ``` - // fun test(x) = - // if x is AA(param0) then - // param0() - // else throw "match error" - // t = tmpRes // <--- this `tmpRes` without binding site causes scope error - // return t + 5 - // fun f(a) = if a is AA then 0 - // let p = AA of - // () => a = 10; tmpRes = a; t = tmpRes; return t + 5; - // test(p) + f(p) - // ``` - // TODO: it will become unnecessary once we have proper binding declarations in the Block IR - // and all uses of never-assigned variables will be known to be dead code - dflt.fold(false)(_.willBeNonEndTailBlock) && arms.forall { case (_, body) => body.willBeNonEndTailBlock } - => - super.applyBlock(Match(scrut, arms, dflt, End(""))) - case _ => super.applyBlock(b) - - override def applyResult(r: Result): Result = r match - case _: Call => - // calls to fusing contructors are handled in `applyResult2` - // here we only handle calls to non-fusing constructors and functions - assert(!d.filteredCtorDests.isDefinedAt(r.uid)) - super.applyResult(r) - case _ => super.applyResult(r) - - override def applyResult2(r: Result)(k: Result => Block): Block = - def handleCallLike(f: Path, args: Ls[Path], uid: ResultId) = - val c = f match - case s: Select => s.symbol.get.asCls.get - case Value.Ref(l) => l.asCls.get - case _ => ??? - d.filteredCtorDests.get(uid).get match - case CtorFinalDest.Match(scrut, expr, sels, selsMap) => - // use pre-determined symbols, create temp symbols for un-used fields - val usedFieldIdentToSymbolsToBeReplaced = selsMap._1 - val allFieldIdentToSymbolsToBeReplaced = d.getClsFields(c).map: f => - f.id -> usedFieldIdentToSymbolsToBeReplaced.getOrElse(f.id, TempSymbol(N, s"_deforest_${c.name}_${f.id.name}_unused")) - - // if all vars are temp vars, no need to create more temp vars - // otherwise, create temps for var symbols (which will be function params with these temp vars flowing in) - val assignedTempSyms = - if allFieldIdentToSymbolsToBeReplaced.forall(_._2.isInstanceOf[TempSymbol]) then - allFieldIdentToSymbolsToBeReplaced.map(a => a._1 -> a._2.asInstanceOf[TempSymbol]) - else - allFieldIdentToSymbolsToBeReplaced.map { case (id, s) => s match - case ts: TempSymbol => id -> ts - case vs: VarSymbol => id -> TempSymbol(N, s"${vs.name}_tmp") - } - - val bodyAndRestInLam = matchArms.getOrElseUpdate( - scrut, - expr, - c, - sels.toSet, - assignedTempSyms.filter(a => usedFieldIdentToSymbolsToBeReplaced.contains(a._1)).map(a => a._1 -> Value.Ref(a._2).asInstanceOf[Value.Ref]).toMap, - selsMap._1 -> selsMap._2) - - args.zip(assignedTempSyms.map(_._2)).foldRight[Block](k(bodyAndRestInLam)): - case ((a, tmp), rest) => applyResult2(a) { r => Assign(tmp, r, rest) } - - case CtorFinalDest.Sel(s) => - val selFieldName = s.getResult match { case Select(p, nme) => nme } - val idx = d.getClsFields(c).indexWhere(s => s.id === selFieldName) - k(args(idx)) - - r match - case call@Call(f, args) if d.filteredCtorDests.isDefinedAt(call.uid) => - handleCallLike(f, args.map { case Arg(false, value) => value }, call.uid) - case ins@Instantiate(cls, args) if d.filteredCtorDests.isDefinedAt(ins.uid) => - handleCallLike(cls, args, ins.uid) - case _ => super.applyResult2(r)(k) - - def handleObjFusing(objCallExprUid: ResultId, objClsSym: ModuleSymbol) = - // must be a pat mat on objects; no support for selection on objects yet - val CtorFinalDest.Match(scrut, expr, sels, selsMap) = d.filteredCtorDests(objCallExprUid): @unchecked - val body = expr.arms.find{ case (Case.Cls(m, _) -> body) => m === objClsSym }.map(_._2).orElse(expr.dflt).get - matchArms.getOrElseUpdate(scrut, expr, objClsSym, Set.empty, Map.empty) - - override def applyPath(p: Path): Path = p match - // a selection which is a consumer on its own - case s@Select(p, nme) if d.rewritingSelConsumers.contains(s.uid) => applyPath(p) - - // a selection inside a fusing match that needs to be replaced by pre-computed symbols - case s@Select(p, nme) if replaceSelInfo.get(s.uid).isDefined => Value.Ref(replaceSelInfo(s.uid)) - - case s@Select(p, nme) => s.symbol.flatMap(_.asObj) match - // a fusing object constructor - case Some(obj) if d.filteredCtorDests.isDefinedAt(s.uid) => handleObjFusing(s.uid, obj) - case _ => super.applyPath(s) - - case v: Value => applyValue(v) - case _ => super.applyPath(p) - - override def applyValue(v: Value): Value = v match - case r@Value.Ref(l) => l.asObj match - case None => r - case Some(obj) if d.filteredCtorDests.isDefinedAt(r.uid) => handleObjFusing(r.uid, obj) - case _ => super.applyValue(v) - case _ => super.applyValue(v) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 7ca7716734..b984d76ee5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -19,7 +19,7 @@ class StratVarState(val uid: StratVarId, val name: Str, val generatedForDef: Opt lazy val asConsStrat = ConsVar(this) override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" object StratVarState: - def freshVar(nme: String, generatedForDef: Opt[BlockMemberSymbol])(using vuid: Uid.StratVarNew.State) = + def freshVar(nme: String, generatedForDef: Opt[BlockMemberSymbol])(using vuid: Uid.StratVar.State) = val newId = vuid.nextUid StratVarState(newId, nme, generatedForDef) trait StratVar(s: StratVarState): @@ -102,7 +102,7 @@ class ProdStratScheme(s: StratVarState, constraints: Ls[ProdStrat -> ConsStrat]) newProd class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: - given stratVarUidState: Uid.StratVarNew.State = new Uid.StratVarNew.State + given stratVarUidState: Uid.StratVar.State = new Uid.StratVar.State import StratVarState.freshVar val noProdStratVar = freshVar("primitive", N).asProdStrat @@ -220,7 +220,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): - given stratVarUidState: Uid.StratVarNew.State = preAnalyzer.stratVarUidState + given stratVarUidState: Uid.StratVar.State = preAnalyzer.stratVarUidState import StratVarState.freshVar val constraints = processTopLevel(preAnalyzer.b) From a0c314cafca7bd72c91df0f8bbbb45b7f20841ab Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 18 Jun 2025 00:44:17 +0800 Subject: [PATCH 293/303] new deforest runner --- .../hkmc2/codegen/deforest/Deforest.scala | 42 +++++++++ .../deforest/def-dup/new-dup-simple.mls | 3 + .../test/scala/hkmc2/JSBackendDiffMaker.scala | 87 ++++++++----------- 3 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala new file mode 100644 index 0000000000..5aa398a0a0 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala @@ -0,0 +1,42 @@ +package hkmc2 +package codegen +package deforest + +import semantics.* +import syntax.Tree +import utils.* +import mlscript.utils.*, shorthands.* +import scala.collection.mutable +import Result.ResultId + + +object Deforest: + class State: + val topLevelFunSymToFunDefn = collection.mutable.Map.empty[BlockMemberSymbol, FunDefn] + + def apply(p: Program)(using State, Elaborator.State): Either[Program -> String -> String, String] = + try + val pre = new DeforestPreAnalyzer(p.main) + val col = new DeforestConstraintsCollector(pre) + val ana = new DeforestConstrainSolver(col) + val rwp = new DeforestRewritePrepare(ana) + val rw = new DeforestRewriter(rwp) + val detail = col.constraints + .map: (p, c) => + (s"$p --> $c") + .mkString("\n") + val summary = rwp.ctorIdToFinalDest + .map: (ctorid, dest) => + pre.getResult(ctorid._1).toString() + + "@" + + pre.getStableResultId(ctorid._1) + + "@" + + ctorid._2.makeSuffix(pre) + + " --> " + + dest.toString(pre) + .mkString("\n") + val deforestRes = rw() + L: + Program(p.imports, deforestRes) -> summary -> detail + catch + case NotDeforestableException(msg) => R(msg) diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls index e84e9e9759..dd85db68ff 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/new-dup-simple.mls @@ -132,6 +132,7 @@ f(Nil) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ---------- deforest summary ---------- +//│ //│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -223,6 +224,7 @@ f(A(4)) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ---------- deforest summary ---------- +//│ //│ -------------- executing ------------- //│ = 0 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -240,6 +242,7 @@ g(o) + f(o) + o.x //│ o = A(1) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ---------- deforest summary ---------- +//│ //│ -------------- executing ------------- //│ = 3 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 2029691abd..6fcf81ec0d 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -61,6 +61,10 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: h private var hostCreated = false + + + given deforestState: deforest.Deforest.State = new deforest.Deforest.State + override def run(): Unit = try super.run() finally if hostCreated then host.terminate() @@ -93,15 +97,12 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if deforestFlag.isSet then import codegen.deforest.* output(">>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>>") - val pre = new DeforestPreAnalyzer(le.main) - val col = new DeforestConstraintsCollector(pre) - val ana = new DeforestConstrainSolver(col) - val rwp = new DeforestRewritePrepare(ana) - val rw = new DeforestRewriter(rwp) - val deforestRes = rw() - val jsStr = baseScp.nest.givenIn: - jsb.program(Program(Nil, deforestRes), N, wd).stripBreaks.mkString(100) - output(jsStr) + Deforest(le) match + case R(msg) => output(s"Not deforestable: $msg") + case L(deforestRes -> _ -> _) => + val jsStr = baseScp.nest.givenIn: + jsb.program(deforestRes, N, wd).stripBreaks.mkString(100) + output(jsStr) output("<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<<") if js.isSet then @@ -245,45 +246,33 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val deforestLow = ltl.givenIn: codegen.Lowering() val le = deforestLow.program(blk) - val pre = new DeforestPreAnalyzer(le.main) - val col = new DeforestConstraintsCollector(pre) - if deforestInfo.isSet then - col.constraints.foreach: (p, c) => - output(s"$p --> $c") - val ana = new DeforestConstrainSolver(col) - val rwp = new DeforestRewritePrepare(ana) - output("---------- deforest summary ----------") - rwp.ctorIdToFinalDest.foreach: - case (ctorid -> dest) => - output: - pre.getResult(ctorid._1).toString() + - "@" + - pre.getStableResultId(ctorid._1) + - "@" + - ctorid._2.makeSuffix(pre) + - " --> " + - dest.toString(pre) - val rw = new DeforestRewriter(rwp) - val deforestRes = rw() - val resSym -> resNme = getResSymAndResNme("block$res_deforest") - val deforestRes2 = assignResultSymForBlock(Program(Nil, deforestRes), resSym) - if showLoweredTree.isSet then - output(s"Lowered:") - output(deforestRes2.showAsTree) - if ppLoweredTree.isSet then - output(s"Pretty Lowered:") - output(Printer.mkDocument(deforestRes2)(using summon[Raise], baseScp).toString) - val (preStr, jsStr) = mkJS(deforestRes2) - if showSanitizedJS.isSet then - output("------ deforested sanitized js -------") - output(jsStr) - output("-------------- executing -------------") - executeJS(preStr, jsStr, resNme) - if silent.isUnset then - handleDefinedValues("", resSym, expect.get): result => - if correctResult.fold(false)(_ != result) then raise: - ErrorReport( - msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, - source = Diagnostic.Source.Runtime) + Deforest(le) match + case R(msg) => output(s"Not deforestable: $msg") + case L(deforestRes -> summary -> detail) => + if deforestInfo.isSet then + output(detail) + output("---------- deforest summary ----------") + output(summary) + val resSym -> resNme = getResSymAndResNme("block$res_deforest") + val deforestRes2 = assignResultSymForBlock(deforestRes, resSym) + if showLoweredTree.isSet then + output(s"Lowered:") + output(deforestRes2.showAsTree) + if ppLoweredTree.isSet then + output(s"Pretty Lowered:") + output(Printer.mkDocument(deforestRes2)(using summon[Raise], baseScp).toString) + val (preStr, jsStr) = mkJS(deforestRes2) + if showSanitizedJS.isSet then + output("------ deforested sanitized js -------") + output(jsStr) + output("-------------- executing -------------") + executeJS(preStr, jsStr, resNme) + if silent.isUnset then + handleDefinedValues("", resSym, expect.get): result => + if correctResult.fold(false)(_ != result) then raise: + ErrorReport( + msg"The result from deforestated program (\"${result}\") is different from the one computed by the original prorgam (\"${correctResult.get}\")" -> N :: Nil, + source = Diagnostic.Source.Runtime) + output("<<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<") From 67157575276c99e625200ebdb66c138424eea201 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:12:47 +0800 Subject: [PATCH 294/303] handle functions nested in functions --- .../hkmc2/codegen/deforest/Analyze.scala | 23 ++++++++++++++----- .../hkmc2/codegen/deforest/Rewrite.scala | 6 ++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index b984d76ee5..70c93b7f53 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -146,15 +146,17 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: private var inFunDef: Opt[BlockMemberSymbol] = N private var symsDefinedForFun: Opt[Set[Symbol]] = N override def applyFunDefn(fun: FunDefn): Unit = - funSymToFun += fun.sym -> fun inFunDef match case N => + funSymToFun += fun.sym -> fun inFunDef = S(fun.sym) symsDefinedForFun = S(fun.body.definedVars ++ fun.params.flatMap(_.params.map(_.sym)) + fun.sym) - case S(value) => throw NotDeforestableException("not expecting nested function definitions") - super.applyFunDefn(fun) - inFunDef = N - symsDefinedForFun = N + super.applyFunDefn(fun) + inFunDef = N + symsDefinedForFun = N + case S(value) => + // nothing special for non-top-level functions + super.applyFunDefn(fun) override def applySymbol(s: Symbol): Unit = s match case s: BlockMemberSymbol if s.trmImplTree.fold(false)(_.k is syntax.Fun) => symToStratVar.updateWith(s): @@ -323,7 +325,16 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): processBlock(rest) case Define(defn, rest) => defn match - case f: FunDefn => () // skip as toplevel fundefs are processed when needed + case FunDefn(_, sym, params, body) => + if processingDefs.nonEmpty then + val paramSyms = params.head.params.map: // TODO: handle multiple param list and the `restParam` + case Param(sym = sym, _) => preAnalyzer.getProdVarForSym(sym).asConsStrat + val bodyStrat = processBlock(body) + val res = freshVar(s"${sym.nme}_res", cc.forFun) + cc.constrain(bodyStrat, res.asConsStrat) + cc.constrain(ProdFun(paramSyms, res.asProdStrat), preAnalyzer.getProdVarForSym(sym).asConsStrat) + else + () // skip toplevel fundefs are they are processed when needed case v: ValDefn => throw NotDeforestableException("No support for `ValDefn` yet") case c: ClsLikeDefn => throw NotDeforestableException("No support for `ClsLikeDefn` yet") processBlock(rest) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 613c03709d..1eaf12129a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -482,7 +482,11 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case _ => super.applyValue(v) case _ => super.applyValue(v) - override def applyFunDefn(fun: FunDefn): FunDefn = fun + override def applyFunDefn(fun: FunDefn): FunDefn = + if instId.isEmpty then + fun // skip top level functions + else + super.applyFunDefn(fun) def apply(b: Block) = applyBlock(b) From c26bfab79d5cbdeb7893b4e01e57ef013c8f3e16 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Wed, 18 Jun 2025 08:10:56 +0800 Subject: [PATCH 295/303] slightly improve sym to strat var --- .../main/scala/hkmc2/codegen/deforest/Analyze.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 70c93b7f53..e52187f07a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -17,7 +17,7 @@ type InstantiationId = Ls[ResultId] class StratVarState(val uid: StratVarId, val name: Str, val generatedForDef: Opt[BlockMemberSymbol]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) - override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}" + override def toString(): String = s"${if name.isEmpty() then "var" else name}@${uid}@$generatedForDef" object StratVarState: def freshVar(nme: String, generatedForDef: Opt[BlockMemberSymbol])(using vuid: Uid.StratVar.State) = val newId = vuid.nextUid @@ -182,7 +182,11 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: override def applyPath(p: Path): Unit = resultIdToResult += p.uid -> p p match - case s@Select(path, nme) => selsToMatchingArmsContainingIt += s.uid -> inMatchScrutsArms + case s@Select(path, nme) => + selsToMatchingArmsContainingIt += s.uid -> inMatchScrutsArms + s.symbol.flatMap(_.asBlkMember).foreach: b => + b.trmTree.foreach: t => + if t.k is syntax.Fun then usedFunSyms += b case _ => () super.applyPath(p) @@ -240,7 +244,9 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): def getOrUpdate(s: BlockMemberSymbol)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector): ProdVar | ProdStratScheme = preAnalyzer.getFunDefnForSym(s) match // not a fun defined in the current block, just return its prodvar - case None => preAnalyzer.getProdVarForSym(s) + case None => + preAnalyzer.noProdStratVar + // preAnalyzer.getProdVarForSym(s) // TODO: consider functions being imported? case Some(funDefn) => store.get(s) match case Some(scheme) => scheme case None => processingDefs.filter(_ is s) match From 168e1e1249cd099e30c06e7b7840a6d027f70bfc Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 19 Jun 2025 16:30:08 +0800 Subject: [PATCH 296/303] more fixes for lambdas and nested functions; update tests --- .../hkmc2/codegen/deforest/Analyze.scala | 30 +- .../hkmc2/codegen/deforest/Rewrite.scala | 5 +- .../src/test/mlscript/deforest/append.mls | 39 +- .../test/mlscript/deforest/def-dup/simple.mls | 805 +++--------------- .../test/mlscript/deforest/def-dup/todo.mls | 87 +- .../test/mlscript/deforest/determinism.mls | 141 +-- .../src/test/mlscript/deforest/imperative.mls | 30 +- .../mlscript/deforest/listComprehension.mls | 80 +- .../test/mlscript/deforest/nestedMatch.mls | 206 +++-- .../src/test/mlscript/deforest/recursive.mls | 160 ++-- .../deforest/selectionsInNestedMatch.mls | 79 +- .../src/test/mlscript/deforest/simple.mls | 301 ++++--- .../src/test/mlscript/deforest/todos.mls | 173 +++- .../src/test/mlscript/deforest/zipunzip.mls | 46 +- 14 files changed, 999 insertions(+), 1183 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index e52187f07a..1dc28e7a9e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -107,19 +107,20 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: val noProdStratVar = freshVar("primitive", N).asProdStrat val resultIdToResult = mutable.Map.empty[ResultId, Result] - val funSymToFun = mutable.Map.empty[BlockMemberSymbol, FunDefn] + val topLevelFunSymToFun = mutable.Map.empty[BlockMemberSymbol, FunDefn] val matchScrutToMatchBlock = mutable.Map.empty[ResultId, Match] val matchScrutToParentMatchScruts = mutable.Map.empty[ResultId, Ls[ResultId]] val matchScrutInFunDef = mutable.Map.empty[ResultId, Opt[BlockMemberSymbol]] val selsToMatchingArmsContainingIt = mutable.Map.empty[ResultId, Ls[ResultId -> Opt[ClassLikeSymbol]]] val symToStratVar = mutable.Map.empty[Symbol, ProdVar] val usedFunSyms = mutable.Set.empty[BlockMemberSymbol] - lazy val definedFunSyms = funSymToFun.keySet + lazy val topLevelDefinedFunSyms = topLevelFunSymToFun.keySet + val nonTopLevelDefinedFunSyms = mutable.Set.empty[BlockMemberSymbol] def getProdVarForSym(s: Symbol) = s match case _: (BuiltinSymbol | TopLevelSymbol) => noProdStratVar case _ if s.asCls.isDefined => noProdStratVar case _ => symToStratVar(s) - def getFunDefnForSym(s: BlockMemberSymbol) = funSymToFun.get(s) + def getTopLevelFunDefnForSym(s: BlockMemberSymbol) = topLevelFunSymToFun.get(s) def getCtorSymFromCtorLikeExprId(id: ResultId): Opt[ClassLikeSymbol] = resultIdToResult(id).getCtorSymFromCtorLikeExpr def getMatchFromMatchScrutExprId(scrutExprId: ResultId): Opt[Match] = @@ -131,7 +132,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: prev def getReferredFunSym(id: ResultId) = def chk(s: BlockMemberSymbol) = - assert(s.trmImplTree.exists(_.k is syntax.Fun)) + // assert(s.trmImplTree.exists(_.k is syntax.Fun)) s resultIdToResult(id) match case s: Select => chk(s.symbol.get.asBlkMember.get) @@ -148,7 +149,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: override def applyFunDefn(fun: FunDefn): Unit = inFunDef match case N => - funSymToFun += fun.sym -> fun + topLevelFunSymToFun += fun.sym -> fun inFunDef = S(fun.sym) symsDefinedForFun = S(fun.body.definedVars ++ fun.params.flatMap(_.params.map(_.sym)) + fun.sym) super.applyFunDefn(fun) @@ -156,6 +157,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: symsDefinedForFun = N case S(value) => // nothing special for non-top-level functions + nonTopLevelDefinedFunSyms += fun.sym super.applyFunDefn(fun) override def applySymbol(s: Symbol): Unit = s match @@ -242,11 +244,11 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val store = mutable.Map.empty[BlockMemberSymbol, ProdStratScheme] val recursiveGroups = mutable.Map.empty[BlockMemberSymbol, Ls[BlockMemberSymbol]] def getOrUpdate(s: BlockMemberSymbol)(using processingDefs: Ls[BlockMemberSymbol], cc: ConstraintsAndCacheHitCollector): ProdVar | ProdStratScheme = - preAnalyzer.getFunDefnForSym(s) match + preAnalyzer.getTopLevelFunDefnForSym(s) match // not a fun defined in the current block, just return its prodvar case None => - preAnalyzer.noProdStratVar - // preAnalyzer.getProdVarForSym(s) // TODO: consider functions being imported? + // preAnalyzer.noProdStratVar + preAnalyzer.getProdVarForSym(s) // TODO: consider functions being imported? case Some(funDefn) => store.get(s) match case Some(scheme) => scheme case None => processingDefs.filter(_ is s) match @@ -284,7 +286,11 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): cc.constrain(strat, NoCons) cc.constrain(preAnalyzer.noProdStratVar, NoCons) cc.constrain(NoProd, preAnalyzer.noProdStratVar.asConsStrat) - // TODO: for used but not defined fun syms, constrain them with NoProd + preAnalyzer.usedFunSyms + .diff(preAnalyzer.topLevelDefinedFunSyms) + .diff(preAnalyzer.nonTopLevelDefinedFunSyms) + .foreach: usedButNotDefined => + cc.constrain(NoProd, preAnalyzer.getProdVarForSym(usedButNotDefined).asConsStrat) // TODO: for defined but not fun syms, constrain them with NoCons cc.constraints @@ -417,7 +423,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case Some(s) if s.asObj.isDefined => new Ctor(sel.uid, instantiationId, s.asObj.get, Nil) case Some(s) if s.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && - preAnalyzer.definedFunSyms.contains(s.asBlkMember.get) => + preAnalyzer.topLevelDefinedFunSyms.contains(s.asBlkMember.get) => funSymToProdStratScheme.getOrUpdate(s.asBlkMember.get) match case v: ProdVar => v case t: ProdStratScheme => @@ -447,8 +453,8 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case v@Value.Ref(l) => l.asObj match case None => - if l.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && - preAnalyzer.definedFunSyms.contains(l.asBlkMember.get) then + // println(s"ref $l: ${l.asBlkMember.map(_.trees)}") + if l.asBlkMember.exists(preAnalyzer.topLevelDefinedFunSyms.contains) then funSymToProdStratScheme.getOrUpdate(l.asBlkMember.get) match case v: ProdVar => v case t: ProdStratScheme => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 1eaf12129a..1e030b8b3c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -228,7 +228,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora // 1. find original fundefs // 2. transform fun body under the specific instantiation id // 3. accumulate the definition - val FunDefn(_, _, param, body) = preAnalyzer.getFunDefnForSym(oldSym).get + val FunDefn(_, _, param, body) = preAnalyzer.getTopLevelFunDefnForSym(oldSym).get val oldToNewParam = mutable.Map.empty[VarSymbol, VarSymbol] val newParam = param.map: case ParamList(flags, params, restParam) => @@ -471,7 +471,8 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(r.uid.withInstId) => matchArmsOfFusingMatches.getOrElseUpdate(r.uid.withInstId) case None => l.asBlkMember match - case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => + // case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => + case Some(blk) if preAnalyzer.topLevelDefinedFunSyms.contains(blk) => val inTheSameRecursiveGroup = instId.lastOption.fold(false): currentReferSite => val currentSym = preAnalyzer.getReferredFunSym(currentReferSite) rewritePrepare.sol.collector.funSymToProdStratScheme.recursiveGroups(currentSym).contains(blk) diff --git a/hkmc2/shared/src/test/mlscript/deforest/append.mls b/hkmc2/shared/src/test/mlscript/deforest/append.mls index 7f1effc580..f34b761d7e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/append.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/append.mls @@ -1,11 +1,18 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class (::) Cons(h, t) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -22,7 +29,12 @@ appendThree of id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // maybe the fusion target for the previous program fun appendReified(ys, zs) = if ys is @@ -40,7 +52,12 @@ test of id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun idList(l) = if l is @@ -56,7 +73,14 @@ appendThree of id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@4@inst_0_1_2_tsni --> Ref(xs)@inst_0_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(h)), Arg(false,Ref($tmp))))@5@inst_0_1_2_tsni --> Ref(xs)@inst_0_3_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Ref(h)), Arg(false,Ref($tmp))))@6@inst_0_1_tsni --> Ref(xs)@inst_0_3_tsni@Cons +//│ -------------- executing ------------- +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -71,4 +95,9 @@ concat of id of (3 :: 4 :: Nil) :: (5 :: 6 :: Nil) :: Nil //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls index 018d3ab919..44f02b9d4f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/simple.mls @@ -1,14 +1,24 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class A(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class (::) Cons(h, t) object Nil +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + -:deforestDup // `A(1)` is in the definition of `test`, but // no duplication is helpful fun test() = @@ -20,99 +30,31 @@ fun test() = a + b test() + test() //│ = 4 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 4 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + -:sjs -:deforestDup fun p(d) = A(3) fun c1(x) = if x is A then 1 fun c2(x) = if x is A then 2 fun test() = c1(p(1)) + c2(p(2)) test() -//│ JS (unsanitized): -//│ let p, test1, c1, c2; -//│ p = function p(d) { -//│ return A1(3) -//│ }; -//│ c1 = function c1(x) { -//│ if (x instanceof A1.class) { -//│ return 1 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ c2 = function c2(x) { -//│ if (x instanceof A1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ test1 = function test() { -//│ let tmp2, tmp3, tmp4, tmp5; -//│ tmp2 = p(1); -//│ tmp3 = c1(tmp2); -//│ tmp4 = p(2); -//│ tmp5 = c2(tmp4); -//│ return tmp3 + tmp5 -//│ }; -//│ test1() -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@931 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@932 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let p, test1, c1, c2, p_duplicated, p_duplicated1; -//│ p_duplicated = function p_duplicated(d) { -//│ let _deforest_A_x_unused; -//│ _deforest_A_x_unused = 3; -//│ return () => { -//│ return 1 -//│ } -//│ }; -//│ p_duplicated1 = function p_duplicated(d) { -//│ let _deforest_A_x_unused; -//│ _deforest_A_x_unused = 3; -//│ return () => { -//│ return 2 -//│ } -//│ }; -//│ p = function p(d) { -//│ return A1(3) -//│ }; -//│ c1 = function c1(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ c2 = function c2(x) { -//│ return runtime.safeCall(x()) -//│ }; -//│ test1 = function test() { -//│ let tmp2, tmp3, tmp4, tmp5; -//│ tmp2 = p_duplicated(1); -//│ tmp3 = c1(tmp2); -//│ tmp4 = p_duplicated1(2); -//│ tmp5 = c2(tmp4); -//│ return tmp3 + tmp5 -//│ }; -//│ test1() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@947 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@948 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(3)))))@5@inst_0_1_tsni --> Ref(x)@inst_0_3_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(3)))))@5@inst_0_2_tsni --> Ref(x)@inst_0_4_tsni@A +//│ -------------- executing ------------- //│ = 3 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ A --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs -:deforestDup + fun p(x) = A(x) fun f1(a1) = if a1 is A(aa) then aa fun f2(a2) = if a2 is A(aa) then aa @@ -120,189 +62,20 @@ fun f3(a3) = if a3 is A(aa) then aa fun f4(a4) = if a4 is A(aa) then aa fun f5(a5) = if a5 is A(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) -//│ JS (unsanitized): -//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; -//│ p1 = function p(x) { -//│ return A1(x) -//│ }; -//│ f1 = function f1(a1) { -//│ let param0, aa; -//│ if (a1 instanceof A1.class) { -//│ param0 = a1.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f2 = function f2(a2) { -//│ let param0, aa; -//│ if (a2 instanceof A1.class) { -//│ param0 = a2.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f3 = function f3(a3) { -//│ let param0, aa; -//│ if (a3 instanceof A1.class) { -//│ param0 = a3.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f4 = function f4(a4) { -//│ let param0, aa; -//│ if (a4 instanceof A1.class) { -//│ param0 = a4.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f5 = function f5(a5) { -//│ let param0, aa; -//│ if (a5 instanceof A1.class) { -//│ param0 = a5.x; -//│ aa = param0; -//│ return aa -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp2 = p1(1); -//│ tmp3 = f1(tmp2); -//│ tmp4 = p1(2); -//│ tmp5 = f2(tmp4); -//│ tmp6 = tmp3 + tmp5; -//│ tmp7 = p1(3); -//│ tmp8 = f3(tmp7); -//│ tmp9 = tmp6 + tmp8; -//│ tmp10 = p1(4); -//│ tmp11 = f4(tmp10); -//│ tmp12 = tmp9 + tmp11; -//│ tmp13 = p1(5); -//│ tmp14 = f5(tmp13); -//│ tmp12 + tmp14 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1016 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1017 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1018 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1019 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1020 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f1, f5, f3, p1, f4, f2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, p_duplicated2, p_duplicated3, p_duplicated4, p_duplicated5, p_duplicated6; -//│ p_duplicated2 = function p_duplicated(x) { -//│ let _deforest_A_x; -//│ _deforest_A_x = x; -//│ return () => { -//│ let param0, aa; -//│ param0 = _deforest_A_x; -//│ aa = param0; -//│ return aa -//│ } -//│ }; -//│ p_duplicated3 = function p_duplicated(x) { -//│ let _deforest_A_x; -//│ _deforest_A_x = x; -//│ return () => { -//│ let param0, aa; -//│ param0 = _deforest_A_x; -//│ aa = param0; -//│ return aa -//│ } -//│ }; -//│ p_duplicated4 = function p_duplicated(x) { -//│ let _deforest_A_x; -//│ _deforest_A_x = x; -//│ return () => { -//│ let param0, aa; -//│ param0 = _deforest_A_x; -//│ aa = param0; -//│ return aa -//│ } -//│ }; -//│ p_duplicated5 = function p_duplicated(x) { -//│ let _deforest_A_x; -//│ _deforest_A_x = x; -//│ return () => { -//│ let param0, aa; -//│ param0 = _deforest_A_x; -//│ aa = param0; -//│ return aa -//│ } -//│ }; -//│ p_duplicated6 = function p_duplicated(x) { -//│ let _deforest_A_x; -//│ _deforest_A_x = x; -//│ return () => { -//│ let param0, aa; -//│ param0 = _deforest_A_x; -//│ aa = param0; -//│ return aa -//│ } -//│ }; -//│ p1 = function p(x) { -//│ return A1(x) -//│ }; -//│ f1 = function f1(a1) { -//│ return runtime.safeCall(a1()) -//│ }; -//│ f2 = function f2(a2) { -//│ return runtime.safeCall(a2()) -//│ }; -//│ f3 = function f3(a3) { -//│ return runtime.safeCall(a3()) -//│ }; -//│ f4 = function f4(a4) { -//│ return runtime.safeCall(a4()) -//│ }; -//│ f5 = function f5(a5) { -//│ return runtime.safeCall(a5()) -//│ }; -//│ tmp2 = p_duplicated2(1); -//│ tmp3 = f1(tmp2); -//│ tmp4 = p_duplicated3(2); -//│ tmp5 = f2(tmp4); -//│ tmp6 = tmp3 + tmp5; -//│ tmp7 = p_duplicated4(3); -//│ tmp8 = f3(tmp7); -//│ tmp9 = tmp6 + tmp8; -//│ tmp10 = p_duplicated5(4); -//│ tmp11 = f4(tmp10); -//│ tmp12 = tmp9 + tmp11; -//│ tmp13 = p_duplicated6(5); -//│ tmp14 = f5(tmp13); -//│ tmp12 + tmp14 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 15 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1059 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1060 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1061 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1062 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:p_duplicated@1063 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@10@inst_0_tsni --> Ref(a5)@inst_5_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@10@inst_1_tsni --> Ref(a4)@inst_6_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@10@inst_2_tsni --> Ref(a3)@inst_7_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@10@inst_3_tsni --> Ref(a2)@inst_8_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@10@inst_4_tsni --> Ref(a1)@inst_9_tsni@A +//│ -------------- executing ------------- //│ = 15 -//│ 5 fusion opportunities: -//│ A --match--> `if a1 is ...` -//│ A --match--> `if a2 is ...` -//│ A --match--> `if a3 is ...` -//│ A --match--> `if a4 is ...` -//│ A --match--> `if a5 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:sjs -:deforestDup + // calls to `to` should be able to be duplicated fun to(n) = if n > 0 then @@ -323,254 +96,23 @@ fun f4(ls4) = if ls4 is h :: t then h + f4(t) Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) -//│ JS (unsanitized): -//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37; -//│ to = function to(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ f11 = function f1(ls1) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls1 instanceof Cons1.class) { -//│ param0 = ls1.h; -//│ param1 = ls1.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f11(t); -//│ return h + tmp38 -//│ } else if (ls1 instanceof Nil1.class) { -//│ return 2 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f21 = function f2(ls2) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls2 instanceof Cons1.class) { -//│ param0 = ls2.h; -//│ param1 = ls2.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f21(t); -//│ return h + tmp38 -//│ } else if (ls2 instanceof Nil1.class) { -//│ return 3 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f31 = function f3(ls3) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls3 instanceof Cons1.class) { -//│ param0 = ls3.h; -//│ param1 = ls3.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f31(t); -//│ return h + tmp38 -//│ } else if (ls3 instanceof Nil1.class) { -//│ return 4 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ f41 = function f4(ls4) { -//│ let param0, param1, h, t, tmp38; -//│ if (ls4 instanceof Cons1.class) { -//│ param0 = ls4.h; -//│ param1 = ls4.t; -//│ h = param0; -//│ t = param1; -//│ tmp38 = f41(t); -//│ return h + tmp38 -//│ } else if (ls4 instanceof Nil1.class) { -//│ return 5 -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ tmp28 = to(4); -//│ tmp29 = f11(tmp28); -//│ tmp30 = to(5); -//│ tmp31 = f21(tmp30); -//│ tmp32 = tmp29 + tmp31; -//│ tmp33 = to(6); -//│ tmp34 = f31(tmp33); -//│ tmp35 = tmp32 + tmp34; -//│ tmp36 = to(7); -//│ tmp37 = f41(tmp36); -//│ tmp35 + tmp37 -//│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1154 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1155 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1156 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1157 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f11, to, f31, f41, f21, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, to_duplicated, to_duplicated1, to_duplicated2, to_duplicated3; -//│ to_duplicated = function to_duplicated(n) { -//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp39 = n - 1; -//│ m = tmp39; -//│ tmp38 = to_duplicated(m); -//│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp38; -//│ return () => { -//│ let param0, param1, h, t, tmp40; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp40 = f11(t); -//│ return h + tmp40 -//│ } -//│ } else { -//│ return () => { -//│ return 2 -//│ } -//│ } -//│ }; -//│ to_duplicated1 = function to_duplicated(n) { -//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp39 = n - 1; -//│ m = tmp39; -//│ tmp38 = to_duplicated1(m); -//│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp38; -//│ return () => { -//│ let param0, param1, h, t, tmp40; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp40 = f21(t); -//│ return h + tmp40 -//│ } -//│ } else { -//│ return () => { -//│ return 3 -//│ } -//│ } -//│ }; -//│ to_duplicated2 = function to_duplicated(n) { -//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp39 = n - 1; -//│ m = tmp39; -//│ tmp38 = to_duplicated2(m); -//│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp38; -//│ return () => { -//│ let param0, param1, h, t, tmp40; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp40 = f31(t); -//│ return h + tmp40 -//│ } -//│ } else { -//│ return () => { -//│ return 4 -//│ } -//│ } -//│ }; -//│ to_duplicated3 = function to_duplicated(n) { -//│ let tmp38, m, tmp39, scrut, _deforest_Cons_t, _deforest_Cons_h; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp39 = n - 1; -//│ m = tmp39; -//│ tmp38 = to_duplicated3(m); -//│ _deforest_Cons_h = n; -//│ _deforest_Cons_t = tmp38; -//│ return () => { -//│ let param0, param1, h, t, tmp40; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp40 = f41(t); -//│ return h + tmp40 -//│ } -//│ } else { -//│ return () => { -//│ return 5 -//│ } -//│ } -//│ }; -//│ to = function to(n) { -//│ let scrut, m, tmp38, tmp39; -//│ scrut = n > 0; -//│ if (scrut === true) { -//│ tmp38 = n - 1; -//│ m = tmp38; -//│ tmp39 = to(m); -//│ return Cons1(n, tmp39) -//│ } else { -//│ return Nil1 -//│ } -//│ }; -//│ f11 = function f1(ls1) { -//│ return runtime.safeCall(ls1()) -//│ }; -//│ f21 = function f2(ls2) { -//│ return runtime.safeCall(ls2()) -//│ }; -//│ f31 = function f3(ls3) { -//│ return runtime.safeCall(ls3()) -//│ }; -//│ f41 = function f4(ls4) { -//│ return runtime.safeCall(ls4()) -//│ }; -//│ tmp28 = to_duplicated(4); -//│ tmp29 = f11(tmp28); -//│ tmp30 = to_duplicated1(5); -//│ tmp31 = f21(tmp30); -//│ tmp32 = tmp29 + tmp31; -//│ tmp33 = to_duplicated2(6); -//│ tmp34 = f31(tmp33); -//│ tmp35 = tmp32 + tmp34; -//│ tmp36 = to_duplicated3(7); -//│ tmp37 = f41(tmp36); -//│ tmp35 + tmp37 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 88 -//│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:to_duplicated@1220 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:to_duplicated@1221 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))))) <-- dup --> member:to_duplicated@1222 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(7))))) <-- dup --> member:to_duplicated@1223 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@8@inst_0_tsni --> Ref(ls4)@inst_4_tsni@Cons +//│ Ref(member:Nil)@9@inst_0_tsni --> Ref(ls4)@inst_4_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@8@inst_1_tsni --> Ref(ls3)@inst_5_tsni@Cons +//│ Ref(member:Nil)@9@inst_1_tsni --> Ref(ls3)@inst_5_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@8@inst_2_tsni --> Ref(ls2)@inst_6_tsni@Cons +//│ Ref(member:Nil)@9@inst_2_tsni --> Ref(ls2)@inst_6_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@8@inst_3_tsni --> Ref(ls1)@inst_7_tsni@Cons +//│ Ref(member:Nil)@9@inst_3_tsni --> Ref(ls1)@inst_7_tsni@Nil +//│ -------------- executing ------------- //│ = 88 -//│ 8 fusion opportunities: -//│ Cons --match--> `if ls1 is ...` -//│ Cons --match--> `if ls2 is ...` -//│ Cons --match--> `if ls3 is ...` -//│ Cons --match--> `if ls4 is ...` -//│ Nil --match--> `if ls1 is ...` -//│ Nil --match--> `if ls2 is ...` -//│ Nil --match--> `if ls3 is ...` -//│ Nil --match--> `if ls4 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:deforestDup + fun to(n) = if n > 0 then let m = n - 1 @@ -583,15 +125,15 @@ fun f1(ls1) = if ls1 is f1(to(4)) //│ = 12 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls1)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls1)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = 12 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls1 is ...` -//│ Nil --match--> `if ls1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:deforestDup -:sjs + fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) @@ -600,114 +142,20 @@ fun double(x) = x * 2 fun test() = map(map(1 :: 2 :: Nil, succ), double) test() -//│ JS (unsanitized): -//│ let succ, test2, double1, map; -//│ map = function map(ls, f) { -//│ let param0, param1, h, t, tmp50, tmp51; -//│ if (ls instanceof Nil1.class) { -//│ return Nil1 -//│ } else if (ls instanceof Cons1.class) { -//│ param0 = ls.h; -//│ param1 = ls.t; -//│ h = param0; -//│ t = param1; -//│ tmp50 = runtime.safeCall(f(h)); -//│ tmp51 = map(t, f); -//│ return Cons1(tmp50, tmp51) -//│ } else { -//│ throw new globalThis.Error("match error"); -//│ } -//│ }; -//│ succ = function succ(x) { -//│ return x + 1 -//│ }; -//│ double1 = function (x) { -//│ return x * 2 -//│ }; -//│ test2 = function test() { -//│ let tmp50, tmp51, tmp52; -//│ tmp50 = Cons1(2, Nil1); -//│ tmp51 = Cons1(1, tmp50); -//│ tmp52 = map(tmp51, succ); -//│ return map(tmp52, double1) -//│ }; -//│ test2() -//│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1323 -//│ ========= -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let succ, test2, double1, map, map_duplicated, match_ls_branch_Cons; -//│ match_ls_branch_Cons = function match_ls_branch_Cons(f, _deforest_Cons_h, _deforest_Cons_t) { -//│ let param0, t, tmp50, param1, h, tmp51, _deforest_Cons_t1, _deforest_Cons_h1; -//│ param0 = _deforest_Cons_h; -//│ param1 = _deforest_Cons_t; -//│ h = param0; -//│ t = param1; -//│ tmp51 = runtime.safeCall(f(h)); -//│ tmp50 = map_duplicated(t, f); -//│ _deforest_Cons_h1 = tmp51; -//│ _deforest_Cons_t1 = tmp50; -//│ return (f6) => { -//│ let param01, param11, h1, t1, tmp52, tmp53; -//│ param01 = _deforest_Cons_h1; -//│ param11 = _deforest_Cons_t1; -//│ h1 = param01; -//│ t1 = param11; -//│ tmp52 = runtime.safeCall(f6(h1)); -//│ tmp53 = map(t1, f6); -//│ return Cons1(tmp52, tmp53) -//│ } -//│ }; -//│ map_duplicated = function map_duplicated(ls, f) { -//│ return runtime.safeCall(ls(f)) -//│ }; -//│ map = function map(ls, f) { -//│ return runtime.safeCall(ls(f)) -//│ }; -//│ succ = function succ(x) { -//│ return x + 1 -//│ }; -//│ double1 = function (x) { -//│ return x * 2 -//│ }; -//│ test2 = function test() { -//│ let tmp50, tmp51, tmp52, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1; -//│ _deforest_Cons_h_tmp = 2; -//│ _deforest_Cons_t_tmp = (f) => { -//│ return (f6) => { -//│ return Nil1 -//│ } -//│ }; -//│ tmp50 = (f) => { -//│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp, _deforest_Cons_t_tmp) -//│ }; -//│ _deforest_Cons_h_tmp1 = 1; -//│ _deforest_Cons_t_tmp1 = tmp50; -//│ tmp51 = (f) => { -//│ return match_ls_branch_Cons(f, _deforest_Cons_h_tmp1, _deforest_Cons_t_tmp1) -//│ }; -//│ tmp52 = map_duplicated(tmp51, succ); -//│ return map(tmp52, double1) -//│ }; -//│ test2() -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< //│ = Cons(4, Cons(6, Nil)) -//│ duplication chances: -//│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1359 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@3@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@4@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Ref(member:Nil)@5@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Nil +//│ Ref(member:Nil)@6@inst_0_1_tsni --> Ref(ls)@inst_0_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@7@inst_0_1_tsni --> Ref(ls)@inst_0_2_tsni@Cons +//│ -------------- executing ------------- //│ = Cons(4, Cons(6, Nil)) -//│ 5 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:deforestDup + fun p(x) = A(x) fun g1(b) = if b then p(1) else p(2) fun wrap(b) = g1(b) @@ -716,26 +164,19 @@ fun f1(p) = if p is A(a) then a + 1 fun f2(p) = if p is A(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ = 8 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1435 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1435 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(3))))) <-- dup --> member:p_duplicated@1436 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:p_duplicated@1436 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@9@inst_0_1_tsni --> Ref(p)@inst_7_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@9@inst_0_2_tsni --> Ref(p)@inst_7_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@9@inst_3_4_5_tsni --> Ref(p)@inst_8_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@9@inst_3_4_6_tsni --> Ref(p)@inst_8_tsni@A +//│ -------------- executing ------------- //│ = 8 -//│ 2 fusion opportunities: -//│ A --match--> `if p is ...` -//│ A --match--> `if p is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// ctor─┬►pRes1───┬──►f1Consumer -// │ └──►f2Consumer -// └►pRes2──────►f3Consumer -:deforestDup fun p(x) = A(x) let one = p(1) let two = p(2) @@ -746,36 +187,26 @@ f1(one) + f2(one) + f3(two) //│ = 10 //│ one = A(1) //│ two = A(2) -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1491 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@2@inst_0_tsni --> Ref(p)@inst_1_tsni@A +//│ -------------- executing ------------- //│ = 10 -//│ 1 fusion opportunities: -//│ A --match--> `if p is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// for this example it will be a pity to not duplicate `id(A(1))` and `id(A(2))`, -// although the ctors are not located in the definition of `id` -// A(1)─┬─►id1Res───►f1Consumer -// │ -// A(2)─┴─►id2Res───►f2Consumer -:deforestDup + fun id(x) = x fun f1(a) = if a is A(i) then i fun f2(a) = if a is A(i) then i + 1 f1(id(A(1))) + f2(id(A(2))) //│ = 4 -//│ duplication chances: -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1533 -//│ Call(Ref(member:id),List(Arg(false,Ref($tmp)))) <-- dup --> member:id_duplicated@1534 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(a)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@3@inst__tsni --> Ref(a)@inst_1_tsni@A +//│ -------------- executing ------------- //│ = 4 -//│ 2 fusion opportunities: -//│ A --match--> `if a is ...` -//│ A --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -783,18 +214,7 @@ f1(id(A(1))) + f2(id(A(2))) -:deforestDup -// * The real flow of the program: -// ctor─►toRes┌─►wrapRes1─►f1Consumer -// └─►wrapRes2─►f2Consumer -// * The flow we currently got: -// ┌────►f1Consumer -// ctor┬─►toRes──►f2Consumer -// ├───►wrapRes1─►f1Consumer -// └───►wrapRes2─►f2Consumer -// This program can be fused if we duplicate `wrap(4)` and `wrap(5)` AND the `to(n)` inside them. -// But there are programs that -// have this same flow and cannot be optimized after duplication + fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is h :: t then h @@ -805,24 +225,21 @@ fun f2(ls) = if ls is fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ = 10 -//│ duplication chances: -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:wrap_duplicated@1590 -//│ Call(Ref(member:wrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:wrap_duplicated@1591 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@5@inst_0_1_tsni --> Ref(ls)@inst_3_tsni@Cons +//│ Ref(member:Nil)@6@inst_0_1_tsni --> Ref(ls)@inst_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref($tmp))))@5@inst_2_1_tsni --> Ref(ls)@inst_4_tsni@Cons +//│ Ref(member:Nil)@6@inst_2_1_tsni --> Ref(ls)@inst_4_tsni@Nil +//│ -------------- executing ------------- //│ = 10 -//│ 4 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -:deforestDup + fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) fun f1(ls, acc) = if ls is Nil then acc @@ -832,43 +249,52 @@ fun f2(ls, acc) = if ls is h :: t then f2(t, acc + h + 1) f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ = 51 -//│ duplication chances: -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1676 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(6))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1676 -//│ Call(Ref(member:to),List(Arg(false,Lit(IntLit(5))), Arg(false,Ref(member:Nil)))) <-- dup --> member:to_duplicated@1677 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls)@inst_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_0_tsni --> Ref(ls)@inst_3_tsni@Cons +//│ Ref(member:Nil)@8@inst__tsni --> Ref(ls)@inst_4_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_1_tsni --> Ref(ls)@inst_4_tsni@Cons +//│ Ref(member:Nil)@9@inst__tsni --> Ref(ls)@inst_5_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(n)), Arg(false,Ref(acc))))@7@inst_2_tsni --> Ref(ls)@inst_5_tsni@Cons +//│ -------------- executing ------------- //│ = 51 -//│ 5 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// // TODO: don't group two maps together -// :deforestDup -// fun map(ls, f) = if ls is -// Nil then Nil -// h :: t then f(h) :: map(t, f) -// fun succ(x) = x + 1 -// fun double(x) = x * 2 -// fun triple(x) = x * 3 -// fun test() = -// map(map(map(1 :: 2 :: Nil, succ), double), triple) -// test() + +fun map(ls, f) = if ls is + Nil then Nil + h :: t then f(h) :: map(t, f) +fun succ(x) = x + 1 +fun double(x) = x * 2 +fun triple(x) = x * 3 +fun test() = + map(map(map(1 :: 2 :: Nil, succ), double), triple) +test() // //│ = Cons(12, Cons(18, Nil)) // //│ duplication chances: // //│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:succ)))) <-- dup --> member:map_duplicated@1750 // //│ Call(Ref(member:map),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:double)))) <-- dup --> member:map_duplicated@1750 // //│ ========= // //│ No fusion opportunity +//│ = Cons(12, Cons(18, Nil)) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Ref(member:Nil)@6@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Nil +//│ Ref(member:Nil)@7@inst_0_1_tsni --> Ref(ls)@inst_0_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@8@inst_0_1_tsni --> Ref(ls)@inst_0_2_tsni@Cons +//│ Ref(member:Nil)@7@inst_0_2_tsni --> Ref(ls)@inst_0_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@8@inst_0_2_tsni --> Ref(ls)@inst_0_3_tsni@Cons +//│ -------------- executing ------------- +//│ = Cons(12, Cons(18, Nil)) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + -:deforestDup fun p(x) = A(x) fun f1(a1) = if a1 is A(a) then a + 1 fun f2(a2) = if a2 is A(a) then a + 2 @@ -877,13 +303,10 @@ fun test() = f1(wrap(p(2))) + f2(p(1)) test() //│ = 6 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1745 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))))) <-- dup --> member:p_duplicated@1746 -//│ ========= //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@5@inst_0_1_tsni --> Ref(a1)@inst_0_3_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@5@inst_0_2_tsni --> Ref(a2)@inst_0_4_tsni@A +//│ -------------- executing ------------- //│ = 6 -//│ 2 fusion opportunities: -//│ A --match--> `if a1 is ...` -//│ A --match--> `if a2 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls index 5ccfea99ea..9618fdcfb6 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/def-dup/todo.mls @@ -1,28 +1,25 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class A(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class (::) Cons(h, t) object Nil +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + -// TODO: This program cannot be fused no matter how we duplicate, and we creately useless duplications. -// But it has essentially the same flow as the program below, which we create useful duplications -// ``` -// fun to(n) = if n > 0 then n :: to(n - 1) else Nil -// fun f1(ls) = if ls is -// h :: t then h -// Nil then 2 -// fun f2(ls) = if ls is -// h :: t then h + 1 -// Nil then 3 -// fun wrap(n) = to(n) -// f1(wrap(4)) + f2(wrap(5)) -// ``` -:deforestDup fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is h :: t then h @@ -36,21 +33,16 @@ fun badWrap(n) = x f1(badWrap(4)) + f2(badWrap(5)) //│ = 10 -//│ duplication chances: -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(4))))) <-- dup --> member:badWrap_duplicated@941 -//│ Call(Ref(member:badWrap),List(Arg(false,Lit(IntLit(5))))) <-- dup --> member:badWrap_duplicated@942 -//│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 10 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + -// TODO: no duplication of `p(1)` or `p(2)` is helpful in this case -// but duplicating `f2(two)` AND `p(2)` is helpful... -// ctor─┬─►pRes1─┬──►f1Consumer -// │ └──►f2Consumer -// │ ▲ -// └─►pRes2 ────────┘ -:deforestDup fun p(x) = A(x) let one = p(1) let two = p(2) @@ -60,17 +52,15 @@ f1(one) + f2(one) + f2(two) //│ = 9 //│ one = A(1) //│ two = A(2) -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))))) <-- dup --> member:p_duplicated@1008 -//│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref(x))))@2@inst_0_tsni --> Ref(p)@inst_1_tsni@A +//│ -------------- executing ------------- +//│ = 9 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// TODO: no duplication seems to be helpful for this program -// can we avoid duplicating this? since the constructor calls -// are not in the definitions that we are duplicating... -:deforestDup fun p(a) = a fun f1(a1) = if a1 is A(x1) then x1 fun f2(a2) = if a2 is A(x2) then x2 + 1 @@ -84,19 +74,15 @@ fun test() = f1(res1) + f1(res2) + f2(res3) test() //│ = 5 -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Ref($tmp)))) <-- dup --> member:p_duplicated@1069 -//│ Call(Ref(member:p),List(Arg(false,Ref(x)))) <-- dup --> member:p_duplicated@1069 -//│ Call(Ref(member:p),List(Arg(false,Ref(y)))) <-- dup --> member:p_duplicated@1070 -//│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< -// TODO: no duplication seem to be helpful, and maybe we can also -// avoid this, because for the `clashedCtor` flows into pRes1 and pRes2 -// its ctor expr also does not locate in the definition of `p` -:deforestDup fun f1(a1) = if a1 is A(x1) then x1 fun f2(a2) = if a2 is A(x2) then x2 + 1 fun p(x, clashedCtor) = @@ -106,11 +92,12 @@ let clashed = A(3) f1(p(1, clashed)) + f2(p(2, clashed)) //│ = 4 //│ clashed = A(3) -//│ duplication chances: -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1112 -//│ Call(Ref(member:p),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(clashed)))) <-- dup --> member:p_duplicated@1113 -//│ ========= -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 4 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/determinism.mls b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls index 7c9d6853f7..07c1c5de36 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/determinism.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls @@ -25,35 +25,35 @@ if B(1,2,3,4,5) is //│ } else { //│ throw new this.Error("match error"); //│ } -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let scrut, _deforest_B_e, _deforest_B_d, _deforest_B_c, _deforest_B_b, _deforest_B_a; -//│ _deforest_B_a = 1; -//│ _deforest_B_b = 2; -//│ _deforest_B_c = 3; -//│ _deforest_B_d = 4; -//│ _deforest_B_e = 5; +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let scrut, a, b, c, d, e; +//│ a = 1; +//│ b = 2; +//│ c = 3; +//│ d = 4; +//│ e = 5; //│ scrut = () => { -//│ let param0, param1, param2, param3, param4, a, b, c, d, e; -//│ param0 = _deforest_B_a; -//│ param1 = _deforest_B_b; -//│ param2 = _deforest_B_c; -//│ param3 = _deforest_B_d; -//│ param4 = _deforest_B_e; -//│ a = param0; -//│ b = param1; -//│ c = param2; -//│ d = param3; -//│ e = param4; +//│ let param0, param1, param2, param3, param4, a1, b1, c1, d1, e1; +//│ param0 = a; +//│ param1 = b; +//│ param2 = c; +//│ param3 = d; +//│ param4 = e; +//│ a1 = param0; +//│ b1 = param1; +//│ c1 = param2; +//│ d1 = param3; +//│ e1 = param4; //│ return 0 //│ }; //│ runtime.safeCall(scrut()) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:B),List(Arg(false,Lit(IntLit(1))), Arg(false,Lit(IntLit(2))), Arg(false,Lit(IntLit(3))), Arg(false,Lit(IntLit(4))), Arg(false,Lit(IntLit(5)))))@0@inst__tsni --> Ref($scrut)@inst__tsni@B +//│ -------------- executing ------------- //│ = 0 -//│ 1 fusion opportunities: -//│ B --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -65,7 +65,7 @@ c2 of A(A(A(A(A(A(1)))))) //│ JS (unsanitized): //│ let c2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; //│ c2 = function c2(x) { -//│ let param01, param02, param03, param04, param05, param06, a1; +//│ let param01, param02, param03, param04, param05, param06, a2; //│ if (x instanceof A1.class) { //│ param01 = x.a; //│ if (param01 instanceof A1.class) { @@ -78,8 +78,8 @@ c2 of A(A(A(A(A(A(1)))))) //│ param05 = param04.a; //│ if (param05 instanceof A1.class) { //│ param06 = param05.a; -//│ a1 = param06; -//│ return a1 +//│ a2 = param06; +//│ return a2 //│ } else { //│ throw new globalThis.Error("match error"); //│ } @@ -106,59 +106,94 @@ c2 of A(A(A(A(A(A(1)))))) //│ tmp4 = A1(tmp3); //│ tmp5 = A1(tmp4); //│ c2(tmp5) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let c2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, _deforest_A_a, _deforest_A_a1, _deforest_A_a2, _deforest_A_a3, _deforest_A_a4, _deforest_A_a5; -//│ c2 = function c2(x) { +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let c2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, c2_inst_0_tsni, a2, a3, a4, a5, a6, a7; +//│ c2_inst_0_tsni = function c2_inst_0_tsni(x) { //│ return runtime.safeCall(x()) //│ }; -//│ _deforest_A_a5 = 1; +//│ c2 = function c2(x) { +//│ let param01, param02, param03, param04, param05, param06, a8; +//│ if (x instanceof A1.class) { +//│ param01 = x.a; +//│ if (param01 instanceof A1.class) { +//│ param02 = param01.a; +//│ if (param02 instanceof A1.class) { +//│ param03 = param02.a; +//│ if (param03 instanceof A1.class) { +//│ param04 = param03.a; +//│ if (param04 instanceof A1.class) { +//│ param05 = param04.a; +//│ if (param05 instanceof A1.class) { +//│ param06 = param05.a; +//│ a8 = param06; +//│ return a8 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ a7 = 1; //│ tmp = () => { -//│ let param01, a1; -//│ param01 = _deforest_A_a5; -//│ a1 = param01; -//│ return a1 +//│ let param01, a8; +//│ param01 = a7; +//│ a8 = param01; +//│ return a8 //│ }; -//│ _deforest_A_a4 = tmp; +//│ a6 = tmp; //│ tmp1 = () => { //│ let param01; -//│ param01 = _deforest_A_a4; +//│ param01 = a6; //│ return runtime.safeCall(param01()) //│ }; -//│ _deforest_A_a3 = tmp1; +//│ a5 = tmp1; //│ tmp2 = () => { //│ let param01; -//│ param01 = _deforest_A_a3; +//│ param01 = a5; //│ return runtime.safeCall(param01()) //│ }; -//│ _deforest_A_a2 = tmp2; +//│ a4 = tmp2; //│ tmp3 = () => { //│ let param01; -//│ param01 = _deforest_A_a2; +//│ param01 = a4; //│ return runtime.safeCall(param01()) //│ }; -//│ _deforest_A_a1 = tmp3; +//│ a3 = tmp3; //│ tmp4 = () => { //│ let param01; -//│ param01 = _deforest_A_a1; +//│ param01 = a3; //│ return runtime.safeCall(param01()) //│ }; -//│ _deforest_A_a = tmp4; +//│ a2 = tmp4; //│ tmp5 = () => { //│ let param01; -//│ param01 = _deforest_A_a; +//│ param01 = a2; //│ return runtime.safeCall(param01()) //│ }; -//│ c2(tmp5) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ c2_inst_0_tsni(tmp5) +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@2@inst__tsni --> Ref($param0)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref($param0)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@4@inst__tsni --> Ref($param0)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Ref($tmp))))@5@inst__tsni --> Ref($param0)@inst_0_tsni@A +//│ Call(Ref(member:A),List(Arg(false,Lit(IntLit(1)))))@6@inst__tsni --> Ref($param0)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = 1 -//│ 6 fusion opportunities: -//│ A --match--> `if param0 is ...` -//│ A --match--> `if param0 is ...` -//│ A --match--> `if param0 is ...` -//│ A --match--> `if param0 is ...` -//│ A --match--> `if param0 is ...` -//│ A --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index 45894e3f22..4d377e398a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -1,7 +1,11 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object A object B @@ -10,6 +14,9 @@ object B // class BB(bb) // object None // class Some(value) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // * Not fused: two `x is A` consumers let x = if true then A else B @@ -19,7 +26,11 @@ fun foo(x) = A then 1 B then 2 //│ x = A -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // * We could make it work. But it's a special case that's probably not very important let x = if true then A else B @@ -27,7 +38,11 @@ fun foo(x) = if x is A do print(123) if x is B do print(456) //│ x = A -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun foo(k, x) = if x === 0 do k(A) @@ -40,11 +55,12 @@ fun bar(v) = if v is foo(bar, 123) //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:B)@2@inst_0_tsni --> Ref(v)@inst_1_tsni@B +//│ Ref(member:A)@3@inst_0_tsni --> Ref(v)@inst_1_tsni@A +//│ Ref(member:A)@4@inst_0_tsni --> Ref(v)@inst_1_tsni@A +//│ -------------- executing ------------- //│ = 1 -//│ 3 fusion opportunities: -//│ A --match--> `if v is ...` -//│ A --match--> `if v is ...` -//│ B --match--> `if v is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls index e22cabd87f..95893feec4 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls @@ -1,12 +1,21 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class (::) Cons(h, t) data class Pair(a, b) object A object B +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) @@ -25,7 +34,27 @@ fun test() = lscomp1(zip(enumFromTo(1, 3), enumFromTo(2, 4))) test() //│ = Cons(Pair(1, 3), Cons(Pair(2, 4), Cons(Pair(3, 5), Nil))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@7@inst_0_1_tsni --> Ref(xs_zip)@inst_0_3_tsni@Cons +//│ Ref(member:Nil)@8@inst_0_1_tsni --> Ref(xs_zip)@inst_0_3_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@7@inst_0_2_tsni --> Ref(ys_zip)@inst_0_3_tsni@Cons +//│ Ref(member:Nil)@8@inst_0_2_tsni --> Ref(ys_zip)@inst_0_3_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@9@inst_0_3_tsni --> Ref(ls2)@inst_0_tsni@Cons +//│ Ref(member:Nil)@10@inst_0_3_tsni --> Ref(ls2)@inst_0_tsni@dflt +//│ Ref(member:Nil)@11@inst_0_3_tsni --> Ref(ls2)@inst_0_tsni@dflt +//│ Call(Ref(member:Pair),List(Arg(false,Ref(x)), Arg(false,Ref(y))))@12@inst_0_3_tsni --> Ref($param0)@inst_0_tsni@Pair +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@7@inst_0_4_tsni --> Ref(xs_zip)@inst_0_6_tsni@Cons +//│ Ref(member:Nil)@8@inst_0_4_tsni --> Ref(xs_zip)@inst_0_6_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@7@inst_0_5_tsni --> Ref(ys_zip)@inst_0_6_tsni@Cons +//│ Ref(member:Nil)@8@inst_0_5_tsni --> Ref(ys_zip)@inst_0_6_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@9@inst_0_6_tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Ref(member:Nil)@10@inst_0_6_tsni --> Ref(ls)@inst_0_tsni@dflt +//│ Ref(member:Nil)@11@inst_0_6_tsni --> Ref(ls)@inst_0_tsni@dflt +//│ Call(Ref(member:Pair),List(Arg(false,Ref(x)), Arg(false,Ref(y))))@12@inst_0_6_tsni --> Ref($param0)@inst_0_tsni@Pair +//│ -------------- executing ------------- +//│ = Cons(Pair(1, 3), Cons(Pair(2, 4), Cons(Pair(3, 5), Nil))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun append(xs, ys) = if xs is @@ -44,14 +73,19 @@ fun test() = test() //│ = Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Call(Ref(member:Pair),List(Arg(false,Lit(IntLit(1))), Arg(false,Lit(IntLit(3)))))@4@inst_0_tsni --> Ref(a2)@inst_0_tsni@Pair +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@5@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Call(Ref(member:Pair),List(Arg(false,Lit(IntLit(2))), Arg(false,Lit(IntLit(3)))))@6@inst_0_tsni --> Ref(a2)@inst_0_tsni@Pair +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:Nil))))@7@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Cons +//│ Call(Ref(member:Pair),List(Arg(false,Ref(a)), Arg(false,Ref(b))))@8@inst_0_tsni --> Ref(a2)@inst_0_tsni@Pair +//│ Ref(member:Nil)@9@inst_0_tsni --> Ref(ls)@inst_0_1_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref(member:Nil))))@10@inst_0_tsni --> Ref(ls)@inst_0_2_tsni@Cons +//│ Call(Ref(member:Pair),List(Arg(false,Lit(IntLit(5))), Arg(false,Lit(IntLit(10)))))@11@inst_0_tsni --> Ref(a1)@inst_0_tsni@Pair +//│ Ref(member:Nil)@12@inst_0_tsni --> Ref(ls)@inst_0_2_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) -//│ 6 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -65,14 +99,15 @@ fun lscomp2(ls2, h1, t1) = if ls2 is lscomp1(1 :: 2 :: Nil, 3 :: 4 :: Nil) //│ = Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(3))), Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(ls2)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref(member:Nil))))@2@inst__tsni --> Ref(ls2)@inst_0_tsni@Cons +//│ Ref(member:Nil)@3@inst__tsni --> Ref(ls2)@inst_0_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ls1)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ls1)@inst_0_tsni@Cons +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ls1)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) -//│ 6 fusion opportunities: -//│ Cons --match--> `if ls1 is ...` -//│ Cons --match--> `if ls1 is ...` -//│ Cons --match--> `if ls2 is ...` -//│ Cons --match--> `if ls2 is ...` -//│ Nil --match--> `if ls1 is ...` -//│ Nil --match--> `if ls2 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -86,11 +121,12 @@ fun test(k, ls) = test(A, 1 :: 2 :: 3 :: Nil) //│ = Cons(2, Cons(3, Cons(4, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(k)@inst_0_tsni@A +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(3))), Arg(false,Ref(member:Nil))))@4@inst__tsni --> Ref(ls)@inst_0_tsni@Cons +//│ Ref(member:Nil)@5@inst__tsni --> Ref(ls)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(2, Cons(3, Cons(4, Nil))) -//│ 5 fusion opportunities: -//│ A --match--> `if k is ...` -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 4d320f26b4..671a88c23e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1,19 +1,32 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object A object B object C data class AA(x) data class BB(x) data class CC(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class Cons(h, t) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object None data class Some(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun f(x, y) = @@ -31,10 +44,13 @@ f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ = 37 //│ a = AA(AA(3)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(4)))))@4@inst__tsni --> Ref(a)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(x)@inst_1_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@6@inst__tsni --> Ref(a)@inst_2_tsni@AA +//│ -------------- executing ------------- //│ = 37 -//│ 2 fusion opportunities: -//│ AA --match--> `if a is ...` -//│ AA --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -48,12 +64,13 @@ fun f(x, y) = f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ = 30 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(4)))))@3@inst__tsni --> Ref(a)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(a)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 30 -//│ 4 fusion opportunities: -//│ AA --match--> `if a is ...` -//│ AA --match--> `if a is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -67,10 +84,11 @@ fun f(x) = f(AA(AA(3))) //│ = 5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(a)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 5 -//│ 2 fusion opportunities: -//│ AA --match--> `if a is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -82,12 +100,13 @@ fun f(x) = f(AA(AA(A))) + f(AA(AA(A))) //│ = "A3A3" //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@3@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@5@inst__tsni --> Ref($param0)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = "A3A3" -//│ 4 fusion opportunities: -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -97,10 +116,11 @@ fun c2(x) = if x is c2(AA(AA(0))) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(0)))))@2@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 0 -//│ 2 fusion opportunities: -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -110,11 +130,12 @@ fun f(a) = if a is f(AA(BB(B))) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(a)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@2@inst__tsni --> Ref($param0)@inst_0_tsni@BB +//│ Ref(member:B)@3@inst__tsni --> Ref($param0)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 3 -//│ 3 fusion opportunities: -//│ AA --match--> `if a is ...` -//│ B --match--> `if param0 is ...` -//│ BB --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -128,14 +149,15 @@ fun test(x, y, z, i) = if x is test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ = 10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(z)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(y)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(z)@inst_1_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@6@inst__tsni --> Ref(y)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@7@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 10 -//│ 6 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if y is ...` -//│ AA --match--> `if y is ...` -//│ BB --match--> `if z is ...` -//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -154,10 +176,12 @@ f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) //│ = "ccf2f3cc" //│ cc = CC("cc") //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(x))))@4@inst_0_tsni --> Ref(x)@inst_2_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(x))))@4@inst_1_tsni --> Ref(x)@inst_3_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(cc))))@5@inst__tsni --> Ref(x1)@inst_3_tsni@BB +//│ -------------- executing ------------- //│ = "ccf2f3cc" -//│ 2 fusion opportunities: -//│ BB --match--> `if x1 is ...` -//│ BB --match--> `if x1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -172,10 +196,11 @@ c(AA(2), y) //│ = 4 //│ y = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Ref(member:A)@2@inst__tsni --> Ref(y)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = 4 -//│ 2 fusion opportunities: -//│ A --match--> `if y is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -190,9 +215,10 @@ c(AA(2), y) + c2(y) //│ = 9 //│ y = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 9 -//│ 1 fusion opportunities: -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -209,9 +235,12 @@ test(p) + f(p) + test(AA(A)) //│ = "105035" //│ p = AA(AA(AA("10"))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Ref(member:A)@3@inst__tsni --> Ref($param0)@inst_0_tsni@dflt +//│ Call(Ref(member:AA),List(Arg(false,Lit(StrLit(10)))))@4@inst__tsni --> Ref($param0)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = "105035" -//│ 1 fusion opportunities: -//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -232,12 +261,13 @@ f(aa, 3) + f(aa, 4) //│ = 13 //│ aa = AA(3) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(tmp)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst_0_tsni --> Ref($scrut)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref($tmp))))@2@inst_1_tsni --> Ref(tmp)@inst_1_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst_1_tsni --> Ref($scrut)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 13 -//│ 4 fusion opportunities: -//│ AA --match--> `if scrut is ...` -//│ AA --match--> `if scrut is ...` -//│ AA --match--> `if x is ...` -//│ BB --match--> `if tmp is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -253,13 +283,15 @@ fun test(n, p, q) = test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ = 16 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(0)))))@2@inst_0_tsni --> Ref($scrut)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@3@inst__tsni --> Ref(q)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@4@inst__tsni --> Ref(p)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(0)))))@2@inst_1_tsni --> Ref($scrut)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(q)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@6@inst__tsni --> Ref(p)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 16 -//│ 5 fusion opportunities: -//│ AA --match--> `if p is ...` -//│ AA --match--> `if p is ...` -//│ AA --match--> `if q is ...` -//│ AA --match--> `if q is ...` -//│ AA --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -282,12 +314,13 @@ test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ = 13 //│ a = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(q)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(p)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@4@inst__tsni --> Ref(q)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@5@inst__tsni --> Ref(p)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 13 -//│ 4 fusion opportunities: -//│ AA --match--> `if p is ...` -//│ AA --match--> `if p is ...` -//│ AA --match--> `if q is ...` -//│ AA --match--> `if q is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -304,14 +337,15 @@ fun test(x, y, z, i) = test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ = 18 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(z)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(y)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(z)@inst_1_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@6@inst__tsni --> Ref(y)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@7@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 18 -//│ 6 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if y is ...` -//│ AA --match--> `if y is ...` -//│ BB --match--> `if z is ...` -//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -328,14 +362,15 @@ fun test(x, y, z, i) = test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ = 10 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Ref(z)@inst_0_tsni@BB +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(y)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(z)@inst_1_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@6@inst__tsni --> Ref(y)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@7@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 10 -//│ 6 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if y is ...` -//│ BB --match--> `if y is ...` -//│ BB --match--> `if z is ...` -//│ BB --match--> `if z is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -350,11 +385,14 @@ fun p(x) = AA(x) test(p(BB(3))) + test(p(CC(3))) + c(p(A)) //│ = 12 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(x))))@6@inst_0_tsni --> Ref(x)@inst_3_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(x))))@6@inst_1_tsni --> Ref(x)@inst_4_tsni@AA +//│ Call(Ref(member:CC),List(Arg(false,Lit(IntLit(3)))))@7@inst__tsni --> Ref($param0)@inst_4_tsni@CC +//│ Call(Ref(member:AA),List(Arg(false,Ref(x))))@6@inst_2_tsni --> Ref(x)@inst_5_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@8@inst__tsni --> Ref($param0)@inst_5_tsni@BB +//│ -------------- executing ------------- //│ = 12 -//│ 3 fusion opportunities: -//│ A --match--> `if param0 is ...` -//│ BB --match--> `if param0 is ...` -//│ CC --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -375,12 +413,15 @@ fun f(x, y, z, k) = f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ = 41 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst_0_tsni --> Ref($scrut)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@3@inst_0_tsni --> Ref($scrut)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(5)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst_1_tsni --> Ref($scrut)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@3@inst_1_tsni --> Ref($scrut)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 41 -//│ 4 fusion opportunities: -//│ AA --match--> `if scrut is ...` -//│ AA --match--> `if scrut is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -396,7 +437,8 @@ test(p) + f(p) //│ = 15 //│ p = AA(AA(AA(10))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(10)))))@1@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 15 -//│ 1 fusion opportunities: -//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls index 45434baa57..9bc4c9eb6c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls @@ -1,7 +1,11 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil @@ -12,6 +16,9 @@ object A object B data class T(n, l, r) object L +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun map(f, ls_map) = if ls_map is @@ -26,10 +33,11 @@ fun test(ls) = map1(incr, map(double, ls)) test(id(mk(5))) //│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_0_1_tsni --> Ref(ls_map1)@inst_0_2_tsni@Cons +//│ Ref(member:Nil)@4@inst_0_1_tsni --> Ref(ls_map1)@inst_0_2_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls_map1 is ...` -//│ Nil --match--> `if ls_map1 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -41,10 +49,11 @@ fun map(ls) = if ls is map(enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil @@ -54,10 +63,11 @@ fun sum(ls) = if ls is sum(enumFromTo(1,10)) //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = 45 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil @@ -68,10 +78,11 @@ fun map(f, ls) = map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil @@ -83,10 +94,11 @@ fun map(f, ls) = map(x => x + 4, enumFromTo(1, 4)) //│ = Cons(5, Cons(6, Cons(7, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = Cons(5, Cons(6, Cons(7, Nil))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -98,10 +110,11 @@ fun sum(ls, a) = if ls is sum(enumFromTo(1, 10), 0) //│ = 45 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(ls)@inst_1_tsni@Cons +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(ls)@inst_1_tsni@Nil +//│ -------------- executing ------------- //│ = 45 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls is ...` -//│ Nil --match--> `if ls is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -118,10 +131,11 @@ fun test(xs) = map(incr, rev(xs, Nil)) test(id(mk(5))) //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@3@inst_0_tsni --> Ref(xs_map)@inst_0_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref(acc))))@4@inst_0_1_tsni --> Ref(xs_map)@inst_0_2_tsni@Cons +//│ -------------- executing ------------- //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if xs_map is ...` -//│ Nil --match--> `if xs_map is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -138,15 +152,19 @@ fun test(xs) = rev(map(incr, xs), Nil) test(id(mk(3))) //│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@3@inst_0_1_tsni --> Ref(xs_rev)@inst_0_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@4@inst_0_1_tsni --> Ref(xs_rev)@inst_0_2_tsni@Cons +//│ -------------- executing ------------- //│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) -//│ 2 fusion opportunities: -//│ Cons --match--> `if xs_rev is ...` -//│ Nil --match--> `if xs_rev is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< data class Pair(a, b) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun pair_up(xs) = @@ -160,7 +178,12 @@ fun mk(n) = if n > 0 then (n - 1) :: n :: (n + 1) :: mk(n - 1) else Nil fun test(x) = pair_up(mk(x)) test(4) //│ = Cons(Pair(3, 4), Cons(Pair(5, 2), Cons(Pair(3, 4), Cons(Pair(1, 2), Cons(Pair(3, 0), Cons(Pair(1, 2), Nil)))))) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Cons(Pair(3, 4), Cons(Pair(5, 2), Cons(Pair(3, 4), Cons(Pair(1, 2), Cons(Pair(3, 0), Cons(Pair(1, 2), Nil)))))) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -179,10 +202,11 @@ fun test(ls) = sum(flatten(ls)) test(id(mk2d(4))) //│ = 20 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(x)), Arg(false,Ref($tmp))))@4@inst_0_1_2_tsni --> Ref(ls_sum)@inst_0_3_tsni@Cons +//│ Ref(member:Nil)@5@inst_0_1_tsni --> Ref(ls_sum)@inst_0_3_tsni@Nil +//│ -------------- executing ------------- //│ = 20 -//│ 2 fusion opportunities: -//│ Cons --match--> `if ls_sum is ...` -//│ Nil --match--> `if ls_sum is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -195,13 +219,14 @@ fun rev(a, ys) = if ys is count(0, rev(Nil, 1 :: 2 :: Nil)) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@2@inst__tsni --> Ref(xs)@inst_1_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(h)), Arg(false,Ref(a))))@3@inst_0_tsni --> Ref(xs)@inst_1_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ys)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ys)@inst_0_tsni@Cons +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ys)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = 2 -//│ 5 fusion opportunities: -//│ Cons --match--> `if xs is ...` -//│ Cons --match--> `if ys is ...` -//│ Cons --match--> `if ys is ...` -//│ Nil --match--> `if xs is ...` -//│ Nil --match--> `if ys is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -214,7 +239,12 @@ let p = 1 :: 2 :: Nil last(p) //│ = Some(2) //│ p = Cons(1, Cons(2, Nil)) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Some(2) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -224,13 +254,14 @@ fun map(f, xs_map) = if xs_map is map(x => if x is A then 1 else 0, A :: B :: Nil) //│ = Cons(1, Cons(0, Nil)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref(member:A)), Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(xs_map)@inst_0_tsni@Cons +//│ Ref(member:A)@3@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ Call(Ref(member:Cons),List(Arg(false,Ref(member:B)), Arg(false,Ref(member:Nil))))@4@inst__tsni --> Ref(xs_map)@inst_0_tsni@Cons +//│ Ref(member:Nil)@5@inst__tsni --> Ref(xs_map)@inst_0_tsni@Nil +//│ Ref(member:B)@6@inst__tsni --> Ref(x)@inst_1_tsni@dflt +//│ -------------- executing ------------- //│ = Cons(1, Cons(0, Nil)) -//│ 5 fusion opportunities: -//│ A --match--> `if x is ...` -//│ B --match--> `if x is ...` -//│ Cons --match--> `if xs_map is ...` -//│ Cons --match--> `if xs_map is ...` -//│ Nil --match--> `if xs_map is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -245,18 +276,19 @@ fun c(x) = if x is c(T(A, T(B, L, L), T(A, L, L))) //│ = T(0, T(1, L, L), T(0, L, L)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:T),List(Arg(false,Ref(member:A)), Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@T +//│ Ref(member:A)@2@inst__tsni --> Ref(n)@inst_0_tsni@A +//│ Call(Ref(member:T),List(Arg(false,Ref(member:A)), Arg(false,Ref(member:L)), Arg(false,Ref(member:L))))@3@inst__tsni --> Ref(x)@inst_0_tsni@T +//│ Ref(member:L)@4@inst__tsni --> Ref(x)@inst_0_tsni@L +//│ Ref(member:L)@5@inst__tsni --> Ref(x)@inst_0_tsni@L +//│ Ref(member:A)@6@inst__tsni --> Ref(n)@inst_0_tsni@A +//│ Call(Ref(member:T),List(Arg(false,Ref(member:B)), Arg(false,Ref(member:L)), Arg(false,Ref(member:L))))@7@inst__tsni --> Ref(x)@inst_0_tsni@T +//│ Ref(member:L)@8@inst__tsni --> Ref(x)@inst_0_tsni@L +//│ Ref(member:L)@9@inst__tsni --> Ref(x)@inst_0_tsni@L +//│ Ref(member:B)@10@inst__tsni --> Ref(n)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = T(0, T(1, L, L), T(0, L, L)) -//│ 10 fusion opportunities: -//│ A --match--> `if n is ...` -//│ A --match--> `if n is ...` -//│ B --match--> `if n is ...` -//│ L --match--> `if x is ...` -//│ L --match--> `if x is ...` -//│ L --match--> `if x is ...` -//│ L --match--> `if x is ...` -//│ T --match--> `if x is ...` -//│ T --match--> `if x is ...` -//│ T --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -268,14 +300,15 @@ fun max(ms, m) = if ms is max(3 :: 2 :: 4 :: 1 :: 0 :: Nil, 0) //│ = 4 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(3))), Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(ms)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(2))), Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(ms)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(4))), Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(ms)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(ms)@inst_0_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Lit(IntLit(0))), Arg(false,Ref(member:Nil))))@5@inst__tsni --> Ref(ms)@inst_0_tsni@Cons +//│ Ref(member:Nil)@6@inst__tsni --> Ref(ms)@inst_0_tsni@Nil +//│ -------------- executing ------------- //│ = 4 -//│ 6 fusion opportunities: -//│ Cons --match--> `if ms is ...` -//│ Cons --match--> `if ms is ...` -//│ Cons --match--> `if ms is ...` -//│ Cons --match--> `if ms is ...` -//│ Cons --match--> `if ms is ...` -//│ Nil --match--> `if ms is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -294,6 +327,11 @@ fun last(ls_last) = fun lastFilter(ls, f) = last(filter(ls, f)) lastFilter(id(1 :: 2 :: 3 :: 4 :: 5 :: Nil), x => (x % 2 == 0)) //│ = Some(4) -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = Some(4) +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 0c379fab08..7001c5c8b6 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -1,18 +1,31 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object A object B object C data class AA(x) data class BB(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class Cons(h, t) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object None data class Some(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -24,10 +37,11 @@ fun c(x) = if x is c(AA(2)) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if scrut is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -38,10 +52,11 @@ fun c(x, y) = if x is c(AA(2), A) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(y)@inst_0_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if y is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -54,10 +69,11 @@ fun p() = AA(2) c(p(), A) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@2@inst__tsni --> Ref(y)@inst_1_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst_0_tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if y is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -72,10 +88,11 @@ fun p() = AA(2) c(p(), A) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@2@inst__tsni --> Ref(y)@inst_1_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@3@inst_0_tsni --> Ref(x)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = 3 -//│ 2 fusion opportunities: -//│ A --match--> `if y is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -89,14 +106,15 @@ fun g(x) = if x is f(AA(AA(AA(AA(AA(A)))))) //│ = 42 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@2@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@3@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@5@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ Ref(member:A)@6@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = 42 -//│ 6 fusion opportunities: -//│ A --match--> `if x is ...` -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun f(x) = if x is @@ -104,10 +122,11 @@ fun f(x) = if x is f(AA(AA(A))) //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@2@inst__tsni --> Ref($param0)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = A -//│ 2 fusion opportunities: -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun f(x) = if x is @@ -116,10 +135,11 @@ fun p() = AA(AA(A)) f(p()) //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@2@inst_0_tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@3@inst_0_tsni --> Ref($param0)@inst_1_tsni@AA +//│ -------------- executing ------------- //│ = A -//│ 2 fusion opportunities: -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -133,8 +153,9 @@ fun c(x, y) = c(AA(2), 10) //│ = 12 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 12 -//│ 2 fusion opportunities: -//│ A --match--> `if scrut is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index abd673d700..ce642cac1c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1,7 +1,11 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object A object B @@ -13,6 +17,9 @@ data class BBB(x, y) data class CCC(c) object None data class Some(value) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun test() = let x = if true then A else B @@ -22,10 +29,11 @@ fun test() = test() //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref(x)@inst_0_tsni@A +//│ Ref(member:B)@2@inst_0_tsni --> Ref(x)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 1 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun test() = @@ -36,10 +44,11 @@ fun test() = test() //│ = A //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@1@inst_0_tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@2@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ -------------- executing ------------- //│ = A -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -54,12 +63,13 @@ fun test() = test() //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@3@inst_0_tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@4@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ Ref(member:A)@5@inst_0_tsni --> Ref(a)@inst_0_1_tsni@A +//│ Ref(member:B)@6@inst_0_tsni --> Ref(a)@inst_0_2_tsni@B +//│ -------------- executing ------------- //│ = 1 -//│ 4 fusion opportunities: -//│ A --match--> `if a is ...` -//│ AA --match--> `if x is ...` -//│ B --match--> `if a is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -80,12 +90,13 @@ fun test() = test() //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@3@inst_0_tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@4@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ Ref(member:A)@5@inst_0_tsni --> Ref(a)@inst_0_1_tsni@A +//│ Ref(member:B)@6@inst_0_tsni --> Ref(a)@inst_0_2_tsni@B +//│ -------------- executing ------------- //│ = 1 -//│ 4 fusion opportunities: -//│ A --match--> `if a is ...` -//│ AA --match--> `if x is ...` -//│ B --match--> `if a is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -98,10 +109,11 @@ fun test() = test() //│ = 11 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(11)))))@1@inst_0_tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(22)))))@2@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ -------------- executing ------------- //│ = 11 -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -117,7 +129,12 @@ fun test() = B then 2 test() //│ = 2 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -131,10 +148,11 @@ fun test() = test() //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref(x)@inst_0_tsni@A +//│ Ref(member:B)@2@inst_0_tsni --> Ref(y)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -149,12 +167,13 @@ c(A) + c(B) //│ > 2 //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:B)@2@inst__tsni --> Ref(a)@inst_0_tsni@B +//│ Ref(member:A)@3@inst__tsni --> Ref(a)@inst_1_tsni@A +//│ -------------- executing ------------- //│ > 1 //│ > 2 //│ = 3 -//│ 2 fusion opportunities: -//│ A --match--> `if a is ...` -//│ B --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -173,7 +192,12 @@ fun c(x) = n + t c(AA(3)) //│ = 5 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 5 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -183,10 +207,11 @@ fun f(a, b) = if a is f(A, B) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(a)@inst_0_tsni@A +//│ Ref(member:B)@2@inst__tsni --> Ref(b)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 3 -//│ 2 fusion opportunities: -//│ A --match--> `if a is ...` -//│ B --match--> `if b is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -194,7 +219,12 @@ fun f(x) = if x is Some then if x.value > 1 then f(Some(x.value - 1)) else 0 f(Some(2)) //│ = 0 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -208,10 +238,11 @@ if y is //│ x = A //│ y = B //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:B)@0@inst__tsni --> Ref(y)@inst__tsni@B +//│ Ref(member:A)@1@inst__tsni --> Ref(x)@inst__tsni@A +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -226,10 +257,11 @@ fun test() = test() //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref(x)@inst_0_tsni@A +//│ Ref(member:B)@2@inst_0_tsni --> Ref(y)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ B --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -242,12 +274,13 @@ fun f(x, y) = f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ = 21 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BBB),List(Arg(false,Lit(IntLit(4))), Arg(false,Lit(IntLit(6)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@BBB +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(3))), Arg(false,Lit(IntLit(2)))))@5@inst__tsni --> Ref(x)@inst_1_tsni@AAA +//│ Call(Ref(member:BBB),List(Arg(false,Lit(IntLit(2))), Arg(false,Lit(IntLit(3)))))@6@inst__tsni --> Ref(x)@inst_2_tsni@BBB +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(1))), Arg(false,Lit(IntLit(3)))))@7@inst__tsni --> Ref(x)@inst_3_tsni@AAA +//│ -------------- executing ------------- //│ = 21 -//│ 4 fusion opportunities: -//│ AAA --match--> `if x is ...` -//│ AAA --match--> `if x is ...` -//│ BBB --match--> `if x is ...` -//│ BBB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -261,9 +294,10 @@ fun p(a) = c1(a) + c2(a) p(AA(1)) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@2@inst_0_1_tsni --> Ref($scrut)@inst_0_1_tsni@A +//│ -------------- executing ------------- //│ = 2 -//│ 1 fusion opportunities: -//│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -275,10 +309,11 @@ fun c(x, y) = if x is c(AA(2), A) //│ = 2 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(y)@inst_0_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 2 -//│ 2 fusion opportunities: -//│ A --match--> `if y is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -290,12 +325,13 @@ fun f(x, y) = f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ = 24 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:CCC),List(Arg(false,Lit(IntLit(4)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@CCC +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(3))), Arg(false,Lit(IntLit(2)))))@5@inst__tsni --> Ref(x)@inst_1_tsni@AAA +//│ Call(Ref(member:CCC),List(Arg(false,Lit(IntLit(2)))))@6@inst__tsni --> Ref(x)@inst_2_tsni@CCC +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(1))), Arg(false,Lit(IntLit(3)))))@7@inst__tsni --> Ref(x)@inst_3_tsni@AAA +//│ -------------- executing ------------- //│ = 24 -//│ 4 fusion opportunities: -//│ AAA --match--> `if x is ...` -//│ AAA --match--> `if x is ...` -//│ CCC --match--> `if x is ...` -//│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -309,11 +345,12 @@ fun f(x, y) = f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ = 22 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(3))), Arg(false,Lit(IntLit(2)))))@3@inst__tsni --> Ref(x)@inst_0_tsni@AAA +//│ Call(Ref(member:CCC),List(Arg(false,Lit(IntLit(2)))))@4@inst__tsni --> Ref(x)@inst_1_tsni@CCC +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(1))), Arg(false,Lit(IntLit(3)))))@5@inst__tsni --> Ref(x)@inst_2_tsni@AAA +//│ -------------- executing ------------- //│ = 22 -//│ 3 fusion opportunities: -//│ AAA --match--> `if x is ...` -//│ AAA --match--> `if x is ...` -//│ CCC --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -323,9 +360,10 @@ fun c(x, m) = if x is c(BB(3), 0) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@dflt +//│ -------------- executing ------------- //│ = 0 -//│ 1 fusion opportunities: -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -338,10 +376,11 @@ fun g(b) = if b is f(AA(AA(0))) //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(0)))))@3@inst__tsni --> Ref(b)@inst_0_1_tsni@AA +//│ -------------- executing ------------- //│ = 1 -//│ 2 fusion opportunities: -//│ AA --match--> `if b is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -350,10 +389,11 @@ fun f(x) = x.aa.aa f(AA(AA(3))) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Select(Ref(x),Ident(aa))@inst_0_tsni +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@2@inst__tsni --> Select(Select(Ref(x),Ident(aa)),Ident(aa))@inst_0_tsni +//│ -------------- executing ------------- //│ = 3 -//│ 2 fusion opportunities: -//│ AA --sel--> `.Ident(aa)` -//│ AA --sel--> `.Ident(aa)` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun inner(x, y) = @@ -376,10 +416,10 @@ outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ = 103 //│ p = AA(AA(3)) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(5)))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 103 -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -388,9 +428,10 @@ fun mapHead(f, x) = if x is mapHead(x => x, AAA(1, AAA(2, None))) //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AAA),List(Arg(false,Lit(IntLit(1))), Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AAA +//│ -------------- executing ------------- //│ = 1 -//│ 1 fusion opportunities: -//│ AAA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -403,9 +444,10 @@ fun test() = test() //│ = 5 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ -------------- executing ------------- //│ = 5 -//│ 1 fusion opportunities: -//│ A --match--> `if scrut is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun test(a) = if a is @@ -414,10 +456,11 @@ fun test(a) = if a is test(B) + test(C) //│ = 4 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:C)@2@inst__tsni --> Ref(a)@inst_0_tsni@dflt +//│ Ref(member:B)@3@inst__tsni --> Ref(a)@inst_1_tsni@dflt +//│ -------------- executing ------------- //│ = 4 -//│ 2 fusion opportunities: -//│ B --match--> `if a is ...` -//│ C --match--> `if a is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -433,10 +476,11 @@ fun test(x, y, z) = if x is test(AA(1), AA(2), AA(3)) //│ = "2AA(3)NaN" //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@1@inst__tsni --> Ref(y)@inst_0_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = "2AA(3)NaN" -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ AA --match--> `if y is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -451,10 +495,11 @@ test(p) //│ = 141 //│ p = AA([function CCC]) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:CCC))))@1@inst__tsni --> Ref(x)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:CCC))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 141 -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -470,10 +515,11 @@ test(p, id(CCC(123))) //│ = 141 //│ p = AA([function CCC]) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:CCC))))@1@inst__tsni --> Ref(x)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:CCC))))@2@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 141 -//│ 2 fusion opportunities: -//│ AA --match--> `if x is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -482,21 +528,44 @@ fun f(x) = if x is A then 1 f(id(A)) + f(A) //│ JS (unsanitized): -//│ let f10, tmp111, tmp112, tmp113; -//│ f10 = function f(x1) { -//│ if (x1 instanceof A1.class) { +//│ let f10, tmp113, tmp114, tmp115; +//│ f10 = function f(x10) { +//│ if (x10 instanceof A1.class) { //│ return 1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ tmp111 = Predef.id(A1); -//│ tmp112 = f10(tmp111); -//│ tmp113 = f10(A1); -//│ tmp112 + tmp113 -//│ No fusion opportunity +//│ tmp113 = Predef.id(A1); +//│ tmp114 = f10(tmp113); +//│ tmp115 = f10(A1); +//│ tmp114 + tmp115 +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let f10, tmp113, tmp114, tmp115, f_inst_0_tsni6; +//│ f_inst_0_tsni6 = function f_inst_0_tsni(x10) { +//│ return runtime.safeCall(x10()) +//│ }; +//│ f10 = function f(x10) { +//│ if (x10 instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp113 = Predef.id(A1); +//│ tmp114 = f10(tmp113); +//│ tmp115 = f_inst_0_tsni6(() => { +//│ return 1 +//│ }); +//│ tmp114 + tmp115 +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 2 -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ -------------- executing ------------- +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -509,11 +578,12 @@ fun g(x) = if x is f(g(AA(1))) //│ = 0 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@2@inst_0_tsni --> Ref(x)@inst_1_tsni@A +//│ Ref(member:B)@3@inst_0_tsni --> Ref(x)@inst_1_tsni@B +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@4@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 0 -//│ 3 fusion opportunities: -//│ A --match--> `if x is ...` -//│ AA --match--> `if x is ...` -//│ B --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -523,10 +593,11 @@ fun f(a, b) = if a is f(A, B) //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst__tsni --> Ref(a)@inst_0_tsni@A +//│ Ref(member:B)@2@inst__tsni --> Ref(b)@inst_0_tsni@B +//│ -------------- executing ------------- //│ = 1 -//│ 2 fusion opportunities: -//│ A --match--> `if a is ...` -//│ B --match--> `if b is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -543,10 +614,12 @@ fun c2(x) = if x is c1(app(AA)) + c1(AA(2)) + c2(AA(3)) + c2(A) //│ = 6 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@3@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(3)))))@4@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(2)))))@5@inst__tsni --> Ref(x)@inst_2_tsni@AA +//│ -------------- executing ------------- //│ = 6 -//│ 2 fusion opportunities: -//│ A --match--> `if x is ...` -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< :re @@ -562,13 +635,18 @@ c(B) //│ > 5 //│ ═══[RUNTIME ERROR] Error: e1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@5@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ Ref(member:B)@6@inst__tsni --> Ref(x)@inst_0_tsni@dflt +//│ Ref(member:A)@5@inst_1_tsni --> Ref($scrut)@inst_1_tsni@A +//│ Instantiate(Select(Ref(member:AA),Ident(class)),List(Lit(IntLit(2))))@7@inst_2_tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@8@inst_2_tsni --> Ref(x)@inst_1_tsni@BB +//│ Ref(member:A)@5@inst_3_tsni --> Ref($scrut)@inst_3_tsni@A +//│ Instantiate(Select(Ref(member:AA),Ident(class)),List(Lit(IntLit(2))))@7@inst_4_tsni --> Ref(x)@inst_3_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(3)))))@8@inst_4_tsni --> Ref(x)@inst_3_tsni@BB +//│ -------------- executing ------------- //│ > 5 //│ ═══[RUNTIME ERROR] Error: e1 -//│ 4 fusion opportunities: -//│ A --match--> `if scrut is ...` -//│ AA --match--> `if x is ...` -//│ B --match--> `if x is ...` -//│ BB --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -577,8 +655,9 @@ fun f(a, b) = if a is f(AA(1), BB(2)) //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:BB),List(Arg(false,Lit(IntLit(2)))))@1@inst__tsni --> Select(Ref(b),Ident(bb))@inst_0_tsni +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(1)))))@2@inst__tsni --> Ref(a)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 3 -//│ 2 fusion opportunities: -//│ AA --match--> `if a is ...` -//│ BB --sel--> `.Ident(bb)` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 1dc8dfcb2c..ca7ee9aa2f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -1,19 +1,32 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object A object B object C data class AA(x) data class BB(x) data class CC(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class Cons(h, t) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object None data class Some(x) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // since `x.x` is considered as being consumed @@ -41,30 +54,43 @@ f(AA(AA(3))) //│ tmp = AA1(3); //│ tmp1 = AA1(tmp); //│ f(tmp1) -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let f, tmp, tmp1, _deforest_AA_x; -//│ f = function f(x) { -//│ return runtime.safeCall(x()) +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let f, tmp, tmp1, f_inst_0_tsni, x; +//│ f_inst_0_tsni = function f_inst_0_tsni(x1) { +//│ return runtime.safeCall(x1()) +//│ }; +//│ f = function f(x1) { +//│ let scrut; +//│ if (x1 instanceof AA1.class) { +//│ scrut = x1.x; +//│ if (scrut instanceof AA1.class) { +//│ return x1.x.x +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } //│ }; //│ tmp = AA1(3); -//│ _deforest_AA_x = tmp; +//│ x = tmp; //│ tmp1 = () => { //│ let scrut; -//│ scrut = _deforest_AA_x; +//│ scrut = x; //│ if (scrut instanceof AA1.class) { -//│ return _deforest_AA_x.x +//│ return x.x //│ } else { //│ throw new this.Error("match error"); //│ } //│ }; -//│ f(tmp1) -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ f_inst_0_tsni(tmp1) +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 3 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@1@inst__tsni --> Ref(x)@inst_0_tsni@AA +//│ -------------- executing ------------- //│ = 3 -//│ 1 fusion opportunities: -//│ AA --match--> `if x is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -81,10 +107,10 @@ let p = AA(AA(AA(10))) test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ JS (unsanitized): //│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; -//│ test = function test(x) { +//│ test = function test(x1) { //│ let t, param0, param01, param02, a, tmp16, tmp17, tmp18, tmp19, tmp20; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; +//│ if (x1 instanceof AA1.class) { +//│ param0 = x1.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; //│ if (param01 instanceof AA1.class) { @@ -134,14 +160,27 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ tmp14 = tmp9 + tmp13; //│ tmp15 = test(B1); //│ tmp14 + tmp15 -//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> -//│ ==== JS (deforested): ==== -//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, _deforest_AA_x_tmp, match_param0_branch_AA, _deforest_AA_x_tmp1; -//│ match_param0_branch_AA = function match_param0_branch_AA(_deforest_AA_x1) { -//│ let t, param0, a, tmp16, tmp17, tmp18, tmp19, tmp20; -//│ param0 = _deforest_AA_x1; -//│ a = param0; -//│ tmp16 = a; +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let test, f1, p, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, test_inst_0_tsni, test_inst_1_tsni, test_inst_2_tsni, x1, x2, x3, x4; +//│ test_inst_0_tsni = function test_inst_0_tsni(x5) { +//│ return runtime.safeCall(x5()) +//│ }; +//│ test_inst_1_tsni = function test_inst_1_tsni(x5) { +//│ return runtime.safeCall(x5()) +//│ }; +//│ test_inst_2_tsni = function test_inst_2_tsni(x5) { +//│ let t, param0, param01, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ if (x5 instanceof AA1.class) { +//│ param0 = x5.x; +//│ if (param0 instanceof AA1.class) { +//│ param01 = param0.x; +//│ return runtime.safeCall(param01()) +//│ } else { +//│ tmp16 = 4; +//│ } +//│ } else { +//│ tmp16 = 4; +//│ } //│ t = tmp16; //│ tmp17 = 5 * 4; //│ tmp18 = t + tmp17; @@ -149,13 +188,19 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ tmp20 = tmp19 + 2; //│ return tmp20 - 1 //│ }; -//│ test = function test(x) { -//│ let t, param0, param01, tmp16, tmp17, tmp18, tmp19, tmp20; -//│ if (x instanceof AA1.class) { -//│ param0 = x.x; +//│ test = function test(x5) { +//│ let t, param0, param01, param02, a, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ if (x5 instanceof AA1.class) { +//│ param0 = x5.x; //│ if (param0 instanceof AA1.class) { //│ param01 = param0.x; -//│ return runtime.safeCall(param01()) +//│ if (param01 instanceof AA1.class) { +//│ param02 = param01.x; +//│ a = param02; +//│ tmp16 = a; +//│ } else { +//│ tmp16 = 4; +//│ } //│ } else { //│ tmp16 = 4; //│ } @@ -182,34 +227,75 @@ test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ _deforest_AA_x_tmp = 10; +//│ x4 = 10; //│ tmp4 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp) +//│ let t, param0, a, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ param0 = x4; +//│ a = param0; +//│ tmp16 = a; +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 //│ }; //│ tmp5 = AA1(tmp4); //│ tmp6 = AA1(tmp5); //│ p = tmp6; -//│ tmp7 = test(p); +//│ tmp7 = test_inst_2_tsni(p); //│ tmp8 = f1(p); //│ tmp9 = tmp7 + tmp8; -//│ _deforest_AA_x_tmp1 = 10; +//│ x3 = 10; //│ tmp10 = () => { -//│ return match_param0_branch_AA(_deforest_AA_x_tmp1) +//│ let t, param0, a, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ param0 = x3; +//│ a = param0; +//│ tmp16 = a; +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 //│ }; -//│ tmp11 = AA1(tmp10); -//│ tmp12 = AA1(tmp11); -//│ tmp13 = test(tmp12); +//│ x2 = tmp10; +//│ tmp11 = () => { +//│ let param0; +//│ param0 = x2; +//│ return runtime.safeCall(param0()) +//│ }; +//│ x1 = tmp11; +//│ tmp12 = () => { +//│ let param0; +//│ param0 = x1; +//│ return runtime.safeCall(param0()) +//│ }; +//│ tmp13 = test_inst_1_tsni(tmp12); //│ tmp14 = tmp9 + tmp13; -//│ tmp15 = test(B1); +//│ tmp15 = test_inst_0_tsni(() => { +//│ let t, tmp16, tmp17, tmp18, tmp19, tmp20; +//│ tmp16 = 4; +//│ t = tmp16; +//│ tmp17 = 5 * 4; +//│ tmp18 = t + tmp17; +//│ tmp19 = tmp18 - 3; +//│ tmp20 = tmp19 + 2; +//│ return tmp20 - 1 +//│ }); //│ tmp14 + tmp15 -//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 78 //│ p = AA(AA(AA(10))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:B)@3@inst__tsni --> Ref(x)@inst_0_tsni@dflt +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@4@inst__tsni --> Ref(x)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Ref($tmp))))@5@inst__tsni --> Ref($param0)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(10)))))@6@inst__tsni --> Ref($param0)@inst_1_tsni@AA +//│ Call(Ref(member:AA),List(Arg(false,Lit(IntLit(10)))))@7@inst__tsni --> Ref($param0)@inst_2_tsni@AA +//│ -------------- executing ------------- //│ = 78 -//│ 2 fusion opportunities: -//│ AA --match--> `if param0 is ...` -//│ AA --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -222,3 +308,6 @@ fun test() = Global(x) then Local(x + 1) test() //│ = Local(2) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls index 73097e5461..a1b7518da5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls @@ -1,12 +1,19 @@ :js :deforest -//│ No fusion opportunity +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< object Nil data class (::) Cons(h, t) data class Pair(a, b) +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ Not deforestable: No support for `ClsLikeDefn` yet +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) @@ -19,12 +26,13 @@ fun testUnzipZip(n) = unzip(zip(id(enumFromTo(1, n)), id(enumFromTo(2, n + 3)))) testUnzipZip(3) //│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@3@inst_0_1_tsni --> Ref(ls_unzip)@inst_0_2_tsni@Cons +//│ Call(Ref(member:Pair),List(Arg(false,Ref(x)), Arg(false,Ref(y))))@4@inst_0_1_tsni --> Ref($param0)@inst_0_2_tsni@Pair +//│ Ref(member:Nil)@5@inst_0_1_tsni --> Ref(ls_unzip)@inst_0_2_tsni@Nil +//│ Ref(member:Nil)@6@inst_0_1_tsni --> Ref(ls_unzip)@inst_0_2_tsni@Nil +//│ -------------- executing ------------- //│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) -//│ 4 fusion opportunities: -//│ Cons --match--> `if ls_unzip is ...` -//│ Nil --match--> `if ls_unzip is ...` -//│ Nil --match--> `if ls_unzip is ...` -//│ Pair --match--> `if param0 is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -41,12 +49,13 @@ fun testZipUnzip(n) = if unzip(id(makeZippedList(n))) is testZipUnzip(3) //│ = Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@3@inst_0_1_tsni --> Ref(xs_zip)@inst_0_2_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref(atail))))@4@inst_0_1_tsni --> Ref(xs_zip)@inst_0_2_tsni@Cons +//│ Ref(member:Nil)@5@inst_0_1_tsni --> Ref(ys_zip)@inst_0_2_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref(b)), Arg(false,Ref(btail))))@6@inst_0_1_tsni --> Ref(ys_zip)@inst_0_2_tsni@Cons +//│ -------------- executing ------------- //│ = Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) -//│ 4 fusion opportunities: -//│ Cons --match--> `if xs_zip is ...` -//│ Cons --match--> `if ys_zip is ...` -//│ Nil --match--> `if xs_zip is ...` -//│ Nil --match--> `if ys_zip is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -69,10 +78,15 @@ fun testZipMapBothUnzip(n) = if unzip(id(makeZippedList(n))) is testZipMapBothUnzip(4) //│ = Cons(Pair(5, 25), Cons(Pair(4, 16), Cons(Pair(3, 9), Cons(Pair(2, 4), Nil)))) //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:Nil)@5@inst_0_1_tsni --> Ref(ls_map)@inst_0_2_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(a)), Arg(false,Ref(atail))))@6@inst_0_1_tsni --> Ref(ls_map)@inst_0_2_tsni@Cons +//│ Ref(member:Nil)@7@inst_0_1_tsni --> Ref(ls_map)@inst_0_3_tsni@Nil +//│ Call(Ref(member:Cons),List(Arg(false,Ref(b)), Arg(false,Ref(btail))))@8@inst_0_1_tsni --> Ref(ls_map)@inst_0_3_tsni@Cons +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@9@inst_0_2_tsni --> Ref(xs_zip)@inst_0_4_tsni@Cons +//│ Ref(member:Nil)@10@inst_0_2_tsni --> Ref(xs_zip)@inst_0_4_tsni@dflt +//│ Call(Ref(member:Cons),List(Arg(false,Ref($tmp)), Arg(false,Ref($tmp))))@9@inst_0_3_tsni --> Ref(ys_zip)@inst_0_4_tsni@Cons +//│ Ref(member:Nil)@10@inst_0_3_tsni --> Ref(ys_zip)@inst_0_4_tsni@dflt +//│ -------------- executing ------------- //│ = Cons(Pair(5, 25), Cons(Pair(4, 16), Cons(Pair(3, 9), Cons(Pair(2, 4), Nil)))) -//│ 4 fusion opportunities: -//│ Cons --match--> `if ls_map is ...` -//│ Cons --match--> `if ls_map is ...` -//│ Nil --match--> `if ls_map is ...` -//│ Nil --match--> `if ls_map is ...` //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 9f85855c0f20b56637355ac659c7dacf9cbc3d1d Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:59:58 +0800 Subject: [PATCH 297/303] make it possible to know whether a symbol is for a function...? --- .../main/scala/hkmc2/codegen/LambdaRewriter.scala | 5 ++++- .../scala/hkmc2/codegen/deforest/Analyze.scala | 14 +++++--------- .../scala/hkmc2/codegen/deforest/Rewrite.scala | 5 ++--- .../src/main/scala/hkmc2/semantics/Symbol.scala | 2 ++ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala index 6005f51d4a..70e36ed524 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -24,7 +24,10 @@ object LambdaRewriter: val lambdaRewriter = new BlockDataTransformer(SymbolSubst()): override def applyValue(v: Value): Value = v match case lam: Value.Lam => - val sym = BlockMemberSymbol("lambda", Nil, nameIsMeaningful = false) + val sym = BlockMemberSymbol( + "lambda", + syntax.Tree.TermDef(syntax.Fun, syntax.Tree.Empty(), N) :: Nil, + nameIsMeaningful = false) lambdasList ::= (sym -> super.applyLam(lam)) Value.Ref(sym) case _ => super.applyValue(v) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 1dc28e7a9e..122c6b29ae 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -132,7 +132,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: prev def getReferredFunSym(id: ResultId) = def chk(s: BlockMemberSymbol) = - // assert(s.trmImplTree.exists(_.k is syntax.Fun)) + assert(s.isFunction) s resultIdToResult(id) match case s: Select => chk(s.symbol.get.asBlkMember.get) @@ -161,7 +161,7 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: super.applyFunDefn(fun) override def applySymbol(s: Symbol): Unit = s match - case s: BlockMemberSymbol if s.trmImplTree.fold(false)(_.k is syntax.Fun) => symToStratVar.updateWith(s): + case s: BlockMemberSymbol if s.isFunction => symToStratVar.updateWith(s): case N => S(freshVar(s.nme, S(s)).asProdStrat) case S(x) => S(x) // term symbol: variable in patterns so they are always inside the current fundefn (if any) @@ -186,18 +186,14 @@ class DeforestPreAnalyzer(val b: Block) extends BlockTraverser: p match case s@Select(path, nme) => selsToMatchingArmsContainingIt += s.uid -> inMatchScrutsArms - s.symbol.flatMap(_.asBlkMember).foreach: b => - b.trmTree.foreach: t => - if t.k is syntax.Fun then usedFunSyms += b + if s.symbol.exists(_.isFunction) then usedFunSyms += s.symbol.get.asBlkMember.get case _ => () super.applyPath(p) override def applyValue(v: Value): Unit = resultIdToResult += v.uid -> v v match - case Value.Ref(l) => l.asBlkMember.foreach: b => - b.trmTree.foreach: t => - if t.k is syntax.Fun then usedFunSyms += b + case Value.Ref(l) if l.isFunction => usedFunSyms += l.asBlkMember.get case _ => () super.applyValue(v) @@ -422,7 +418,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): case sel@Select(p, nme) => sel.symbol match case Some(s) if s.asObj.isDefined => new Ctor(sel.uid, instantiationId, s.asObj.get, Nil) - case Some(s) if s.asBlkMember.exists(_.trmImplTree.exists(_.k is syntax.Fun)) && + case Some(s) if s.isFunction && preAnalyzer.topLevelDefinedFunSyms.contains(s.asBlkMember.get) => funSymToProdStratScheme.getOrUpdate(s.asBlkMember.get) match case v: ProdVar => v diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 1e030b8b3c..907baa90ed 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -455,7 +455,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(s.uid.withInstId) => matchArmsOfFusingMatches.getOrElseUpdate(s.uid.withInstId) case _ => s.symbol.flatMap(_.asBlkMember) match - case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => + case Some(blk) if blk.isFunction && preAnalyzer.topLevelDefinedFunSyms.contains(blk) => val inTheSameRecursiveGroup = instId.lastOption.fold(false): currentReferSite => val currentSym = preAnalyzer.getReferredFunSym(currentReferSite) rewritePrepare.sol.collector.funSymToProdStratScheme.recursiveGroups(currentSym).contains(blk) @@ -471,8 +471,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora case Some(obj) if rewritePrepare.ctorIdToFinalDest.isDefinedAt(r.uid.withInstId) => matchArmsOfFusingMatches.getOrElseUpdate(r.uid.withInstId) case None => l.asBlkMember match - // case Some(blk) if blk.trmImplTree.fold(false)(_.k is syntax.Fun) => - case Some(blk) if preAnalyzer.topLevelDefinedFunSyms.contains(blk) => + case Some(blk) if blk.isFunction && preAnalyzer.topLevelDefinedFunSyms.contains(blk) => val inTheSameRecursiveGroup = instId.lastOption.fold(false): currentReferSite => val currentSym = preAnalyzer.getReferredFunSym(currentReferSite) rewritePrepare.sol.collector.funSymToProdStratScheme.recursiveGroups(currentSym).contains(blk) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index 3a1fc77854..de8be00ae3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -29,6 +29,7 @@ abstract class Symbol(using State) extends Located: def refsNumber: Int = directRefs.size def isModule: Bool = asMod.nonEmpty + def isFunction: Bool = asBlkMember.exists(b => b.isFunctionSymbol) def asCls: Opt[ClassSymbol] = this match case cls: ClassSymbol => S(cls) @@ -156,6 +157,7 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree], val nameIsMeaningful: case t: Tree.TermDef if t.rhs.isDefined => t def isParameterizedMethod: Bool = trmTree.exists(_.sParameterizedMethod) + def isFunctionSymbol: Bool = trmTree.exists(_.k is syntax.Fun) lazy val hasLiftedClass: Bool = objTree.isDefined || trmTree.isDefined || clsTree.exists(_.paramLists.nonEmpty) From 364298d1e49d4c111a56ac49d378cbcffe27e65c Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:52:02 +0800 Subject: [PATCH 298/303] cleanup --- .../hkmc2/codegen/deforest/Analyze.scala | 24 +++++++++---------- .../hkmc2/codegen/deforest/Rewrite.scala | 4 ++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 122c6b29ae..2a247ad358 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -28,7 +28,7 @@ trait StratVar(s: StratVarState): def asConsStrat = s.asConsStrat def uid = s.uid -// TODO: examine what info is really needed in the ctors + sealed abstract class ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVar(s) case class ProdFun(params: Ls[ConsStrat], res: ProdStrat) extends ProdStrat @@ -230,11 +230,11 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val constraints = processTopLevel(preAnalyzer.b) class ConstraintsAndCacheHitCollector(val forFun: Opt[BlockMemberSymbol]): var constraints: Ls[ProdStrat -> ConsStrat] = Nil - var cacheHit: Ls[BlockMemberSymbol] = Nil // TODO: a better name to say it actually get the symbol of funs in a recursive group + var trackedFunctionSymbolsInOneRecGroup: Ls[BlockMemberSymbol] = Nil def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def constrain(cs: Ls[ProdStrat -> ConsStrat]) = constraints :::= cs - def hit(s: BlockMemberSymbol) = cacheHit ::= s - def hit(ss: Ls[BlockMemberSymbol]) = cacheHit :::= ss + def hit(s: BlockMemberSymbol) = trackedFunctionSymbolsInOneRecGroup ::= s + def hit(ss: Ls[BlockMemberSymbol]) = trackedFunctionSymbolsInOneRecGroup :::= ss object funSymToProdStratScheme: val store = mutable.Map.empty[BlockMemberSymbol, ProdStratScheme] @@ -243,7 +243,6 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): preAnalyzer.getTopLevelFunDefnForSym(s) match // not a fun defined in the current block, just return its prodvar case None => - // preAnalyzer.noProdStratVar preAnalyzer.getProdVarForSym(s) // TODO: consider functions being imported? case Some(funDefn) => store.get(s) match case Some(scheme) => scheme @@ -258,14 +257,14 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): // then: 1. the referred function belongs to the same recursion group and need to share the constraints 2. return the prodvar // else: we found a new recursive group, for each member of the group, update the store with the correct type scheme and return the type scheme val newcc = processFunDefn(funDefn, processingDefs) - if newcc.cacheHit.exists(x => processingDefs.contains(x)) then - cc.hit(newcc.cacheHit) + if newcc.trackedFunctionSymbolsInOneRecGroup.exists(x => processingDefs.contains(x)) then + cc.hit(newcc.trackedFunctionSymbolsInOneRecGroup) cc.constrain(newcc.constraints) processingDefs.headOption.foreach: h => cc.hit(h) preAnalyzer.getProdVarForSym(s) else - val recursiveGroupMembers = (s :: newcc.cacheHit).distinct + val recursiveGroupMembers = (s :: newcc.trackedFunctionSymbolsInOneRecGroup).distinct recursiveGroupMembers.foreach: f => store.updateWith(f): case N => S(ProdStratScheme(preAnalyzer.getProdVarForSym(f).s, newcc.constraints)) @@ -287,7 +286,6 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): .diff(preAnalyzer.nonTopLevelDefinedFunSyms) .foreach: usedButNotDefined => cc.constrain(NoProd, preAnalyzer.getProdVarForSym(usedButNotDefined).asConsStrat) - // TODO: for defined but not fun syms, constrain them with NoCons cc.constraints def processFunDefn(defn: FunDefn, processingDefs: Ls[BlockMemberSymbol]): ConstraintsAndCacheHitCollector = @@ -311,7 +309,7 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then arms.map: - case (Case.Cls(clsSym, _), body) => processBlock(body) // TODO: check + case (Case.Cls(clsSym, _), body) => processBlock(body) else arms.map: case (_, armBody) => processBlock(armBody) @@ -513,7 +511,7 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): dtorSources.update(d, c) c.args.find(a => a._1.id == d.field).map: p => handle(p._2 -> d.consVar) - case (c: Ctor, d: ConsFun) => () // ignore, TODO: maybe a warning? + case (c: Ctor, d: ConsFun) => () case (p: ProdVar, _) => upperBounds += p.uid -> (cons :: upperBounds(p.uid)) lowerBounds(p.uid).foreach: l => @@ -557,8 +555,8 @@ class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): constraints.foreach(handle) val resolveClashes = - val ctorToDtor = ctorDests.store.clone() // TODO: clone is only helpful for debugging - val dtorToCtor = dtorSources.store.clone() + val ctorToDtor = ctorDests.store + val dtorToCtor = dtorSources.store def removeCtor(rm: (Ctor | ResultId)): Unit = rm match case rm: Ctor => for diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 907baa90ed..ed716642d7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -16,7 +16,7 @@ type CtorId = ResultId -> InstantiationId type MatchId = ResultId -> InstantiationId type SelId = ResultId -> InstantiationId -// TODO: maybe better design this to not use secondary param list for case class + enum FinalDest: // A match arm is considered a final destination. // `selsInArm` totally depends on `matchId` and `arm`, while @@ -55,7 +55,7 @@ class DeforestRewritePrepare(val sol: DeforestConstrainSolver)(using Elaborator. old -> newSymbol mapping.toMap // TODO: can merge all ids that *only* have dtor to one id, but also need to consider all other - // referred functions along the way + // referred functions along the way? for (dtorOrSel, _) <- sol.resolveClashes._2 instantiationId = dtorOrSel match From a72843fcd61b1f77cc885a2af9eadd83a95ce3ff Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:05:14 +0800 Subject: [PATCH 299/303] wip --- .../src/test/mlscript/deforest/simple.mls | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index ce642cac1c..30163eaea2 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -51,7 +51,7 @@ test() //│ = A //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< - +:sjs fun f(a) = if a is A then 1 B then 2 @@ -61,6 +61,106 @@ fun test() = AA(x) then f(x) BB(x) then f(x) test() +//│ JS (unsanitized): +//│ let test2, f; +//│ f = function f(a) { +//│ if (a instanceof A1.class) { +//│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test2 = function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = AA1(A1); +//│ } else { +//│ tmp = BB1(B1); +//│ } +//│ x = tmp; +//│ if (x instanceof AA1.class) { +//│ param01 = x.aa; +//│ x2 = param01; +//│ return f(x2) +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.bb; +//│ x1 = param0; +//│ return f(x1) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test2() +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let test2, f, test_inst_0_tsni2, f_inst_0_1_tsni, f_inst_0_2_tsni; +//│ test_inst_0_tsni2 = function test_inst_0_tsni() { +//│ let x, scrut, tmp, aa, bb; +//│ scrut = true; +//│ if (scrut === true) { +//│ aa = () => { +//│ return 1 +//│ }; +//│ tmp = () => { +//│ let param0, x1; +//│ param0 = aa; +//│ x1 = param0; +//│ return f_inst_0_1_tsni(x1) +//│ }; +//│ } else { +//│ bb = () => { +//│ return 2 +//│ }; +//│ tmp = () => { +//│ let param0, x1; +//│ param0 = bb; +//│ x1 = param0; +//│ return f_inst_0_2_tsni(x1) +//│ }; +//│ } +//│ x = tmp; +//│ return runtime.safeCall(x()) +//│ }; +//│ f_inst_0_1_tsni = function f_inst_0_1_tsni(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ f_inst_0_2_tsni = function f_inst_0_2_tsni(a) { +//│ return runtime.safeCall(a()) +//│ }; +//│ f = function f(a) { +//│ if (a instanceof A1.class) { +//│ return 1 +//│ } else if (a instanceof B1.class) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test2 = function test() { +//│ let x, scrut, param0, x1, param01, x2, tmp; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp = AA1(A1); +//│ } else { +//│ tmp = BB1(B1); +//│ } +//│ x = tmp; +//│ if (x instanceof AA1.class) { +//│ param01 = x.aa; +//│ x2 = param01; +//│ return f(x2) +//│ } else if (x instanceof BB1.class) { +//│ param0 = x.bb; +//│ x1 = param0; +//│ return f(x1) +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ test_inst_0_tsni2() +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ---------- deforest summary ---------- From dc083b40eb7c66fbe7c9fde422715a6f205a0392 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 20 Jun 2025 02:01:19 +0800 Subject: [PATCH 300/303] new todos --- .../src/test/mlscript/deforest/new-todo.mls | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 hkmc2/shared/src/test/mlscript/deforest/new-todo.mls diff --git a/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls b/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls new file mode 100644 index 0000000000..15c405e650 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls @@ -0,0 +1,96 @@ +:js + + +data class AA(x) +data class BB(x) +object A +object B + +// TODO: should fuse `A` and `B` with the pattern match in `f` +:deforest +fun f(a) = if a is + A then 1 + B then 2 +fun test() = + let x = if true then AA(A) else BB(B) + if x is + AA(x) then f(x) + BB(x) then f(x) +test() +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@1@inst_0_tsni --> Ref(x)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@2@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ -------------- executing ------------- +//│ = 1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// TODO: fusion opportunity is lost +// when instantiation ids of dtors are also tracked +:deforest +fun inner(x) = if x is A then 0 else 1 +fun outter1(x) = inner(x) +fun outter2(x) = inner(x) +fun test() = + let x = A + outter1(x) + outter2(x) +test() +//│ = 0 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ +//│ -------------- executing ------------- +//│ = 0 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// TODO: duplicated instantiations of the consumer `f` +:sjs +:deforest +fun f(x) = if x is A then 1 +f(A) + f(A) +//│ JS (unsanitized): +//│ let f1, tmp, tmp1; +//│ f1 = function f(x) { +//│ if (x instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = f1(A1); +//│ tmp1 = f1(A1); +//│ tmp + tmp1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>> Deforestation JS >>>>>>>>>>>>>>>>>>>>>>>>>> +//│ let f1, tmp, tmp1, f_inst_0_tsni, f_inst_1_tsni; +//│ f_inst_0_tsni = function f_inst_0_tsni(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ f_inst_1_tsni = function f_inst_1_tsni(x) { +//│ return runtime.safeCall(x()) +//│ }; +//│ f1 = function f(x) { +//│ if (x instanceof A1.class) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ tmp = f_inst_1_tsni(() => { +//│ return 1 +//│ }); +//│ tmp1 = f_inst_0_tsni(() => { +//│ return 1 +//│ }); +//│ tmp + tmp1 +//│ <<<<<<<<<<<<<<<<<<<<<<<<< Deforestation JS <<<<<<<<<<<<<<<<<<<<<<<<<< +//│ = 2 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@2@inst__tsni --> Ref(x)@inst_0_tsni@A +//│ Ref(member:A)@3@inst__tsni --> Ref(x)@inst_1_tsni@A +//│ -------------- executing ------------- +//│ = 2 +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< From 1e2509d36cfa746e0601e307a2b509aad0cdc7cb Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:03:26 +0800 Subject: [PATCH 301/303] wip: noprod for arrays for now --- .../shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala index 2a247ad358..0c8f6f1f99 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala @@ -467,7 +467,8 @@ class DeforestConstraintsCollector(val preAnalyzer: DeforestPreAnalyzer): val res = freshVar(s"lam_res", generatedForDef) cc.constrain(bodyStrat, res.asConsStrat) ProdFun(paramSyms, res.asProdStrat) - case Value.Arr(elems) => throw NotDeforestableException("No support for arrays yet") + case Value.Arr(elems) => + NoProd class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): From cc9eb03db3da3d3eacb8a451173496a40c79ccf6 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:20:01 +0800 Subject: [PATCH 302/303] minor --- hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index ed716642d7..e6d46cb890 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -249,6 +249,7 @@ class DeforestRewriter(val rewritePrepare: DeforestRewritePrepare)(using Elabora object matchRestOfFusingMatches: // from match scrut expr id to either a function def with a set of args that should be applied + // or a block containing the computation in the `rest` of the match val store = mutable.Map.empty[MatchId, Either[FunDefn -> Ls[Symbol], Block]] def getAllFunDefs = rewritePrepare.fusingMatchIdToMatchRestFunSymbols.foldRight(identity: Block => Block): From 917aafae65fea89dc826d7d751593b07962e4a44 Mon Sep 17 00:00:00 2001 From: CrescentonC <43136427+CrescentonC@users.noreply.github.com> Date: Sat, 21 Jun 2025 17:34:26 +0800 Subject: [PATCH 303/303] update new todos --- .../src/test/mlscript/deforest/new-todo.mls | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls b/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls index 15c405e650..3293fcdb5e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/new-todo.mls @@ -6,29 +6,34 @@ data class BB(x) object A object B -// TODO: should fuse `A` and `B` with the pattern match in `f` +// TODO: should fuse `A` with the pattern match in `f`. +// This is because the same symbol is used for the `x`s +// introduced in the patterns of both branches, causing +// `A` also flows into consumers in `g1` and `g2` :deforest -fun f(a) = if a is - A then 1 - B then 2 +fun f(x) = if x is A then 1 else 2 +fun g1(x) = if x is A then 2 else 3 +fun g2(x) = if x is A then 3 else 4 fun test() = - let x = if true then AA(A) else BB(B) - if x is + let prod = if true then AA(A) else BB(B) + if prod is AA(x) then f(x) - BB(x) then f(x) + BB(x) then g1(x) + g2(x) test() //│ = 1 //│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> //│ ---------- deforest summary ---------- -//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@1@inst_0_tsni --> Ref(x)@inst_0_tsni@AA -//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@2@inst_0_tsni --> Ref(x)@inst_0_tsni@BB +//│ Call(Ref(member:AA),List(Arg(false,Ref(member:A))))@1@inst_0_tsni --> Ref(prod)@inst_0_tsni@AA +//│ Call(Ref(member:BB),List(Arg(false,Ref(member:B))))@2@inst_0_tsni --> Ref(prod)@inst_0_tsni@BB //│ -------------- executing ------------- //│ = 1 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< // TODO: fusion opportunity is lost -// when instantiation ids of dtors are also tracked +// when instantiation ids of dtors are also tracked: +// two call sites to `inner` leads to two `inner`s +// to be called, causing a clashing consumer. :deforest fun inner(x) = if x is A then 0 else 1 fun outter1(x) = inner(x) @@ -94,3 +99,28 @@ f(A) + f(A) //│ -------------- executing ------------- //│ = 2 //│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<< + + +// TODO: after lowering the definition of `f` is located before the +// defining site of `x`, so that `f` is not in the `rest` of the fusing +// pattern matching and `x` in `f` becomes free. +:re +:ge +:deforest +fun test() = + let x = if A is + A then 1 + fun f() = x + f() +test() +//│ = 1 +//│ >>>>>>>>>>>>>>>>>>>>>>>>>>> Deforestation >>>>>>>>>>>>>>>>>>>>>>>>>>> +//│ ---------- deforest summary ---------- +//│ Ref(member:A)@1@inst_0_tsni --> Ref($scrut)@inst_0_tsni@A +//│ ╔══[COMPILATION ERROR] No definition found in scope for 'x' +//│ ║ l.111: let x = if A is +//│ ╙── ^ +//│ -------------- executing ------------- +//│ ═══[RUNTIME ERROR] ReferenceError: x is not defined +//│ ═══[RUNTIME ERROR] The result from deforestated program ("undefined") is different from the one computed by the original prorgam ("1") +//│ <<<<<<<<<<<<<<<<<<<<<<<<<<< Deforestation <<<<<<<<<<<<<<<<<<<<<<<<<<<