Skip to content

fix(ir,codegen): reconcile freshened Vars and emit scalar param bindings#1329

Merged
lyfne123 merged 3 commits intohw-native-sys:mainfrom
luohuan19:l3-adapt
May 11, 2026
Merged

fix(ir,codegen): reconcile freshened Vars and emit scalar param bindings#1329
lyfne123 merged 3 commits intohw-native-sys:mainfrom
luohuan19:l3-adapt

Conversation

@luohuan19
Copy link
Copy Markdown
Contributor

@luohuan19 luohuan19 commented May 10, 2026

Summary

  • fix(ir): Reconcile freshened Vars after scope outline substitution. IRMutator::VisitExpr_(VarPtr) mints fresh Vars when a Var's type embeds a remapped shape Var. For tensor inputs whose shape references another input scalar, the outlined body ended up referencing a Var not present in input_params, causing codegen param-binding failures. Replaced the opaque Substitute() call with a TrackingSubstituteMutator that exposes post-substitution var_remap_ state, then reconciled input_params / outlined_output_vars / return_types with any freshened Var instances.
  • feat(codegen): Emit scalar param local bindings in distributed codegen. Orchestrator parameters live in the tensors dict, but scalar params (e.g. pl.Scalar[pl.BOOL]) may appear in bare-name contexts such as if-conditions. Emit name = tensors["name"] at the top of each function body so the bare name resolves correctly. Also refactored duplicated call-collection logic in pto_backend.py into a reusable _CallCollector IRVisitor, and extracted the C++ param registration loop into RegisterParamsAndEmitScalarBindings().

Testing

  • All tests pass
  • Code review completed

luohuan19 added 2 commits May 10, 2026 17:20
Orchestrator parameters live in the tensors dict, but scalar params
(e.g. pl.Scalar[pl.BOOL]) may appear in bare-name contexts such as
if-conditions.  Emit `name = tensors["name"]` at the top of each
function body so the bare name resolves correctly.

Also refactor duplicated call-collection logic in pto_backend.py into
a reusable _CallCollector IRVisitor, and extract the C++ param
registration loop into RegisterParamsAndEmitScalarBindings().
IRMutator::VisitExpr_(VarPtr) mints fresh Vars when a Var's type embeds
a remapped shape Var. For tensor inputs whose shape references another
input scalar, the outlined body ends up referencing a Var not present in
input_params, causing codegen param-binding failures.

Replace the opaque Substitute() call with a TrackingSubstituteMutator
that exposes post-substitution var_remap_ state, then reconcile
input_params / outlined_output_vars / return_types with any freshened
Var instances.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds scalar parameter binding to distributed code generation, refactors call discovery using a visitor pattern in the Python backend, and improves variable tracking during scope outline substitution to handle freshened variables created during type transformation.

Changes

Scalar Parameter Binding and Visitor Refactoring

Layer / File(s) Summary
Interface Declaration
include/pypto/codegen/distributed/distributed_codegen.h
Private helper method RegisterParamsAndEmitScalarBindings(const ir::FunctionPtr& func) declared in DistributedCodegen class.
Scalar Parameter Binding
src/codegen/distributed/distributed_codegen.cpp
New include pypto/ir/kind_traits.h added for type checking. Helper registers parameter names in declared_vars_ and emits name = tensors["name"] for scalar-typed parameters. Called from EmitFunction and EmitEntryFunction replacing prior manual registration.
Call Collection Visitor
python/pypto/backend/pto_backend.py
_CallCollector visitor introduced to collect GlobalVar callee names from IR function bodies. Replaces manual flatten_to_stmts iteration and statement-type pattern matching in both _extract_group_member_names and _collect_chip_task_functions.
Variable Remapping Tracking
include/pypto/ir/transforms/utils/scope_outline_utils.h
TrackingSubstituteMutator replaces generic Substitute call in scope outlining to record variable remappings. Reconciles input_params, non-store outlined_output_vars, and return_types to use freshened Vars/types produced during substitution, excluding store-target outputs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Hzfengsy
  • lyfne123

