WebGPU: enable INT64 for Equal/Sub/Where/ReduceSum under enable_int64 flag to support Gemma4#29392
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the WebGPU EP kernel registration for Equal, Sub, Where, and ReduceSum so that INT64 type constraints are included when enable_int64=true, avoiding unintended CPU fallback (notably in graph-capture scenarios).
Changes:
- Converted
Equal/Sub/Where/ReduceSumfrom static macro/table-based registration to factory-function registration, enabling conditional INT64 type constraints. - Wired the new factory registrations into
RegisterKernels(enable_graph_capture, enable_int64)alongside existing conditional-int64 kernels (e.g., Cast/Unsqueeze/Expand/Range). - Preserved existing special-casing (e.g.,
ReduceSumv13+ axes input remainsOrtMemTypeCPUInput).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc | Removes static table entries for the 4 ops and registers them via RegisterKernels() with enable_int64 propagation. |
| onnxruntime/core/providers/webgpu/tensor/where.h | Declares Where kernel factory functions to support conditional INT64 registration. |
| onnxruntime/core/providers/webgpu/tensor/where.cc | Implements Where kernel factory functions and conditional type constraints. |
| onnxruntime/core/providers/webgpu/reduction/reduction_ops.h | Declares ReduceSum kernel factory functions to support conditional INT64 registration. |
| onnxruntime/core/providers/webgpu/reduction/reduction_ops.cc | Implements ReduceSum kernel factory functions with conditional INT64 type constraints and preserves CPU axes input for v13+. |
| onnxruntime/core/providers/webgpu/math/binary_elementwise_ops.h | Declares Equal/Sub kernel factory functions to support conditional INT64 registration. |
| onnxruntime/core/providers/webgpu/math/binary_elementwise_ops.cc | Implements Equal/Sub kernel factory functions with conditional INT64 type constraints. |
cc3a5a2 to
a6a73a4
Compare
Review: PR #29392 — WebGPU: enable INT64 for Equal/Sub/Where/ReduceSum under enable_int64 flag to support Gemma4LGTM overall. The What's good
Non-blocking items
|
… flag These four ops were forcing CPU fallback when inputs were INT64, preventing WebGPU graph capture. Converted from static macro registration to the factory function pattern (same as Cast/Unsqueeze/Expand/Range) so INT64 type constraints are included when enable_int64=true (which is always set when enable_graph_capture=true via GetKernelRegistry). Co-Authored-By: Claude <noreply@anthropic.com>
…4 changes - Use GetOpTypeConstraints(enable_int64, true) in Where factory functions instead of the local WhereOpTypeConstraints helper - Replace KernelDefBuilder::Create() with KernelDefBuilder() in Where and ReduceSum factory functions for consistency with other WebGPU kernels - Remove unused webgpu_execution_provider.h includes from where.cc, binary_elementwise_ops.cc, and reduction_ops.cc Co-Authored-By: Claude <noreply@anthropic.com>
…/Sub/Where/ReduceSum Add USE_WEBGPU-guarded unit tests with kEnableInt64=1 for all four ops: - Sub_int64_webgpu, Equal_int64_webgpu (element_wise_ops_test.cc) - Where_int64_webgpu (where_op_test.cc) - ReduceSum_int64_webgpu (reduction_ops_test.cc, opset 13 with axes input) Add NOTE comments on Sub, Equal, and ReduceSum factory functions documenting that int64 arithmetic uses i32 element type (low 32 bits only), matching the same limitation already documented in Range. Co-Authored-By: Claude <noreply@anthropic.com>
BinaryElementwise: disable vec4 vectorization for INT64 inputs (stored as vec2<u32>), use component=1 for correct buffer binding, and compute vec_size as element count rather than rounded-up vec4 count for INT64 outputs. Fix scalar input path to not apply .x dereference on top of GetByOffset which already extracts the i32 element for INT64. Where: fix non-broadcast INT64 path to use the correct element-per-thread shader instead of the vec4 fast path that truncates to i32. Add is_int64 to the cache hint to prevent float and INT64 shaders sharing a cache entry. Register shape indices for non-broadcast INT64 so BroadcastedIndicesToOffset has the required helpers available. Co-Authored-By: Claude <noreply@anthropic.com>
Equal with INT64 inputs and bool output was dispatching vec_size=(N+3)/4
threads but only reading element 0 of each input and broadcasting it as
a vec4 comparison. Fix: in the element-wise path, when is_int64_ and
output is Boolx4, explicitly read 4 consecutive elements per thread and
pack them into vec4<bool> before SetByOffset.
ReduceSum_int64_webgpu test declared output shape {} (scalar) but
ReduceSum opset-13 defaults to keepdims=1, so the output is {1}.
Co-Authored-By: Claude <noreply@anthropic.com>
…nds checks and update test to size=5 - Add `element_count` uniform to BinaryElementwiseProgram - Guard INT64+bool output lanes 1-3 with `if (base+Xu < n)` to avoid OOB reads when tensor size is not a multiple of 4 - Update Equal_int64_webgpu test from size=4 to size=5 to exercise tail case
95d6187 to
4b96d3b
Compare
…lementwise - Fix Equal INT64 broadcast bug: remove erroneous `is_int64 ||` from the element-wise condition in ComputeInternal so non-scalar INT64 broadcast correctly routes to the broadcast path (vec4<i32>==vec4<i32> works fine) - Extract `can_use_element_wise_mode` to replace duplicated scalar/shape condition, shared by both the INT64+bool fast path and the general path - Add ORT_ENFORCE in ReduceSum to guard against future INT64 vectorization - Add comment in where.cc explaining why INT64 is excluded from the fast path - Add Equal tests: size%4==0, lhs_scalar, rhs_scalar, broadcast, broadcast_size%4==0 - Add ReduceSum test: size%4==0 - Add Where broadcast test Co-Authored-By: Claude <noreply@anthropic.com>
The fast path uses global_idx as a vec4 index (vec_size=output_size/4), so c_input.GetByOffset reads a packed-bool u32 as vec4<bool> directly. For INT64, vec_size=output_size (element index), so the same GetByOffset call would read the wrong condition word — hence the is_int64_ branch uses explicit bit extraction via offset_c/4u and byte masking. Co-Authored-By: Claude <noreply@anthropic.com>
- where.cc: use c_input.GetByOffset helper instead of raw c_data access - reduction_ops.cc: replace tautological ORT_ENFORCE with meaningful framework capability check (ToProgramVariableDataType(INT64,4)==InvalidType) - binary_elementwise_ops.cc: clarify that INT64 broadcast uses this path correctly with component=1 Co-Authored-By: Claude <noreply@anthropic.com>
…ec4 structure Instead of a dedicated code path for INT64+bool output (Equal), integrate INT64 handling into the existing element-wise and broadcast structure: - Read 4 component-1 elements into vec4 (splat for scalar inputs) - Reuse the generic expression_ for all types - Split output write: bool output uses packed SetByOffset, INT64 output writes element-by-element with bounds guards - Remove ORT_ENFORCE from ReduceSum (reviewer feedback: unnecessary guard) - Add Sub_webgpu_int64_broadcast test case Co-Authored-By: Claude <noreply@anthropic.com>
- Rename is_int64_ to is_int64_input_ and pass is_int64_output to the program instead of computing it locally from output var_type - Rename is_int64/is_output_int64 to is_int64_input/is_int64_output in ComputeInternal for consistency - Add per-lane bounds guards for INT64 input reads in element-wise path (matching cast.cc pattern) to avoid OOB reads when size % 4 != 0 Co-Authored-By: Claude <noreply@anthropic.com>
…se in output Move `base` and `element_count` declarations to a shared location before the element-wise/broadcast branch so both paths can reference them in the INT64 output block. Use `base` instead of `global_idx * 4u` for clarity. Co-Authored-By: Claude <noreply@anthropic.com>
b9741c3 to
0371cb5
Compare
Summary
These four ops were forcing CPU fallback when inputs were INT64, preventing WebGPU graph capture. Converted from static macro registration to the factory function pattern (same as Cast/Unsqueeze/Expand/Range) so INT64 type constraints are included when
enable_int64=true.enable_int64is alwaystruewhenenable_graph_capture=true—GetKernelRegistrycallsRegisterKernels(true, true)unconditionally in that path — so this fix has no effect on the default (non-graph-capture) execution path.Changed ops
EqualSubWhereReduceSumApproach
Each op follows the established factory function pattern:
CreateXVersionedKernelInfo<Start, End>(bool enable_int64)/CreateXKernelInfo<Since>(bool enable_int64)build_kernel_create_info_function_table[]RegisterKernels()alongside Cast/Unsqueeze/Expand/RangeReduceSumversion 13+ retainsInputMemoryType(OrtMemTypeCPUInput, 1)(axes input must be on CPU) — unchanged from the original registration.Wherefactory functions delegate toGetOpTypeConstraints(enable_int64, /*enable_bool=*/true)rather than a local duplicate type list. All three files (where.cc,binary_elementwise_ops.cc,reduction_ops.cc) use stack-allocatedKernelDefBuilder()consistent with other WebGPU factory functions, and drop the unusedwebgpu_execution_provider.hinclude.Runtime fixes for INT64 inference
BinaryElementwise(Sub,Equal) andWhererequired additional shader-level fixes to correctly handle INT64 tensors at inference time:vec2<u32>(8 bytes/element) and has no vec4 representation. Vectorization is now disabled when either input is INT64, andcomponent=1is used for correct buffer binding.vec_sizeis computed as element count (not rounded-up vec4 count) for INT64 outputs..xdereference on top ofGetByOffset, which already extracts thei32element for INT64.Wherenow uses the element-per-thread shader path instead of the vec4 fast path that would truncate values to i32.is_int64is included in theWherecache hint, preventing float and INT64 shaders from incorrectly sharing a cached entry.WheresoBroadcastedIndicesToOffsethas the required helpers available.vec4<bool>before writing to the bool output buffer. Previously, only element 0 was read and broadcast across all 4 positions of the comparison, giving wrong results.Testing
WebGpuExecutionProvider(previously ~20 nodes fell back to CPU due to INT64 Equal/Sub/Where/ReduceSum)USE_WEBGPU-guarded unit tests added for all four ops withkEnableInt64=1:Sub_int64_webgpu,Equal_int64_webgpu,Where_int64_webgpu,ReduceSum_int64_webgpuRelated