[ExecuTorch][WebGPU] Add fp32 matmul ops (mm, bmm, linear) to the WebGPU backend#21070
Conversation
…GPU backend Pull Request resolved: #20917 Add fp32 GEMM handlers (`aten.mm`, `aten.bmm`, `aten.linear`) — the dense matmuls the on-device training tail needs (tiled + vec4 WGSL). Key changes: - `runtime/ops/{mm,bmm,linear}/` — tiled + vec4 fp32 GEMM WGSL kernels + handlers - `CMakeLists.txt` `WEBGPU_SRCS` — wire the three sources Reuses the shared Vulkan partitioner (`aten.mm`/`bmm`/`linear` already have Vulkan `OpFeatures`); this adds the WebGPU kernels only. Co-authored-with: Claude Code. ghstack-source-id: 405026038 @exported-using-ghexport Differential Revision: [D111755135](https://our.internmc.facebook.com/intern/diff/D111755135/)
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21070
Note: Links to docs will display an error until the docs builds have been completed. ❗ 1 Active SEVsThere are 1 currently active SEVs. If your PR is affected, please view them below: ⏳ 100 Pending, 3 Unrelated FailuresAs of commit c85672d with merge base 0fc1f61 ( 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. |
This PR needs a
|
Pull Request resolved: #20918 Export-delegation + fp64 golden tests for the matmul (`mm`, `bmm`, `linear`) ops. Key changes: - `test/ops/{test_matmul,test_bmm,test_linear}.py` — fp64 library-reference goldens + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026043 @exported-using-ghexport Differential Revision: [D111755120](https://our.internmc.facebook.com/intern/diff/D111755120/)
…WebGPU backend Pull Request resolved: #20919 Add `aten._softmax` + `aten._log_softmax` handlers (max-stable online softmax) for the training tail. Key changes: - `runtime/ops/{softmax,log_softmax}/` — max-stable softmax + log_softmax WGSL kernels + handlers - `CMakeLists.txt` `WEBGPU_SRCS` — wire the sources Reuses the shared Vulkan partitioner (`aten._softmax`/`_log_softmax` already registered); WebGPU kernels only. Co-authored-with: Claude Code. ghstack-source-id: 405026047 @exported-using-ghexport Differential Revision: [D111755118](https://our.internmc.facebook.com/intern/diff/D111755118/)
Pull Request resolved: #20920 Export-delegation + fp64 golden tests for the softmax (`_softmax`, `_log_softmax`) ops. Key changes: - `test/ops/{test_softmax,test_log_softmax}.py` — fp64 goldens + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026051 @exported-using-ghexport Differential Revision: [D111755117](https://our.internmc.facebook.com/intern/diff/D111755117/)
…GPU backend Pull Request resolved: #20921 Add broadcast-aware `aten.div.Tensor` + `aten.sub.Tensor` elementwise handlers for the training tail. Key changes: - `runtime/ops/{div,sub}/` — broadcast elementwise WGSL kernels + handlers - `CMakeLists.txt` `WEBGPU_SRCS` — wire the sources Reuses the shared Vulkan partitioner (`aten.div.Tensor`/`sub.Tensor` already registered); WebGPU kernels only. Co-authored-with: Claude Code. ghstack-source-id: 405026052 @exported-using-ghexport Differential Revision: [D111755128](https://our.internmc.facebook.com/intern/diff/D111755128/)
Pull Request resolved: #20922 Export-delegation + fp64 golden tests for the binary (`div`, `sub`) ops. Key changes: - `test/ops/{test_div,test_sub}.py` — fp64 goldens (incl. broadcast) + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026054 @exported-using-ghexport Differential Revision: [D111755114](https://our.internmc.facebook.com/intern/diff/D111755114/)
Pull Request resolved: #20923 Add an `aten.sum.dim_IntList` / `aten.mean.dim` reduction handler for the training tail. Key changes: - `runtime/ops/reduce/` — per-row reduction WGSL kernel + handler - `CMakeLists.txt` `WEBGPU_SRCS` — wire the source Reuses the shared Vulkan partitioner (`aten.sum.dim_IntList`/`mean.dim` already registered); WebGPU kernel only. Co-authored-with: Claude Code. ghstack-source-id: 405026057 @exported-using-ghexport Differential Revision: [D111755133](https://our.internmc.facebook.com/intern/diff/D111755133/)
Pull Request resolved: #20924 Export-delegation + fp64 golden tests for the reduce (`sum`/`mean`) ops. Key changes: - `test/ops/test_reduce.py` — fp64 goldens (dim variants) + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026059 @exported-using-ghexport Differential Revision: [D111755115](https://our.internmc.facebook.com/intern/diff/D111755115/)
…logical_not) to the WebGPU backend Pull Request resolved: #20925 Add `aten.where.self`, scalar comparisons (`aten.{eq,ne,le,ge,lt,gt}.Scalar`), and `aten.logical_not.default` handlers for the training tail. The scalar compares and `logical_not` are generated from ONE templated byte-packed-bool shader (`runtime/ops/boolean_op/`) rather than per-op hand-written kernels — mirroring the `binary_op` codegen and Vulkan's grouping of comparison ops as `${OPERATOR}` variants (`backends/vulkan/runtime/graph/ops/glsl/binary_op_buffer.yaml`). Key changes: - `runtime/ops/boolean_op/` — one `boolean_op.wgsl` template + `boolean_op.yaml` (variants `compare_{eq,ne,le,ge,lt,gt}` + `logical_not`) expand to the per-variant `*_wgsl.h`; `BooleanOp.cpp` holds one shared `dispatch_bool_op` helper (pack 4 bools per `u32` word, one thread per word) plus the seven `WEBGPU_REGISTER_OP`s. - `runtime/ops/where/` — ternary select, kept as its own kernel/handler (non-bool-family, as in Vulkan). - `op_registry.py` — register the net-new `aten.{ne,lt,le,ge,gt}.Scalar` + `aten.logical_not.default` `OpFeatures` (`eq.Scalar` already on master). - `CMakeLists.txt` `WEBGPU_SRCS` — wire `boolean_op/BooleanOp.cpp`. `aten.where.self` already had Vulkan `OpFeatures`; the scalar-compare + `logical_not` registrations are additive to the shared Vulkan registry. Co-authored-with: Claude Code. ghstack-source-id: 405026062 @exported-using-ghexport Differential Revision: [D111755122](https://our.internmc.facebook.com/intern/diff/D111755122/)
Pull Request resolved: #20926 Export-delegation + fp64 golden tests for the select/bool (`where`, scalar compares, `logical_not`) ops. Key changes: - `test/ops/{test_where,test_compare,test_logical_not}.py` — fp64 goldens + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026072 @exported-using-ghexport Differential Revision: [D111755124](https://our.internmc.facebook.com/intern/diff/D111755124/)
Pull Request resolved: #20927 Add `aten.gather.default` (row-gather) + `aten.embedding.default` handlers for the training tail. Key changes: - `runtime/ops/{gather,embedding}/` — gather / row-gather WGSL kernels + handlers - `CMakeLists.txt` `WEBGPU_SRCS` — wire the sources Reuses the shared Vulkan partitioner (`aten.gather`/`embedding` already registered); WebGPU kernels only. Co-authored-with: Claude Code. ghstack-source-id: 405026068 @exported-using-ghexport Differential Revision: [D111755125](https://our.internmc.facebook.com/intern/diff/D111755125/)
Pull Request resolved: #20928 Export-delegation + fp64 golden tests for the gather (`gather`, `embedding`) ops. Key changes: - `test/ops/{test_gather,test_embedding}.py` — fp64 goldens + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026067 @exported-using-ghexport Differential Revision: [D111755129](https://our.internmc.facebook.com/intern/diff/D111755129/)
…) to the WebGPU backend Pull Request resolved: #20929 Add `_clone_dim_order` (DMA copy), `aten.expand_copy` (broadcast), and `fill` (`aten.full`/`full_like`/`scalar_tensor`) handlers for the training tail. Key changes: - `runtime/ops/{dim_order,expand_copy,fill}/` — WGSL kernels + handlers (dim_order is a native buffer DMA copy) - `CMakeLists.txt` `WEBGPU_SRCS` — wire the sources Reuses the shared Vulkan partitioner (`_clone_dim_order`/`expand_copy`/`full`* already registered); WebGPU kernels only. Co-authored-with: Claude Code. ghstack-source-id: 405026073 @exported-using-ghexport Differential Revision: [D111755131](https://our.internmc.facebook.com/intern/diff/D111755131/)
Pull Request resolved: #20930 Export-delegation + fp64 golden tests for the shape/copy (`dim_order`, `expand_copy`, `fill`) ops. Key changes: - `test/ops/{test_dim_order,test_expand_copy,test_fill}.py` — fp64 goldens + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026074 @exported-using-ghexport Differential Revision: [D111755126](https://our.internmc.facebook.com/intern/diff/D111755126/)
…d, requant) to the WebGPU backend Pull Request resolved: #20931 Add the 4-bit fine-tuning weight-path custom ops — `et_vk.linear_q4gsw_backward` (input grad dX), `et_vk.linear_dW` (weight grad dW — a generic fp32 weight-gradient matmul, not q4gsw-specific), and `et_vk.q4gsw_requant` (STE re-quant of fp32 latents → 4-bit codes) — that close the on-device training loop through a frozen 4-bit base. Key changes: - `runtime/ops/quantized_linear/{QuantizedLinearBackward,QuantizedLinearRequant}.cpp` + `LinearDw.cpp` + `q4gsw_{backward,requant}.wgsl` + `linear_dW.wgsl` — the three training kernels - `custom_ops_lib.py` — 3 custom-op defs (`linear_q4gsw_backward` + `register_autograd` on `linear_q4gsw`, `linear_dW`, `q4gsw_requant`) - `op_registry.py` — 3 `OpFeatures` - `CMakeLists.txt` `WEBGPU_SRCS` — wire the sources Training-only, WebGPU-only custom ops registered under the shared Vulkan partitioner; Vulkan has no kernels for them yet (a separate Vulkan-kernel track covers that). Guards mirror the sibling `Dw`/`Requant` ops (scales-rank + `K==0`). Co-authored-with: Claude Code. ghstack-source-id: 405026076 @exported-using-ghexport Differential Revision: [D111755130](https://our.internmc.facebook.com/intern/diff/D111755130/)
Pull Request resolved: #20932 Export-delegation + fp64 golden tests for the q4gsw training (`backward`, `dw`, `requant`) ops. Key changes: - `test/ops/{test_quantized_linear_backward,test_linear_q4gsw_dw,test_q4gsw_requant}.py` — fp64 dequant-matmul / weight-grad / re-quant goldens + a round-trip + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026079 @exported-using-ghexport Differential Revision: [D111755121](https://our.internmc.facebook.com/intern/diff/D111755121/)
…WebGPU backend Pull Request resolved: #20933 Add `et_vk.fused_ce` — a fused cross-entropy (loss + dlogits in one op) for the on-device training tail. Key changes: - `runtime/ops/fused_ce/` — fused-CE WGSL kernel (+ the `reduce` shader it depends on) + handler - `custom_ops_lib.py` — `fused_ce` custom-op def + `register_autograd` - `op_registry.py` — `OpFeatures` - `CMakeLists.txt` `WEBGPU_SRCS` — wire the source Training-only, WebGPU-only custom op registered under the shared Vulkan partitioner; no Vulkan kernel yet. Co-authored-with: Claude Code. ghstack-source-id: 405026078 @exported-using-ghexport Differential Revision: [D111755132](https://our.internmc.facebook.com/intern/diff/D111755132/)
Pull Request resolved: #20934 Export-delegation + fp64 golden tests for the fused_ce ops. Key changes: - `test/ops/test_fused_ce.py` — fp64 loss + dlogits goldens (incl. masked-label) + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026080 @exported-using-ghexport Differential Revision: [D111755119](https://our.internmc.facebook.com/intern/diff/D111755119/)
…p) to the WebGPU backend Pull Request resolved: #20935 Add `et_vk.adamw_step` — an on-GPU AdamW optimizer step (delegated in-place parameter update) for on-device training. Key changes: - `runtime/ops/adamw/` — elementwise AdamW-update WGSL kernel + handler - `custom_ops_lib.py` — `adamw_step` custom-op def (mutating, `Tensor(a!)`) - `op_registry.py` — `OpFeatures` (string key `et_vk::adamw_step`) - `CMakeLists.txt` `WEBGPU_SRCS` — wire the source Delegating the optimizer step to the GPU is new to ExecuTorch — its optimizers are host-side C++ (`extension/training/optimizer/adamw.cpp`). Training-only WebGPU custom op under the shared Vulkan partitioner; no Vulkan kernel yet. Co-authored-with: Claude Code. ghstack-source-id: 405026081 @exported-using-ghexport Differential Revision: [D111755116](https://our.internmc.facebook.com/intern/diff/D111755116/)
Pull Request resolved: #20936 Export-delegation + fp64 golden tests for the adamw (`adamw_step`) ops. Key changes: - `test/ops/test_adamw.py` — fp64 in-place param/m/v goldens (incl. non-zero weight-decay) + delegation checks — fp64 library-reference goldens + delegation checks (≥2 cases/op). Co-authored-with: Claude Code. ghstack-source-id: 405026082 @exported-using-ghexport Differential Revision: [D111755127](https://our.internmc.facebook.com/intern/diff/D111755127/)
…zer step) Pull Request resolved: #20937 Vulkan GLSL kernel + handler for the training custom op `et_vk.adamw_step` (elementwise in-place AdamW parameter update). Mirrors `impl/BinaryOp.cpp` elementwise structure + `unary_op.glsl` push-constant scalars; param/m/v are read-modify-write (`kReadWrite`). AOT registration already exists under the shared Vulkan partitioner. ghstack-source-id: 405026087 @exported-using-ghexport Differential Revision: [D111761777](https://our.internmc.facebook.com/intern/diff/D111761777/)
Pull Request resolved: #20938 Vulkan op-test golden for `et_vk.adamw_step` (ATen library-computed reference of the CPU-eager update; in-place param/m/v vs the kernel). 3 cases incl. non-zero weight-decay + a large 1-D tail. Wires `adamw_step_test` in `targets.bzl` + `CMakeLists.txt`. ghstack-source-id: 405030805 @exported-using-ghexport Differential Revision: [D111761775](https://our.internmc.facebook.com/intern/diff/D111761775/)
…put-grad) Pull Request resolved: #20943 **Adds the Vulkan `et_vk.linear_q4gsw_backward` kernel** — the input-gradient of the frozen 4-bit `linear_q4gsw` base, for on-device adapter training. Computes `d_x[M, K] = d_out[M, N] @ dequant(W)[N, K]`, contracting over N. **Problem:** `et_vk.linear_q4gsw_backward` is registered in the shared Vulkan partitioner (`custom_ops_lib.py` + `op_registry.py`) but Vulkan had no runtime kernel, so the op could not run. **Solution:** a 4M x 4K register-tiled GLSL kernel that reads the SAME W_4X8 block-packed weight the forward reads, re-deriving the nibble/scale addressing so the training loop stays consistent with the forward with no re-pack. `dequant(W[n, k]) = (code - 8) * scale`. Key changes: - `glsl/q4gsw_backward.{glsl,yaml}` — buffer x float; W_4X8 nibble unpack mirroring `glsl/q4gsw_linear_gemm__w_4x8.glsl` (even-N low nibble, odd-N high, `N4_padded` ivec4 stride, `[num_groups, N]` scales). - `impl/QuantizedLinearBackward.cpp` — reuses `prepack_q4_w_4x8_nc_buffer` + `prepack_q4_scales` from the forward; 1D tile dispatch `ceil(M/4) * ceil(K/4)` with a workgroup-count guard; `group_size` specialization constant. **Constraints:** buffer storage, fp32; `N % 4 == 0`, `K % 4 == 0`, `group_size % 4 == 0` (matches the forward prepack). Weight/scale layout identical to the forward. ghstack-source-id: 405059111 @exported-using-ghexport Differential Revision: [D111797529](https://our.internmc.facebook.com/intern/diff/D111797529/)
Pull Request resolved: #20944 **Correctness tests for the Vulkan `et_vk.linear_q4gsw_backward` kernel** (stacked above the op diff). **Coverage:** the golden `d_x = d_out @ dequant(W)` is computed with ATen (`matmul` over the library-dequantized weight), mirroring the CPU-eager `linear_q4gsw_backward_impl` in `custom_ops_lib.py` — never a hand-rolled matmul. The packed weight is fed as a constant `tensorref` so the op exercises the real `prepack_q4_w_4x8_nc_buffer` W_4X8 path. Cases: - `test_tile_aligned` — single group, tile-aligned M/N/K. - `test_grouped` — multiple quantization groups along K. - `test_odd_n4_partial_m` — `N % 8 != 0` (odd N4 -> padded W_4X8 stride) plus a partial-M tile (exercises the `min()` clamps). Also wires `quantized_linear_backward_test` into `targets.bzl` + `CMakeLists.txt` (mirrors the sibling `linear_q4gsw_dw_test`). ghstack-source-id: 405059117 @exported-using-ghexport Differential Revision: [D111797530](https://our.internmc.facebook.com/intern/diff/D111797530/)
…W_4X8) Pull Request resolved: #20945 **Adds the Vulkan `et_vk.q4gsw_requant` kernel** — straight-through re-quant of fp32 latent weights into the frozen-scale 4-bit codes, for on-device weight training. Writes the codes directly in the forward's W_4X8 layout so no per-step re-pack is needed. **Problem:** `et_vk.q4gsw_requant` is registered in the shared Vulkan partitioner but Vulkan had no runtime kernel. **Solution:** a GLSL kernel that quantizes `round(latent / scale)` clamped to `[-8, 7]` and packs the codes in the W_4X8 block layout the forward reads, reusing the exact byte-pair convention of `glsl/pack_q4_linear_weight__w_4x8.glsl` (even-N low nibble, odd-N high; one ivec4 per 4K x 8N block at `(k4, n8)`; OOB upper tile = the bias-zero `0x88888888`). A zero scale yields code 8 (no divide-by-zero), matching the eager reference. Key changes: - `glsl/q4gsw_requant.{glsl,yaml}` — buffer x float; 2D dispatch over `(k4, n8)`. - `impl/QuantizedLinearRequant.cpp` — prepacks the constant scales; output is the W_4X8 int buffer `[K4 * N4_padded * 2]`; `group_size` spec constant; dispatch-grid guard. **Design note (for review):** per the layout decision, requant writes W_4X8 to match the Vulkan forward. Today the forward and backward each prepack their own flat `[N, K/2]` weight internally, so nothing yet consumes an externally-produced W_4X8 buffer — closing the training loop needs a forward path that reads a mutable pre-packed weight (the weight-lifecycle follow-up). The op's AOT meta in `custom_ops_lib.py` still describes the flat `[N, K/2]` output and should be reconciled with this W_4X8 output. **Constraints:** buffer storage, fp32 latent/scales; `N % 4 == 0`, `K % 4 == 0`, `group_size % 4 == 0`. ghstack-source-id: 405059118 @exported-using-ghexport Differential Revision: [D111797527](https://our.internmc.facebook.com/intern/diff/D111797527/)
Pull Request resolved: #20946 **Correctness tests for the Vulkan `et_vk.q4gsw_requant` kernel** (stacked above the op diff). **Coverage:** the golden codes are computed with ATen (`round`/`clamp`, zero-scale -> code 8), mirroring `quant_nibble`, then packed into the expected W_4X8 int buffer with a small bit-packing reference (data-reshaping only, no hand-rolled math). The kernel output is compared int-for-int against that buffer, which locks the exact byte layout the forward reads. The latent is built as `code * scale` so `round()` is unambiguous (no `.5` tie-break divergence). Cases: - `test_tile_aligned` — single group, tile-aligned N/K. - `test_grouped` — multiple quantization groups along K. - `test_odd_n4` — `N % 8 != 0` (odd N4 -> padded stride + bias-zero OOB tile). - `test_zero_scale` — a zero scale must yield code 8, not a divide-by-zero. Also wires `q4gsw_requant_test` into `targets.bzl` + `CMakeLists.txt`. ghstack-source-id: 405059122 @exported-using-ghexport Differential Revision: [D111797526](https://our.internmc.facebook.com/intern/diff/D111797526/)
Pull Request resolved: #21005 Replace the hand-maintained explicit `WEBGPU_SRCS` op-handler list with `file(GLOB WEBGPU_OP_SRCS CONFIGURE_DEPENDS runtime/ops/*/*.cpp)` so adding a new op no longer requires editing this file (addresses review feedback). The five `runtime/*.cpp` sources and `runtime/ops/OperatorRegistry.cpp` (which sits directly under `ops/`, not a per-op subdir) stay explicit. `CONFIGURE_DEPENDS` re-globs at build time when op sources are added or removed. The glob resolves to exactly the 40 op handlers the explicit list enumerated (verified set-equal) — no op added or dropped, and static-init registration is order-independent under `--whole-archive`. Co-authored-with: Claude Code. ghstack-source-id: 405059133 @exported-using-ghexport Differential Revision: [D112482039](https://our.internmc.facebook.com/intern/diff/D112482039/)
This PR was created by the merge bot to help merge the original PR into the main branch.
ghstack PR number: #20917 by @JCNTH
^ Please use this as the source of truth for the PR details, comments, and reviews
ghstack PR base: https://github.com/pytorch/executorch/tree/gh/JCNTH/56/base
ghstack PR head: https://github.com/pytorch/executorch/tree/gh/JCNTH/56/head
Merge bot PR base: https://github.com/pytorch/executorch/tree/main
Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/JCNTH/56/orig
@diff-train-skip-merge