Poem

A rabbit hops through parameters fine,
Binding scalars in each binding line,
Call collectors gather names with care,
Tracking vars through substitution's dance fair! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: fixing reconciliation of freshened Vars in IR scope outline and adding scalar param bindings in distributed codegen.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, detailing fixes for variable reconciliation in scope outline substitution and scalar parameter bindings in distributed codegen.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances IR transformations and code generation by implementing a TrackingSubstituteMutator to manage freshened variables during scope outlining and ensuring scalar parameters are correctly bound in the distributed codegen. It also refactors call collection in the Python backend using a visitor pattern. Feedback was provided to improve the variable reconciliation logic in scope_outline_utils.h, specifically to handle store targets whose types might be affected by shape remapping, ensuring consistency between function signatures and their bodies.

Comment thread include/pypto/ir/transforms/utils/scope_outline_utils.h
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
include/pypto/ir/transforms/utils/scope_outline_utils.h (2)

645-655: 💤 Low value

Consider promoting TrackingSubstituteMutator out of the method body.

Defining a class with a base-class inheritance inside a 400-line member function works, but it's unusual and:

  • bloats OutlineScope further at a different level of abstraction,
  • prevents any unit test from exercising the substitution-tracking behavior in isolation,
  • silently relies on IRMutator::var_remap_ being non-private, which is an internal coupling worth making explicit.

A cleaner shape would be either (a) a private nested class of ScopeOutliner (or a peer in outline_utils), or (b) a protected IRMutator::GetVarRemap() accessor in the base so this whole subclass disappears. Option (b) is the most localized change and keeps the encapsulation story for IRMutator honest.

♻️ Sketch for option (b)
 // in include/pypto/ir/transforms/base/mutator.h (IRMutator)
