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
35using std::vector;
36using std::pair;
37using std::make_pair;
38using std::transform;
39using std::copy;
40using std::tuple;
41
42
43/* Implementation of helper and test classes */
44void *FakeCodeline::mem_ctx = nullptr;
45
46FakeCodeline::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
63FakeCodeline::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
89FakeCodeline::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
114FakeCodeline::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
139FakeCodeline::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
158template <typename st_reg>
159void 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
174void 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
189bool 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
200st_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
206static 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
228st_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
235st_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
255st_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
264st_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
286st_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
306st_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
336st_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
352st_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
358st_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
378st_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
404glsl_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
428void FakeCodeline::set_mem_ctx(void *ctx)
429{
430   mem_ctx = ctx;
431}
432
433FakeShader::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
450FakeShader::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
467int FakeShader::get_num_arrays() const
468{
469   return num_arrays;
470}
471
472int FakeShader::get_num_temps() const
473{
474   return num_temps;
475}
476
477exec_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
488size_t FakeShader::length() const
489{
490   return program.size();
491}
492
493const FakeCodeline& FakeShader::line(unsigned i) const
494{
495   return program[i];
496}
497
498void MesaTestWithMemCtx::SetUp()
499{
500   mem_ctx = ralloc_context(nullptr);
501   FakeCodeline::set_mem_ctx(mem_ctx);
502}
503
504void MesaTestWithMemCtx::TearDown()
505{
506   ralloc_free(mem_ctx);
507   FakeCodeline::set_mem_ctx(nullptr);
508   mem_ctx = nullptr;
509}
510
511
512LifetimeEvaluatorTest::life_range_result
513LifetimeEvaluatorTest::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
526void 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
535void 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
544void 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
553void 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
563void 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
572void 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
586void 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
609void 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