1 /*
2 * Copyright (c) 2023 Intel Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <gtest/gtest.h>
7 #include "elk_fs.h"
8 #include "elk_fs_builder.h"
9 #include "elk_cfg.h"
10
11 using namespace elk;
12
13 class PredicatedBreakTest : public ::testing::Test {
14 virtual void SetUp();
15 virtual void TearDown();
16
17 public:
18 bool debug;
19 void *mem_ctx;
20 elk_compiler compiler;
21 elk_compile_params params;
22 intel_device_info devinfo;
23 struct elk_wm_prog_data prog_data;
24 struct gl_shader_program *shader_prog;
25
26 elk_fs_visitor *shader_a;
27 elk_fs_visitor *shader_b;
28
29 bool elk_opt_predicated_break(elk_fs_visitor *s);
30 };
31
32 void
SetUp()33 PredicatedBreakTest::SetUp()
34 {
35 debug = getenv("TEST_DEBUG");
36
37 mem_ctx = ralloc_context(NULL);
38
39 devinfo = {};
40 devinfo.ver = 9;
41 devinfo.verx10 = 90;
42
43 compiler = {};
44 compiler.devinfo = &devinfo;
45 elk_init_isa_info(&compiler.isa, &devinfo);
46
47 params = {};
48 params.mem_ctx = mem_ctx;
49
50 prog_data = {};
51 nir_shader *nir =
52 nir_shader_create(mem_ctx, MESA_SHADER_FRAGMENT, NULL, NULL);
53
54 shader_a = new elk_fs_visitor(&compiler, ¶ms, NULL,
55 &prog_data.base, nir, 8, false, false);
56
57 shader_b = new elk_fs_visitor(&compiler, ¶ms, NULL,
58 &prog_data.base, nir, 8, false, false);
59 }
60
61 void
TearDown()62 PredicatedBreakTest::TearDown()
63 {
64 delete shader_a;
65 delete shader_b;
66 ralloc_free(mem_ctx);
67 mem_ctx = NULL;
68 }
69
70 bool
elk_opt_predicated_break(elk_fs_visitor * s)71 PredicatedBreakTest::elk_opt_predicated_break(elk_fs_visitor *s)
72 {
73 const bool print = getenv("TEST_DEBUG");
74
75 if (print) {
76 fprintf(stderr, "= Before =\n");
77 s->cfg->dump();
78 }
79
80 bool ret = ::elk_opt_predicated_break(s);
81
82 if (print) {
83 fprintf(stderr, "\n= After =\n");
84 s->cfg->dump();
85 }
86
87 return ret;
88 }
89
90 static fs_builder
make_builder(elk_fs_visitor * s)91 make_builder(elk_fs_visitor *s)
92 {
93 return fs_builder(s, s->dispatch_width).at_end();
94 }
95
96 static testing::AssertionResult
shaders_match(const char * a_expr,const char * b_expr,elk_fs_visitor * a,elk_fs_visitor * b)97 shaders_match(const char *a_expr, const char *b_expr,
98 elk_fs_visitor *a, elk_fs_visitor *b)
99 {
100 /* Using the CFG string dump for this. Not ideal but it is
101 * convenient that covers some CFG information, helping to
102 * check if the optimization is keeping the CFG valid.
103 */
104
105 char *a_str = NULL;
106 size_t a_size = 0;
107 FILE *a_file = open_memstream(&a_str, &a_size);
108 a->cfg->dump(a_file);
109 fclose(a_file);
110
111 char *b_str = NULL;
112 size_t b_size = 0;
113 FILE *b_file = open_memstream(&b_str, &b_size);
114 b->cfg->dump(b_file);
115 fclose(b_file);
116
117 if (a_size != b_size || strcmp(a_str, b_str) != 0) {
118 std::stringstream result;
119
120 result << "Shaders don't match.\n\n"
121 << a_expr << " is:\n\n" << a_str
122 << "\n---\n"
123 << b_expr << " is:\n\n" << b_str
124 << "\n";
125
126 free(a_str);
127 free(b_str);
128 return testing::AssertionFailure() << result.str();
129 }
130
131 free(a_str);
132 free(b_str);
133 return testing::AssertionSuccess();
134 }
135
136 #define ASSERT_SHADERS_MATCH(a, b) ASSERT_PRED_FORMAT2(shaders_match, a, b)
137
TEST_F(PredicatedBreakTest,TopBreakWithoutContinue)138 TEST_F(PredicatedBreakTest, TopBreakWithoutContinue)
139 {
140 fs_builder a = make_builder(shader_a);
141 fs_builder b = make_builder(shader_b);
142
143 elk_fs_reg r1 = elk_vec8_grf(1, 0);
144 elk_fs_reg r2 = elk_vec8_grf(2, 0);
145 elk_fs_reg r3 = elk_vec8_grf(3, 0);
146
147 a.DO();
148 a.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
149 a.IF(ELK_PREDICATE_NORMAL);
150 a.BREAK();
151 a.ENDIF();
152 a.ADD(r1, r2, r3);
153 a.WHILE();
154 a.NOP(); /* There's always going to be something after a WHILE. */
155 shader_a->calculate_cfg();
156
157 /* The IF/ENDIF around the BREAK is expected to be removed. */
158 bool progress = elk_opt_predicated_break(shader_a);
159 EXPECT_TRUE(progress);
160
161 b.DO();
162 b.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
163 b.BREAK()->predicate = ELK_PREDICATE_NORMAL;
164 b.ADD(r1, r2, r3);
165 b.WHILE();
166 b.NOP();
167 shader_b->calculate_cfg();
168
169 ASSERT_SHADERS_MATCH(shader_a, shader_b);
170 }
171
TEST_F(PredicatedBreakTest,TopBreakWithContinue)172 TEST_F(PredicatedBreakTest, TopBreakWithContinue)
173 {
174 fs_builder a = make_builder(shader_a);
175 fs_builder b = make_builder(shader_b);
176
177 elk_fs_reg r1 = elk_vec8_grf(1, 0);
178 elk_fs_reg r2 = elk_vec8_grf(2, 0);
179 elk_fs_reg r3 = elk_vec8_grf(3, 0);
180
181 a.DO();
182 a.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
183 a.IF(ELK_PREDICATE_NORMAL);
184 a.BREAK();
185 a.ENDIF();
186 a.ADD(r1, r2, r3);
187 a.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
188 a.IF(ELK_PREDICATE_NORMAL);
189 a.CONTINUE();
190 a.ENDIF();
191 a.MUL(r1, r2, r3);
192 a.WHILE();
193 a.NOP(); /* There's always going to be something after a WHILE. */
194 shader_a->calculate_cfg();
195
196 /* The IF/ENDIF around the BREAK and the CONTINUE are expected to be
197 * removed.
198 */
199 bool progress = elk_opt_predicated_break(shader_a);
200 EXPECT_TRUE(progress);
201
202 b.DO();
203 b.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
204 b.BREAK()->predicate = ELK_PREDICATE_NORMAL;
205 b.ADD(r1, r2, r3);
206 b.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
207 b.CONTINUE()->predicate = ELK_PREDICATE_NORMAL;
208 b.MUL(r1, r2, r3);
209 b.WHILE();
210 b.NOP();
211 shader_b->calculate_cfg();
212
213 ASSERT_SHADERS_MATCH(shader_a, shader_b);
214 }
215
TEST_F(PredicatedBreakTest,DISABLED_BottomBreakWithoutContinue)216 TEST_F(PredicatedBreakTest, DISABLED_BottomBreakWithoutContinue)
217 {
218 fs_builder a = make_builder(shader_a);
219 fs_builder b = make_builder(shader_b);
220
221 elk_fs_reg r1 = elk_vec8_grf(1, 0);
222 elk_fs_reg r2 = elk_vec8_grf(2, 0);
223 elk_fs_reg r3 = elk_vec8_grf(3, 0);
224
225 a.DO();
226 a.ADD(r1, r2, r3);
227 a.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
228 a.IF(ELK_PREDICATE_NORMAL);
229 a.BREAK();
230 a.ENDIF();
231 a.WHILE();
232 a.NOP(); /* There's always going to be something after a WHILE. */
233 shader_a->calculate_cfg();
234
235 /* BREAK is the only way to exit the loop, so expect to remove the
236 * IF/BREAK/ENDIF and add a predicate to WHILE.
237 */
238 bool progress = elk_opt_predicated_break(shader_a);
239 EXPECT_TRUE(progress);
240
241 b.DO();
242 b.ADD(r1, r2, r3);
243 b.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
244 auto w = b.WHILE();
245 w->predicate = ELK_PREDICATE_NORMAL;
246 w->predicate_inverse = true;
247 b.NOP();
248 shader_b->calculate_cfg();
249
250 ASSERT_SHADERS_MATCH(shader_a, shader_b);
251 }
252
253
TEST_F(PredicatedBreakTest,BottomBreakWithContinue)254 TEST_F(PredicatedBreakTest, BottomBreakWithContinue)
255 {
256 fs_builder a = make_builder(shader_a);
257 fs_builder b = make_builder(shader_b);
258
259 elk_fs_reg r1 = elk_vec8_grf(1, 0);
260 elk_fs_reg r2 = elk_vec8_grf(2, 0);
261 elk_fs_reg r3 = elk_vec8_grf(3, 0);
262
263 a.DO();
264 a.ADD(r1, r2, r3);
265 a.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
266 a.IF(ELK_PREDICATE_NORMAL);
267 a.CONTINUE();
268 a.ENDIF();
269 a.MUL(r1, r2, r3);
270 a.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
271 a.IF(ELK_PREDICATE_NORMAL);
272 a.BREAK();
273 a.ENDIF();
274 a.WHILE();
275 a.NOP(); /* There's always going to be something after a WHILE. */
276 shader_a->calculate_cfg();
277
278 /* With a CONTINUE, the BREAK can't be removed, but still remove the
279 * IF/ENDIF around both of them.
280 */
281 bool progress = elk_opt_predicated_break(shader_a);
282 EXPECT_TRUE(progress);
283
284 b.DO();
285 b.ADD(r1, r2, r3);
286 b.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
287 b.CONTINUE()->predicate = ELK_PREDICATE_NORMAL;
288 b.MUL(r1, r2, r3);
289 b.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
290 b.BREAK()->predicate = ELK_PREDICATE_NORMAL;
291 b.WHILE();
292 b.NOP();
293 shader_b->calculate_cfg();
294
295 ASSERT_SHADERS_MATCH(shader_a, shader_b);
296 }
297
TEST_F(PredicatedBreakTest,TwoBreaks)298 TEST_F(PredicatedBreakTest, TwoBreaks)
299 {
300 fs_builder a = make_builder(shader_a);
301 fs_builder b = make_builder(shader_b);
302
303 elk_fs_reg r1 = elk_vec8_grf(1, 0);
304 elk_fs_reg r2 = elk_vec8_grf(2, 0);
305 elk_fs_reg r3 = elk_vec8_grf(3, 0);
306
307 a.DO();
308 a.ADD(r1, r2, r3);
309 a.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
310 a.IF(ELK_PREDICATE_NORMAL);
311 a.BREAK();
312 a.ENDIF();
313 a.MUL(r1, r2, r3);
314 a.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
315 a.IF(ELK_PREDICATE_NORMAL);
316 a.BREAK();
317 a.ENDIF();
318 a.AND(r1, r2, r3);
319 a.WHILE();
320 a.NOP(); /* There's always going to be something after a WHILE. */
321 shader_a->calculate_cfg();
322
323 /* The IF/ENDIF around the breaks are expected to be removed. */
324 bool progress = elk_opt_predicated_break(shader_a);
325 EXPECT_TRUE(progress);
326
327 b.DO();
328 b.ADD(r1, r2, r3);
329 b.CMP(r1, r2, r3, ELK_CONDITIONAL_NZ);
330 b.BREAK()->predicate = ELK_PREDICATE_NORMAL;
331 b.MUL(r1, r2, r3);
332 b.CMP(r1, r2, r3, ELK_CONDITIONAL_GE);
333 b.BREAK()->predicate = ELK_PREDICATE_NORMAL;
334 b.AND(r1, r2, r3);
335 b.WHILE();
336 b.NOP(); /* There's always going to be something after a WHILE. */
337 shader_b->calculate_cfg();
338
339 ASSERT_SHADERS_MATCH(shader_a, shader_b);
340 }
341