1 /*
2 * Copyright © 2017 Gert Wollny
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 "st_tests_common.h"
25
26 #include "mesa/program/prog_instruction.h"
27 #include "tgsi/tgsi_info.h"
28 #include "tgsi/tgsi_ureg.h"
29 #include "compiler/glsl/list.h"
30 #include "gtest/gtest.h"
31
32 #include <utility>
33 #include <algorithm>
34
35 using std::vector;
36 using std::pair;
37 using std::make_pair;
38 using std::transform;
39 using std::copy;
40 using std::tuple;
41
42
43 /* Implementation of helper and test classes */
44 void *FakeCodeline::mem_ctx = nullptr;
45
FakeCodeline(tgsi_opcode _op,const vector<int> & _dst,const vector<int> & _src,const vector<int> & _to)46 FakeCodeline::FakeCodeline(tgsi_opcode _op, const vector<int>& _dst,
47 const vector<int>& _src, const vector<int>&_to):
48 op(_op),
49 max_temp_id(0),
50 max_array_id(0)
51 {
52 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
53 [this](int i) { return create_dst_register(i);});
54
55 transform(_src.begin(), _src.end(), std::back_inserter(src),
56 [this](int i) { return create_src_register(i);});
57
58 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
59 [this](int i) { return create_src_register(i);});
60
61 }
62
FakeCodeline(tgsi_opcode _op,const vector<pair<int,int>> & _dst,const vector<pair<int,const char * >> & _src,const vector<pair<int,const char * >> & _to,SWZ with_swizzle)63 FakeCodeline::FakeCodeline(tgsi_opcode _op, const vector<pair<int,int>>& _dst,
64 const vector<pair<int, const char *>>& _src,
65 const vector<pair<int, const char *>>&_to,
66 SWZ with_swizzle):
67 op(_op),
68 max_temp_id(0),
69 max_array_id(0)
70 {
71 (void)with_swizzle;
72
73 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
74 [this](pair<int,int> r) {
75 return create_dst_register(r.first, r.second);
76 });
77
78 transform(_src.begin(), _src.end(), std::back_inserter(src),
79 [this](const pair<int,const char *>& r) {
80 return create_src_register(r.first, r.second);
81 });
82
83 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
84 [this](const pair<int,const char *>& r) {
85 return create_src_register(r.first, r.second);
86 });
87 }
88
FakeCodeline(tgsi_opcode _op,const vector<tuple<int,int,int>> & _dst,const vector<tuple<int,int,int>> & _src,const vector<tuple<int,int,int>> & _to,RA with_reladdr)89 FakeCodeline::FakeCodeline(tgsi_opcode _op, const vector<tuple<int,int,int>>& _dst,
90 const vector<tuple<int,int,int>>& _src,
91 const vector<tuple<int,int,int>>&_to, RA with_reladdr):
92 op(_op),
93 max_temp_id(0),
94 max_array_id(0)
95 {
96 (void)with_reladdr;
97
98 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
99 [this](const tuple<int,int,int>& r) {
100 return create_dst_register(r);
101 });
102
103 transform(_src.begin(), _src.end(), std::back_inserter(src),
104 [this](const tuple<int,int,int>& r) {
105 return create_src_register(r);
106 });
107
108 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
109 [this](const tuple<int,int,int>& r) {
110 return create_src_register(r);
111 });
112 }
113
FakeCodeline(tgsi_opcode _op,const vector<tuple<int,int,int>> & _dst,const vector<tuple<int,int,const char * >> & _src,const vector<tuple<int,int,const char * >> & _to,ARR with_array)114 FakeCodeline::FakeCodeline(tgsi_opcode _op, const vector<tuple<int,int,int>>& _dst,
115 const vector<tuple<int,int, const char*>>& _src,
116 const vector<tuple<int,int, const char*>>&_to,
117 ARR with_array):
118 FakeCodeline(_op)
119 {
120 (void)with_array;
121
122 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
123 [this](const tuple<int,int,int>& r) {
124 return create_array_dst_register(r);
125 });
126
127 transform(_src.begin(), _src.end(), std::back_inserter(src),
128 [this](const tuple<int,int,const char*>& r) {
129 return create_array_src_register(r);
130 });
131
132 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
133 [this](const tuple<int,int,const char*>& r) {
134 return create_array_src_register(r);
135 });
136
137 }
138
FakeCodeline(const glsl_to_tgsi_instruction & instr)139 FakeCodeline::FakeCodeline(const glsl_to_tgsi_instruction& instr):
140 op(instr.op),
141 max_temp_id(0),
142 max_array_id(0)
143 {
144 int nsrc = num_inst_src_regs(&instr);
145 int ndst = num_inst_dst_regs(&instr);
146
147 copy(instr.src, instr.src + nsrc, std::back_inserter(src));
148 copy(instr.dst, instr.dst + ndst, std::back_inserter(dst));
149
150 for (auto& s: src)
151 read_reg(s);
152
153 for (auto& d: dst)
154 read_reg(d);
155
156 }
157
158 template <typename st_reg>
read_reg(const st_reg & s)159 void FakeCodeline::read_reg(const st_reg& s)
160 {
161 if (s.file == PROGRAM_ARRAY) {
162 if (s.array_id > max_array_id)
163 max_array_id = s.array_id;
164 if (s.reladdr)
165 read_reg(*s.reladdr);
166 if (s.reladdr2)
167 read_reg(*s.reladdr2);
168 } else if (s.file == PROGRAM_TEMPORARY) {
169 if (s.index > max_temp_id)
170 max_temp_id = s.index;
171 }
172 }
173
print(std::ostream & os) const174 void FakeCodeline::print(std::ostream& os) const
175 {
176 const struct tgsi_opcode_info *info = tgsi_get_opcode_info(op);
177 os << tgsi_get_opcode_name(info->opcode) << " ";
178
179 for (auto d: dst) {
180 os << d << " ";
181 }
182 os << " <- ";
183 for (auto s: src) {
184 os << s << " ";
185 }
186 os << "\n";
187 }
188
operator ==(const FakeCodeline & lhs,const FakeCodeline & rhs)189 bool operator == (const FakeCodeline& lhs, const FakeCodeline& rhs)
190 {
191 if ((lhs.op != rhs.op) ||
192 (lhs.src.size() != rhs.src.size()) ||
193 (lhs.dst.size() != rhs.dst.size()))
194 return false;
195
196 return std::equal(lhs.src.begin(), lhs.src.end(), rhs.src.begin()) &&
197 std::equal(lhs.dst.begin(), lhs.dst.end(), rhs.dst.begin());
198 }
199
create_src_register(int src_idx)200 st_src_reg FakeCodeline::create_src_register(int src_idx)
201 {
202 return create_src_register(src_idx,
203 src_idx < 0 ? PROGRAM_INPUT : PROGRAM_TEMPORARY);
204 }
205
swizzle_from_char(const char * sw)206 static int swizzle_from_char(const char *sw)
207 {
208 int swizzle = 0;
209 if (!sw || sw[0] == 0)
210 return SWIZZLE_XYZW;
211
212 const char *isw = sw;
213 for (int i = 0; i < 4; ++i) {
214 switch (*isw) {
215 case 'x': break; /* is zero */
216 case 'y': swizzle |= SWIZZLE_Y << 3 * i; break;
217 case 'z': swizzle |= SWIZZLE_Z << 3 * i; break;
218 case 'w': swizzle |= SWIZZLE_W << 3 * i; break;
219 default:
220 assert(!"This test uses an unknown swizzle character");
221 }
222 if (isw[1] != 0)
223 ++isw;
224 }
225 return swizzle;
226 }
227
create_src_register(int src_idx,const char * sw)228 st_src_reg FakeCodeline::create_src_register(int src_idx, const char *sw)
229 {
230 st_src_reg result = create_src_register(src_idx);
231 result.swizzle = swizzle_from_char(sw);
232 return result;
233 }
234
create_src_register(int src_idx,gl_register_file file)235 st_src_reg FakeCodeline::create_src_register(int src_idx, gl_register_file file)
236 {
237 st_src_reg retval;
238 retval.file = file;
239 retval.index = src_idx >= 0 ? src_idx : 1 - src_idx;
240
241 if (file == PROGRAM_TEMPORARY) {
242 if (max_temp_id < src_idx)
243 max_temp_id = src_idx;
244 } else if (file == PROGRAM_ARRAY) {
245 retval.array_id = 1;
246 if (max_array_id < 1)
247 max_array_id = 1;
248 }
249 retval.swizzle = SWIZZLE_XYZW;
250 retval.type = GLSL_TYPE_INT;
251
252 return retval;
253 }
254
create_rel_src_register(int idx)255 st_src_reg *FakeCodeline::create_rel_src_register(int idx)
256 {
257 st_src_reg *retval = ralloc(mem_ctx, st_src_reg);
258 *retval = st_src_reg(PROGRAM_TEMPORARY, idx, GLSL_TYPE_INT);
259 if (max_temp_id < idx)
260 max_temp_id = idx;
261 return retval;
262 }
263
create_array_src_register(const tuple<int,int,const char * > & r)264 st_src_reg FakeCodeline::create_array_src_register(const tuple<int,int, const char*>& r)
265 {
266
267 int array_id = std::get<0>(r);
268 int idx = std::get<1>(r);
269
270 st_src_reg retval = create_src_register(idx, std::get<2>(r));
271
272 if (array_id > 0) {
273 retval.file = PROGRAM_ARRAY;
274
275 retval.array_id = array_id;
276 if (max_array_id < array_id)
277 max_array_id = array_id;
278 } else {
279 if (max_temp_id < idx)
280 max_temp_id = idx;
281 }
282
283 return retval;
284 }
285
create_array_dst_register(const tuple<int,int,int> & r)286 st_dst_reg FakeCodeline::create_array_dst_register(const tuple<int,int,int>& r)
287 {
288
289 int array_id = std::get<0>(r);
290 int idx = std::get<1>(r);
291
292 st_dst_reg retval = create_dst_register(idx, std::get<2>(r));
293
294 if (array_id > 0) {
295 retval.file = PROGRAM_ARRAY;
296 retval.array_id = array_id;
297 if (max_array_id < array_id)
298 max_array_id = array_id;
299 } else {
300 if (max_temp_id < idx)
301 max_temp_id = idx;
302 }
303 return retval;
304 }
305
create_src_register(const tuple<int,int,int> & src)306 st_src_reg FakeCodeline::create_src_register(const tuple<int,int,int>& src)
307 {
308 int src_idx = std::get<0>(src);
309 int relidx1 = std::get<1>(src);
310 int relidx2 = std::get<2>(src);
311
312 gl_register_file file = PROGRAM_TEMPORARY;
313 if (src_idx < 0)
314 file = PROGRAM_OUTPUT;
315 else if (relidx1 || relidx2) {
316 file = PROGRAM_ARRAY;
317 }
318
319 st_src_reg retval = create_src_register(src_idx, file);
320 if (src_idx >= 0) {
321 if (relidx1 || relidx2) {
322 retval.array_id = 1;
323
324 if (relidx1)
325 retval.reladdr = create_rel_src_register(relidx1);
326 if (relidx2) {
327 retval.reladdr2 = create_rel_src_register(relidx2);
328 retval.has_index2 = true;
329 retval.index2D = 10;
330 }
331 }
332 }
333 return retval;
334 }
335
create_dst_register(int dst_idx,int writemask)336 st_dst_reg FakeCodeline::create_dst_register(int dst_idx,int writemask)
337 {
338 gl_register_file file;
339 int idx = 0;
340 if (dst_idx >= 0) {
341 file = PROGRAM_TEMPORARY;
342 idx = dst_idx;
343 if (max_temp_id < idx)
344 max_temp_id = idx;
345 } else {
346 file = PROGRAM_OUTPUT;
347 idx = 1 - dst_idx;
348 }
349 return st_dst_reg(file, writemask, GLSL_TYPE_INT, idx);
350 }
351
create_dst_register(int dst_idx)352 st_dst_reg FakeCodeline::create_dst_register(int dst_idx)
353 {
354 return create_dst_register(dst_idx, dst_idx < 0 ?
355 PROGRAM_OUTPUT : PROGRAM_TEMPORARY);
356 }
357
create_dst_register(int dst_idx,gl_register_file file)358 st_dst_reg FakeCodeline::create_dst_register(int dst_idx, gl_register_file file)
359 {
360 st_dst_reg retval;
361 retval.file = file;
362 retval.index = dst_idx >= 0 ? dst_idx : 1 - dst_idx;
363
364 if (file == PROGRAM_TEMPORARY) {
365 if (max_temp_id < dst_idx)
366 max_temp_id = dst_idx;
367 } else if (file == PROGRAM_ARRAY) {
368 retval.array_id = 1;
369 if (max_array_id < 1)
370 max_array_id = 1;
371 }
372 retval.writemask = 0xF;
373 retval.type = GLSL_TYPE_INT;
374
375 return retval;
376 }
377
create_dst_register(const tuple<int,int,int> & dst)378 st_dst_reg FakeCodeline::create_dst_register(const tuple<int,int,int>& dst)
379 {
380 int dst_idx = std::get<0>(dst);
381 int relidx1 = std::get<1>(dst);
382 int relidx2 = std::get<2>(dst);
383
384 gl_register_file file = PROGRAM_TEMPORARY;
385 if (dst_idx < 0)
386 file = PROGRAM_OUTPUT;
387 else if (relidx1 || relidx2) {
388 file = PROGRAM_ARRAY;
389 }
390 st_dst_reg retval = create_dst_register(dst_idx, file);
391
392 if (relidx1 || relidx2) {
393 if (relidx1)
394 retval.reladdr = create_rel_src_register(relidx1);
395 if (relidx2) {
396 retval.reladdr2 = create_rel_src_register(relidx2);
397 retval.has_index2 = true;
398 retval.index2D = 10;
399 }
400 }
401 return retval;
402 }
403
get_codeline() const404 glsl_to_tgsi_instruction *FakeCodeline::get_codeline() const
405 {
406 glsl_to_tgsi_instruction *next_instr = new(mem_ctx) glsl_to_tgsi_instruction();
407 next_instr->op = op;
408 next_instr->info = tgsi_get_opcode_info(op);
409
410 assert(src.size() == num_inst_src_regs(next_instr));
411 assert(dst.size() == num_inst_dst_regs(next_instr));
412 assert(tex_offsets.size() < 3);
413
414 copy(src.begin(), src.end(), next_instr->src);
415 copy(dst.begin(), dst.end(), next_instr->dst);
416
417 next_instr->tex_offset_num_offset = tex_offsets.size();
418
419 if (next_instr->tex_offset_num_offset > 0) {
420 next_instr->tex_offsets = ralloc_array(mem_ctx, st_src_reg, tex_offsets.size());
421 copy(tex_offsets.begin(), tex_offsets.end(), next_instr->tex_offsets);
422 } else {
423 next_instr->tex_offsets = nullptr;
424 }
425 return next_instr;
426 }
427
set_mem_ctx(void * ctx)428 void FakeCodeline::set_mem_ctx(void *ctx)
429 {
430 mem_ctx = ctx;
431 }
432
FakeShader(const vector<FakeCodeline> & source)433 FakeShader::FakeShader(const vector<FakeCodeline>& source):
434 program(source),
435 num_temps(0),
436 num_arrays(0)
437 {
438 for (const FakeCodeline& i: source) {
439 int t = i.get_max_reg_id();
440 if (t > num_temps)
441 num_temps = t;
442
443 int a = i.get_max_array_id();
444 if (a > num_arrays)
445 num_arrays = a;
446 }
447 ++num_temps;
448 }
449
FakeShader(exec_list * tgsi_prog)450 FakeShader::FakeShader(exec_list *tgsi_prog):
451 num_temps(0),
452 num_arrays(0)
453 {
454 FakeCodeline nop(TGSI_OPCODE_NOP);
455 FakeCodeline& last = nop;
456
457 foreach_in_list(glsl_to_tgsi_instruction, inst, tgsi_prog) {
458 program.push_back(last = FakeCodeline(*inst));
459 if (last.get_max_array_id() > num_arrays)
460 num_arrays = last.get_max_array_id();
461 if (num_temps < last.get_max_reg_id())
462 num_temps = last.get_max_reg_id();
463 }
464 ++num_temps;
465 }
466
get_num_arrays() const467 int FakeShader::get_num_arrays() const
468 {
469 return num_arrays;
470 }
471
get_num_temps() const472 int FakeShader::get_num_temps() const
473 {
474 return num_temps;
475 }
476
get_program(void * ctx) const477 exec_list* FakeShader::get_program(void *ctx) const
478 {
479 exec_list *prog = new(ctx) exec_list();
480
481 for (const FakeCodeline& i: program) {
482 prog->push_tail(i.get_codeline());
483 }
484
485 return prog;
486 }
487
length() const488 size_t FakeShader::length() const
489 {
490 return program.size();
491 }
492
line(unsigned i) const493 const FakeCodeline& FakeShader::line(unsigned i) const
494 {
495 return program[i];
496 }
497
SetUp()498 void MesaTestWithMemCtx::SetUp()
499 {
500 mem_ctx = ralloc_context(nullptr);
501 FakeCodeline::set_mem_ctx(mem_ctx);
502 }
503
TearDown()504 void MesaTestWithMemCtx::TearDown()
505 {
506 ralloc_free(mem_ctx);
507 FakeCodeline::set_mem_ctx(nullptr);
508 mem_ctx = nullptr;
509 }
510
511
512 LifetimeEvaluatorTest::life_range_result
run(const vector<FakeCodeline> & code,bool & success)513 LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, bool& success)
514 {
515 FakeShader shader(code);
516 life_range_result result = make_pair(life_range_result::first_type(shader.get_num_temps()),
517 life_range_result::second_type(shader.get_num_arrays()));
518
519 success =
520 get_temp_registers_required_live_ranges(mem_ctx, shader.get_program(mem_ctx),
521 shader.get_num_temps(),&result.first[0],
522 shader.get_num_arrays(), &result.second[0]);
523 return result;
524 }
525
run(const vector<FakeCodeline> & code,const temp_lt_expect & e)526 void LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, const temp_lt_expect& e)
527 {
528 bool success = false;
529 auto result = run(code, success);
530 ASSERT_TRUE(success);
531 ASSERT_EQ(result.first.size(), e.size());
532 check(result.first, e);
533 }
534
run(const vector<FakeCodeline> & code,const array_lt_expect & e)535 void LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, const array_lt_expect& e)
536 {
537 bool success = false;
538 auto result = run(code, success);
539 ASSERT_TRUE(success);
540 ASSERT_EQ(result.second.size(), e.size());
541 check(result.second, e);
542 }
543
check(const vector<register_live_range> & lifetimes,const temp_lt_expect & e)544 void LifetimeEvaluatorExactTest::check( const vector<register_live_range>& lifetimes,
545 const temp_lt_expect& e)
546 {
547 for (unsigned i = 1; i < lifetimes.size(); ++i) {
548 EXPECT_EQ(lifetimes[i].begin, e[i][0]);
549 EXPECT_EQ(lifetimes[i].end, e[i][1]);
550 }
551 }
552
check(const vector<array_live_range> & lifetimes,const array_lt_expect & e)553 void LifetimeEvaluatorExactTest::check(const vector<array_live_range>& lifetimes,
554 const array_lt_expect& e)
555 {
556 for (unsigned i = 0; i < lifetimes.size(); ++i) {
557 EXPECT_EQ(lifetimes[i].begin(), e[i].begin());
558 EXPECT_EQ(lifetimes[i].end(), e[i].end());
559 EXPECT_EQ(lifetimes[i].access_mask(), e[i].access_mask());
560 }
561 }
562
check(const vector<register_live_range> & lifetimes,const temp_lt_expect & e)563 void LifetimeEvaluatorAtLeastTest::check( const vector<register_live_range>& lifetimes,
564 const temp_lt_expect& e)
565 {
566 for (unsigned i = 1; i < lifetimes.size(); ++i) {
567 EXPECT_LE(lifetimes[i].begin, e[i][0]);
568 EXPECT_GE(lifetimes[i].end, e[i][1]);
569 }
570 }
571
check(const vector<array_live_range> & lifetimes,const array_lt_expect & e)572 void LifetimeEvaluatorAtLeastTest::check(const vector<array_live_range>& lifetimes,
573 const array_lt_expect& e)
574 {
575 for (unsigned i = 0; i < lifetimes.size(); ++i) {
576 EXPECT_LE(lifetimes[i].begin(), e[i].begin());
577 EXPECT_GE(lifetimes[i].end(), e[i].end());
578
579 /* Tests that lifetimes doesn't add unexpected swizzles */
580 EXPECT_EQ(lifetimes[i].access_mask()| e[i].access_mask(),
581 e[i].access_mask());
582 }
583 }
584
585
run(const vector<register_live_range> & lt,const vector<int> & expect)586 void RegisterRemappingTest::run(const vector<register_live_range>& lt,
587 const vector<int>& expect)
588 {
589 rename_reg_pair proto{false,0};
590 vector<rename_reg_pair> result(lt.size(), proto);
591
592 get_temp_registers_remapping(mem_ctx, lt.size(), <[0], &result[0]);
593
594 vector<int> remap(lt.size());
595 for (unsigned i = 0; i < lt.size(); ++i) {
596 remap[i] = result[i].valid ? result[i].new_reg : i;
597 }
598
599 std::transform(remap.begin(), remap.end(), result.begin(), remap.begin(),
600 [](int x, const rename_reg_pair& rn) {
601 return rn.valid ? rn.new_reg : x;
602 });
603
604 for(unsigned i = 1; i < remap.size(); ++i) {
605 EXPECT_EQ(remap[i], expect[i]);
606 }
607 }
608
run(const vector<FakeCodeline> & code,const vector<int> & expect)609 void RegisterLifetimeAndRemappingTest::run(const vector<FakeCodeline>& code,
610 const vector<int>& expect)
611 {
612 FakeShader shader(code);
613 std::vector<register_live_range> lt(shader.get_num_temps());
614 std::vector<array_live_range> alt(shader.get_num_arrays());
615 get_temp_registers_required_live_ranges(mem_ctx, shader.get_program(mem_ctx),
616 shader.get_num_temps(), <[0],
617 shader.get_num_arrays(), &alt[0]);
618 this->run(lt, expect);
619 }
620