Fix create_constant_placeholder crashing when torch.fx renames the requested placeholder - #21542
Fix create_constant_placeholder crashing when torch.fx renames the requested placeholder#21542slipstr34m wants to merge 3 commits into
Conversation
…of the name torch.fx assigned torch.fx renames a placeholder when the requested name is not a valid identifier or collides with an existing node. create_constant_placeholder looked the node up by the requested name afterwards, so any rename raised ValueError from inside the pass that asked for the node. FuseBatchNormPass hits this on any top-level nn.Sequential, whose parameter FQNs start with a digit, and on torchvision regnet under Vulkan, whose module names contain a hyphen. A placeholder's target is also emitted verbatim as a function parameter name on recompile, and the graph signature requires the state_dict key it records to exist, so the target, the state_dict key and the signature now all follow the name fx assigned. The requested name is kept in node.meta and used for the shared-weight dedup, which previously matched on target and therefore also missed after a rename. Un-skips test_conv_bn in runtime/test/test_runtime_xnnpack.py, which was skipped on this bug. Fixes pytorch#14055
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21542
Note: Links to docs will display an error until the docs builds have been completed.
|
|
@pytorchbot label "release notes: none" |
| def test_fp32_conv_batch_norm_fusion_top_level_sequential(self): | ||
| """ | ||
| A top-level nn.Sequential yields digit-leading fused placeholder names, | ||
| which torch.fx renames. The pass must survive the rename (#14055). |
| """ | ||
|
|
||
| target = name | ||
| # If this helper already created a placeholder for this name, return it to |
There was a problem hiding this comment.
please make this comment more concise
|
@claude please review |
|
Claude finished @JakeStevens's task in 1m 17s —— View job Code ReviewI read the three PR commits ( The fix is correct and well-targeted. The core insight — one name everywhere ( Correctness ✅
Worth a thought (non-blocking)
Tests ✅
Nits (optional)
Nothing blocking from my side — the change is minimal, the name flow is now internally consistent, and the coverage is convincing. LGTM. |
|
create_mutable_buffer is similar, there may be a latent bug there too which can be resolved in follow up PR |
|
Sounds about right to me, will investigate and push a follow up PR later. |
Fixes #14055
Fixes #21541
Problem
Found while verifying #21489: the textbook CNN used there, one top-level
nn.Sequentialof conv, batchnorm, relu and maxpool, gets past the pooling check it used to crash on and now dies one pass later, inFuseBatchNormPass.create_constant_placeholderrecords the name it requested, not the name torch.fx assigned:torch.fx renames the node whenever the requested name is not a valid identifier or collides with an existing node. Two plain models trigger this today. A top-level
nn.Sequentialnames its parameters0.weight, soFuseBatchNormPassrequests0_weight_fused_bn, fx assigns_0_weight_fused_bn, and lowering to XNNPACK dies withValueError: '0_weight_fused_bn' is not in list. torchvision regnet under Vulkan hits the same lines through its hyphenated module names (#14055). The identical models behind a named attribute lower fine, which is why existing coverage never reached this path.Two further defects sit behind the crash. A placeholder's
targetis emitted verbatim as a function parameter name on recompile, so fixing only the lookup producesdef forward(self, 0_weight_fused_bn, ...), a SyntaxError. And the shared-weight dedup from #18031 matched ontarget == name, so after a rename it misses and creates a duplicate placeholder.Fix
One name everywhere:
node.name,node.target, the state_dict key and the graph signature all follow the name fx assigned. The requested name is kept innode.metaand used for the dedup, so the #18031 contract is preserved: a second request for the same name returns the existing node, now also when fx renamed it. Keying the state_dict by the assigned name inherits the fx namespace uniqueness guarantee, so a colliding request can never overwrite an existing parameter.For any requested name that is a valid identifier with no collision, the assigned name equals the requested name and behavior is unchanged.
Effect
Same script on either side of the change, executed on executor_runner built from this branch. maxdiff is deviation from eager.
Testing
Two helper tests in
test_create_delete_constant_placeholder.py: a digit-leading request checks node, signature, state_dict and a recompile round trip; a dedup test checks that requesting the same name twice returns the existing node for a clean, a digit-leading and a hyphenated name. One pass test intest_batch_norm_fusion.pyfuses a top-level Sequential end to end.test_conv_bninruntime/test/test_runtime_xnnpack.py, skipped on this bug, is re-enabled (linux-gated, runs in CI).Verified failing-first by reverting only
backends/transforms/utils.py: exactly the three new tests fail, all withValueErroratutils.py:151.lintrunner reports no issues on all changed files.
The helper is shared by the Arm, Vulkan and XNNPACK backends. Callers passing valid unique names get identical behavior; renamed cases previously crashed, so no caller can depend on the old behavior.
cc @GregoryComer @digantdesai @cbilgin @JakeStevens