Skip to content

Merged main - #4501

Open
micwill755 wants to merge 5 commits into
mainfrom
4466
Open

Merged main#4501
micwill755 wants to merge 5 commits into
mainfrom
4466

Conversation

@micwill755

Copy link
Copy Markdown
Collaborator

Description

Dynamo constant folding can replace input-independent factories (e.g. torch.zeros) with persistent _frozen_param* attributes. When those attributes are also graph outputs and eager code after a graph break mutates them in-place, later invocations observe leftover state instead of fresh tensors.

This change clones folded _frozen_param* values at the graph-output boundary so downstream in-place mutation cannot modify compiler-owned state, preserving eager semantics across calls.

Fixes #4466

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • My code follows the style guidelines of this project (You can use the linters)
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas and hacks
  • I have made corresponding changes to the documentation
  • I have added tests to verify my fix or my feature
  • New and existing unit tests pass locally with my changes
  • I have added the relevant labels to my PR so that relevant reviewers are notified

@meta-cla meta-cla Bot added the cla signed label Aug 17, 2026
@github-actions github-actions Bot added component: tests Issues re: Tests component: lowering Issues re: The lowering / preprocessing passes component: core Issues re: The core compiler component: api [Python] Issues re: Python API component: dynamo Issues relating to the `torch.compile` or `torch._dynamo.export` paths labels Aug 17, 2026
@lanluo-nvidia
lanluo-nvidia self-requested a review August 17, 2026 20:00

@lanluo-nvidia lanluo-nvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What codex review says:

  • P1 — Preserve aliases for repeated folded outputs. _clone_folded_constants_at_outputs creates a new clone for every output occurrence. If a graph returns the same folded tensor twice (return x, x), the PR changes eager semantics: the two
    results no longer share storage. Cache one clone per get_attr node and reuse it for repeated outputs; add a regression test that mutates one returned value and observes the other.

@narendasan

Copy link
Copy Markdown
Collaborator

@micwill755 can you explain a bit more about this?

Dynamo constant folding can replace input-independent factories (e.g. torch.zeros) with persistent _frozen_param* attributes. When those attributes are also graph outputs and eager code after a graph break mutates them in-place, later invocations observe leftover state instead of fresh tensors.

@micwill755

Copy link
Copy Markdown
Collaborator Author

@micwill755 can you explain a bit more about this?

Dynamo constant folding can replace input-independent factories (e.g. torch.zeros) with persistent _frozen_param* attributes. When those attributes are also graph outputs and eager code after a graph break mutates them in-place, later invocations observe leftover state instead of fresh tensors.

Constant folding turns torch functions like torch.zeros(...) into a persistent module attribute i.e. _frozen_param0 which is reused across calls. If that attribute is also returned from the graph, eager code after a graph break can mutate it in-place, e.g. x += 1, which edits the stored constant. This means the next call then sees that leftover state instead of a fresh tensor of zeros, unlike normal eager PyTorch, which allocates new storage every time.

@narendasan

Copy link
Copy Markdown
Collaborator

What is the case where a constant is returned as output?

@micwill755

Copy link
Copy Markdown
Collaborator Author

What is the case where a constant is returned as output?

If an input-independent factory op e.g. torch.zeros is folded into a frozen_param and that value is also a graph return, then a constant is returned as an output. A graph break is a common reason this happens because the the factory lives in the compiled subgraph, and eager code after the break can mutate that returned constant in-place, impacting later calls.

@cehongwang

Copy link
Copy Markdown
Collaborator

The motivation makes sense. Instead of doing the clone, can we disable the constant folding for the frozen parameter that goes to the output? Also, in AoT compilation, when we are doing constant folding, the graph has not been split yet (the partition happens after the constant folding). After the partition, there could be new subgraphs that add output to the intermediate subgraphs. So we may need to adjust the frozen_parameter after the partitioning.

@lanluo-nvidia lanluo-nvidia modified the milestones: v2.14.0, v2.15.0 Aug 18, 2026
Clone compiler-owned _frozen_param outputs late so in-place mutation cannot poison later calls, while leaving user-owned placeholders aliased.
Comments describe folded constructors in plain language instead of Sphinx markup and implementation attribute names.
output_node = next(node for node in gm.graph.nodes if node.op == "output")
clone_cache: Dict[torch.fx.Node, torch.fx.Node] = {}

def clone_folded_output(node: torch.fx.Node) -> torch.fx.Node:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@micwill755 I thought we talked about the distinction between module state and function state

class Mod(nn.Module):
    def __init__(self):
        self.weight = torch.zeros(...)

    def forward(self, x):
         
class Mod(nn.Module):
    def __init__(self):


    def forward(self, x):
        self.weight = torch.zeros(...)

these two modules would have different semantics wrt mutation between calls right? Is this handled if we just pattern match on get_attr / frozen?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also we talked about if we should clone at construction or clone on return right?

…o module state stays persistent while function-local factories reset each call.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla signed component: api [Python] Issues re: Python API component: core Issues re: The core compiler component: dynamo Issues relating to the `torch.compile` or `torch._dynamo.export` paths component: lowering Issues re: The lowering / preprocessing passes component: tests Issues re: Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dynamo constant folding can leak mutable state through graph outputs

4 participants