• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(), &lt[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(), &lt[0],
617 					     shader.get_num_arrays(), &alt[0]);
618      this->run(lt, expect);
619 }
620