Skip to content

Handle zero-extent rms_norm decomposition - #4678

Open
mukul-sosalekeerthi-ARM wants to merge 3 commits into
llvm:mainfrom
mukul-sosalekeerthi-ARM:rms-norm-zero-extent
Open

Handle zero-extent rms_norm decomposition#4678
mukul-sosalekeerthi-ARM wants to merge 3 commits into
llvm:mainfrom
mukul-sosalekeerthi-ARM:rms-norm-zero-extent

Conversation

@mukul-sosalekeerthi-ARM

Copy link
Copy Markdown
Collaborator

Summary:

  • Fixes Torch-MLIR aten.rms_norm decomposition for valid zero-extent Torch tensors.
  • Adds lit regression coverage.
  • Adds PT1 e2e coverage via RMSNormZeroExtentModule_basic.
  • Handles TOSA as unsupported/xfail because TOSA ref-model cannot execute zero-extent tensors.

Validation:

  • Torch decomposition lit test passed.
  • RMSNormZeroExtentModule_basic passes with native_torch.
  • TOSA configs fail expectedly for zero-extent tensors.
  • Black Duck snippet scan completed successfully: no matches found.

@mukul-sosalekeerthi-ARM
mukul-sosalekeerthi-ARM force-pushed the rms-norm-zero-extent branch 3 times, most recently from b1fe74a to 0cce3b3 Compare August 6, 2026 09:11
@@ -8184,6 +8184,14 @@ class DecomposeAtenRMSLayerNormOp : public OpRewritePattern<AtenRmsNormOp> {
if (!outputTy.hasDtype())
return rewriter.notifyMatchFailure(op, "output should have a dtype.");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If RMS norm input has a zero dimension aten.rms_norm operator is directly replaced with the input tensor.

ValueTensorType::get(context, reducedShape, inputTy.getDtype());
// x^2
Value inputSquared = AtenSquareOp::create(rewriter, loc, inputTy, input);
// x^2. Emit multiplication directly instead of aten.square so zero-extent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This avoids emitting aten.square operator which later becomes torch.aten.pow.Tensor_Scalar. The TOSA legalization was failing on zero-extent pow.Tensor_Scalar.

};
} // namespace

namespace {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Separate fold patterns are needed because the pipeline may see RMSNorm either before or after it has already been decomposed. The direct aten.rms_norm rewrite handles the intact op, but already-decomposed zero-extent cases can end at different ops depending on dtype.

FoldZeroExtentRmsNormDecompositionFromMul handles the float32 decomposition ending in aten.mul.Tensor. FoldZeroExtentRmsNormDecompositionFromConvert handles paths ending in prims.convert_element_type. FoldZeroExtentRmsNormDecompositionFromAtenToDtype handles fp16/bf16 eager-opmath paths ending in aten.to.dtype.

All three apply the same rule: if the matched chain is just RMSNorm computation over a statically zero-sized input, replace the chain with the original empty input so illegal zero-extent pow or cast ops do not reach TOSA legalization.

@mukul-sosalekeerthi-ARM
mukul-sosalekeerthi-ARM force-pushed the rms-norm-zero-extent branch 2 times, most recently from 2faffcc to 48a9d8d Compare August 10, 2026 09:11

@Lallapallooza Lallapallooza left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the patch, few comments.

Comment on lines +8187 to +8193
if (llvm::is_contained(inputTy.getSizes(), 0)) {
if (input.getType() != op.getType())
return rewriter.notifyMatchFailure(
op, "zero-extent input and output types should match.");
rewriter.replaceOp(op, input);
return success();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we move this fast path behind the existing normalized-shape gate and the other statically visible RMSNorm checks? Please validate normalized shape, known weight shape, and supported dtype before folding, with negative lit coverage.

return success();
}

static LogicalResult matchMeanOfPow2(Value value, Value input) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The current path for this eps=0.5 case emits pow -> mean -> add(eps) -> rsqrt -> mul -> mul, but matchMeanOfPow2 accepts only a mean or sum/div directly below rsqrt.

return input;
}

static void eraseDeadRmsNormDecompositionOp(Operation *op,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

eraseDeadRmsNormDecompositionFromOperands can walk a saved Value after another operand branch has erased its defining op.

mukul-sosalekeerthi-ARM added a commit that referenced this pull request Aug 10, 2026
Fixes zero-extent RMSNorm folding by validating normalized shape, weight shape/dtype, and floating dtypes before replacing the op with the input.

Also handles already-decomposed RMSNorm graphs with eps by matching the add.Scalar before rsqrt, and makes dead decomposition cleanup safe when shared branches are erased.

Fixes #4678
mukul-sosalekeerthi-ARM added a commit to mukul-sosalekeerthi-ARM/torch-mlir that referenced this pull request Aug 10, 2026
Fixes zero-extent RMSNorm folding by validating normalized shape, weight shape/dtype, and floating dtypes before replacing the op with the input.

Also handles already-decomposed RMSNorm graphs with eps by matching the add.Scalar before rsqrt, and makes dead decomposition cleanup safe when shared branches are erased.

Fixes llvm#4678
mukul-sosalekeerthi-ARM added a commit to mukul-sosalekeerthi-ARM/torch-mlir that referenced this pull request Aug 11, 2026
Fixes zero-extent RMSNorm folding by validating normalized shape, weight shape/dtype, and floating dtypes before replacing the op with the input.

Also handles already-decomposed RMSNorm graphs with eps by matching the add.Scalar before rsqrt, and makes dead decomposition cleanup safe when shared branches are erased.

Fixes llvm#4678
Fixes zero-extent RMSNorm folding by validating normalized shape, weight shape/dtype, and floating dtypes before replacing the op with the input.

Also handles already-decomposed RMSNorm graphs with eps by matching the add.Scalar before rsqrt, and makes dead decomposition cleanup safe when shared branches are erased.

Fixes llvm#4678
return nullptr;
}

static FailureOr<Value> matchZeroExtentRmsNormDecomposition(Value value) {

@sahas3 sahas3 Aug 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the patch, but I am not following the motivation to match an already decomposed RMSNorm op here. torch-mlir doesn't decompose this op by default

DEFAULT_DECOMPOSITIONS = [
-- it'll be decomposed if caller runs ep = ep.run_decompositions() on the exported program and then feeds that to fx.export_and_import. Is there a real use-case to support this?

My concern is that it won't be scalable to add support for every decomposed op in this way. If there is truly a use-case, then I think you should raise the decomposed pattern back to rms_norm op in

and let the decomposition pattern here deal with only rms_norm. That keeps the decomposition path free of raising operations/pattern-matching to keep the responsibilites (decompose vs recompose) separate in their own passes.

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.

3 participants