Conversation
46c5ce1 to
414cf0c
Compare
# Conflicts: # hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala
…into hkmc2-deforest-new-new
21b6e91 to
a4b9fbf
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a deforestation optimization pass to the hkmc2 compiler. Deforestation is an optimization that eliminates intermediate data structures by fusing producers (constructors) with consumers (pattern matches/selections). The PR supersedes PR #335.
Changes:
- Adds a complete deforestation pipeline: pre-analysis, constraint collection, constraint solving, and rewriting phases
- Integrates the deforestation pass into the lowering pipeline, controlled by a
:deforestcommand - Fixes determinism in the SCC partitioning algorithm and adds supporting infrastructure changes
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
hkmc2/shared/src/main/scala/hkmc2/Config.scala |
Adds Deforest config case class and deforest field to Config |
hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala |
Adds :deforest command for test files |
hkmc2/shared/src/main/scala/hkmc2/Uid.scala |
Adds Result and StratVar UID handlers for deforestation |
hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala |
Sets tsym on BlockMemberSymbol in FunDefn.withFreshSymbol |
hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala |
Refactors to use FunDefn.withFreshSymbol |
hkmc2/shared/src/main/scala/utils/TraceLogger.scala |
Widens emitDbg visibility to protected[hkmc2] |
core/shared/main/scala/utils/algorithms.scala |
Fixes nondeterminism in SCC by using LinkedHashSet |
hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala |
Integrates deforestation pass into lowering pipeline |
hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala |
Core types, extractors, and entry point for deforestation |
hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Analyze.scala |
Pre-analysis, constraint collection, and constraint solving |
hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala |
Rewriting pass that applies deforestation transformations |
hkmc2/shared/src/test/mlscript/deforest/*.mls |
Comprehensive test suite for deforestation (12 test files) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
LPTK
left a comment
There was a problem hiding this comment.
Ok, I don't have time to review everything in detail at this time, but I believe this is more or less ready to merge!
| if locally: | ||
| ctorSym.isDefined | ||
| || paramsOpt.isDefined | ||
| || auxParams.nonEmpty | ||
| || parentPath.isDefined | ||
| || methods.nonEmpty | ||
| || privateFields.nonEmpty | ||
| || publicFields.nonEmpty | ||
| || !preCtor.matches: | ||
| case End("") => true | ||
| || !ctor.matches: | ||
| case Return(Select(Value.Ref(runtimeSym, None), Tree.Ident("Unit")), true) => | ||
| runtimeSym is elabState.runtimeSymbol | ||
| then () | ||
| else | ||
| mod.foreach(applyClsLikeBody) |
There was a problem hiding this comment.
Strangely convoluted way of writing
| if locally: | |
| ctorSym.isDefined | |
| || paramsOpt.isDefined | |
| || auxParams.nonEmpty | |
| || parentPath.isDefined | |
| || methods.nonEmpty | |
| || privateFields.nonEmpty | |
| || publicFields.nonEmpty | |
| || !preCtor.matches: | |
| case End("") => true | |
| || !ctor.matches: | |
| case Return(Select(Value.Ref(runtimeSym, None), Tree.Ident("Unit")), true) => | |
| runtimeSym is elabState.runtimeSymbol | |
| then () | |
| else | |
| mod.foreach(applyClsLikeBody) | |
| if ctorSym.isEmpty | |
| && paramsOpt.isEmpty | |
| && auxParams.nonDefined | |
| && parentPath.isEmpty | |
| && methods.isEmpty | |
| && privateFields.isEmpty | |
| && publicFields.isEmpty | |
| && preCtor.matches: | |
| case End("") => true | |
| && ctor.matches: | |
| case Return(Select(Value.Ref(runtimeSym, None), Tree.Ident("Unit")), true) => | |
| runtimeSym is elabState.runtimeSymbol | |
| then mod.foreach(applyClsLikeBody) |
|
|
||
| // =================================================== | ||
|
|
||
| locally { |
There was a problem hiding this comment.
| locally { | |
| locally: |
| val fusingCtorInfo = MutMap.empty[CtorDtorId, ConcreteProducer] | ||
| val fusingDtorInfo = MutMap.empty[CtorDtorId, ConcreteConsumer] | ||
| // propagate | ||
| locally { |
There was a problem hiding this comment.
| locally { | |
| locally { |
| end DeforestConstraintsCollector | ||
|
|
||
|
|
||
| class DeforestConstrainSolver(val collector: DeforestConstraintsCollector): |
There was a problem hiding this comment.
Doesn't this belong in its own file?
| case (Arg(N, a) -> s, rest) => | ||
| applyPath(a): fusedField => | ||
| Scoped(Set.single(s), Assign(s, fusedField, rest)) | ||
| case _ => die |
There was a problem hiding this comment.
I think this should be
| case _ => die | |
| case (Arg(spd, a) -> s, rest) => TODO(spd) |
There was a problem hiding this comment.
Please only use die and lastWords for things that can't happen, not for things that are left to implement.
| inline def exit() = indent -= 1 | ||
|
|
||
| protected def emitDbg(str: Str): Unit = scala.Predef.println(str) | ||
| protected[hkmc2] def emitDbg(str: Str): Unit = scala.Predef.println(str) |
There was a problem hiding this comment.
I don't think this change is needed?
Supersedes and closes #335