Skip to content

Preliminary deforestation implementation for hkmc2 #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 223 commits into
base: hkmc2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
223 commits
Select commit Hold shift + click to select a range
d42cd71
wip
CrescentonC Jan 2, 2025
374f2f2
wip
CrescentonC Jan 9, 2025
738df06
wip
CrescentonC Jan 9, 2025
80be404
wip
CrescentonC Jan 10, 2025
7d72fa5
wip
CrescentonC Jan 11, 2025
7f34cb2
wip
CrescentonC Jan 11, 2025
837dfc3
wip
CrescentonC Jan 11, 2025
78364b4
wip
CrescentonC Jan 13, 2025
84582c8
wip
CrescentonC Jan 13, 2025
c49b902
wip
CrescentonC Jan 14, 2025
50475fc
wip
CrescentonC Jan 14, 2025
960438d
wip
CrescentonC Jan 17, 2025
45d7ed4
wip
CrescentonC Jan 19, 2025
d76a91b
wip
CrescentonC Jan 20, 2025
476511e
wip
CrescentonC Jan 20, 2025
c838208
wip
CrescentonC Jan 21, 2025
342db56
wip
CrescentonC Jan 21, 2025
ad91803
wip
CrescentonC Jan 21, 2025
c2ecb6e
wip
CrescentonC Jan 22, 2025
e37fb12
wip
CrescentonC Jan 22, 2025
85cf293
wip
CrescentonC Jan 22, 2025
faeb52d
wip
CrescentonC Jan 22, 2025
5947353
wip: Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Jan 22, 2025
3783c31
wip: fix after merge
CrescentonC Jan 22, 2025
9401488
fix after merge
CrescentonC Jan 22, 2025
0065624
wip: start to use blocktransformer
CrescentonC Jan 22, 2025
dd3e57f
wip
CrescentonC Jan 23, 2025
9f6b669
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Jan 24, 2025
de68a14
update after merge
CrescentonC Jan 24, 2025
cf96bda
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Jan 27, 2025
11d6bab
exprid
CrescentonC Jan 28, 2025
cf4c316
wip cleanup
CrescentonC Jan 28, 2025
2792e52
wip handle call args
CrescentonC Feb 3, 2025
4c4ce71
cleanup
CrescentonC Feb 3, 2025
f3808e7
fuse selection as consumer
CrescentonC Feb 3, 2025
163df5d
fix: recurse into rewritten body
CrescentonC Feb 3, 2025
d9b8e65
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Feb 6, 2025
dd3acf8
wip: fix merge
CrescentonC Feb 6, 2025
fec747d
wip: fix merge
CrescentonC Feb 6, 2025
8a6ad5e
wip: fix merge
CrescentonC Feb 6, 2025
f1f9ee1
fix after merge
CrescentonC Feb 6, 2025
2d4e428
fix merging of mkQuery function
CrescentonC Feb 6, 2025
0b37088
merge match arms
CrescentonC Feb 6, 2025
f026427
match use uid
CrescentonC Feb 6, 2025
c16cb0a
add test
CrescentonC Feb 6, 2025
943a329
better dtor strat defn
CrescentonC Feb 6, 2025
f6a920a
better arm merging
CrescentonC Feb 7, 2025
d3d2a3b
noprod for unknown blockmembersymbols
CrescentonC Feb 8, 2025
fa81521
wip: keep track of the whole match block
CrescentonC Feb 10, 2025
e7b4562
keep track of the whole match block
CrescentonC Feb 10, 2025
ec12a65
process rest before defn for Define blocks
CrescentonC Feb 11, 2025
d67f667
wip: allocate freshvar for all symbols before
CrescentonC Feb 12, 2025
2ab41d7
no need to process rest before defn for Define blocks
CrescentonC Feb 12, 2025
77762b9
wip: ad hoc tail transformation for match blocks in rewriting
CrescentonC Feb 13, 2025
3430239
do not print all definedvalues for deforested programs
CrescentonC Feb 13, 2025
294d10d
remove useless functions; improve explicit return when rewriting
CrescentonC Feb 13, 2025
881b859
better replace select
CrescentonC Feb 13, 2025
ae2601e
update tests
CrescentonC Feb 14, 2025
35704a2
forgot to recurse into rest of matches; wip: extract rests of matches…
CrescentonC Feb 14, 2025
9709578
wip
CrescentonC Feb 15, 2025
d4358d7
FIXME: blocktransformer: subst vs applyLocal in applyValue?
CrescentonC Feb 17, 2025
fc630a3
extract the rest of consumer matches as functions
CrescentonC Feb 17, 2025
7823ec5
wip
CrescentonC Feb 17, 2025
f290844
wip: handle free vars...
CrescentonC Feb 18, 2025
7e5160c
wip
CrescentonC Feb 18, 2025
8b5453b
CHECK LATER: change the definition of freeVars
CrescentonC Feb 19, 2025
fd4dd8b
wip: fix the handling of free vars
CrescentonC Feb 19, 2025
88f3dd8
cleanup for free vars handling
CrescentonC Feb 19, 2025
6e089d7
update tests
CrescentonC Feb 19, 2025
3ed8c7a
ctor expr ids needed for prodstrats
CrescentonC Feb 19, 2025
d306937
minor
CrescentonC Feb 19, 2025
bb12dfc
Changes from meeting
LPTK Feb 20, 2025
85f0381
found more fixmes
CrescentonC Feb 20, 2025
0c0805d
avoid replacing selects twice for rewrittenBody
CrescentonC Feb 20, 2025
1495b25
minor fix on free vars
CrescentonC Feb 21, 2025
fc67528
wip: a select can be in multiple match arms (nested)
CrescentonC Feb 21, 2025
b11d654
also keep track of which matching stmts a selection belongs to
CrescentonC Feb 22, 2025
4e221bc
correct selections in match stmts
CrescentonC Feb 25, 2025
ab886a1
fix the filtering of strategies
CrescentonC Feb 25, 2025
935b5ff
does not generate tmp vars for function arguments
CrescentonC Feb 26, 2025
1b5901b
improve previous commit on avoiding tmp vars for function args
CrescentonC Feb 27, 2025
58c4cef
selections in nested matching arms are not considered
CrescentonC Feb 27, 2025
210d96a
update tests
CrescentonC Feb 27, 2025
414e23f
better rewritten of rest
CrescentonC Feb 27, 2025
6ed7e76
minor
CrescentonC Feb 28, 2025
479bcd1
cleanup
CrescentonC Feb 28, 2025
210fa1d
wip: functions for match arms
CrescentonC Mar 1, 2025
264983e
function for match arms
CrescentonC Mar 3, 2025
31376e5
update tests
CrescentonC Mar 3, 2025
8e64cd6
clean up
CrescentonC Mar 3, 2025
d1f99fe
fix parameter order and cleanup
CrescentonC Mar 3, 2025
b248653
use set to record dtor sources
CrescentonC Mar 4, 2025
a81ca85
wip: try to replace select when rewriting
CrescentonC Mar 4, 2025
a187b4f
wip: restore changes to keep track of selections in nested matches; u…
CrescentonC Mar 4, 2025
84ff209
Merge branch 'hkmc2' into hkmc2-deforest
CrescentonC Mar 5, 2025
57df882
wip: update after merge, no need to flatten and merge arms in defores…
CrescentonC Mar 5, 2025
f27b72c
toplevel symbols are not freevars
CrescentonC Mar 5, 2025
270495a
Merge remote-tracking branch 'anto/hkmc2-deforest' into hkmc2-deforest
CrescentonC Mar 5, 2025
f3833d6
fix error by updating free var for fun defns
CrescentonC Mar 5, 2025
d7c7e42
use blocktraverser
CrescentonC Mar 5, 2025
32130a5
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Mar 6, 2025
da297ea
update tests
CrescentonC Mar 6, 2025
8e3eebd
wip: restore my changes on function `Block.freeVars`
CrescentonC Mar 6, 2025
aaac95e
use my own free var function
CrescentonC Mar 6, 2025
0a02e4f
restore my changes to the definition of Block.Match
CrescentonC Mar 6, 2025
f4a80b7
update tests
CrescentonC Mar 6, 2025
7f07ab8
revert my changes toBblockTransformer and properly implement ReplaceL…
CrescentonC Mar 6, 2025
64994f4
minor
CrescentonC Mar 6, 2025
c6550cf
better scopeExtrusionInfo; more tests
CrescentonC Mar 6, 2025
ca4cdd5
add test
CrescentonC Mar 6, 2025
dec504d
wip: proper handling of sels in nested matches
CrescentonC Mar 6, 2025
bf504b6
wip: proper handling of sels in nested matches
CrescentonC Mar 7, 2025
291b868
wip
CrescentonC Mar 7, 2025
f021437
wip: use pre-computed symbols for rewriting, also prevent passing unu…
CrescentonC Mar 7, 2025
d144789
seems to work; need cleanup
CrescentonC Mar 7, 2025
b6ee001
wip: update tests
CrescentonC Mar 7, 2025
d626ecf
ctordest and ctorfinaldests are already branch-specific
CrescentonC Mar 7, 2025
8eca442
some cleanup and update tests
CrescentonC Mar 7, 2025
832db64
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Mar 7, 2025
eb0a955
fix non determinism
CrescentonC Mar 7, 2025
06a4ddb
further cleanup
CrescentonC Mar 8, 2025
4c472fd
fix: there should be one CtorFinalDest per arm for each pat mat expr
CrescentonC Mar 8, 2025
8a0fcc6
wip: add problematic tests
CrescentonC Mar 9, 2025
42977e6
wip
CrescentonC Mar 10, 2025
8b2496e
wip: move DeforestTransformer out
CrescentonC Mar 10, 2025
c39b54d
further fixes to free vars of matches; found the new problem of missi…
CrescentonC Mar 10, 2025
2365471
update test
CrescentonC Mar 11, 2025
44239ab
keep track of how matches are nested
CrescentonC Mar 11, 2025
75cf3af
wip: seems to fix the missing `rest`s
CrescentonC Mar 11, 2025
43f738e
update tests
CrescentonC Mar 12, 2025
79c489f
minor fixes for selections
CrescentonC Mar 12, 2025
bf718c8
update todos with more test cases and comments
CrescentonC Mar 12, 2025
3f3fe05
cleanup and more tests
CrescentonC Mar 12, 2025
5d96ecb
better usage of blocktransformer
CrescentonC Mar 13, 2025
2268f3c
further fix to take care of the `rest`s from non-fusing parent match …
CrescentonC Mar 13, 2025
41fd30c
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Mar 14, 2025
7f85ac1
fix after merge: add val to public class fields; remove subst for blo…
CrescentonC Mar 14, 2025
f117a68
another problematic test
CrescentonC Mar 17, 2025
8991cd3
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Mar 17, 2025
640cc59
more comments on tests
CrescentonC Mar 18, 2025
87781c3
check and compare outputs from deforestated programs
CrescentonC Mar 18, 2025
c5afea7
update test
CrescentonC Mar 18, 2025
921665d
remove a useless field from CtorFinalDest.Match
CrescentonC Mar 19, 2025
f901a80
add another test
CrescentonC Mar 19, 2025
120d7be
wip: try to take care of matches from multiple levels up
CrescentonC Mar 20, 2025
5615ba8
update test; need to clean undefined vars in dead code...
CrescentonC Mar 21, 2025
6a664fc
better name; wip: tidy up computation of free vars
CrescentonC Mar 24, 2025
7d9ce98
remove wrong fixmes; add more comments
CrescentonC Mar 24, 2025
4d337fd
don't rewrite already rewritten blocks
CrescentonC Mar 24, 2025
b258545
revisit free var computation and update comments...
CrescentonC Mar 24, 2025
2373e06
improve `matchRest` impl
CrescentonC Mar 25, 2025
58cc209
minor fix and a new test
CrescentonC Mar 25, 2025
35679fe
include runtimeSymbol as always in scope
CrescentonC Mar 25, 2025
d65224e
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Mar 25, 2025
4614a0d
better handling for dflt arms
CrescentonC Mar 27, 2025
d029daf
better names for fields of classes
CrescentonC Mar 27, 2025
4ba1ea9
update fixme tests
CrescentonC Mar 28, 2025
a855c0f
tests output program despite of undefined var
CrescentonC Mar 29, 2025
a666b60
avoid creating needless `match_rest` functions
CrescentonC Mar 29, 2025
8d8a83a
use extension method for resultId
CrescentonC Mar 29, 2025
b61ea2e
update test: found code duplication; some problems are masked by chan…
CrescentonC Mar 30, 2025
7a3cb55
another duplication
CrescentonC Mar 31, 2025
f4c693f
fix for avoiding the previous duplication
CrescentonC Mar 31, 2025
f86dc2c
minor
CrescentonC Mar 31, 2025
f8c2fc5
use flatten to remove some dead code which contains free vars
CrescentonC Mar 31, 2025
b648936
remove dead code which contains f.v.s in match `rest`
CrescentonC Apr 1, 2025
5194522
return block unchanged instead of panic for unsupported cases
CrescentonC Apr 1, 2025
4b27f05
wip: no need for two repl hosts
CrescentonC Apr 2, 2025
0739bf6
better handling for noprod
CrescentonC Apr 2, 2025
8bcb753
update test
CrescentonC Apr 2, 2025
a16a2b7
minor fix
CrescentonC Apr 2, 2025
31bc090
fusion stats
CrescentonC Apr 2, 2025
0e68eec
improve jsbackenddiffmaker
CrescentonC Apr 2, 2025
2486b4d
lessen output
CrescentonC Apr 2, 2025
f75d2be
use locally; use `data class`
CrescentonC Apr 2, 2025
b92abee
cleanup jsbackenddiffmaker
CrescentonC Apr 3, 2025
3a47e3b
better code: avoid local `object`s
CrescentonC Apr 3, 2025
33f4703
tests from meeting
CrescentonC Apr 3, 2025
1553cc8
more comments
CrescentonC Apr 3, 2025
ca9a63f
wip: more needs to be done for things like `id` to block fusion
CrescentonC Apr 7, 2025
a3b4308
use of functions without defn now really blocks fusion
CrescentonC Apr 7, 2025
ce79cb5
more helpful deforestation debug output from `tl.log`
CrescentonC Apr 7, 2025
f0c022c
fresh type var for `throw` to avoid blocking fusion
CrescentonC Apr 8, 2025
0e96f9e
more recursive tests
CrescentonC Apr 8, 2025
418ee33
zip unzip tests
CrescentonC Apr 9, 2025
c91409c
use infix `::`; more tests
CrescentonC Apr 9, 2025
987e7bd
more tests
CrescentonC Apr 9, 2025
2efe8c6
fix: match block now correctly contain the scrut as a f.v. (if it is …
CrescentonC Apr 10, 2025
6ade5c3
update fix
CrescentonC Apr 10, 2025
8f7639d
fix nested definitions using `definedVar`; more tests
CrescentonC Apr 10, 2025
aafc673
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Apr 10, 2025
2519a80
style: use `locally:`
CrescentonC Apr 10, 2025
7e835e1
trailing whitespaces
CrescentonC Apr 10, 2025
dbd9173
remove useless `locally`; add reified tests
CrescentonC Apr 11, 2025
3125840
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Apr 11, 2025
15d1524
minor
CrescentonC Apr 15, 2025
56e2373
Merge branch 'hkmc2' into hkmc2-deforest
LPTK Apr 16, 2025
04bdcfb
Merge branch 'hkmc2' into hkmc2-deforest
LPTK Apr 22, 2025
5e19b31
Merge branch 'hkmc2' into hkmc2-deforest
LPTK Apr 22, 2025
51485b3
fix `Deforetation.scala`
CrescentonC Apr 22, 2025
1048529
deduplicate logic in `JSBackendDiffMaker.scala`
CrescentonC Apr 23, 2025
d7ce214
use identity for ResultId and remove global state
CrescentonC Apr 24, 2025
48467a7
remove awkward type projection
CrescentonC Apr 24, 2025
cadb80b
better documentation on the todo about ctor as function; properly blo…
CrescentonC Apr 24, 2025
79e38c7
just use `ResultId`
CrescentonC Apr 24, 2025
10c9d22
better handling of throw and instantiate
CrescentonC Apr 24, 2025
daf8749
update inappropriate uses of `???` in `Deforestation.scala`
CrescentonC Apr 25, 2025
793c57e
minor update on comment
CrescentonC Apr 25, 2025
fd306ac
minor
CrescentonC Apr 25, 2025
7edb825
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC Apr 26, 2025
e382b8e
wip: use linkedhashmap for determinism; mutate map instead of generat…
CrescentonC Apr 27, 2025
8b2b77a
fix another nondeterministic behavior; add some tests
CrescentonC Apr 27, 2025
f5da463
remove the useless ordering and the unnecessary lazy val which only s…
CrescentonC Apr 27, 2025
e92a50f
cleanup
CrescentonC Apr 27, 2025
a529545
do not silently discard extra parameter lists
CrescentonC Apr 27, 2025
f4f7f95
clarify comment
CrescentonC Apr 28, 2025
5d3c3ff
minor fix
CrescentonC Apr 28, 2025
ef9c008
Merge branch 'hkmc2' into hkmc2-deforest
LPTK Apr 30, 2025
028f5f4
improve fusion clash resolving impl
CrescentonC May 1, 2025
0546800
improve stratvar uid impl
CrescentonC May 1, 2025
0da2822
further improve clash resolving impl
CrescentonC May 1, 2025
817d408
Merge remote-tracking branch 'origin/hkmc2' into hkmc2-deforest
CrescentonC May 1, 2025
d9d9ace
update comment to include an example for dead code using never assign…
CrescentonC May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
142 changes: 80 additions & 62 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Deforestation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ 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
import Result.ResultId

type StratVar
type StratVarId = Uid[StratVar]
Expand All @@ -25,50 +26,52 @@ 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) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you remove all empty lines around these definitions, but use empty lines lower, down?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I am not sure if I fully get your question. I think I just removed this empty line (line 29) without thinking about the reason...😨 and by "but use empty lines lower, down" do you mean empty lines at line 35-36? For those I also just randomly kept them there...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure exactly what I meant by "lower, down" either, but your use of whitespace looks weird and inconsistent, mainly around this new StratVarUidHandler definition, which comes right in between the StratVarState class and its companion and is somehow not separated by whitespace below it. By the way, why not renamr StratVarUidHandler to StratVar and move it next to the homonym type definition?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I see. I did the renaming, and I also realize that all the handler objects seem to be defined in Uid.scala, so I move the definition there.

