mesa/st/tests: Add tests for lifetime tracking with indirect addressing
[mesa.git] / src / mesa / state_tracker / tests / st_tests_common.cpp
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
46 FakeCodeline::FakeCodeline(unsigned _op, const vector<int>& _dst,
47 const vector<int>& _src, const vector<int>&_to):
48 op(_op),
49 max_temp_id(0)
50 {
51 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
52 [this](int i) { return create_dst_register(i);});
53
54 transform(_src.begin(), _src.end(), std::back_inserter(src),
55 [this](int i) { return create_src_register(i);});
56
57 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
58 [this](int i) { return create_src_register(i);});
59
60 }
61
62 FakeCodeline::FakeCodeline(unsigned _op, const vector<pair<int,int>>& _dst,
63 const vector<pair<int, const char *>>& _src,
64 const vector<pair<int, const char *>>&_to,
65 SWZ with_swizzle):
66 op(_op),
67 max_temp_id(0)
68 {
69 (void)with_swizzle;
70
71 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
72 [this](pair<int,int> r) {
73 return create_dst_register(r.first, r.second);
74 });
75
76 transform(_src.begin(), _src.end(), std::back_inserter(src),
77 [this](const pair<int,const char *>& r) {
78 return create_src_register(r.first, r.second);
79 });
80
81 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
82 [this](const pair<int,const char *>& r) {
83 return create_src_register(r.first, r.second);
84 });
85 }
86
87 FakeCodeline::FakeCodeline(unsigned _op, const vector<tuple<int,int,int>>& _dst,
88 const vector<tuple<int,int,int>>& _src,
89 const vector<tuple<int,int,int>>&_to, RA with_reladdr):
90 op(_op),
91 max_temp_id(0)
92 {
93 (void)with_reladdr;
94
95 transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
96 [this](const tuple<int,int,int>& r) {
97 return create_dst_register(r);
98 });
99
100 transform(_src.begin(), _src.end(), std::back_inserter(src),
101 [this](const tuple<int,int,int>& r) {
102 return create_src_register(r);
103 });
104
105 transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
106 [this](const tuple<int,int,int>& r) {
107 return create_src_register(r);
108 });
109 }
110
111 FakeCodeline::FakeCodeline(const glsl_to_tgsi_instruction& instr):
112 op(instr.op),
113 max_temp_id(0)
114 {
115 int nsrc = num_inst_src_regs(&instr);
116 int ndst = num_inst_dst_regs(&instr);
117
118 copy(instr.src, instr.src + nsrc, std::back_inserter(src));
119 copy(instr.dst, instr.dst + ndst, std::back_inserter(dst));
120
121 for (auto& s: src)
122 read_reg(s);
123
124 for (auto& d: dst)
125 read_reg(d);
126
127 }
128
129 template <typename st_reg>
130 void FakeCodeline::read_reg(const st_reg& s)
131 {
132 if (s.file == PROGRAM_TEMPORARY) {
133 if (s.index > max_temp_id)
134 max_temp_id = s.index;
135 }
136 }
137
138 void FakeCodeline::print(std::ostream& os) const
139 {
140 const struct tgsi_opcode_info *info = tgsi_get_opcode_info(op);
141 os << tgsi_get_opcode_name(info->opcode) << " ";
142
143 for (auto d: dst) {
144 os << d << " ";
145 }
146 os << " <- ";
147 for (auto s: src) {
148 os << s << " ";
149 }
150 os << "\n";
151 }
152
153 bool operator == (const FakeCodeline& lhs, const FakeCodeline& rhs)
154 {
155 if ((lhs.op != rhs.op) ||
156 (lhs.src.size() != rhs.src.size()) ||
157 (lhs.dst.size() != rhs.dst.size()))
158 return false;
159
160 return std::equal(lhs.src.begin(), lhs.src.end(), rhs.src.begin()) &&
161 std::equal(lhs.dst.begin(), lhs.dst.end(), rhs.dst.begin());
162 }
163
164 st_src_reg FakeCodeline::create_src_register(int src_idx)
165 {
166 return create_src_register(src_idx,
167 src_idx < 0 ? PROGRAM_INPUT : PROGRAM_TEMPORARY);
168 }
169
170 static int swizzle_from_char(const char *sw)
171 {
172 int swizzle = 0;
173 if (!sw || sw[0] == 0)
174 return SWIZZLE_XYZW;
175
176 const char *isw = sw;
177 for (int i = 0; i < 4; ++i) {
178 switch (*isw) {
179 case 'x': break; /* is zero */
180 case 'y': swizzle |= SWIZZLE_Y << 3 * i; break;
181 case 'z': swizzle |= SWIZZLE_Z << 3 * i; break;
182 case 'w': swizzle |= SWIZZLE_W << 3 * i; break;
183 default:
184 assert(!"This test uses an unknown swizzle character");
185 }
186 if (isw[1] != 0)
187 ++isw;
188 }
189 return swizzle;
190 }
191
192 st_src_reg FakeCodeline::create_src_register(int src_idx, const char *sw)
193 {
194 st_src_reg result = create_src_register(src_idx);
195 result.swizzle = swizzle_from_char(sw);
196 return result;
197 }
198
199 st_src_reg FakeCodeline::create_src_register(int src_idx, gl_register_file file)
200 {
201 st_src_reg retval;
202 retval.file = file;
203 retval.index = src_idx >= 0 ? src_idx : 1 - src_idx;
204
205 if (file == PROGRAM_TEMPORARY) {
206 if (max_temp_id < src_idx)
207 max_temp_id = src_idx;
208 } else if (file == PROGRAM_ARRAY) {
209 retval.array_id = 1;
210 }
211 retval.swizzle = SWIZZLE_XYZW;
212 retval.type = GLSL_TYPE_INT;
213
214 return retval;
215 }
216
217 st_src_reg *FakeCodeline::create_rel_src_register(int idx)
218 {
219 st_src_reg *retval = ralloc(mem_ctx, st_src_reg);
220 *retval = st_src_reg(PROGRAM_TEMPORARY, idx, GLSL_TYPE_INT);
221 if (max_temp_id < idx)
222 max_temp_id = idx;
223 return retval;
224 }
225
226 st_src_reg FakeCodeline::create_src_register(const tuple<int,int,int>& src)
227 {
228 int src_idx = std::get<0>(src);
229 int relidx1 = std::get<1>(src);
230 int relidx2 = std::get<2>(src);
231
232 gl_register_file file = PROGRAM_TEMPORARY;
233 if (src_idx < 0)
234 file = PROGRAM_OUTPUT;
235 else if (relidx1 || relidx2) {
236 file = PROGRAM_ARRAY;
237 }
238
239 st_src_reg retval = create_src_register(src_idx, file);
240 if (src_idx >= 0) {
241 if (relidx1 || relidx2) {
242 retval.array_id = 1;
243
244 if (relidx1)
245 retval.reladdr = create_rel_src_register(relidx1);
246 if (relidx2) {
247 retval.reladdr2 = create_rel_src_register(relidx2);
248 retval.has_index2 = true;
249 retval.index2D = 10;
250 }
251 }
252 }
253 return retval;
254 }
255
256 st_dst_reg FakeCodeline::create_dst_register(int dst_idx,int writemask)
257 {
258 gl_register_file file;
259 int idx = 0;
260 if (dst_idx >= 0) {
261 file = PROGRAM_TEMPORARY;
262 idx = dst_idx;
263 if (max_temp_id < idx)
264 max_temp_id = idx;
265 } else {
266 file = PROGRAM_OUTPUT;
267 idx = 1 - dst_idx;
268 }
269 return st_dst_reg(file, writemask, GLSL_TYPE_INT, idx);
270 }
271
272 st_dst_reg FakeCodeline::create_dst_register(int dst_idx)
273 {
274 return create_dst_register(dst_idx, dst_idx < 0 ?
275 PROGRAM_OUTPUT : PROGRAM_TEMPORARY);
276 }
277
278 st_dst_reg FakeCodeline::create_dst_register(int dst_idx, gl_register_file file)
279 {
280 st_dst_reg retval;
281 retval.file = file;
282 retval.index = dst_idx >= 0 ? dst_idx : 1 - dst_idx;
283
284 if (file == PROGRAM_TEMPORARY) {
285 if (max_temp_id < dst_idx)
286 max_temp_id = dst_idx;
287 } else if (file == PROGRAM_ARRAY) {
288 retval.array_id = 1;
289 }
290 retval.writemask = 0xF;
291 retval.type = GLSL_TYPE_INT;
292
293 return retval;
294 }
295
296 st_dst_reg FakeCodeline::create_dst_register(const tuple<int,int,int>& dst)
297 {
298 int dst_idx = std::get<0>(dst);
299 int relidx1 = std::get<1>(dst);
300 int relidx2 = std::get<2>(dst);
301
302 gl_register_file file = PROGRAM_TEMPORARY;
303 if (dst_idx < 0)
304 file = PROGRAM_OUTPUT;
305 else if (relidx1 || relidx2) {
306 file = PROGRAM_ARRAY;
307 }
308 st_dst_reg retval = create_dst_register(dst_idx, file);
309
310 if (relidx1 || relidx2) {
311 if (relidx1)
312 retval.reladdr = create_rel_src_register(relidx1);
313 if (relidx2) {
314 retval.reladdr2 = create_rel_src_register(relidx2);
315 retval.has_index2 = true;
316 retval.index2D = 10;
317 }
318 }
319 return retval;
320 }
321
322 glsl_to_tgsi_instruction *FakeCodeline::get_codeline() const
323 {
324 glsl_to_tgsi_instruction *next_instr = new(mem_ctx) glsl_to_tgsi_instruction();
325 next_instr->op = op;
326 next_instr->info = tgsi_get_opcode_info(op);
327
328 assert(src.size() == num_inst_src_regs(next_instr));
329 assert(dst.size() == num_inst_dst_regs(next_instr));
330 assert(tex_offsets.size() < 3);
331
332 copy(src.begin(), src.end(), next_instr->src);
333 copy(dst.begin(), dst.end(), next_instr->dst);
334
335 next_instr->tex_offset_num_offset = tex_offsets.size();
336
337 if (next_instr->tex_offset_num_offset > 0) {
338 next_instr->tex_offsets = ralloc_array(mem_ctx, st_src_reg, tex_offsets.size());
339 copy(tex_offsets.begin(), tex_offsets.end(), next_instr->tex_offsets);
340 } else {
341 next_instr->tex_offsets = nullptr;
342 }
343 return next_instr;
344 }
345
346 void FakeCodeline::set_mem_ctx(void *ctx)
347 {
348 mem_ctx = ctx;
349 }
350
351 FakeShader::FakeShader(const vector<FakeCodeline>& source):
352 program(source),
353 num_temps(0)
354 {
355 for (const FakeCodeline& i: source) {
356 int t = i.get_max_reg_id();
357 if (t > num_temps)
358 num_temps = t;
359 }
360 ++num_temps;
361 }
362
363 FakeShader::FakeShader(exec_list *tgsi_prog):
364 num_temps(0)
365 {
366 FakeCodeline nop(TGSI_OPCODE_NOP);
367 FakeCodeline& last = nop;
368
369 foreach_in_list(glsl_to_tgsi_instruction, inst, tgsi_prog) {
370 program.push_back(last = FakeCodeline(*inst));
371 if (num_temps < last.get_max_reg_id())
372 num_temps = last.get_max_reg_id();
373 }
374 ++num_temps;
375 }
376
377 int FakeShader::get_num_temps() const
378 {
379 return num_temps;
380 }
381
382 exec_list* FakeShader::get_program(void *ctx) const
383 {
384 exec_list *prog = new(ctx) exec_list();
385
386 for (const FakeCodeline& i: program) {
387 prog->push_tail(i.get_codeline());
388 }
389
390 return prog;
391 }
392
393 void MesaTestWithMemCtx::SetUp()
394 {
395 mem_ctx = ralloc_context(nullptr);
396 FakeCodeline::set_mem_ctx(mem_ctx);
397 }
398
399 void MesaTestWithMemCtx::TearDown()
400 {
401 ralloc_free(mem_ctx);
402 FakeCodeline::set_mem_ctx(nullptr);
403 mem_ctx = nullptr;
404 }
405
406
407 LifetimeEvaluatorTest::lifetime_result
408 LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, bool& success)
409 {
410 FakeShader shader(code);
411 lifetime_result result(shader.get_num_temps());
412
413 success =
414 get_temp_registers_required_lifetimes(mem_ctx, shader.get_program(mem_ctx),
415 shader.get_num_temps(),
416 &result[0]);
417
418 return result;
419 }
420
421 void LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, const temp_lt_expect& e)
422 {
423 FakeShader shader(code);
424 lifetime_result result(shader.get_num_temps());
425 bool success =
426 get_temp_registers_required_lifetimes(mem_ctx, shader.get_program(mem_ctx),
427 shader.get_num_temps(),
428 &result[0]);
429 ASSERT_TRUE(success);
430 ASSERT_EQ(result.size(), e.size());
431 check(result, e);
432 }
433
434 void LifetimeEvaluatorExactTest::check( const vector<lifetime>& lifetimes,
435 const temp_lt_expect& e)
436 {
437 for (unsigned i = 1; i < lifetimes.size(); ++i) {
438 EXPECT_EQ(lifetimes[i].begin, e[i][0]);
439 EXPECT_EQ(lifetimes[i].end, e[i][1]);
440 }
441 }
442
443 void LifetimeEvaluatorAtLeastTest::check( const vector<lifetime>& lifetimes,
444 const temp_lt_expect& e)
445 {
446 for (unsigned i = 1; i < lifetimes.size(); ++i) {
447 EXPECT_LE(lifetimes[i].begin, e[i][0]);
448 EXPECT_GE(lifetimes[i].end, e[i][1]);
449 }
450 }
451
452 void RegisterRemappingTest::run(const vector<lifetime>& lt,
453 const vector<int>& expect)
454 {
455 rename_reg_pair proto{false,0};
456 vector<rename_reg_pair> result(lt.size(), proto);
457
458 get_temp_registers_remapping(mem_ctx, lt.size(), &lt[0], &result[0]);
459
460 vector<int> remap(lt.size());
461 for (unsigned i = 0; i < lt.size(); ++i) {
462 remap[i] = result[i].valid ? result[i].new_reg : i;
463 }
464
465 std::transform(remap.begin(), remap.end(), result.begin(), remap.begin(),
466 [](int x, const rename_reg_pair& rn) {
467 return rn.valid ? rn.new_reg : x;
468 });
469
470 for(unsigned i = 1; i < remap.size(); ++i) {
471 EXPECT_EQ(remap[i], expect[i]);
472 }
473 }
474
475 void RegisterLifetimeAndRemappingTest::run(const vector<FakeCodeline>& code,
476 const vector<int>& expect)
477 {
478 FakeShader shader(code);
479 std::vector<lifetime> lt(shader.get_num_temps());
480 get_temp_registers_required_lifetimes(mem_ctx, shader.get_program(mem_ctx),
481 shader.get_num_temps(), &lt[0]);
482 this->run(lt, expect);
483 }