From 5f049af73f5a90ae9db1b6ac0c2d9611e670bc9e Mon Sep 17 00:00:00 2001
From: Brandon Jones <bajones@chromium.org>
Date: Wed, 13 Mar 2024 15:01:33 -0700
Subject: [PATCH 1/4] Add validation test for min/max builtin functions

---
 src/webgpu/listing_meta.json                  |  6 ++
 .../expression/call/builtin/max.spec.ts       | 85 +++++++++++++++++++
 .../expression/call/builtin/min.spec.ts       | 85 +++++++++++++++++++
 3 files changed, 176 insertions(+)
 create mode 100644 src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
 create mode 100644 src/webgpu/shader/validation/expression/call/builtin/min.spec.ts

diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json
index e6bf7b75b652..8305c360c7f5 100644
--- a/src/webgpu/listing_meta.json
+++ b/src/webgpu/listing_meta.json
@@ -1909,6 +1909,12 @@
   "webgpu:shader,validation,expression,call,builtin,log:integer_argument:*": { "subcaseMS": 1.134 },
   "webgpu:shader,validation,expression,call,builtin,log:must_use:*": { "subcaseMS": 1597.590 },
   "webgpu:shader,validation,expression,call,builtin,log:values:*": { "subcaseMS": 0.291 },
+  "webgpu:shader,validation,expression,call,builtin,max:bad_args:*": { "subcaseMS": 79.423 },
+  "webgpu:shader,validation,expression,call,builtin,max:must_use:*": { "subcaseMS": 8.286 },
+  "webgpu:shader,validation,expression,call,builtin,max:values:*": { "subcaseMS": 260241.074 },
+  "webgpu:shader,validation,expression,call,builtin,min:bad_args:*": { "subcaseMS": 40.137 },
+  "webgpu:shader,validation,expression,call,builtin,min:must_use:*": { "subcaseMS": 3.899 },
+  "webgpu:shader,validation,expression,call,builtin,min:values:*": { "subcaseMS": 246733.594 },
   "webgpu:shader,validation,expression,call,builtin,modf:integer_argument:*": { "subcaseMS": 1.089 },
   "webgpu:shader,validation,expression,call,builtin,modf:values:*": { "subcaseMS": 1.866 },
   "webgpu:shader,validation,expression,call,builtin,pack4xI8:args:*": { "subcaseMS": 36.226 },
diff --git a/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
new file mode 100644
index 000000000000..a1cfabeb00c0
--- /dev/null
+++ b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
@@ -0,0 +1,85 @@
+const builtin = 'max';
+export const description = `
+Validation tests for the ${builtin}() builtin.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js';
+import {
+  Type,
+  kAllNumericScalarsAndVectors,
+  scalarTypeOf,
+} from '../../../../../util/conversion.js';
+import { ShaderValidationTest } from '../../../shader_validation_test.js';
+
+import {
+  fullRangeForType,
+  kConstantAndOverrideStages,
+  stageSupportsType,
+  validateConstOrOverrideBuiltinEval,
+} from './const_override_validation.js';
+
+export const g = makeTestGroup(ShaderValidationTest);
+
+const kValuesTypes = objectsToRecord(kAllNumericScalarsAndVectors);
+
+g.test('values')
+  .desc(
+    `
+Validates that constant evaluation and override evaluation of ${builtin}() never errors
+`
+  )
+  .params(u =>
+    u
+      .combine('stage', kConstantAndOverrideStages)
+      .combine('type', keysOf(kValuesTypes))
+      .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type]))
+      .beginSubcases()
+      .expand('a', u => fullRangeForType(kValuesTypes[u.type]))
+      .expand('b', u => fullRangeForType(kValuesTypes[u.type]))
+  )
+  .beforeAllSubcases(t => {
+    if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) {
+      t.selectDeviceOrSkipTestCase('shader-f16');
+    }
+  })
+  .fn(t => {
+    const type = kValuesTypes[t.params.type];
+    const expectedResult = true; // should never error
+    validateConstOrOverrideBuiltinEval(
+      t,
+      builtin,
+      expectedResult,
+      [type.create(t.params.a), type.create(t.params.b)],
+      t.params.stage
+    );
+  });
+
+const kGoodArgs = '(1.1, 2.2)';
+const kBadArgs = {
+  no_parens: '',
+  // Bad number of args
+  '0args': '()',
+  '1arg': '(1.0)',
+  // Bad value for arg 0
+  '0bool': '(false, 1.0)',
+  '0array': '(array(1.1,2.2), 1.0)',
+  '0struct': '(modf(2.2), 1.0)',
+  // Bad value for arg 1
+  '1bool': '(1.0, true)',
+  '1array': '(1.0, array(1.1,2.2))',
+  '1struct': '(1.0, modf(2.2))',
+};
+
+g.test('bad_args')
+  .desc(`Test compilation failure of ${builtin} with bad arguments`)
+  .params(u => u.combine('arg', keysOf(kBadArgs)))
+  .fn(t => {
+    t.expectCompileResult(false, `const c = ${builtin}${kBadArgs[t.params.arg]};`);
+  });
+
+g.test('must_use')
+  .desc(`Result of ${builtin} must be used`)
+  .fn(t => {
+    t.expectCompileResult(false, `fn f() { ${builtin}${kGoodArgs}; }`);
+  });
diff --git a/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
new file mode 100644
index 000000000000..f31d8971e133
--- /dev/null
+++ b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
@@ -0,0 +1,85 @@
+const builtin = 'min';
+export const description = `
+Validation tests for the ${builtin}() builtin.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js';
+import {
+  Type,
+  kAllNumericScalarsAndVectors,
+  scalarTypeOf,
+} from '../../../../../util/conversion.js';
+import { ShaderValidationTest } from '../../../shader_validation_test.js';
+
+import {
+  fullRangeForType,
+  kConstantAndOverrideStages,
+  stageSupportsType,
+  validateConstOrOverrideBuiltinEval,
+} from './const_override_validation.js';
+
+export const g = makeTestGroup(ShaderValidationTest);
+
+const kValuesTypes = objectsToRecord(kAllNumericScalarsAndVectors);
+
+g.test('values')
+  .desc(
+    `
+Validates that constant evaluation and override evaluation of ${builtin}() never errors
+`
+  )
+  .params(u =>
+    u
+      .combine('stage', kConstantAndOverrideStages)
+      .combine('type', keysOf(kValuesTypes))
+      .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type]))
+      .beginSubcases()
+      .expand('a', u => fullRangeForType(kValuesTypes[u.type]))
+      .expand('b', u => fullRangeForType(kValuesTypes[u.type]))
+  )
+  .beforeAllSubcases(t => {
+    if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) {
+      t.selectDeviceOrSkipTestCase('shader-f16');
+    }
+  })
+  .fn(t => {
+    const type = kValuesTypes[t.params.type];
+    const expectedResult = true; // should never error
+    validateConstOrOverrideBuiltinEval(
+      t,
+      builtin,
+      expectedResult,
+      [type.create(t.params.a), type.create(t.params.b)],
+      t.params.stage
+    );
+  });
+
+const kGoodArgs = '(1.1, 2.2)';
+const kBadArgs = {
+  no_parens: '',
+  // Bad number of args
+  '0args': '()',
+  '1arg': '(1.0)',
+  // Bad value for arg 0
+  '0bool': '(false, 1.0)',
+  '0array': '(array(1.1,2.2), 1.0)',
+  '0struct': '(modf(2.2), 1.0)',
+  // Bad value for arg 1
+  '1bool': '(1.0, true)',
+  '1array': '(1.0, array(1.1,2.2))',
+  '1struct': '(1.0, modf(2.2))',
+};
+
+g.test('bad_args')
+  .desc(`Test compilation failure of ${builtin} with bad arguments`)
+  .params(u => u.combine('arg', keysOf(kBadArgs)))
+  .fn(t => {
+    t.expectCompileResult(false, `const c = ${builtin}${kBadArgs[t.params.arg]};`);
+  });
+
+g.test('must_use')
+  .desc(`Result of ${builtin} must be used`)
+  .fn(t => {
+    t.expectCompileResult(false, `fn f() { ${builtin}${kGoodArgs}; }`);
+  });

From fcb464a3964abaeca1a920e5d72283c7bf6a9cbf Mon Sep 17 00:00:00 2001
From: Brandon Jones <bajones@chromium.org>
Date: Thu, 14 Mar 2024 15:23:51 -0700
Subject: [PATCH 2/4] Address feedback from @dneto

---
 src/webgpu/listing_meta.json                  |  2 +
 .../expression/call/builtin/max.spec.ts       | 40 +++++++++++--------
 .../expression/call/builtin/min.spec.ts       | 40 +++++++++++--------
 3 files changed, 48 insertions(+), 34 deletions(-)

diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json
index 8305c360c7f5..c1466abd0087 100644
--- a/src/webgpu/listing_meta.json
+++ b/src/webgpu/listing_meta.json
@@ -1909,9 +1909,11 @@
   "webgpu:shader,validation,expression,call,builtin,log:integer_argument:*": { "subcaseMS": 1.134 },
   "webgpu:shader,validation,expression,call,builtin,log:must_use:*": { "subcaseMS": 1597.590 },
   "webgpu:shader,validation,expression,call,builtin,log:values:*": { "subcaseMS": 0.291 },
+  "webgpu:shader,validation,expression,call,builtin,max:args:*": { "subcaseMS": 77.529 },
   "webgpu:shader,validation,expression,call,builtin,max:bad_args:*": { "subcaseMS": 79.423 },
   "webgpu:shader,validation,expression,call,builtin,max:must_use:*": { "subcaseMS": 8.286 },
   "webgpu:shader,validation,expression,call,builtin,max:values:*": { "subcaseMS": 260241.074 },
+  "webgpu:shader,validation,expression,call,builtin,min:args:*": { "subcaseMS": 67.260 },
   "webgpu:shader,validation,expression,call,builtin,min:bad_args:*": { "subcaseMS": 40.137 },
   "webgpu:shader,validation,expression,call,builtin,min:must_use:*": { "subcaseMS": 3.899 },
   "webgpu:shader,validation,expression,call,builtin,min:values:*": { "subcaseMS": 246733.594 },
diff --git a/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
index a1cfabeb00c0..7a1df57e5795 100644
--- a/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
+++ b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
@@ -55,31 +55,37 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
     );
   });
 
-const kGoodArgs = '(1.1, 2.2)';
-const kBadArgs = {
-  no_parens: '',
+const kArgCases = {
+  'good': '(1.1, 2.2)',
+  bad_no_parens: '',
   // Bad number of args
-  '0args': '()',
-  '1arg': '(1.0)',
+  'bad_0args': '()',
+  'bad_1arg': '(1.0)',
+  'bad_3arg': '(1.0, 2.0, 3.0)',
   // Bad value for arg 0
-  '0bool': '(false, 1.0)',
-  '0array': '(array(1.1,2.2), 1.0)',
-  '0struct': '(modf(2.2), 1.0)',
-  // Bad value for arg 1
-  '1bool': '(1.0, true)',
-  '1array': '(1.0, array(1.1,2.2))',
-  '1struct': '(1.0, modf(2.2))',
+  'bad_0bool': '(false, 1.0)',
+  'bad_0array': '(array(1.1,2.2), 1.0)',
+  'bad_0struct': '(modf(2.2), 1.0)',
+  // Bad value type for arg 1
+  'bad_1bool': '(1.0, true)',
+  'bad_1array': '(1.0, array(1.1,2.2))',
+  'bad_1struct': '(1.0, modf(2.2))',
 };
 
-g.test('bad_args')
-  .desc(`Test compilation failure of ${builtin} with bad arguments`)
-  .params(u => u.combine('arg', keysOf(kBadArgs)))
+g.test('args')
+  .desc(`Test compilation failure of ${builtin} with variously shaped and typed arguments`)
+  .params(u => u.combine('arg', keysOf(kArgCases)))
   .fn(t => {
-    t.expectCompileResult(false, `const c = ${builtin}${kBadArgs[t.params.arg]};`);
+    t.expectCompileResult(
+      t.params.arg === 'good',
+      `const c = ${builtin}${kArgCases[t.params.arg]};`
+    );
   });
 
 g.test('must_use')
   .desc(`Result of ${builtin} must be used`)
+  .params(u => u.combine('use', [true, false]))
   .fn(t => {
-    t.expectCompileResult(false, `fn f() { ${builtin}${kGoodArgs}; }`);
+    const use_it = t.params.use ? '_ = ' : '';
+    t.expectCompileResult(t.params.use, `fn f() { ${use_it}${builtin}${kArgCases['good']}; }`);
   });
diff --git a/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
index f31d8971e133..d4b3673b7d0e 100644
--- a/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
+++ b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
@@ -55,31 +55,37 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
     );
   });
 
-const kGoodArgs = '(1.1, 2.2)';
-const kBadArgs = {
-  no_parens: '',
+const kArgCases = {
+  'good': '(1.1, 2.2)',
+  bad_no_parens: '',
   // Bad number of args
-  '0args': '()',
-  '1arg': '(1.0)',
+  'bad_0args': '()',
+  'bad_1arg': '(1.0)',
+  'bad_3arg': '(1.0, 2.0, 3.0)',
   // Bad value for arg 0
-  '0bool': '(false, 1.0)',
-  '0array': '(array(1.1,2.2), 1.0)',
-  '0struct': '(modf(2.2), 1.0)',
-  // Bad value for arg 1
-  '1bool': '(1.0, true)',
-  '1array': '(1.0, array(1.1,2.2))',
-  '1struct': '(1.0, modf(2.2))',
+  'bad_0bool': '(false, 1.0)',
+  'bad_0array': '(array(1.1,2.2), 1.0)',
+  'bad_0struct': '(modf(2.2), 1.0)',
+  // Bad value type for arg 1
+  'bad_1bool': '(1.0, true)',
+  'bad_1array': '(1.0, array(1.1,2.2))',
+  'bad_1struct': '(1.0, modf(2.2))',
 };
 
-g.test('bad_args')
-  .desc(`Test compilation failure of ${builtin} with bad arguments`)
-  .params(u => u.combine('arg', keysOf(kBadArgs)))
+g.test('args')
+  .desc(`Test compilation failure of ${builtin} with variously shaped and typed arguments`)
+  .params(u => u.combine('arg', keysOf(kArgCases)))
   .fn(t => {
-    t.expectCompileResult(false, `const c = ${builtin}${kBadArgs[t.params.arg]};`);
+    t.expectCompileResult(
+      t.params.arg === 'good',
+      `const c = ${builtin}${kArgCases[t.params.arg]};`
+    );
   });
 
 g.test('must_use')
   .desc(`Result of ${builtin} must be used`)
+  .params(u => u.combine('use', [true, false]))
   .fn(t => {
-    t.expectCompileResult(false, `fn f() { ${builtin}${kGoodArgs}; }`);
+    const use_it = t.params.use ? '_ = ' : '';
+    t.expectCompileResult(t.params.use, `fn f() { ${use_it}${builtin}${kArgCases['good']}; }`);
   });

From 0e90becc854a38c80d1c84419a11df4516c7dfee Mon Sep 17 00:00:00 2001
From: Brandon Jones <tojiro@gmail.com>
Date: Fri, 15 Mar 2024 13:34:05 -0700
Subject: [PATCH 3/4] Reduce number of values tested (was timing out in
 browser)

---
 .../expression/call/builtin/max.spec.ts       | 24 +++++++++----------
 .../expression/call/builtin/min.spec.ts       | 24 +++++++++----------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
index 7a1df57e5795..f32589fcf673 100644
--- a/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
+++ b/src/webgpu/shader/validation/expression/call/builtin/max.spec.ts
@@ -35,8 +35,8 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
       .combine('type', keysOf(kValuesTypes))
       .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type]))
       .beginSubcases()
-      .expand('a', u => fullRangeForType(kValuesTypes[u.type]))
-      .expand('b', u => fullRangeForType(kValuesTypes[u.type]))
+      .expand('a', u => fullRangeForType(kValuesTypes[u.type], 5))
+      .expand('b', u => fullRangeForType(kValuesTypes[u.type], 5))
   )
   .beforeAllSubcases(t => {
     if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) {
@@ -56,20 +56,20 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
   });
 
 const kArgCases = {
-  'good': '(1.1, 2.2)',
+  good: '(1.1, 2.2)',
   bad_no_parens: '',
   // Bad number of args
-  'bad_0args': '()',
-  'bad_1arg': '(1.0)',
-  'bad_3arg': '(1.0, 2.0, 3.0)',
+  bad_0args: '()',
+  bad_1arg: '(1.0)',
+  bad_3arg: '(1.0, 2.0, 3.0)',
   // Bad value for arg 0
-  'bad_0bool': '(false, 1.0)',
-  'bad_0array': '(array(1.1,2.2), 1.0)',
-  'bad_0struct': '(modf(2.2), 1.0)',
+  bad_0bool: '(false, 1.0)',
+  bad_0array: '(array(1.1,2.2), 1.0)',
+  bad_0struct: '(modf(2.2), 1.0)',
   // Bad value type for arg 1
-  'bad_1bool': '(1.0, true)',
-  'bad_1array': '(1.0, array(1.1,2.2))',
-  'bad_1struct': '(1.0, modf(2.2))',
+  bad_1bool: '(1.0, true)',
+  bad_1array: '(1.0, array(1.1,2.2))',
+  bad_1struct: '(1.0, modf(2.2))',
 };
 
 g.test('args')
diff --git a/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
index d4b3673b7d0e..2222c44e9228 100644
--- a/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
+++ b/src/webgpu/shader/validation/expression/call/builtin/min.spec.ts
@@ -35,8 +35,8 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
       .combine('type', keysOf(kValuesTypes))
       .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type]))
       .beginSubcases()
-      .expand('a', u => fullRangeForType(kValuesTypes[u.type]))
-      .expand('b', u => fullRangeForType(kValuesTypes[u.type]))
+      .expand('a', u => fullRangeForType(kValuesTypes[u.type], 5))
+      .expand('b', u => fullRangeForType(kValuesTypes[u.type], 5))
   )
   .beforeAllSubcases(t => {
     if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) {
@@ -56,20 +56,20 @@ Validates that constant evaluation and override evaluation of ${builtin}() never
   });
 
 const kArgCases = {
-  'good': '(1.1, 2.2)',
+  good: '(1.1, 2.2)',
   bad_no_parens: '',
   // Bad number of args
-  'bad_0args': '()',
-  'bad_1arg': '(1.0)',
-  'bad_3arg': '(1.0, 2.0, 3.0)',
+  bad_0args: '()',
+  bad_1arg: '(1.0)',
+  bad_3arg: '(1.0, 2.0, 3.0)',
   // Bad value for arg 0
-  'bad_0bool': '(false, 1.0)',
-  'bad_0array': '(array(1.1,2.2), 1.0)',
-  'bad_0struct': '(modf(2.2), 1.0)',
+  bad_0bool: '(false, 1.0)',
+  bad_0array: '(array(1.1,2.2), 1.0)',
+  bad_0struct: '(modf(2.2), 1.0)',
   // Bad value type for arg 1
-  'bad_1bool': '(1.0, true)',
-  'bad_1array': '(1.0, array(1.1,2.2))',
-  'bad_1struct': '(1.0, modf(2.2))',
+  bad_1bool: '(1.0, true)',
+  bad_1array: '(1.0, array(1.1,2.2))',
+  bad_1struct: '(1.0, modf(2.2))',
 };
 
 g.test('args')

From fee7357bebd67e19c5cbb3cf5df3314f6143a8a6 Mon Sep 17 00:00:00 2001
From: Brandon Jones <tojiro@gmail.com>
Date: Fri, 15 Mar 2024 13:45:33 -0700
Subject: [PATCH 4/4] Removing non-existent tests from listing_meta.json

---
 src/webgpu/listing_meta.json | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json
index c1466abd0087..591636e87af9 100644
--- a/src/webgpu/listing_meta.json
+++ b/src/webgpu/listing_meta.json
@@ -1910,11 +1910,9 @@
   "webgpu:shader,validation,expression,call,builtin,log:must_use:*": { "subcaseMS": 1597.590 },
   "webgpu:shader,validation,expression,call,builtin,log:values:*": { "subcaseMS": 0.291 },
   "webgpu:shader,validation,expression,call,builtin,max:args:*": { "subcaseMS": 77.529 },
-  "webgpu:shader,validation,expression,call,builtin,max:bad_args:*": { "subcaseMS": 79.423 },
   "webgpu:shader,validation,expression,call,builtin,max:must_use:*": { "subcaseMS": 8.286 },
   "webgpu:shader,validation,expression,call,builtin,max:values:*": { "subcaseMS": 260241.074 },
   "webgpu:shader,validation,expression,call,builtin,min:args:*": { "subcaseMS": 67.260 },
-  "webgpu:shader,validation,expression,call,builtin,min:bad_args:*": { "subcaseMS": 40.137 },
   "webgpu:shader,validation,expression,call,builtin,min:must_use:*": { "subcaseMS": 3.899 },
   "webgpu:shader,validation,expression,call,builtin,min:values:*": { "subcaseMS": 246733.594 },
   "webgpu:shader,validation,expression,call,builtin,modf:integer_argument:*": { "subcaseMS": 1.089 },