val newId = vuid.nextUid
val s = StratVarState(newId, nme)
val p = s.asProdStrat
val c = s.asConsStrat
p -> c


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) =
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 =>
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 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



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)(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
Expand Down Expand Up @@ -177,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) =>
Expand Down Expand Up @@ -235,8 +239,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)
Expand Down Expand Up @@ -267,10 +270,13 @@ 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


val resultIdToResult = mutable.Map.empty[ResultId, Result]

def apply(p: Program): Opt[Program] -> String -> Int =
val mainBlk = p.main

Expand Down Expand Up @@ -310,7 +316,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 ...`"

Expand Down Expand Up @@ -360,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
Expand All @@ -383,7 +396,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)))
val armsRes = if arms.forall{ case (cse, _) => cse.isInstanceOf[Case.Cls] } then
arms.map:
case (Case.Cls(s, _), body) =>
Expand Down Expand Up @@ -415,19 +428,19 @@ 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
// 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],
Expand All @@ -444,11 +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
Expand Down Expand Up @@ -485,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 =>
Expand Down Expand Up @@ -520,26 +533,26 @@ 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))
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):
Expand Down Expand Up @@ -597,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)
Expand All @@ -623,10 +636,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
Expand Down Expand Up @@ -666,7 +679,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

Expand All @@ -688,9 +701,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:
Expand All @@ -717,8 +730,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]]
Expand Down Expand Up @@ -773,7 +786,7 @@ class Deforest(using TL, Raise, Elaborator.State):
throw Error("more than one consumer")
None
)
else ???
else die
}
res.updateWith(ctor){_ => filteredDtor}
}
Expand All @@ -789,7 +802,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
Expand Down Expand Up @@ -1033,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
Expand All @@ -1066,16 +1079,21 @@ 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: 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
Expand Down
Loading
Loading