• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2024 Valve Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "nir_test.h"
25 
26 class nir_opt_loop_test : public nir_test {
27 protected:
28    nir_opt_loop_test();
29 
30    nir_deref_instr *add_loop_terminators(nir_if **term1, nir_if **term2,
31                                          bool break_in_else, bool deref_array);
32    void create_loop_phis(nir_loop *loop, nir_if *term1, nir_if *term2,
33                          nir_def *def1, nir_def *def2);
34    void test_merged_if(bool break_in_else);
35 
36    nir_def *in_def;
37    nir_variable *out_var;
38    nir_variable *ubo_var;
39    nir_variable *ubo_var_array;
40 };
41 
nir_opt_loop_test()42 nir_opt_loop_test::nir_opt_loop_test()
43    : nir_test::nir_test("nir_opt_loop_test", MESA_SHADER_FRAGMENT)
44 {
45    nir_variable *var = nir_variable_create(b->shader, nir_var_shader_in, glsl_int_type(), "in");
46    in_def = nir_load_var(b, var);
47 
48    ubo_var = nir_variable_create(b->shader, nir_var_mem_ubo, glsl_int_type(), "ubo1");
49    ubo_var_array = nir_variable_create(b->shader, nir_var_mem_ubo, glsl_array_type(glsl_int_type(), 4, 0), "ubo_array");
50 
51    out_var = nir_variable_create(b->shader, nir_var_shader_out, glsl_int_type(), "out");
52 }
53 
54 nir_deref_instr *
add_loop_terminators(nir_if ** term1,nir_if ** term2,bool break_in_else,bool deref_array)55 nir_opt_loop_test::add_loop_terminators(nir_if **term1, nir_if **term2,
56                                         bool break_in_else, bool deref_array)
57 {
58    /* Add first terminator */
59    nir_def *one = nir_imm_int(b, 1);
60    nir_def *cmp_result = nir_ieq(b, in_def, one);
61    nir_if *nif = nir_push_if(b, cmp_result);
62 
63    if (break_in_else)
64       nir_push_else(b, nif);
65 
66    nir_jump(b, nir_jump_break);
67    nir_pop_if(b, nif);
68 
69    if (term1)
70       *term1 = nif;
71 
72    nir_deref_instr *deref;
73    if (deref_array) {
74       nir_def *index = nir_imm_int(b, 3);
75       deref = nir_build_deref_array(b, nir_build_deref_var(b, ubo_var_array), index);
76    } else {
77       deref = nir_build_deref_var(b, ubo_var);
78    }
79    nir_def *ubo_def = nir_load_deref(b, deref);
80 
81    /* Add second terminator */
82    nir_def *two = nir_imm_int(b, 2);
83    nir_def *cmp_result2 = nir_ieq(b, ubo_def, two);
84    nir_if *nif2 = nir_push_if(b, cmp_result2);
85 
86    if (break_in_else)
87       nir_push_else(b, nif2);
88 
89    nir_jump(b, nir_jump_break);
90    nir_pop_if(b, nif2);
91 
92    if (term2)
93       *term2 = nif2;
94 
95    return deref;
96 }
97 
98 void
create_loop_phis(nir_loop * loop,nir_if * term1,nir_if * term2,nir_def * def1,nir_def * def2)99 nir_opt_loop_test::create_loop_phis(nir_loop *loop,
100                                     nir_if *term1, nir_if *term2,
101                                     nir_def *def1, nir_def *def2)
102 {
103    nir_phi_instr *phi_instr = nir_phi_instr_create(b->shader);
104    nir_def_init(&phi_instr->instr, &phi_instr->def, 1, 32);
105    nir_phi_instr_add_src(phi_instr, nir_if_first_then_block(term1), def1);
106    nir_phi_instr_add_src(phi_instr, nir_if_first_then_block(term2), def2);
107 
108    nir_instr_insert(nir_after_cf_node(&loop->cf_node),
109                     &phi_instr->instr);
110 }
111 
112 void
test_merged_if(bool break_in_else)113 nir_opt_loop_test::test_merged_if(bool break_in_else)
114 {
115    /* Tests that opt_loop_merge_terminators results in valid nir and that
116     * the test condition is correct based on the location of the break in
117     * the terminators.
118     */
119    nir_loop *loop = nir_push_loop(b);
120 
121    nir_if *term1;
122    nir_if *term2;
123    add_loop_terminators(&term1, &term2, break_in_else, false);
124 
125    nir_pop_loop(b, loop);
126 
127    ASSERT_TRUE(nir_opt_loop(b->shader));
128 
129    nir_validate_shader(b->shader, NULL);
130 }
131 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_break_in_then)132 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_break_in_then)
133 {
134    test_merged_if(false);
135 
136    check_nir_string(NIR_REFERENCE_SHADER(R"(
137       shader: MESA_SHADER_FRAGMENT
138       name: nir_opt_loop_test
139       subgroup_size: 0
140       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
141       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
142       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
143       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
144       decl_function main () (entrypoint)
145 
146       impl main {
147           block b0:   // preds:
148           32     %0 = deref_var &in (shader_in int)
149           32     %1 = @load_deref (%0) (access=none)
150                       // succs: b1
151           loop {
152               block b1:   // preds: b0 b7
153               32     %2 = load_const (0x00000001)
154               1      %3 = ieq %1, %2 (0x1)
155                           // succs: b2 b3
156               if %3 {
157                   block b2:   // preds: b1
158                   1      %4 = undefined
159                               // succs: b4
160               } else {
161                   block b3:   // preds: b1
162                   32     %5 = deref_var &ubo1 (ubo int)
163                   32     %6 = @load_deref (%5) (access=none)
164                   32     %7 = load_const (0x00000002)
165                   1      %8 = ieq %6, %7 (0x2)
166                               // succs: b4
167               }
168               block b4:   // preds: b2 b3
169               1      %9 = phi b3: %8, b2: %4
170               1     %10 = ior %9, %3
171                           // succs: b5 b6
172               if %10 {
173                   block b5:// preds: b4
174                   break
175                   // succs: b8
176               } else {
177                   block b6:  // preds: b4, succs: b7
178               }
179               block b7:  // preds: b6, succs: b1
180           }
181           block b8:  // preds: b5, succs: b9
182           block b9:
183       }
184    )"));
185 }
186 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_break_in_else)187 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_break_in_else)
188 {
189    test_merged_if(true);
190 
191    check_nir_string(NIR_REFERENCE_SHADER(R"(
192       shader: MESA_SHADER_FRAGMENT
193       name: nir_opt_loop_test
194       subgroup_size: 0
195       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
196       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
197       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
198       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
199       decl_function main () (entrypoint)
200 
201       impl main {
202           block b0:   // preds:
203           32     %0 = deref_var &in (shader_in int)
204           32     %1 = @load_deref (%0) (access=none)
205                       // succs: b1
206           loop {
207               block b1:   // preds: b0 b7
208               32     %2 = load_const (0x00000001)
209               1      %3 = ieq %1, %2 (0x1)
210                           // succs: b2 b3
211               if %3 {
212                   block b2:   // preds: b1
213                   32     %4 = deref_var &ubo1 (ubo int)
214                   32     %5 = @load_deref (%4) (access=none)
215                   32     %6 = load_const (0x00000002)
216                   1      %7 = ieq %5, %6 (0x2)
217                               // succs: b4
218               } else {
219                   block b3:   // preds: b1
220                   1      %8 = undefined
221                               // succs: b4
222               }
223               block b4:   // preds: b2 b3
224               1      %9 = phi b2: %7, b3: %8
225               1     %10 = iand %9, %3
226                           // succs: b5 b6
227               if %10 {
228                   block b5:  // preds: b4, succs: b7
229               } else {
230                   block b6:// preds: b4
231                   break
232                   // succs: b8
233               }
234               block b7:  // preds: b5, succs: b1
235           }
236           block b8:  // preds: b6, succs: b9
237           block b9:
238       }
239    )"));
240 }
241 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_deref_after_first_if)242 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_deref_after_first_if)
243 {
244    /* Tests that opt_loop_merge_terminators creates valid nir after it merges
245     * terminators that have a deref statement between them:
246     */
247    nir_loop *loop = nir_push_loop(b);
248 
249    nir_deref_instr *deref = add_loop_terminators(NULL, NULL, false, false);
250 
251    /* Load from deref that will be moved inside the continue branch of the
252     * first if-statements continue block. If not handled correctly during
253     * the merge this will fail nir validation.
254     */
255    nir_def *ubo_def = nir_load_deref(b, deref);
256    nir_store_var(b, out_var, ubo_def, 1);
257 
258    nir_pop_loop(b, loop);
259 
260    ASSERT_TRUE(nir_opt_loop(b->shader));
261 
262    nir_validate_shader(b->shader, NULL);
263 
264    check_nir_string(NIR_REFERENCE_SHADER(R"(
265       shader: MESA_SHADER_FRAGMENT
266       name: nir_opt_loop_test
267       subgroup_size: 0
268       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
269       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
270       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
271       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
272       decl_function main () (entrypoint)
273 
274       impl main {
275           block b0:   // preds:
276           32     %0 = deref_var &in (shader_in int)
277           32     %1 = @load_deref (%0) (access=none)
278                       // succs: b1
279           loop {
280               block b1:   // preds: b0 b7
281               32     %2 = load_const (0x00000001)
282               1      %3 = ieq %1, %2 (0x1)
283                           // succs: b2 b3
284               if %3 {
285                   block b2:   // preds: b1
286                   1      %4 = undefined
287                               // succs: b4
288               } else {
289                   block b3:   // preds: b1
290                   32     %5 = deref_var &ubo1 (ubo int)
291                   32     %6 = @load_deref (%5) (access=none)
292                   32     %7 = load_const (0x00000002)
293                   1      %8 = ieq %6, %7 (0x2)
294                               // succs: b4
295               }
296               block b4:   // preds: b2 b3
297               1      %9 = phi b3: %8, b2: %4
298               1     %10 = ior %9, %3
299                           // succs: b5 b6
300               if %10 {
301                   block b5:// preds: b4
302                   break
303                   // succs: b8
304               } else {
305                   block b6:  // preds: b4, succs: b7
306               }
307               block b7:   // preds: b6
308               32    %11 = deref_var &ubo1 (ubo int)
309               32    %12 = @load_deref (%11) (access=none)
310               32    %13 = deref_var &out (shader_out int)
311                           @store_deref (%13, %12) (wrmask=x, access=none)
312                           // succs: b1
313           }
314           block b8:  // preds: b5, succs: b9
315           block b9:
316       }
317    )"));
318 }
319 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_deref_phi_index)320 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_deref_phi_index)
321 {
322    /* Tests that opt_loop_merge_terminators creates valid nir after it merges
323     * terminators that have a deref statement and index value between them and
324     * where that deref and index are both later used again later in the code:
325     */
326    nir_loop *loop = nir_push_loop(b);
327 
328    nir_deref_instr *deref = add_loop_terminators(NULL, NULL, false, true);
329 
330    /* Load from deref that will be moved inside the continue branch of the
331     * first if-statements continue block. If not handled correctly during
332     * the merge this will fail nir validation.
333     */
334    nir_def *ubo_def = nir_load_deref(b, deref);
335    nir_store_var(b, out_var, ubo_def, 1);
336 
337    nir_pop_loop(b, loop);
338 
339    ASSERT_TRUE(nir_opt_loop(b->shader));
340 
341    nir_validate_shader(b->shader, NULL);
342 
343    check_nir_string(NIR_REFERENCE_SHADER(R"(
344       shader: MESA_SHADER_FRAGMENT
345       name: nir_opt_loop_test
346       subgroup_size: 0
347       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
348       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
349       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
350       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
351       decl_function main () (entrypoint)
352 
353       impl main {
354           block b0:   // preds:
355           32     %0 = deref_var &in (shader_in int)
356           32     %1 = @load_deref (%0) (access=none)
357                       // succs: b1
358           loop {
359               block b1:   // preds: b0 b7
360               32     %2 = load_const (0x00000001)
361               1      %3 = ieq %1, %2 (0x1)
362                           // succs: b2 b3
363               if %3 {
364                   block b2:   // preds: b1
365                   1      %4 = undefined
366                   32     %5 = undefined
367                               // succs: b4
368               } else {
369                   block b3:   // preds: b1
370                   32     %6 = load_const (0x00000003 = 0.000000)
371                   32     %7 = deref_var &ubo_array (ubo int[4])
372                   32     %8 = deref_array &(*%7)[3] (ubo int)  // &ubo_array[3]
373                   32     %9 = @load_deref (%8) (access=none)
374                   32    %10 = load_const (0x00000002)
375                   1     %11 = ieq %9, %10 (0x2)
376                               // succs: b4
377               }
378               block b4:   // preds: b2 b3
379               1     %12 = phi b3: %11, b2: %4
380               32    %13 = phi b3: %6 (0x3), b2: %5
381               1     %14 = ior %12, %3
382                           // succs: b5 b6
383               if %14 {
384                   block b5:// preds: b4
385                   break
386                   // succs: b8
387               } else {
388                   block b6:  // preds: b4, succs: b7
389               }
390               block b7:   // preds: b6
391               32    %15 = deref_var &ubo_array (ubo int[4])
392               32    %16 = deref_array &(*%15)[%13] (ubo int)  // &ubo_array[%13]
393               32    %17 = @load_deref (%16) (access=none)
394               32    %18 = deref_var &out (shader_out int)
395                           @store_deref (%18, %17) (wrmask=x, access=none)
396                           // succs: b1
397           }
398           block b8:  // preds: b5, succs: b9
399           block b9:
400       }
401    )"));
402 }
403 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_skip_merge_if_phis)404 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_skip_merge_if_phis)
405 {
406    /* Tests that opt_loop_merge_terminators skips merging the terminators if
407     * the loop has phis. We can update or remove this test if support for
408     * phis is added to this pass:
409     */
410    nir_deref_instr *deref = nir_build_deref_var(b, ubo_var);
411    nir_def *ubo_def = nir_load_deref(b, deref);
412 
413    nir_loop *loop = nir_push_loop(b);
414 
415    nir_if *term1;
416    nir_if *term2;
417    add_loop_terminators(&term1, &term2, false, false);
418 
419    nir_pop_loop(b, loop);
420 
421    create_loop_phis(loop, term1, term2, in_def, ubo_def);
422 
423    ASSERT_FALSE(nir_opt_loop(b->shader));
424 
425    nir_validate_shader(b->shader, NULL);
426 
427    check_nir_string(NIR_REFERENCE_SHADER(R"(
428       shader: MESA_SHADER_FRAGMENT
429       name: nir_opt_loop_test
430       subgroup_size: 0
431       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
432       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
433       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
434       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
435       decl_function main () (entrypoint)
436 
437       impl main {
438           block b0:   // preds:
439           32     %0 = deref_var &in (shader_in int)
440           32     %1 = @load_deref (%0) (access=none)
441           32     %2 = deref_var &ubo1 (ubo int)
442           32     %3 = @load_deref (%2) (access=none)
443                       // succs: b1
444           loop {
445               block b1:   // preds: b0 b7
446               32     %4 = load_const (0x00000001)
447               1      %5 = ieq %1, %4 (0x1)
448                           // succs: b2 b3
449               if %5 {
450                   block b2:// preds: b1
451                   break
452                   // succs: b8
453               } else {
454                   block b3:  // preds: b1, succs: b4
455               }
456               block b4:   // preds: b3
457               32     %6 = deref_var &ubo1 (ubo int)
458               32     %7 = @load_deref (%6) (access=none)
459               32     %8 = load_const (0x00000002)
460               1      %9 = ieq %7, %8 (0x2)
461                           // succs: b5 b6
462               if %9 {
463                   block b5:// preds: b4
464                   break
465                   // succs: b8
466               } else {
467                   block b6:  // preds: b4, succs: b7
468               }
469               block b7:  // preds: b6, succs: b1
470           }
471           block b8:   // preds: b2 b5
472           32    %10 = phi b2: %1, b5: %3
473                       // succs: b9
474           block b9:
475       }
476    )"));
477 }
478 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_skip_merge_if_phis_nested_loop)479 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_skip_merge_if_phis_nested_loop)
480 {
481    /* Tests that opt_loop_merge_terminators skips merging the terminators if
482     * the loop has phis. We can update or remove this test if support for
483     * phis is added to this pass:
484     */
485    nir_deref_instr *deref = nir_build_deref_var(b, ubo_var);
486    nir_def *ubo_def = nir_load_deref(b, deref);
487 
488    nir_loop *loop = nir_push_loop(b);
489 
490    /* Add a nested loop to make sure we test the correct loop for trailing phis */
491    nir_loop *nested_loop = nir_push_loop(b);
492    nir_pop_loop(b, nested_loop);
493 
494    nir_if *term1;
495    nir_if *term2;
496    add_loop_terminators(&term1, &term2, false, false);
497 
498    nir_pop_loop(b, loop);
499 
500    create_loop_phis(loop, term1, term2, in_def, ubo_def);
501 
502    ASSERT_FALSE(nir_opt_loop(b->shader));
503 
504    nir_validate_shader(b->shader, NULL);
505 
506    check_nir_string(NIR_REFERENCE_SHADER(R"(
507       shader: MESA_SHADER_FRAGMENT
508       name: nir_opt_loop_test
509       subgroup_size: 0
510       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
511       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
512       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
513       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
514       decl_function main () (entrypoint)
515 
516       impl main {
517           block b0:   // preds:
518           32     %0 = deref_var &in (shader_in int)
519           32     %1 = @load_deref (%0) (access=none)
520           32     %2 = deref_var &ubo1 (ubo int)
521           32     %3 = @load_deref (%2) (access=none)
522                       // succs: b1
523           loop {
524               block b1:  // preds: b0 b9, succs: b2
525               loop {
526                   block b2:  // preds: b1 b2, succs: b2
527               }
528               block b3:   // preds:
529               32     %4 = load_const (0x00000001)
530               1      %5 = ieq %1, %4 (0x1)
531                           // succs: b4 b5
532               if %5 {
533                   block b4:// preds: b3
534                   break
535                   // succs: b10
536               } else {
537                   block b5:  // preds: b3, succs: b6
538               }
539               block b6:   // preds: b5
540               32     %6 = deref_var &ubo1 (ubo int)
541               32     %7 = @load_deref (%6) (access=none)
542               32     %8 = load_const (0x00000002)
543               1      %9 = ieq %7, %8 (0x2)
544                           // succs: b7 b8
545               if %9 {
546                   block b7:// preds: b6
547                   break
548                   // succs: b10
549               } else {
550                   block b8:  // preds: b6, succs: b9
551               }
552               block b9:  // preds: b8, succs: b1
553           }
554           block b10:  // preds: b4 b7
555           32    %10 = phi b4: %1, b7: %3
556                       // succs: b11
557           block b11:
558       }
559    )"));
560 }
561 
TEST_F(nir_opt_loop_test,opt_loop_peel_initial_break_ends_with_jump)562 TEST_F(nir_opt_loop_test, opt_loop_peel_initial_break_ends_with_jump)
563 {
564    nir_loop *loop = nir_push_loop(b);
565 
566    /* the break we want to move down: */
567    nir_break_if(b, nir_imm_true(b));
568 
569    /* do_work_2: */
570    nir_push_if(b, nir_imm_true(b));
571    nir_jump(b, nir_jump_continue);
572    nir_pop_if(b, NULL);
573    nir_jump(b, nir_jump_return);
574 
575    nir_pop_loop(b, loop);
576 
577    ASSERT_FALSE(nir_opt_loop(b->shader));
578 
579    nir_validate_shader(b->shader, NULL);
580 
581    check_nir_string(NIR_REFERENCE_SHADER(R"(
582       shader: MESA_SHADER_FRAGMENT
583       name: nir_opt_loop_test
584       subgroup_size: 0
585       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
586       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
587       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
588       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
589       decl_function main () (entrypoint)
590 
591       impl main {
592           block b0:  // preds:
593           32    %0 = deref_var &in (shader_in int)
594           32    %1 = @load_deref (%0) (access=none)
595                      // succs: b1
596           loop {
597               block b1:  // preds: b0 b5
598               1     %2 = load_const (true)
599                          // succs: b2 b3
600               if %2 (true) {
601                   block b2:// preds: b1
602                   break
603                   // succs: b8
604               } else {
605                   block b3:  // preds: b1, succs: b4
606               }
607               block b4:  // preds: b3
608               1     %3 = load_const (true)
609                          // succs: b5 b6
610               if %3 (true) {
611                   block b5:// preds: b4
612                   continue
613                   // succs: b1
614               } else {
615                   block b6:  // preds: b4, succs: b7
616               }
617               block b7:// preds: b6
618               return
619               // succs: b9
620           }
621           block b8:  // preds: b2, succs: b9
622           block b9:
623       }
624    )"));
625 }
626 
TEST_F(nir_opt_loop_test,opt_loop_peel_initial_break_nontrivial_break)627 TEST_F(nir_opt_loop_test, opt_loop_peel_initial_break_nontrivial_break)
628 {
629    nir_loop *loop = nir_push_loop(b);
630 
631    nir_push_if(b, nir_imm_true(b));
632 
633    nir_push_if(b, nir_imm_true(b));
634    nir_push_if(b, nir_imm_true(b));
635    nir_jump(b, nir_jump_break);
636    nir_pop_if(b, NULL);
637    nir_pop_if(b, NULL);
638    nir_nop(b);
639 
640    nir_jump(b, nir_jump_break);
641    nir_pop_if(b, NULL);
642 
643    /* do_work_2: */
644    nir_nop(b);
645 
646    nir_pop_loop(b, loop);
647 
648    ASSERT_FALSE(nir_opt_loop(b->shader));
649 
650    nir_validate_shader(b->shader, NULL);
651 
652    check_nir_string(NIR_REFERENCE_SHADER(R"(
653       shader: MESA_SHADER_FRAGMENT
654       name: nir_opt_loop_test
655       subgroup_size: 0
656       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
657       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
658       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
659       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
660       decl_function main () (entrypoint)
661 
662       impl main {
663           block b0:  // preds:
664           32    %0 = deref_var &in (shader_in int)
665           32    %1 = @load_deref (%0) (access=none)
666                      // succs: b1
667           loop {
668               block b1:  // preds: b0 b10
669               1     %2 = load_const (true)
670                          // succs: b2 b9
671               if %2 (true) {
672                   block b2:  // preds: b1
673                   1     %3 = load_const (true)
674                              // succs: b3 b7
675                   if %3 (true) {
676                       block b3:  // preds: b2
677                       1     %4 = load_const (true)
678                                  // succs: b4 b5
679                       if %4 (true) {
680                           block b4:// preds: b3
681                           break
682                           // succs: b11
683                       } else {
684                           block b5:  // preds: b3, succs: b6
685                       }
686                       block b6:  // preds: b5, succs: b8
687                   } else {
688                       block b7:  // preds: b2, succs: b8
689                   }
690                   block b8:// preds: b6 b7
691                   @nop
692                   break
693                   // succs: b11
694               } else {
695                   block b9:  // preds: b1, succs: b10
696               }
697               block b10:// preds: b9
698               @nop
699               // succs: b1
700           }
701           block b11:  // preds: b4 b8, succs: b12
702           block b12:
703       }
704    )"));
705 }
706 
TEST_F(nir_opt_loop_test,opt_loop_peel_initial_break_deref)707 TEST_F(nir_opt_loop_test, opt_loop_peel_initial_break_deref)
708 {
709    nir_loop *loop = nir_push_loop(b);
710 
711    nir_deref_instr *var_deref = nir_build_deref_var(b, out_var);
712 
713    nir_push_if(b, nir_imm_true(b));
714    nir_jump(b, nir_jump_break);
715    nir_pop_if(b, NULL);
716 
717    nir_store_deref(b, var_deref, nir_imm_int(b, 42), 0x1);
718 
719    nir_pop_loop(b, loop);
720 
721    ASSERT_TRUE(nir_opt_loop(b->shader));
722 
723    nir_validate_shader(b->shader, NULL);
724 
725    check_nir_string(NIR_REFERENCE_SHADER(R"(
726       shader: MESA_SHADER_FRAGMENT
727       name: nir_opt_loop_test
728       subgroup_size: 0
729       decl_var shader_in INTERP_MODE_SMOOTH none int in (VARYING_SLOT_POS.x, 0, 0)
730       decl_var shader_out INTERP_MODE_NONE none int out (FRAG_RESULT_DEPTH.x, 0, 0)
731       decl_var ubo INTERP_MODE_NONE none int ubo1 (0, 0, 0)
732       decl_var ubo INTERP_MODE_NONE none int[4] ubo_array (0, 0, 0)
733       decl_function main () (entrypoint)
734 
735       impl main {
736           block b0:  // preds:
737           32    %0 = deref_var &in (shader_in int)
738           32    %1 = @load_deref (%0) (access=none)
739           1     %2 = load_const (true)
740                      // succs: b1 b2
741           if %2 (true) {
742               block b1:  // preds: b0, succs: b8
743           } else {
744               block b2:  // preds: b0, succs: b3
745               loop {
746                   block b3:  // preds: b2 b6
747                   32    %3 = load_const (0x0000002a = 42)
748                   32    %4 = deref_var &out (shader_out int)
749                              @store_deref (%4, %3 (0x2a)) (wrmask=x, access=none)
750                   1     %5 = load_const (true)
751                              // succs: b4 b5
752                   if %5 (true) {
753                       block b4:// preds: b3
754                       break
755                       // succs: b7
756                   } else {
757                       block b5:  // preds: b3, succs: b6
758                   }
759                   block b6:  // preds: b5, succs: b3
760               }
761               block b7:  // preds: b4, succs: b8
762           }
763           block b8:  // preds: b1 b7, succs: b9
764           block b9:
765       }
766    )"));
767 }
768