xref: /aosp_15_r20/external/mesa3d/src/compiler/nir/tests/opt_loop_tests.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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")
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    nir_alu_instr *alu = nir_instr_as_alu(term2->condition.ssa->parent_instr);
132    if (break_in_else)
133       ASSERT_TRUE(alu->op == nir_op_iand);
134    else
135       ASSERT_TRUE(alu->op == nir_op_ior);
136 }
137 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_basic)138 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_basic)
139 {
140    test_merged_if(false);
141    test_merged_if(true);
142 }
143 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_deref_after_first_if)144 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_deref_after_first_if)
145 {
146    /* Tests that opt_loop_merge_terminators creates valid nir after it merges
147     * terminators that have a deref statement between them:
148     */
149    nir_loop *loop = nir_push_loop(b);
150 
151    nir_deref_instr *deref = add_loop_terminators(NULL, NULL, false, false);
152 
153    /* Load from deref that will be moved inside the continue branch of the
154     * first if-statements continue block. If not handled correctly during
155     * the merge this will fail nir validation.
156     */
157    nir_def *ubo_def = nir_load_deref(b, deref);
158    nir_store_var(b, out_var, ubo_def, 1);
159 
160    nir_pop_loop(b, loop);
161 
162    ASSERT_TRUE(nir_opt_loop(b->shader));
163 
164    nir_validate_shader(b->shader, NULL);
165 }
166 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_deref_phi_index)167 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_deref_phi_index)
168 {
169    /* Tests that opt_loop_merge_terminators creates valid nir after it merges
170     * terminators that have a deref statement and index value between them and
171     * where that deref and index are both later used again later in the code:
172     */
173    nir_loop *loop = nir_push_loop(b);
174 
175    nir_deref_instr *deref = add_loop_terminators(NULL, NULL, false, true);
176 
177    /* Load from deref that will be moved inside the continue branch of the
178     * first if-statements continue block. If not handled correctly during
179     * the merge this will fail nir validation.
180     */
181    nir_def *ubo_def = nir_load_deref(b, deref);
182    nir_store_var(b, out_var, ubo_def, 1);
183 
184    nir_pop_loop(b, loop);
185 
186    ASSERT_TRUE(nir_opt_loop(b->shader));
187 
188    nir_validate_shader(b->shader, NULL);
189 }
190 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_skip_merge_if_phis)191 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_skip_merge_if_phis)
192 {
193    /* Tests that opt_loop_merge_terminators skips merging the terminators if
194     * the loop has phis. We can update or remove this test if support for
195     * phis is added to this pass:
196     */
197    nir_deref_instr *deref = nir_build_deref_var(b, ubo_var);
198    nir_def *ubo_def = nir_load_deref(b, deref);
199 
200    nir_loop *loop = nir_push_loop(b);
201 
202    nir_if *term1;
203    nir_if *term2;
204    add_loop_terminators(&term1, &term2, false, false);
205 
206    nir_pop_loop(b, loop);
207 
208    create_loop_phis(loop, term1, term2, in_def, ubo_def);
209 
210    ASSERT_FALSE(nir_opt_loop(b->shader));
211 
212    nir_validate_shader(b->shader, NULL);
213 }
214 
TEST_F(nir_opt_loop_test,opt_loop_merge_terminators_skip_merge_if_phis_nested_loop)215 TEST_F(nir_opt_loop_test, opt_loop_merge_terminators_skip_merge_if_phis_nested_loop)
216 {
217    /* Tests that opt_loop_merge_terminators skips merging the terminators if
218     * the loop has phis. We can update or remove this test if support for
219     * phis is added to this pass:
220     */
221    nir_deref_instr *deref = nir_build_deref_var(b, ubo_var);
222    nir_def *ubo_def = nir_load_deref(b, deref);
223 
224    nir_loop *loop = nir_push_loop(b);
225 
226    /* Add a nested loop to make sure we test the correct loop for trailing phis */
227    nir_loop *nested_loop = nir_push_loop(b);
228    nir_pop_loop(b, nested_loop);
229 
230    nir_if *term1;
231    nir_if *term2;
232    add_loop_terminators(&term1, &term2, false, false);
233 
234    nir_pop_loop(b, loop);
235 
236    create_loop_phis(loop, term1, term2, in_def, ubo_def);
237 
238    ASSERT_FALSE(nir_opt_loop(b->shader));
239 
240    nir_validate_shader(b->shader, NULL);
241 }
242 
TEST_F(nir_opt_loop_test,opt_loop_peel_initial_break_ends_with_jump)243 TEST_F(nir_opt_loop_test, opt_loop_peel_initial_break_ends_with_jump)
244 {
245    nir_loop *loop = nir_push_loop(b);
246 
247    /* the break we want to move down: */
248    nir_break_if(b, nir_imm_true(b));
249 
250    /* do_work_2: */
251    nir_push_if(b, nir_imm_true(b));
252    nir_jump(b, nir_jump_continue);
253    nir_pop_if(b, NULL);
254    nir_jump(b, nir_jump_return);
255 
256    nir_pop_loop(b, loop);
257 
258    ASSERT_FALSE(nir_opt_loop(b->shader));
259 
260    nir_validate_shader(b->shader, NULL);
261 }
262 
TEST_F(nir_opt_loop_test,opt_loop_peel_initial_break_nontrivial_break)263 TEST_F(nir_opt_loop_test, opt_loop_peel_initial_break_nontrivial_break)
264 {
265    nir_loop *loop = nir_push_loop(b);
266 
267    nir_push_if(b, nir_imm_true(b));
268 
269    nir_push_if(b, nir_imm_true(b));
270    nir_push_if(b, nir_imm_true(b));
271    nir_jump(b, nir_jump_break);
272    nir_pop_if(b, NULL);
273    nir_pop_if(b, NULL);
274    nir_nop(b);
275 
276    nir_jump(b, nir_jump_break);
277    nir_pop_if(b, NULL);
278 
279    /* do_work_2: */
280    nir_nop(b);
281 
282    nir_pop_loop(b, loop);
283 
284    ASSERT_FALSE(nir_opt_loop(b->shader));
285 
286    nir_validate_shader(b->shader, NULL);
287 }
288