Skip to content

Deforestation mk4#406

Open
ychenfo wants to merge 91 commits intohkust-taco:hkmc2from
ychenfo:hkmc2-deforest-new-new
Open

Deforestation mk4#406
ychenfo wants to merge 91 commits intohkust-taco:hkmc2from
ychenfo:hkmc2-deforest-new-new

Conversation

@ychenfo
Copy link
Member

@ychenfo ychenfo commented Mar 8, 2026

Supersedes and closes #335

@LPTK LPTK force-pushed the hkmc2 branch 2 times, most recently from 46c5ce1 to 414cf0c Compare March 10, 2026 15:42
@ychenfo ychenfo force-pushed the hkmc2-deforest-new-new branch from 21b6e91 to a4b9fbf Compare March 12, 2026 20:30
@LPTK LPTK requested a review from Copilot March 13, 2026 10:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 :deforest command
  • 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.

Copy link
Contributor

@LPTK LPTK left a comment

Choose a reason for hiding this comment

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

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!

Comment on lines +358 to +373
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Strangely convoluted way of writing

Suggested change
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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
locally {
locally:

val fusingCtorInfo = MutMap.empty[CtorDtorId, ConcreteProducer]
val fusingDtorInfo = MutMap.empty[CtorDtorId, ConcreteConsumer]
// propagate
locally {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
locally {
locally {

end DeforestConstraintsCollector


class DeforestConstrainSolver(val collector: DeforestConstraintsCollector):
Copy link
Contributor

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be

Suggested change
case _ => die
case (Arg(spd, a) -> s, rest) => TODO(spd)

Copy link
Contributor

Choose a reason for hiding this comment

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

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)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this change is needed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants