Skip to content

Arm backend: skip FoldScalarMulIntoConv when conv output feeds carried state (#21234)#21234

Open
3l1 wants to merge 2 commits into
mainfrom
export-D113310883
Open

Arm backend: skip FoldScalarMulIntoConv when conv output feeds carried state (#21234)#21234
3l1 wants to merge 2 commits into
mainfrom
export-D113310883

Conversation

@3l1

@3l1 3l1 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:

  • Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  • Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883

@3l1
3l1 requested a review from digantdesai as a code owner July 22, 2026 23:49
@pytorch-bot

pytorch-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21234

Note: Links to docs will display an error until the docs builds have been completed.

❗ 1 Active SEVs

There are 1 currently active SEVs. If your PR is affected, please view them below:

❌ 104 New Failures, 1 Cancelled Job, 13 Unrelated Failures, 8 Unclassified Failures

As of commit b4c9793 with merge base 430b73d (image):

NEW FAILURES - The following jobs have failed:

UNCLASSIFIED FAILURES - DrCI could not classify the following jobs because the workflow did not run on the merge base. The failures may be pre-existing on trunk or introduced by this PR:

CANCELLED JOB - The following job was cancelled. Please retry:

FLAKY - The following jobs failed but were likely due to flakiness present on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 22, 2026
@meta-codesync

meta-codesync Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@3l1 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D113310883.

@github-actions github-actions Bot added ciflow/trunk module: arm Issues related to arm backend labels Jul 22, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@meta-codesync meta-codesync Bot changed the title Arm backend: skip FoldScalarMulIntoConv when conv output feeds carried state Arm backend: skip FoldScalarMulIntoConv when conv output feeds carried state (#21234) Jul 23, 2026
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from 2c508db to 247cdc5 Compare July 23, 2026 02:26
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

FoldScalarMulIntoConv #20838 rewrites conv(x) * scale to a convolution with scaled
weights. This is exact in floating point but moves the scaling inside the
convolution, changing the conv output's quantization (its activation range and
requantization). That is harmless for a plain feed-forward output, but if the
scaled conv output feeds a carried/stateful quantization boundary -- a value
written back into a mutable buffer / input that persists across invocations
(recurrent state) -- the shifted requantization drifts the persisted quantized
state and the error compounds over the sequence.

Make the fold state-aware: skip it when the conv output transitively reaches a
BUFFER_MUTATION / USER_INPUT_MUTATION output in the exported program's graph
signature. Plain feed-forward convolutions are unaffected and still fold.

Add StatefulConvMul (conv output * scale written into a mutable buffer) and
assert the scalar mul survives.

Differential Revision: D113310883
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

FoldScalarMulIntoConv #20838 rewrites conv(x) * scale to a convolution with scaled
weights. This is exact in floating point but moves the scaling inside the
convolution, changing the conv output's quantization (its activation range and
requantization). That is harmless for a plain feed-forward output, but if the
scaled conv output feeds a carried/stateful quantization boundary -- a value
written back into a mutable buffer / input that persists across invocations
(recurrent state) -- the shifted requantization drifts the persisted quantized
state and the error compounds over the sequence.

Make the fold state-aware: skip it when the conv output transitively reaches a
BUFFER_MUTATION / USER_INPUT_MUTATION output in the exported program's graph
signature. Plain feed-forward convolutions are unaffected and still fold.

Add StatefulConvMul (conv output * scale written into a mutable buffer) and
assert the scalar mul survives.

Differential Revision: D113310883
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from 247cdc5 to ac47e4a Compare July 23, 2026 02:27
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

FoldScalarMulIntoConv #20838 rewrites conv(x) * scale to a convolution with scaled
weights. This is exact in floating point but moves the scaling inside the
convolution, changing the conv output's quantization (its activation range and
requantization). That is harmless for a plain feed-forward output, but if the
scaled conv output feeds a carried/stateful quantization boundary -- a value
written back into a mutable buffer / input that persists across invocations
(recurrent state) -- the shifted requantization drifts the persisted quantized
state and the error compounds over the sequence.

Make the fold state-aware: skip it when the conv output transitively reaches a
BUFFER_MUTATION / USER_INPUT_MUTATION output in the exported program's graph
signature. Plain feed-forward convolutions are unaffected and still fold.

Add StatefulConvMul (conv output * scale written into a mutable buffer) and
assert the scalar mul survives.

Differential Revision: D113310883
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from ac47e4a to 6d60502 Compare July 23, 2026 03:31
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from 6d60502 to 4242879 Compare July 23, 2026 03:39
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:
  - Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  - Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:
  - Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  - Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from 4242879 to a28b814 Compare July 23, 2026 03:42
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:
  - Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  - Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from a28b814 to c34b085 Compare July 23, 2026 03:51
meta-codesync Bot pushed a commit that referenced this pull request Jul 23, 2026
…d state (#21234)

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:
  - Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  - Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883
@meta-codesync
meta-codesync Bot force-pushed the export-D113310883 branch from c34b085 to 63a9935 Compare July 23, 2026 03:54
3l1 added 2 commits July 22, 2026 21:04
…hazard (#21152)

Summary:

FoldScalarMulIntoConv (#20838) rewrites
conv(x) * scale into a convolution with scaled weights. This is exact in floating
point (already covered by the existing tests in this file), but it is not safe
under quantization when the scaled conv output feeds a carried/stateful
quantization boundary -- a value written back into a mutable buffer / input that
persists across invocations (recurrent state). Activation qparams are calibrated
and frozen on the pre-fold conv output; folding the scale into the weights moves
the scaling inside the convolution, so the conv output is requantized with the
stale pre-fold scale. That shifted requantization drifts the persisted quantized
state and the error compounds over the sequence.

Add StatefulConvMul (conv output * scale written into a mutable buffer) and an
xfail unit test (test_does_not_fold_when_output_feeds_stateful_buffer) documenting
the hazard: the fold currently fires and drops the mul across the buffer boundary.
The test is marked xfail (strict); the state-aware guard in D113310883 makes it
pass, and the xfail is removed there.

Reviewed By: rascani

Differential Revision: D113310822
…d state (#21234)

Summary:

Follow-up to D113310822, which documents (as a strict xfail) that
FoldScalarMulIntoConv (#20838) is unsafe
when the scaled conv output feeds a carried/stateful quantization boundary: the
fold moves the scaling inside the convolution, shifting the conv output requant,
which drifts a quantized value persisted across invocations (recurrent state) and
compounds over the sequence.

Make the fold state-aware and skip it in that case. Two carried-state signals are
handled:
  - Structural: the conv output transitively reaches a BUFFER_MUTATION /
    USER_INPUT_MUTATION output in the exported program graph signature (mutable
    buffer / input mutation).
  - Explicit: a new mark_carried_quant_state / is_carried_quant_state marker
    (arm_pass_utils) a model can set on the (conv * scale) output. Functional
    recurrent state (state_in arg + state_out return, shared quantization) has no
    structural signal before quantization, so it needs an explicit marker.

Plain feed-forward convolutions are unaffected and still fold.

Un-xfails test_does_not_fold_when_output_feeds_stateful_buffer (the buffer-mutation
case introduced in D113310822) and adds
test_does_not_fold_when_output_marked_carried_state (the explicit-marker case).

Differential Revision: D113310883
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/trunk CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported module: arm Issues related to arm backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants