Skip to content

Support 3-D convolution in the portable convolution kernel - #24460

Merged
JacobSzwejbka merged 1 commit into
release/1.6from
cherry-pick-24385-by-pytorch_bot_bot_
Nov 3, 2026
Merged

JacobSzwejbka merged 1 commit into
release/1.6from
cherry-pick-24385-by-pytorch_bot_bot_

Conversation

@pytorchbot

@pytorchbot pytorchbot commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Merge after #24445. It does not depend on it to build, but #24445 brings the XNNPACK fix for statically quantized 3-D convolutions, and without it the backend test suite would report those cases as failing once this pick removes the 3-D skip.

What is wrong today

A model with torch.nn.Conv3d lowers without an error but fails at run time on CPU, for example with XNNPACK:

Check failed (in.dim() == 3 || in.dim() == 4): Expect input tensor to be 3-D or 4-D, but got, 5.

XNNPACK does not take 3-D or bfloat16 convolutions, so they run on the portable convolution kernel, which only supported 1-D and 2-D. This blocks vision encoders with a Conv3d patch embedding, such as Qwen2-VL and Qwen2.5-VL. It is the failure in #11704.

What this change does

The portable convolution kernel now runs 3-D convolutions, regular and transposed. 1-D and 2-D convolutions run as 3-D ones with extra dims of size 1 and add up their terms in the same order as before, so their results stay the same (except that, with fused multiply-add, a result of -0 can now be +0, as in PyTorch). The input and output may now have different dim orders, because torch.export gives a Conv3d with a channels-last input a contiguous output. The backend test suite no longer skips 3-D convolutions.

It also fixes older bugs in the argument checks, which affect 1-D and 2-D too:

  • A bias of the wrong length was read past its end (missing parentheses), and a Bool bias crashed the kernel.
  • groups = 0 divided by zero, and a huge groups could overflow in the bias check.
  • A huge padding or dilation, or an output size too large for the 32-bit tensor sizes, let a call that should fail run. Stride, padding and dilation must now fit in 32 bits.
  • Like PyTorch, the checks now reject a kernel dim of 0, a regular convolution's kernel larger than the padded input, and an empty spatial dim unless the batch or channel dim is empty too.

Testing

  • New tests cover grouped regular and transposed 3-D convolutions with contiguous, channels-last and mixed layouts, and the checks above. All but one fail without this change; that one checks that an empty batch or channel dim still works.
  • The portable kernel tests pass with AddressSanitizer and UndefinedBehaviorSanitizer on Linux Arm64.
  • The Cortex-M conv3d test, now with several channels, a 2x3x3 kernel, stride and padding, no longer expects a failure and passes in CI on the Arm simulator.
  • A Conv3d patch embedding lowered with XNNPACK, and random convolutions on the portable kernel, match eager PyTorch in float32 and bfloat16.
  • Speed against main on Arm64 and x86-64: regular 1-D and 2-D convolutions are 1.5 to 13 times faster with GCC and with Clang at -Os, and 18 percent faster to 11 percent slower with Clang at -O2 and -O3. Transposed ones are 5 percent faster to 17 percent slower.
  • Code size for a Cortex-M55 (Arm GNU toolchain 15.2): op_convolution.o grows from 32,484 to 33,263 bytes at -Os and from 48,477 to 61,457 at -O3. kernel_ops_util.o, which holds the argument checks, grows from 15,746 to 17,743 bytes at -Os and from 25,932 to 28,979 at -O3.

Notes

## What is wrong today

A model with `torch.nn.Conv3d` lowers without an error but fails at run
time on CPU, for example with XNNPACK:

```
Check failed (in.dim() == 3 || in.dim() == 4): Expect input tensor to be 3-D or 4-D, but got, 5.
```

XNNPACK does not take 3-D or bfloat16 convolutions, so they run on the
portable convolution kernel, which only supported 1-D and 2-D. This
blocks vision encoders with a `Conv3d` patch embedding, such as Qwen2-VL
and Qwen2.5-VL. It is the failure in #11704.

## What this change does

The portable convolution kernel now runs 3-D convolutions, regular and
transposed. 1-D and 2-D convolutions run as 3-D ones with extra dims of
size 1 and add up their terms in the same order as before, so their
results stay the same (except that, with fused multiply-add, a result of
-0 can now be +0, as in PyTorch). The input and output may now have
different dim orders, because `torch.export` gives a `Conv3d` with a
channels-last input a contiguous output. The backend test suite no
longer skips 3-D convolutions.

It also fixes older bugs in the argument checks, which affect 1-D and
2-D too:

- A bias of the wrong length was read past its end (missing
parentheses), and a Bool bias crashed the kernel.
- `groups = 0` divided by zero, and a huge `groups` could overflow in
the bias check.
- A huge padding or dilation, or an output size too large for the 32-bit
tensor sizes, let a call that should fail run. Stride, padding and
dilation must now fit in 32 bits.
- Like PyTorch, the checks now reject a kernel dim of 0, a regular
convolution's kernel larger than the padded input, and an empty spatial
dim unless the batch or channel dim is empty too.

## Testing

- New tests cover grouped regular and transposed 3-D convolutions with
contiguous, channels-last and mixed layouts, and the checks above. All
but one fail without this change; that one checks that an empty batch or
channel dim still works.
- The portable kernel tests pass with AddressSanitizer and
UndefinedBehaviorSanitizer on Linux Arm64.
- The Cortex-M `conv3d` test, now with several channels, a 2x3x3 kernel,
stride and padding, no longer expects a failure and passes in CI on the
Arm simulator.
- A `Conv3d` patch embedding lowered with XNNPACK, and random
convolutions on the portable kernel, match eager PyTorch in float32 and
bfloat16.
- Speed against main on Arm64 and x86-64: regular 1-D and 2-D
convolutions are 1.5 to 13 times faster with GCC and with Clang at
`-Os`, and 18 percent faster to 11 percent slower with Clang at `-O2`
and `-O3`. Transposed ones are 5 percent faster to 17 percent slower.
- Code size for a Cortex-M55 (Arm GNU toolchain 15.2):
`op_convolution.o` grows from 32,484 to 33,263 bytes at `-Os` and from
48,477 to 61,457 at `-O3`. `kernel_ops_util.o`, which holds the argument
checks, grows from 15,746 to 17,743 bytes at `-Os` and from 25,932 to
28,979 at `-O3`.

## Notes

- Depends on #24380. Without it, a static int8 `Conv3d` lowered with
XNNPACK returns wrong values with no error, or fails to load, and the
backend test suite's static int8 XNNPACK `Conv3d` cases fail. Please
merge #24380 first or together with this change.
- An earlier version of 3-D support was reverted, see #19977.

(cherry picked from commit 76efad5)
@pytorch-bot

pytorch-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

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

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

✅ You can merge normally! (2 Unrelated Failures)

As of commit 929ffdc with merge base 4db778e (image):

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

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

@JacobSzwejbka
JacobSzwejbka merged commit ce9da5f into release/1.6 Nov 3, 2026
255 of 259 checks passed
@JacobSzwejbka
JacobSzwejbka deleted the cherry-pick-24385-by-pytorch_bot_bot_ branch November 3, 2026 04:03

This branch was previously deployed

1 inactive deployment
cadence — 929ffdc8 Deployed Nov 2, 2026 by pytorchbot via hifi-op-test / hifi4 #36042
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants