@@ -185,60 +185,296 @@ function generateOp(op: string): string {
185185 }
186186}
187187
188- const kStatementKinds = [ 'if' , 'for' , 'while' , 'switch' , 'break-if' ] as const ;
188+ const kStatementKinds = [
189+ 'if' ,
190+ 'for' ,
191+ 'while' ,
192+ 'switch' ,
193+ 'break-if' ,
194+ 'loop-always-break-op-inside' ,
195+ 'loop-always-return-op-inside' ,
196+ 'loop-always-break-op-continuing' ,
197+ 'loop-always-return-op-continuing' ,
198+ 'loop-always-break-op-after' ,
199+ 'loop-always-return-op-after' ,
200+ 'for-with-cond-always-break-op-inside' ,
201+ 'for-with-cond-always-return-op-inside' ,
202+ 'for-with-cond-always-break-op-after' ,
203+ 'for-with-cond-always-return-op-after' ,
204+ 'for-without-cond-always-break-op-inside' ,
205+ 'for-without-cond-always-return-op-inside' ,
206+ 'for-without-cond-always-break-op-after' ,
207+ 'for-without-cond-always-return-op-after' ,
208+ 'while-always-break-op-inside' ,
209+ 'while-always-return-op-inside' ,
210+ 'while-always-break-op-after' ,
211+ 'while-always-return-op-after' ,
212+ ] as const ;
189213type kStatementType = ( typeof kStatementKinds ) [ number ] ;
190214
215+ type kSnippetInfo = {
216+ // A WGSL code sippet that embeds a condition and operation-operation-under-test
217+ // in a larger construct.
218+ code : string ;
219+ // Is the operation-under-test sensitive to the uniformity of the condition?
220+ sensitive : boolean ;
221+ } ;
191222function generateConditionalStatement (
192223 statement : kStatementType ,
193- condition : string ,
194- op : string
195- ) : string {
196- const code = `` ;
224+ condition_name : string ,
225+ op_name : string
226+ ) : kSnippetInfo {
227+ const cond = generateCondition ( condition_name ) ;
228+ const uniform_cond = generateCondition ( 'uniform_storage_ro' ) ;
229+ const op = generateOp ( op_name ) ;
197230 switch ( statement ) {
198231 case 'if' : {
199- return `if ${ generateCondition ( condition ) } {
200- ${ generateOp ( op ) } ;
201- }
202- ` ;
232+ return {
233+ sensitive : true ,
234+ code : `
235+ if ${ cond } {
236+ ${ op } ;
237+ }` ,
238+ } ;
203239 }
204240 case 'for' : {
205- return `for (; ${ generateCondition ( condition ) } ;) {
206- ${ generateOp ( op ) } ;
207- }
208- ` ;
241+ return {
242+ sensitive : true ,
243+ code : `
244+ for (; ${ cond } ; ) {
245+ ${ op } ;
246+ }` ,
247+ } ;
209248 }
210249 case 'while' : {
211- return `while ${ generateCondition ( condition ) } {
212- ${ generateOp ( op ) } ;
213- }
214- ` ;
250+ return {
251+ sensitive : true ,
252+ code : `
253+ while ${ cond } {
254+ ${ op } ;
255+ }` ,
256+ } ;
215257 }
216258 case 'switch' : {
217- return `switch u32(${ generateCondition ( condition ) } ) {
218- case 0: {
219- ${ generateOp ( op ) } ;
220- }
221- default: { }
222- }
223- ` ;
259+ return {
260+ sensitive : true ,
261+ code : `
262+ switch u32(${ cond } ) {
263+ case 0: {
264+ ${ op } ;
265+ }
266+ default: { }
267+ }` ,
268+ } ;
224269 }
225270 case 'break-if' : {
226271 // The initial 'if' prevents the loop from being infinite. Its condition
227272 // is uniform, to ensure the first iteration of the the body executes
228273 // uniformly. The uniformity of the second iteration depends entirely on
229274 // the uniformity of the break-if condition.
230- return `loop {
231- if ${ generateCondition ( 'uniform_storage_ro' ) } { break; }
232- ${ generateOp ( op ) }
233- continuing {
234- break if ${ generateCondition ( condition ) } ;
235- }
236- }
237- ` ;
275+ return {
276+ sensitive : true ,
277+ code : `
278+ loop {
279+ if ${ uniform_cond } { break; }
280+ ${ op }
281+ continuing {
282+ break if ${ cond } ;
283+ }
284+ }` ,
285+ } ;
286+ }
287+ case 'loop-always-break-op-inside' : {
288+ return {
289+ sensitive : false , // The op is unreachable.
290+ code : `
291+ loop {
292+ break;
293+ if ${ cond } { ${ op } }
294+ }` ,
295+ } ;
296+ }
297+ case 'loop-always-return-op-inside' : {
298+ return {
299+ sensitive : false , // The op is unreachable.
300+ code : `
301+ loop {
302+ return;
303+ if ${ cond } { ${ op } }
304+ }` ,
305+ } ;
306+ }
307+ case 'loop-always-break-op-continuing' : {
308+ return {
309+ sensitive : false , // The op is unreachable.
310+ code : `
311+ loop {
312+ break;
313+ continuing {
314+ if ${ cond } { ${ op } }
315+ }
316+ }` ,
317+ } ;
318+ }
319+ case 'loop-always-return-op-continuing' : {
320+ return {
321+ sensitive : false , // The op is unreachable.
322+ code : `
323+ loop {
324+ return;
325+ continuing {
326+ if ${ cond } { ${ op } }
327+ }
328+ }` ,
329+ } ;
330+ }
331+ case 'loop-always-break-op-after' : {
332+ return {
333+ sensitive : true ,
334+ code : `
335+ loop {
336+ break;
337+ }
338+ if ${ cond } { ${ op } }` ,
339+ } ;
340+ }
341+ case 'loop-always-return-op-after' : {
342+ return {
343+ sensitive : false , // The op is unreachable.
344+ code : `
345+ loop {
346+ return;
347+ }
348+ if ${ cond } { ${ op } }` ,
349+ } ;
350+ }
351+ case 'for-with-cond-always-break-op-inside' : {
352+ return {
353+ sensitive : false , // The op is unreachable.
354+ code : `
355+ for ( ;${ uniform_cond } ; ) {
356+ break;
357+ if ${ cond } { ${ op } }
358+ }` ,
359+ } ;
360+ }
361+ case 'for-with-cond-always-return-op-inside' : {
362+ return {
363+ sensitive : false , // The op is unreachable.
364+ code : `
365+ for ( ;${ uniform_cond } ; ) {
366+ return;
367+ if ${ cond } { ${ op } }
368+ }` ,
369+ } ;
370+ }
371+ case 'for-with-cond-always-break-op-after' : {
372+ return {
373+ sensitive : true ,
374+ code : `
375+ for ( ;${ uniform_cond } ; ) {
376+ break;
377+ }
378+ if ${ cond } { ${ op } }` ,
379+ } ;
380+ }
381+ case 'for-with-cond-always-return-op-after' : {
382+ return {
383+ // Desugars to a loop with a conditional break,
384+ // before reaching the loop.
385+ sensitive : true ,
386+ code : `
387+ for ( ;${ uniform_cond } ; ) {
388+ return;
389+ }
390+ if ${ cond } { ${ op } }` ,
391+ } ;
392+ }
393+ case 'for-without-cond-always-break-op-inside' : {
394+ return {
395+ sensitive : false , // The op is unreachable.
396+ code : `
397+ for ( ; ; ) {
398+ break;
399+ if ${ cond } { ${ op } }
400+ }` ,
401+ } ;
402+ }
403+ case 'for-without-cond-always-return-op-inside' : {
404+ return {
405+ sensitive : false , // The op is unreachable.
406+ code : `
407+ for ( ; ; ) {
408+ return;
409+ if ${ cond } { ${ op } }
410+ }` ,
411+ } ;
412+ }
413+ case 'for-without-cond-always-break-op-after' : {
414+ return {
415+ sensitive : true ,
416+ code : `
417+ for ( ; ; ) {
418+ break;
419+ }
420+ if ${ cond } { ${ op } }` ,
421+ } ;
422+ }
423+ case 'for-without-cond-always-return-op-after' : {
424+ return {
425+ // Desugars to a loop without a conditional break.
426+ // So the op is unreachable.
427+ sensitive : false ,
428+ code : `
429+ for ( ; ; ) {
430+ return;
431+ }
432+ if ${ cond } { ${ op } }` ,
433+ } ;
434+ }
435+ case 'while-always-break-op-inside' : {
436+ return {
437+ sensitive : false , // The op is unreachable.
438+ code : `
439+ while (${ uniform_cond } ) {
440+ break;
441+ if ${ cond } { ${ op } }
442+ }` ,
443+ } ;
444+ }
445+ case 'while-always-return-op-inside' : {
446+ return {
447+ sensitive : false , // The op is unreachable.
448+ code : `
449+ while (${ uniform_cond } ) {
450+ return;
451+ if ${ cond } { ${ op } }
452+ }` ,
453+ } ;
454+ }
455+ case 'while-always-break-op-after' : {
456+ return {
457+ sensitive : true ,
458+ code : `
459+ while (${ uniform_cond } ) {
460+ break;
461+ }
462+ if ${ cond } { ${ op } }` ,
463+ } ;
464+ }
465+ case 'while-always-return-op-after' : {
466+ return {
467+ // Desugars to a loop with a conditional break,
468+ // before reaching the loop.
469+ sensitive : true ,
470+ code : `
471+ while (${ uniform_cond } ) {
472+ return;
473+ }
474+ if ${ cond } { ${ op } }` ,
475+ } ;
238476 }
239477 }
240-
241- return code ;
242478}
243479
244480g . test ( 'basics' )
@@ -293,11 +529,15 @@ g.test('basics')
293529 ` ;
294530
295531 // Simple control statement containing the op.
296- code += generateConditionalStatement ( t . params . statement , t . params . cond , t . params . op ) ;
532+ const snippet = generateConditionalStatement ( t . params . statement , t . params . cond , t . params . op ) ;
533+ code += snippet . code ;
297534
298535 code += `\n}\n` ;
299536
300- t . expectCompileResult ( t . params . expectation || t . params . op . startsWith ( 'control_case' ) , code ) ;
537+ t . expectCompileResult (
538+ t . params . expectation || t . params . op . startsWith ( 'control_case' ) || ! snippet . sensitive ,
539+ code
540+ ) ;
301541 } ) ;
302542
303543const kSubgroupOps = [
@@ -380,11 +620,15 @@ g.test('basics,subgroups')
380620 ` ;
381621
382622 // Simple control statement containing the op.
383- code += generateConditionalStatement ( t . params . statement , t . params . cond , t . params . op ) ;
623+ const snippet = generateConditionalStatement ( t . params . statement , t . params . cond , t . params . op ) ;
624+ code += snippet . code ;
384625
385626 code += `\n}\n` ;
386627
387- t . expectCompileResult ( t . params . expectation || t . params . op . startsWith ( 'control_case' ) , code ) ;
628+ t . expectCompileResult (
629+ t . params . expectation || t . params . op . startsWith ( 'control_case' ) || ! snippet . sensitive ,
630+ code
631+ ) ;
388632 } ) ;
389633
390634const kFragmentBuiltinValues = [
0 commit comments