Skip to content

[PyTorch] [torch.compile] torch.compile support for LayerNormLinear and LayerNormMLP - #3534

Open
pggPL wants to merge 20 commits into
NVIDIA:mainfrom
pggPL:layernorm_compile
Open

pggPL wants to merge 20 commits into
NVIDIA:mainfrom
pggPL:layernorm_compile

Conversation

@pggPL

@pggPL pggPL commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Description

Add torch.compile custom-op support for LayerNormLinear and LayerNormMLP, including backward, so supported configurations can run with fullgraph=True.

Changes

  • Add forward and backward fake implementations covering output shapes, dtypes, saved tensors and aliases.
  • Support dynamic batch dimensions, normalization outputs and supported FP8 recipes.
  • Share output-gradient setup and quantizer validation across Linear and both LayerNorm modules. Keep unsupported configurations on the eager fallback path.
  • Validate both MLP GEMMs before custom-op dispatch, including FC2 dimensions for gated activations and sequence parallelism.
  • Parameterize existing compile tests across the three modules and cover LayerNorm-specific variants and gradients.

Validation

  • Existing targeted GPU compile suite: 135 passed, 3 skipped, 1 xpassed.
  • Manual checks confirmed that invalid FC2 dimensions are rejected during tracing for GELU and SwiGLU.
  • Eight manual FP32-parameter/BF16-autocast cases passed output and gradient comparisons against eager, covering both LayerNorm modules, LayerNorm/RMSNorm and FP8 CurrentScaling on/off.
  • Pylint on the latest touched production files: 10/10. Python syntax and whitespace checks passed.

Validation used a single RTX 5880 Ada GPU with PyTorch 2.14.0a0+b2c75dd062.nvinternal.main. Distributed TP/SP and Userbuffers execution under compile remain unvalidated.

…rm modules for compile wiring

Behavior-neutral groundwork for registering LayerNormLinear and LayerNormMLP
as torch.compile custom ops (follows the Linear split NVIDIA#2967 / NVIDIA#3053).

dynamo/custom_op.py:
- A bwd args dataclass may declare GRAD_OUTPUT_FIELDS naming one grad field
  per user output; the LN modules have two differentiable outputs (out and
  the returned norm output), so a single grad_output slot is not enough.
- Dict[str, <simple>] annotations are bundle-simple (activation_params).

module/_common.py: sp_out_leading / sp_inp_leading / fake_workspace_valid
shared by Linear and the LN modules (moved out of linear.py).

layernorm_linear.py / layernorm_mlp.py:
- GRAD_OUTPUT_FIELDS on the bwd args; fp8_output carried in the fwd args.
- inp_shape is rederived from grad_output in backward instead of being
  stored on the bwd args (SymInt dims are not hashable in the value bundle).
- LayerNormMLP: the recipe object no longer rides on the backward args; the
  properties the backward needs (float8_block_scaling, custom, dbias-dact
  fusion availability) are bools computed in Module.forward. The activation
  tables are split into per-activation (act, dact) pairs plus the fused dbias
  kernels so the backward looks them up without a recipe.
- The returned-norm-output grad is tolerated as None in backward.

No functional change.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…nd LayerNormMLP

Register the LayerNormLinear and LayerNormMLP forward/backward as
torch.library custom ops on top of the framework from NVIDIA#3053, so both trace
under fullgraph compile with the FP8/MXFP8/NVFP4 recipes. Builds on the
module restructuring and the framework groundwork in the two preceding PRs.

layernorm_linear.py / layernorm_mlp.py:
- compile_unsupported_reason gate, data-free forward/backward fakes on
  TensorSpec, register_custom_op, @no_torch_dynamo eager wrapper, compiled
  dispatch and eager fallback in Module.forward, mirroring Linear.
- Eager fallbacks: debug, quantized input, fsdp_group, differentiable
  fp8_output, CPU offload, delayed wgrad, FP8 weight caching,
  fuse_wgrad_accumulation, non-opaque quantizers; MLP also activation
  checkpointing and gemm_gelu_fusion under FP8.

tests/pytorch/test_torch_compile.py: LN module coverage (recipes x compile
modes, norm/activation/return_layernorm_output variants, FP8 primary weight,
frozen weights / no bias, dynamic shapes, eager fallbacks).

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Keep delay-capable stores in eager even while disabled, preserving toggles between forward and backward. Exclude unused queue-free stores from the custom-op arguments so the main branch's unconditional store forwarding does not disable compilation.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…modules"

This reverts commit aa4cde8.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Keep configuration fallback checks before prepare_forward and remove their duplicates from the argument bundles. Rename the remaining late check to describe its quantizer-only scope.

Validation: GPU compile regression 135 passed, 3 skipped, 1 xpassed; Pylint 3.3.1 rates all three modules 10/10; Python syntax and diff whitespace checks pass.
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Validate each forward's existing quantizer sequence in one common helper instead of duplicating validation and field lists in three forward argument classes.

Validation: GPU torch.compile regression 135 passed, 3 skipped, 1 xpassed; Pylint 3.3.1 four modules 10/10; Black helper check and diff whitespace check pass.
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Remove the single-use requires_grad_params method and evaluate the same predicate directly in the early compile fallback check. Validated Python syntax and git diff --check.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Accept shapes in check_gemm_dims so LayerNormMLP can validate FC2 without materializing its activation input. Derive the activation width and gathered row count for gated activations and sequence parallelism. Update Linear and LayerNormLinear callers to pass shapes.

Validation: existing GPU torch.compile regression 135 passed, 3 skipped, 1 xpassed. One-off GELU and SwiGLU invalid-FC2 probes both reject during tracing before the backend runs. Pylint 3.3.1 rates touched files 10/10; syntax and whitespace checks pass. No test changes.
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Remove the obsolete inp_shape constructor argument and order the packaging import before the TE extension import. Full CI Python lint scope passes with Pylint 3.3.1 (exit 0); the BF16 projection backward matches an FP32 reference on GPU. No test files changed.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Restore optional inp_shape for existing eager callers while keeping shape reconstruction local when it is unset. Restore the attention file to main so the PR has no attention changes. Existing compile regression: 135 passed, 3 skipped, 1 xpassed. Full Python lint passes with the local TE extension classified as third-party, matching CI.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL
pggPL marked this pull request as ready for review September 17, 2026 17:13
@pggPL
pggPL requested a review from ksivaman as a code owner September 17, 2026 17:13
@greptile-apps

greptile-apps Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because the exported custom-op registration API still breaks existing backward argument containers that rely on grad_output or grad_outputs fields.

Findings

  1. P1 Backward API Compatibility Break

Summary

Adds full-graph torch.compile support for LayerNormLinear and LayerNormMLP, extending the existing custom-operation infrastructure used by Linear.

  • Adds fake forward and backward implementations describing outputs, saved tensors, aliases, gradients, and quantized metadata.
  • Adds compiled dispatch eligibility checks and eager fallback for unsupported configurations.
  • Preserves NVFP4 normalization scale-layout metadata and validates both LayerNormMLP GEMMs.
  • Expands eager-versus-compiled tests across modules, recipes, dynamic batches, normalization variants, activations, gradients, and CUDA-graph modes.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[LayerNormLinear or LayerNormMLP call] --> B{Compile configuration supported?}
    B -- No --> C[Eager fallback]
    B -- Yes --> D[Registered forward custom op]
    D --> E[Fake implementation supplies shapes and metadata]
    D --> F[Native forward implementation]
    F --> G[User outputs and saved tensors]
    G --> H[Autograd slices output gradients]
    H --> I[setup_grad_outputs]
    I --> J[Registered backward custom op]
    J --> K[Input and parameter gradients]
Loading

Reviews (3) · Last reviewed commit: "Preserve explicit quantized scale layout..."

bwd_obj.grad_outputs = tuple(user_grads)
else:
bwd_obj.grad_output = user_grads[0]
bwd_obj.setup_grad_outputs(user_grads)

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.

P1 Backward API Compatibility Break

register_custom_op is exported from transformer_engine.pytorch.dynamo, but backward now unconditionally calls setup_grad_outputs. Existing integrations that follow the previous grad_output or grad_outputs field convention can therefore register and compile successfully, then fail with AttributeError during backward. Please preserve the field-based fallback or provide a compatibility default while callers migrate.

Preserve both LayerNorm and OperationFuser compile tests and adapt module registrations to register_custom_op_with_autograd. Validated 156 passing compile tests, 3 skips and 1 xpass, plus full Python lint with the local TE extension classified as third-party to match CI.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Store the actual GEMM scale layout independently of the quantizer optimization request. Preserve it when converting real tensors and rebuilding custom-op outputs. Describe the NVFP4 normalization output layout without changing the quantizer configuration.

Parametrize the existing tensor round-trip test with layouts opposite to the quantizer request. Use deterministic NVFP4 for exact module comparisons and skip the backward override unsupported by eager LayerNormMLP.

Validation: 181 passed, 3 skipped, 1 xpassed on Ada; full Python lint and Black pass. NVFP4 allocation and reassembly metadata agree with C++ for RHT and 4over6. Full NVFP4 forward/backward on Blackwell remains pending.
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
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.

1 participant