+protected:
+  const std::unordered_map<const Expr*, ExprPtr>& GetVarRemap() const { return var_remap_; }
+  void SeedVarRemap(const std::unordered_map<const Var*, VarPtr>& seed) {
+    for (const auto& [k, v] : seed) var_remap_[k] = v;
+  }
-    class TrackingSubstituteMutator : public IRMutator {
-     public:
-      explicit TrackingSubstituteMutator(const std::unordered_map<const Var*, VarPtr>& var_map) {
-        for (const auto& [k, v] : var_map) {
-          var_remap_[k] = v;
-        }
-      }
-      const std::unordered_map<const Expr*, ExprPtr>& GetVarRemap() const { return var_remap_; }
-    };
-    TrackingSubstituteMutator subst_mutator(var_substitution_map);
+    IRMutator subst_mutator;
+    subst_mutator.SeedVarRemap(var_substitution_map);
     auto transformed_body = subst_mutator.VisitStmt(pre_sub_body);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/pypto/ir/transforms/utils/scope_outline_utils.h` around lines 645 -
655, The local subclass TrackingSubstituteMutator is defined inside a large
member function and directly reads IRMutator::var_remap_, so either (preferred)
add a protected accessor IRMutator::GetVarRemap() to expose the remap for
subclasses and then rewrite TrackingSubstituteMutator to call that accessor
(removing direct access to var_remap_), or move TrackingSubstituteMutator out of
the method body into a private nested class of the ScopeOutliner (or a peer
class in outline_utils) so it can be unit-tested; update the constructor and
GetVarRemap() use sites (TrackingSubstituteMutator and the code that constructs
subst_mutator / calls VisitStmt) to use the new location or accessor and ensure
no code accesses IRMutator::var_remap_ directly.

645-680: 🏗️ Heavy lift

The memoization behavior is correctly implemented, but consider two optional improvements:

The chain collapsing mechanism (old → seed → freshened → old → freshened) works as intended: ResolveVarRemapHit (mutator.cpp:222) explicitly memoizes the resolved value back into var_remap_ keyed by the original Expr pointer, so subsequent lookups in the reconciliation loops will find the final freshened Var.

Optional refactor 1: Hoist TrackingSubstituteMutator to a private nested class of ScopeOutliner (rather than a local class inside the method). This improves readability and allows reuse across methods or future tests.

Optional refactor 2: Consider adding a protected IRMutator::GetVarRemap() accessor to the base class instead of relying on subclass access to the var_remap_ member. This would reduce coupling to internal member names and provide a cleaner API for future transformations that need read-only access to the remapping after substitution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/pypto/ir/transforms/utils/scope_outline_utils.h` around lines 645 -
680, Move the local TrackingSubstituteMutator class out of the method and make
it a private nested class of ScopeOutliner (declare it in the same header
alongside ScopeOutliner, keep its constructor signature taking const
std::unordered_map<const Var*, VarPtr>& var_map and its GetVarRemap() method)
and update the method to instantiate ScopeOutliner::TrackingSubstituteMutator;
additionally add a protected accessor on IRMutator, e.g. protected: const
std::unordered_map<const Expr*, ExprPtr>& GetVarRemap() const, so subclasses
(and callers) can read the remap without relying on the concrete member name
var_remap_, then adjust TrackingSubstituteMutator to use the base-class accessor
and ensure existing call sites (subst_mutator.GetVarRemap()) continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@include/pypto/ir/transforms/utils/scope_outline_utils.h`:
- Around line 645-655: The local subclass TrackingSubstituteMutator is defined
inside a large member function and directly reads IRMutator::var_remap_, so
either (preferred) add a protected accessor IRMutator::GetVarRemap() to expose
the remap for subclasses and then rewrite TrackingSubstituteMutator to call that
accessor (removing direct access to var_remap_), or move
TrackingSubstituteMutator out of the method body into a private nested class of
the ScopeOutliner (or a peer class in outline_utils) so it can be unit-tested;
update the constructor and GetVarRemap() use sites (TrackingSubstituteMutator
and the code that constructs subst_mutator / calls VisitStmt) to use the new
location or accessor and ensure no code accesses IRMutator::var_remap_ directly.
- Around line 645-680: Move the local TrackingSubstituteMutator class out of the
method and make it a private nested class of ScopeOutliner (declare it in the
same header alongside ScopeOutliner, keep its constructor signature taking const
std::unordered_map<const Var*, VarPtr>& var_map and its GetVarRemap() method)
and update the method to instantiate ScopeOutliner::TrackingSubstituteMutator;
additionally add a protected accessor on IRMutator, e.g. protected: const
std::unordered_map<const Expr*, ExprPtr>& GetVarRemap() const, so subclasses
(and callers) can read the remap without relying on the concrete member name
var_remap_, then adjust TrackingSubstituteMutator to use the base-class accessor
and ensure existing call sites (subst_mutator.GetVarRemap()) continue to work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 30c66793-d450-4817-afb9-0a16372f1041

📥 Commits

Reviewing files that changed from the base of the PR and between 33130a4 and ebb6764.

📒 Files selected for processing (4)
  • include/pypto/codegen/distributed/distributed_codegen.h
  • include/pypto/ir/transforms/utils/scope_outline_utils.h
  • python/pypto/backend/pto_backend.py
  • src/codegen/distributed/distributed_codegen.cpp

Store-target outlined Vars are not seeded into var_substitution_map, but
their types may still embed remapped shape Vars that trigger freshening
during body substitution.  Previously these were unconditionally skipped,
leaving a potential mismatch between the return type and the body Var.

Now check the outlined Var key in post_remap (not the original key, which
may alias an input param for InOut parameters) and update
outlined_output_vars / return_types when freshening occurred.
@lyfne123 lyfne123 merged commit 748cece into hw-native-sys:main May 11, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants