Skip to content

[Relax] Add Dead Parameter Elimination#18720

Closed
shreyasravi320 wants to merge 4 commits intoapache:mainfrom
shreyasravi320:relax-transform-dead-param-elim
Closed

[Relax] Add Dead Parameter Elimination#18720
shreyasravi320 wants to merge 4 commits intoapache:mainfrom
shreyasravi320:relax-transform-dead-param-elim

Conversation

@shreyasravi320
Copy link

@shreyasravi320 shreyasravi320 commented Feb 7, 2026

[Relax] Add Dead Parameter Elimination

This PR adds dead parameter elimination to src/relax/transform/dead_code_elimination.cc to remove unused parameters from Relax functions.

Motivation

Functions often accumulate unused parameters due to:

  • Deprecated model architectures (e.g., unused weight tensors)
  • API evolution and backwards compatibility
  • Refactoring artifacts

Eliminating these parameters reduces memory usage, speeds up compilation, and simplifies the IR.

Changes

  • Algorithm:
    1. Collect unused parameters across all functions in the module
    2. Update signatures and call sites in topological order
    3. Repeat while changes persist
  • Tests in tests/python/relax/test_transform_dead_param_elimination.py

Example Usage

Before:

@R.function
def forward(x: R.Tensor, unused_weights: R.Tensor):
    return x + 1

After:

@R.function
def forward(x: R.Tensor):
    return x + 1

TODOs

  • CI/CD checks
  • Test mutual recursion more thoroughly
  • Fully benchmark compilation time and instruction count
  • Documentation

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @shreyasravi320, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements a significant optimization pass for the Relax IR: Dead Parameter Elimination. This pass systematically identifies and removes function parameters that are not utilized within their respective function bodies. By doing so, it streamlines the IR, leading to more efficient code generation and reduced resource consumption. The implementation involves an iterative process of analyzing parameter usage and subsequently rewriting function signatures and all associated call sites.

Highlights

  • New Optimization Pass: Introduced Dead Parameter Elimination (DPE) to the Relax IR, which automatically identifies and removes unused parameters from Relax functions.
  • Motivation and Benefits: The DPE pass aims to reduce memory usage, speed up compilation, and simplify the Intermediate Representation (IR) by cleaning up functions that have accumulated unused parameters due to deprecated architectures, API evolution, or refactoring.
  • Algorithm Implementation: The DPE algorithm works by iteratively collecting unused parameters across all functions, then updating function signatures and their corresponding call sites in topological order until no further changes are detected.
  • Test Coverage: New Python tests have been added to validate the DPE pass, specifically covering complex scenarios such as mutual recursion and deep recursion chains to ensure robust parameter removal.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/ir/module.cc
    • Updated IRModuleNode::AddUnchecked to ensure GlobalVarNode's struct_info_ is correctly updated when a function is added, aligning with the function's struct_info_.
  • src/relax/transform/dead_code_elimination.cc
    • Added a new RemoveUnusedParameters function to perform the core dead parameter elimination logic.
    • Integrated RemoveUnusedParameters into the main DeadCodeElimination pass as a new stage (S3), followed by re-running unused function and variable removal stages.
    • Implemented CollectUsedParamIndices to determine which parameters are actively used within each function.
    • Introduced CallSiteUpdater as an ExprMutator to modify call expressions by removing arguments corresponding to eliminated parameters.
    • Updated comments to reflect the new dead parameter elimination capability within the dead_code_elimination.cc file.
  • tests/python/relax/test_transform_dead_code_elimination.py
    • Modified existing test cases to include comments indicating parameters that are expected to be removed by the DPE pass.
    • Added test_mutual_recursion_unused_params to verify DPE functionality in mutually recursive functions.
    • Added test_deep_recursion_chain to test DPE across a chain of recursive calls.
Activity
  • The author, shreyasravi320, has introduced a new Dead Parameter Elimination pass for Relax functions.
  • The pull request includes a detailed description outlining the motivation, algorithm, and example usage of the new pass.
  • New test cases have been added to test_transform_dead_code_elimination.py to validate the DPE pass, including scenarios with mutual and deep recursion.
  • The author has listed several TODO items, indicating plans for further enhancements such as handling mutual recursion (though tests for it are present), benchmarking, and documentation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 introduces a dead parameter elimination pass for Relax, which is a valuable optimization for simplifying IR and improving performance. The implementation is mostly solid, but there are a few areas for improvement regarding correctness, performance, and code style. I've identified a potential bug in how call site struct info is handled, some opportunities for refactoring and performance optimization, and a couple of issues in the new tests. Overall, this is a great addition, and with a few adjustments, it will be even better.

Comment on lines +156 to +158
if (call->struct_info_.defined()) {
new_call->struct_info_ = call->struct_info_;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Manually copying the struct_info_ from the old call to the new call is incorrect. When arguments are removed from a call, the struct_info of the call (which describes the output) may change, especially if the return type depends on the removed arguments. The ExprMutator framework will handle re-inferring the struct_info for the new call node during normalization. Removing this manual assignment will allow the correct struct_info to be inferred.

Comment on lines +109 to +118
PostOrderVisit(func->body, [&](const ObjectRef& obj) {
if (auto v = obj.as<VarNode>()) {
Var var = ffi::GetRef<Var>(v);
for (size_t i = 0; i < func->params.size(); ++i) {
if (var.same_as(func->params[i])) {
used[i] = true;
}
}
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This approach to finding used parameters involves a nested loop within PostOrderVisit, which can be inefficient for functions with large bodies. For each variable encountered, it iterates through all function parameters. This can be optimized by building a map from parameter Var to its index before the visit, and then performing a direct lookup for each variable inside the visitor. This would change the complexity from O(num_vars * num_params) to O(num_vars + num_params).

      std::unordered_map<Var, int, ObjectPtrHash, ObjectPtrEqual> param_to_idx;
      for (size_t i = 0; i < func->params.size(); ++i) {
        param_to_idx[func->params[i]] = i;
      }

      PostOrderVisit(func->body, [&](const ObjectRef& obj) {
        if (auto v = obj.as<VarNode>()) {
          Var var = ffi::GetRef<Var>(v);
          auto it = param_to_idx.find(var);
          if (it != param_to_idx.end()) {
            used[it->second] = true;
          }
        }
      });

Comment on lines +187 to +208
std::vector<GlobalVar> worklist;
std::unordered_set<GlobalVar> visited;
std::function<void(GlobalVar)> dfs = [&](GlobalVar gvar) {
if (visited.count(gvar)) return;
visited.insert(gvar);

if (auto opt_func = mod->Lookup(gvar).as<Function>()) {
auto func = opt_func.value();
PostOrderVisit(func->body, [&](const ObjectRef& obj) {
if (auto call = obj.as<CallNode>()) {
if (auto callee_gvar = call->op.as<GlobalVar>()) {
dfs(callee_gvar.value());
}
}
});
}
worklist.push_back(gvar);
};

for (const auto& [gvar, _] : mod->functions) {
dfs(gvar);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The topological sort of the call graph is performed inside the do-while loop. However, the call graph structure does not change during dead parameter elimination. You can move the topological sort outside the loop to avoid re-computing it on every iteration, which would improve performance.

Comment on lines +287 to +301
// S5: remove unused variables again as some arguments may be removed in S3
{
IRModule updates;
for (const auto& [gvar, base_func] : mod->functions) {
if (auto opt = base_func.as<Function>()) {
auto new_func = Downcast<Function>(RemoveAllUnused(opt.value()));
if (!new_func.same_as(base_func)) {
updates->Add(gvar, new_func);
}
}
}
if (updates->functions.size()) {
mod.CopyOnWrite()->Update(updates);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This block of code for removing unused variables (S5) is identical to the block for S2 (lines 266-279). To improve maintainability and avoid code duplication, you could extract this logic into a helper function, for example RemoveUnusedVariables(IRModule mod), and call it in both places.

shreyasravi320 and others added 3 commits February 6, 2026 17:59
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@tqchen
Copy link
Member

tqchen commented Feb 7, 2026

we do not want parameter change in general since this is the signature

@tqchen tqchen closed this Feb 7, 2026
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.

2 participants