Skip to content

Add linearity and accumulator tracking in flow analysis#485

Open
ychenfo wants to merge 11 commits intohkust-taco:hkmc2from
ychenfo:linearity-accumulator
Open

Add linearity and accumulator tracking in flow analysis#485
ychenfo wants to merge 11 commits intohkust-taco:hkmc2from
ychenfo:linearity-accumulator

Conversation

@ychenfo
Copy link
Copy Markdown
Member

@ychenfo ychenfo commented May 6, 2026

  • Add affinity and accumulator tracking in flow analysis
  • Refactor flow analysis strategy types
  • Update flow analysis related config
  • Pretty print deforest result

Copy link
Copy Markdown
Contributor

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 extends the compiler’s flow analysis to track (1) non-affinity/linearity and (2) accumulator-ness, and wires those signals into deforestation and dead-parameter-elimination behavior and logging. It also refactors the flow-analysis strategy model (introducing ConcreteId, strategy origins, and marker strategies) and updates test expectations to match the new debug/pretty-printed output.

Changes:

  • Introduce FlowAnalysisConfig and refactor Deforest / DeadParamElim configs to share flow-analysis settings (including new tracking/logging toggles).
  • Extend FlowAnalysis with capture tracking, non-affine tracking, and accumulator tracking; propagate markers through constraints/solver; add scoped trace logging for symbol dumps.
  • Update deforest + DPE tests (and add a new fusibility test suite) to match the new fusing/non-affine/accumulator reporting format.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala Adds flag parsing/validation and maps flags into the new flow-analysis config shape for :deforest and :deadParamElim.
hkmc2/shared/src/main/scala/hkmc2/Config.scala Introduces FlowAnalysisConfig; refactors Deforest/DeadParamElim to wrap it; updates config directive parsing.
hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala Switches deforest trace logger creation to FlowAnalysis.mkTraceLogger.
hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala Major refactor: new strategy/origin model, capture tracking, branch-aware affinity counting, accumulator/non-affine propagation, and scoped trace output.
hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala Wires in new flow-analysis inputs (effective tracking toggles) and changes fusion debug output formatting/printing.
hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala Updates IDs from CtorDtorId to ConcreteId and adjusts lookups accordingly.
hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala Reworks concrete IDs, updates flow-analysis invocation to include tracking toggles, and adapts solver logic to new marker strategies.
hkmc2/shared/src/test/mlscript/codegen/ConfigDirective.mls Extends config-directive tests for new deforest logging behavior (logNonAffine).
hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls Adds coverage for :deadParamElim flag parsing/behavior (default, debug, conflicts, off, unknown).
hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls Removes a stray blank line in test input.
hkmc2/shared/src/test/mlscript/deforest/fusibility.mls New deforest tests covering non-affinity and accumulator logging scenarios.
hkmc2/shared/src/test/mlscript/deforest/floatoutSafety.mls Removes older float-out safety test file (replaced by newer fusibility coverage).
hkmc2/shared/src/test/mlscript/deforest/basic.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/append.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/clashes.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/cyclic.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/determinism.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/imperative.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/module.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls Updates expected deforest debug output formatting and some rendered values.
hkmc2/shared/src/test/mlscript/deforest/recursive.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls Updates expected deforest debug output formatting.
hkmc2/shared/src/test/mlscript/deforest/todos.mls Updates expected deforest debug output formatting and rendered values.
hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls Updates expected deforest debug output formatting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala Outdated
Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala
val scope = Scope.empty(Scope.Cfg.default)
given Scope = scope
given ShowCfg = ShowCfg.internal
given SymbolPrinter = new SymbolPrinter(scope)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You shouldn't create your own printer; you should use the one provided here

given dbgPrinter.type = dbgPrinter

ie, just make pp accept it implicitly.

This way, the symbols will be printed consistently with things like :sir and :lot.

Copy link
Copy Markdown
Member Author

@ychenfo ychenfo May 7, 2026

Choose a reason for hiding this comment

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

Updated this to thread the SymbolPrinter (needed by the code printer) through lowering / flow analysis into DeforestFusionSolver, so deforest debug printing no longer creates its own SymbolPrinter and in diff tests we now should use the same dbgPrinter: SymbolPrinter provided by MlsDiffMaker.

For MLsCompiler.scala, I moved the SymbolPrinter and trace logger setup into compileModule because constructing the SymbolPrinter needs a Scope, and Scope.empty requires an Elaborator.State.

Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala Outdated
Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala Outdated
Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala Outdated
cc.constrain(NoProd, preAnalyzer.res.primitiveStratVar.asConsStrat)
processBlock(preAnalyzer.pgrm.main)(using cc, NoCons)

// emit NonAffine for non-affine syms that don't belong to any scc:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you explain the reasoning behind "don't belong to any scc" => "emit NonAffine"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, this was a stale wip note to myself and the wording may be misleading, it's removed now. By this comment I only meant that the correct NonAffine constraints should be added to the correct collector, and the logic is not "doesn’t belong to an scc, therefore emit NonAffine". The symbol is already classified as non-affine by pre-analysis, and this code here is only about placing the non-affine constraints: in mono mode there are no per-scc collectors, so the global collector gets the relevant constraints; in poly mode, scc-owned symbols are handled in their scc strat scheme, and the global collector handles the remaining symbols.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation. Could you document that explanation in the code, at an appropriate and if provide visible place?

Comment thread hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala Outdated
Comment thread hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala Outdated
Comment thread hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala Outdated
//│ let t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15;
//│ return x(t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15, t, tmp11, tmp12, tmp13, tmp14, tmp15)
//│ };
//│ define test_20$test⁰ as fun test_20$test¹(x) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you know why some of these definitions are no longer here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The gone definitions are polymorphic instantiations of functions that used to contain deforestation rewritings. In this example since p is not used affinely, some deforestation matches are gone and then we no longer create that much polymorphic instantiations.

given tl: TraceLogger = constraintSolver.tl
given Raise = preAnalyzer.raise

private given Scope = Scope.empty(Scope.Cfg.default)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do you need a scope?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because the printer below needs a Scope: the codegen.Printer.print method takes a using Scope parameter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, but this scope is used to show the top-level definitions. I think it's important that it correspond to the MLsDiffMaker.scala, so you should thread it through.

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