diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 8c678b1a15ef..98e90430f0b8 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -29,7 +29,7 @@ "webgpu/shader/execution/binary/f32_multiplication.bin": "40527d93", "webgpu/shader/execution/binary/f32_remainder.bin": "d8738967", "webgpu/shader/execution/binary/f32_subtraction.bin": "f5453a8", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "37440f39", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "7fb5b26a", "webgpu/shader/execution/binary/i32_comparison.bin": "9679ab05", "webgpu/shader/execution/binary/u32_arithmetic.bin": "c9fe0078", "webgpu/shader/execution/binary/u32_comparison.bin": "7d0ba49", diff --git a/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin index bacd4c0c54ea..381ff7b6d63d 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin differ diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts index ca7ca879f52a..2ec09770a2d9 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts @@ -81,6 +81,26 @@ export const d = makeCaseCache('binary/i32_arithmetic', { remainder_const: () => { return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_remainder_const); }, + // Negative dividends across non-power-of-two moduli. The default sparse i32 + // range only exercises the moduli 10 and 256, so it misses value-dependent + // codegen paths (e.g. modulus 768) where some implementations compute signed + // `%` as if it were unsigned (e.g. lowering to a poison-prone `OpSRem`), + // yielding 255 instead of -1 for `-1 % 768`. See + // https://github.com/gfx-rs/wgpu/issues/8191. + remainder_negative_non_const: () => { + return generateBinaryToI32Cases( + [-1, -2, -5, -255, -256, -768, -769, -1000, -1234567], + [3, 7, 100, 256, 768, 1000], + i32_remainder_non_const + ); + }, + remainder_negative_const: () => { + return generateBinaryToI32Cases( + [-1, -2, -5, -255, -256, -768, -769, -1000, -1234567], + [3, 7, 100, 256, 768, 1000], + i32_remainder_const + ); + }, addition_scalar_vector2: () => { return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_add); }, diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts index 05eb8ddc10c6..2caba3f586b0 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts @@ -170,6 +170,28 @@ Expression: x %= y await run(t, compoundBinary('%='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); +g.test('remainder_negative') + .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr') + .desc( + ` +Expression: x % y, with negative dividends across non-power-of-two moduli. + +Regression coverage for implementations that compute signed remainder as +unsigned for negative operands (e.g. lowering to a poison-prone OpSRem without +VK_KHR_maintenance8), which returns 255 instead of -1 for -1 % 768. +See https://github.com/gfx-rs/wgpu/issues/8191. +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'remainder_negative_const' : 'remainder_negative_non_const' + ); + await run(t, binary('%'), [Type.i32, Type.i32], Type.i32, t.params, cases); + }); + g.test('addition_scalar_vector') .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr') .desc(