• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params, NULL,
55                              &prog_data.base, nir, 8, false, false);
56 
57    shader_b = new elk_fs_visitor(&compiler, &params, 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