Skip to content

Keep cat_out's nnlib input arrays off variable-length stack arrays (#24319) - #24319

Open
zonglinpeng wants to merge 2 commits into
pytorch:mainfrom
zonglinpeng:export-D128236590
Open

zonglinpeng wants to merge 2 commits into
pytorch:mainfrom
zonglinpeng:export-D128236590

Conversation

@zonglinpeng

@zonglinpeng zonglinpeng commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

impl::G3::native::cat_out declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the xa_nn_cat path applies. A 256-input int64 cat in RT-DETR (rtdetr_r18vd, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in cat_out goes from 11 to 0 in the built DSP image.

Adds a 40-input cat test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with topk_ind.unsqueeze(-1).repeat(1, 1, d_model), and Cadence's ReplaceRepeatWithCatPass lowers a repeat as a cat of the same tensor, so d_model = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering repeat as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590

@pytorch-bot

pytorch-bot Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

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

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

✅ No Failures

As of commit 596866a with merge base dd01cb4 (image):
💚 Looks good so far! There are no failures yet. 💚

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 Oct 30, 2026
@meta-codesync

meta-codesync Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

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

@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 Keep cat_out's nnlib input arrays off variable-length stack arrays Keep cat_out's nnlib input arrays off variable-length stack arrays (#24319) Nov 2, 2026
zonglinpeng added a commit to zonglinpeng/executorch that referenced this pull request Nov 2, 2026
…ytorch#24319)

Summary:

`impl::G3::native::cat_out` declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the `xa_nn_cat` path applies. A 256-input int64 cat in RT-DETR (`rtdetr_r18vd`, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in `cat_out` goes from 11 to 0 in the built DSP image.

Adds a 40-input `cat` test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with `topk_ind.unsqueeze(-1).repeat(1, 1, d_model)`, and Cadence's `ReplaceRepeatWithCatPass` lowers a `repeat` as a cat of the same tensor, so `d_model` = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering `repeat` as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590
zonglinpeng added a commit to zonglinpeng/executorch that referenced this pull request Nov 5, 2026
…ytorch#24319)

Summary:

`impl::G3::native::cat_out` declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the `xa_nn_cat` path applies. A 256-input int64 cat in RT-DETR (`rtdetr_r18vd`, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in `cat_out` goes from 11 to 0 in the built DSP image.

Adds a 40-input `cat` test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with `topk_ind.unsqueeze(-1).repeat(1, 1, d_model)`, and Cadence's `ReplaceRepeatWithCatPass` lowers a `repeat` as a cat of the same tensor, so `d_model` = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering `repeat` as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590
zonglinpeng added a commit to zonglinpeng/executorch that referenced this pull request Nov 11, 2026
…ytorch#24319)

Summary:

`impl::G3::native::cat_out` declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the `xa_nn_cat` path applies. A 256-input int64 cat in RT-DETR (`rtdetr_r18vd`, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in `cat_out` goes from 11 to 0 in the built DSP image.

Adds a 40-input `cat` test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with `topk_ind.unsqueeze(-1).repeat(1, 1, d_model)`, and Cadence's `ReplaceRepeatWithCatPass` lowers a `repeat` as a cat of the same tensor, so `d_model` = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering `repeat` as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590
zonglinpeng added a commit to zonglinpeng/executorch that referenced this pull request Nov 12, 2026
…ytorch#24319)

Summary:

`impl::G3::native::cat_out` declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the `xa_nn_cat` path applies. A 256-input int64 cat in RT-DETR (`rtdetr_r18vd`, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in `cat_out` goes from 11 to 0 in the built DSP image.

Adds a 40-input `cat` test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with `topk_ind.unsqueeze(-1).repeat(1, 1, d_model)`, and Cadence's `ReplaceRepeatWithCatPass` lowers a `repeat` as a cat of the same tensor, so `d_model` = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering `repeat` as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590
…aces (pytorch#24453)

Summary:

`rlx_arbitrary_image_stylization` fails in the ExecuTorch emitter: `InternalError: Tensor has a memory_format that is unsupported in ExecuTorch: torch.channels_last`. Its lowered graph carries `clone(memory_format=torch.channels_last)` on ops that stay on the CPU, the program is lowered with `_skip_dim_order=True`, and the emitter accepts only `contiguous_format` and `preserve_format` for an `aten.clone`.

`RemoveCloneOpsTransform` now runs in two steps:
1. Every `aten.clone` and `aten._to_copy` whose only effect is a non-contiguous `memory_format` is deleted. These ops carry a layout only in programs lowered with `_skip_dim_order=True`, where the emitter cannot represent one. Dim-order copies (`_clone_dim_order`, `_to_dim_order_copy`) describe a layout the runtime honours, so XNNPACK and other dim-order users keep them; they only go through step 2. The graph is then retraced, so every node's `meta["val"]` carries the dim order it has without those copies. A copy that also converts the dtype is kept. Args are read with `get_arg`, so a positional `memory_format` is handled too.
   - A copy that produces a graph output is not deleted. After the retrace, if its input does not already have the copy's layout, the transform raises: deleting it would change the layout callers expect for that output. This includes input-to-output copies under `preserve_input_output_copies`, which used to be skipped and then failed in the emitter.
   - An output copy whose input already has that layout is an identity. It goes to step 2, which still honours `preserve_input_output_copies`.
2. The existing clean-up runs on the retraced graph: `preserve_format` clones and clones of an input already in the requested layout are removed. A contiguous clone of an input that is not contiguous, such as a channels-last placeholder, stays.

The retrace only runs when step 1 deleted something, so graphs without layout copies go through exactly the old path.

This replaces this diff's earlier approach, which rewrote a channels-last `aten.clone` into a contiguous copy.

Reviewed By: DrJessop

Differential Revision: D125145150
…ytorch#24319)

Summary:

`impl::G3::native::cat_out` declared variable-length arrays sized by the number of inputs (about 76 B per input) before it checked whether the `xa_nn_cat` path applies. A 256-input int64 cat in RT-DETR (`rtdetr_r18vd`, int8) therefore grew the frame by about 19 KB on the DSP thread that runs the operator, overflowed that thread's 7488 B stack and tripped the Zephyr stack sentinel on the first inference. Raising the stack did not help: 8 KB, 11.5 KB and 16 KB stacks all overflowed.

The nnlib path now runs only for same-dtype, supported-dtype cats with at most 16 inputs, and uses fixed-size arrays. Larger cats take the existing generic loop. The frame is a fixed 1440 B: the number of dynamic stack adjustments in `cat_out` goes from 11 to 0 in the built DSP image.

Adds a 40-input `cat` test case so the fallback branch is covered.

The 256 inputs are one tensor repeated: every entry of that cat references the same value and buffer. RT-DETR's decoder gathers with `topk_ind.unsqueeze(-1).repeat(1, 1, d_model)`, and Cadence's `ReplaceRepeatWithCatPass` lowers a `repeat` as a cat of the same tensor, so `d_model` = 256 becomes a 256-entry cat. The kernel is still the place to fix the overflow: the arrays are sized by entry count, not by distinct tensors, and a 100-entry cat of distinct inputs (EfficientDet-Lite0's full graph) would need about 7.6 KB of them, more than the whole 7488 B stack. Lowering `repeat` as a broadcast copy would remove RT-DETR's long list but leave genuinely wide cats exposed, so it is at most a later optimization.

Differential Revision: D128236590

This branch was previously deployed

1 inactive deployment
cadence — 596866a6 Deployed Nov 12, 2026 by zonglinpeng via hifi-op-test / hifi4 #38076
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant