summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/parse/literal.spec.ts
blob: 0c5c820040240a287f455ef3fa20a54aa026cc83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
export const description = `Validation tests for literals`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { ShaderValidationTest } from '../shader_validation_test.js';

export const g = makeTestGroup(ShaderValidationTest);

g.test('bools')
  .desc(`Test that valid bools are accepted.`)
  .params(u => u.combine('val', ['true', 'false']).beginSubcases())
  .fn(t => {
    const code = `var test = ${t.params.val};`;
    t.expectCompileResult(true, t.wrapInEntryPoint(code));
  });

const kAbstractIntNonNegative = new Set([
  '0x123', // hex number
  '123', // signed number, no suffix
  '0', // zero
  '0x3f', // hex with 'f' as last character
  '2147483647', // max signed int
]);

const kAbstractIntNegative = new Set([
  '-0x123', // hex number
  '-123', // signed number, no suffix
  '-0x3f', // hex with 'f' as last character
  '-2147483647', // nagative of max signed int
  '-2147483648', // min signed int
]);

const kI32 = new Set([
  '94i', // signed number
  '2147483647i', // max signed int
  '-2147483647i', // min parsable signed int
  'i32(-2147483648)', // min signed int
]);

const kU32 = new Set([
  '42u', // unsigned number
  '0u', // min unsigned int
  '4294967295u', // max unsigned int
]);

{
  const kValidIntegers = new Set([
    ...kAbstractIntNonNegative,
    ...kAbstractIntNegative,
    ...kI32,
    ...kU32,
  ]);
  const kInvalidIntegers = new Set([
    '0123', // Integer does not start with zero
    '2147483648i', // max signed int + 1
    '-2147483649i', // min signed int - 1
    '4294967295', // a untyped lhs will be i32, so this is too big
    '4294967295i', // max unsigned int with i suffix
    '4294967296u', // max unsigned int + 1
    '-1u', // negative unsigned
  ]);
  g.test('abstract_int')
    .desc(`Test that valid integers are accepted, and invalid integers are rejected.`)
    .params(u =>
      u.combine('val', new Set([...kValidIntegers, ...kInvalidIntegers])).beginSubcases()
    )
    .fn(t => {
      const code = `var test = ${t.params.val};`;
      t.expectCompileResult(kValidIntegers.has(t.params.val), t.wrapInEntryPoint(code));
    });
}

{
  const kValidI32 = new Set([...kAbstractIntNonNegative, ...kAbstractIntNegative, ...kI32]);
  const kInvalidI32 = new Set([
    ...kU32,
    '2147483648', // max signed int + 1
    '2147483648i', // max signed int + 1
    '-2147483649', // min signed int - 1
    '-2147483649i', // min signed int - 1
    '1.0', // no conversion from float
    '1.0f', // no conversion from float
    '1.0h', // no conversion from float
  ]);
  g.test('i32')
    .desc(`Test that valid signed integers are accepted, and invalid signed integers are rejected.`)
    .params(u => u.combine('val', new Set([...kValidI32, ...kInvalidI32])).beginSubcases())
    .beforeAllSubcases(t => {
      if (t.params.val.includes('h')) {
        t.selectDeviceOrSkipTestCase('shader-f16');
      }
    })
    .fn(t => {
      const { val } = t.params;
      const code = `var test: i32 = ${val};`;
      const extensionList = val.includes('h') ? ['f16'] : [];
      t.expectCompileResult(kValidI32.has(val), t.wrapInEntryPoint(code, extensionList));
    });
}

{
  const kValidU32 = new Set([
    ...kAbstractIntNonNegative,
    ...kU32,
    '4294967295', // max unsigned
  ]);
  const kInvalidU32 = new Set([
    ...kAbstractIntNegative,
    ...kI32,
    '4294967296', // max unsigned int + 1
    '4294967296u', // min unsigned int + 1
    '-1', // min unsigned int - 1
    '1.0', // no conversion from float
    '1.0f', // no conversion from float
    '1.0h', // no conversion from float
  ]);
  g.test('u32')
    .desc(
      `Test that valid unsigned integers are accepted, and invalid unsigned integers are rejected.`
    )
    .params(u => u.combine('val', new Set([...kValidU32, ...kInvalidU32])).beginSubcases())
    .beforeAllSubcases(t => {
      if (t.params.val.includes('h')) {
        t.selectDeviceOrSkipTestCase('shader-f16');
      }
    })
    .fn(t => {
      const { val } = t.params;
      const code = `var test: u32 = ${val};`;
      const extensionList = val.includes('h') ? ['f16'] : [];
      t.expectCompileResult(kValidU32.has(val), t.wrapInEntryPoint(code, extensionList));
    });
}

const kF32 = new Set([
  '0f', // Zero float
  '0.0f', // Zero float
  '12.223f', // float value
  '12.f', // .f
  '.12f', // No leading number with a f
  '2.4e+4f', // Positive exponent with f suffix
  '2.4e-2f', // Negative exponent with f suffix
  '2.e+4f', // Exponent without decimals
  '1e-4f', // Exponennt without decimal point
  '0x1P+4f', // Hex float no decimal
]);

const kF16 = new Set([
  '0h', // Zero half
  '1h', // Half no decimal
  '.1h', // Half no leading value
  '1.1e2h', // Exponent half no sign
  '1.1E+2h', // Exponent half, plus (uppercase E)
  '2.4e-2h', // Exponent half, negative
  '0xep2h', // Hexfloat half lower case p
  '0xEp-2h', // Hexfloat uppcase hex value
  '0x3p+2h', // Hex float half positive exponent
  '0x3.2p+2h', // Hex float with decimal half
]);

const kAbstractFloat = new Set([
  '0.0', // Zero float without suffix
  '.0', // Zero float without leading value
  '12.', // No decimal points
  '00012.', // Leading zeros allowed
  '.12', // No leading digits
  '1.2e2', // Exponent without sign (lowercase e)
  '1.2E2', // Exponent without sign (uppercase e)
  '1.2e+2', // positive exponent
  '2.4e-2', // Negative exponent
  '.1e-2', // Exponent without leading number
  '0x.3', // Hex float, lowercase X
  '0X.3', // Hex float, uppercase X
  '0xa.fp+2', // Hex float, lowercase p
  '0xa.fP+2', // Hex float, uppercase p
  '0xE.fp+2', // Uppercase E (as hex, but matches non hex exponent char)
  '0X1.fp-4', // Hex float negative exponent
]);

{
  const kValidFloats = new Set([...kF32, ...kF16, ...kAbstractFloat]);
  const kInvalidFloats = new Set([
    '.f', // Must have a number
    '.e-2', // Exponent without leading values
    '1.e&2f', // Exponent invalid sign
    '1.ef', // Exponent without value
    '1.e+f', // Exponent sign no value
    '0x.p2', // Hex float no value
    '0x1p', // Hex float missing exponent
    '0x1p^', // Hex float invalid exponent
    '1.0e+999999999999f', // Too big
    '0x1.0p+999999999999f', // Too big hex
    '0x1.00000001pf0', // Mantissa too big
  ]);
  const kInvalidF16s = new Set([
    '1.1eh', // Missing exponent value
    '1.1e%2h', // Invalid exponent sign
    '1.1e+h', // Missing exponent with sign
    '1.0e+999999h', // Too large
    '0x1.0p+999999h', // Too large hex
    '0xf.h', // Having suffix "h" without "p" or "P"
    '0x3h', // Having suffix "h" without "p" or "P"
  ]);

  g.test('abstract_float')
    .desc(`Test that valid floats are accepted, and invalid floats are rejected`)
    .params(u =>
      u
        .combine('val', new Set([...kValidFloats, ...kInvalidFloats, ...kInvalidF16s]))
        .beginSubcases()
    )
    .beforeAllSubcases(t => {
      if (kF16.has(t.params.val) || kInvalidF16s.has(t.params.val)) {
        t.selectDeviceOrSkipTestCase('shader-f16');
      }
    })
    .fn(t => {
      const code = `var test = ${t.params.val};`;
      const extensionList = kF16.has(t.params.val) || kInvalidF16s.has(t.params.val) ? ['f16'] : [];
      t.expectCompileResult(
        kValidFloats.has(t.params.val),
        t.wrapInEntryPoint(code, extensionList)
      );
    });
}

{
  const kValidF32 = new Set([
    ...kF32,
    ...kAbstractFloat,
    '1', // AbstractInt
    '-1', // AbstractInt
  ]);
  const kInvalidF32 = new Set([
    ...kF16, // no conversion
    '1u', // unsigned
    '1i', // signed
    '1h', // half float
    '.f', // Must have a number
    '.e-2', // Exponent without leading values
    '1.e&2f', // Exponent invalid sign
    '1.ef', // Exponent without value
    '1.e+f', // Exponent sign no value
    '0x.p2', // Hex float no value
    '0x1p', // Hex float missing exponent
    '0x1p^', // Hex float invalid exponent
    '1.0e+999999999999f', // Too big
    '0x1.0p+999999999999f', // Too big hex
    '0x1.00000001pf0', // Mantissa too big
  ]);

  g.test('f32')
    .desc(`Test that valid floats are accepted, and invalid floats are rejected`)
    .params(u => u.combine('val', new Set([...kValidF32, ...kInvalidF32])).beginSubcases())
    .beforeAllSubcases(t => {
      if (kF16.has(t.params.val)) {
        t.selectDeviceOrSkipTestCase('shader-f16');
      }
    })
    .fn(t => {
      const { val } = t.params;
      const code = `var test: f32 = ${val};`;
      const extensionList = kF16.has(val) ? ['f16'] : [];
      t.expectCompileResult(kValidF32.has(val), t.wrapInEntryPoint(code, extensionList));
    });
}

{
  const kValidF16 = new Set([
    ...kF16,
    ...kAbstractFloat,
    '1', // AbstractInt
    '-1', // AbstractInt
  ]);
  const kInvalidF16 = new Set([
    ...kF32,
    '1i', // signed int
    '1u', // unsigned int
    '1f', // no conversion from f32 to f16
    '1.1eh', // Missing exponent value
    '1.1e%2h', // Invalid exponent sign
    '1.1e+h', // Missing exponent with sign
    '1.0e+999999h', // Too large
    '0x1.0p+999999h', // Too large hex
  ]);

  g.test('f16')
    .desc(
      `
Test that valid half floats are accepted, and invalid half floats are rejected

TODO: Need to inject the 'enable fp16' into the shader to enable the parsing.
`
    )
    .params(u => u.combine('val', new Set([...kValidF16, ...kInvalidF16])).beginSubcases())
    .unimplemented();
}