2
* Copyright © 2017 Gert Wollny
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:
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
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.
24
#include "st_tests_common.h"
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"
43
/* Implementation of helper and test classes */
44
void *FakeCodeline::mem_ctx = nullptr;
46
FakeCodeline::FakeCodeline(tgsi_opcode _op, const vector<int>& _dst,
47
const vector<int>& _src, const vector<int>&_to):
52
transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
53
[this](int i) { return create_dst_register(i);});
55
transform(_src.begin(), _src.end(), std::back_inserter(src),
56
[this](int i) { return create_src_register(i);});
58
transform(_to.begin(), _to.end(), std::back_inserter(tex_offsets),
59
[this](int i) { return create_src_register(i);});
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,
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);
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);
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);
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):
98
transform(_dst.begin(), _dst.end(), std::back_inserter(dst),
99
[this](const tuple<int,int,int>& r) {
100
return create_dst_register(r);
103
transform(_src.begin(), _src.end(), std::back_inserter(src),
104
[this](const tuple<int,int,int>& r) {
105
return create_src_register(r);
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);
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,
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);
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);
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);
139
FakeCodeline::FakeCodeline(const glsl_to_tgsi_instruction& instr):
144
int nsrc = num_inst_src_regs(&instr);
145
int ndst = num_inst_dst_regs(&instr);
147
copy(instr.src, instr.src + nsrc, std::back_inserter(src));
148
copy(instr.dst, instr.dst + ndst, std::back_inserter(dst));
158
template <typename st_reg>
159
void FakeCodeline::read_reg(const st_reg& s)
161
if (s.file == PROGRAM_ARRAY) {
162
if (s.array_id > max_array_id)
163
max_array_id = s.array_id;
165
read_reg(*s.reladdr);
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;
174
void FakeCodeline::print(std::ostream& os) const
176
const struct tgsi_opcode_info *info = tgsi_get_opcode_info(op);
177
os << tgsi_get_opcode_name(info->opcode) << " ";
189
bool operator == (const FakeCodeline& lhs, const FakeCodeline& rhs)
191
if ((lhs.op != rhs.op) ||
192
(lhs.src.size() != rhs.src.size()) ||
193
(lhs.dst.size() != rhs.dst.size()))
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());
200
st_src_reg FakeCodeline::create_src_register(int src_idx)
202
return create_src_register(src_idx,
203
src_idx < 0 ? PROGRAM_INPUT : PROGRAM_TEMPORARY);
206
static int swizzle_from_char(const char *sw)
209
if (!sw || sw[0] == 0)
212
const char *isw = sw;
213
for (int i = 0; i < 4; ++i) {
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;
220
assert(!"This test uses an unknown swizzle character");
228
st_src_reg FakeCodeline::create_src_register(int src_idx, const char *sw)
230
st_src_reg result = create_src_register(src_idx);
231
result.swizzle = swizzle_from_char(sw);
235
st_src_reg FakeCodeline::create_src_register(int src_idx, gl_register_file file)
239
retval.index = src_idx >= 0 ? src_idx : 1 - src_idx;
241
if (file == PROGRAM_TEMPORARY) {
242
if (max_temp_id < src_idx)
243
max_temp_id = src_idx;
244
} else if (file == PROGRAM_ARRAY) {
246
if (max_array_id < 1)
249
retval.swizzle = SWIZZLE_XYZW;
250
retval.type = GLSL_TYPE_INT;
255
st_src_reg *FakeCodeline::create_rel_src_register(int idx)
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)
264
st_src_reg FakeCodeline::create_array_src_register(const tuple<int,int, const char*>& r)
267
int array_id = std::get<0>(r);
268
int idx = std::get<1>(r);
270
st_src_reg retval = create_src_register(idx, std::get<2>(r));
273
retval.file = PROGRAM_ARRAY;
275
retval.array_id = array_id;
276
if (max_array_id < array_id)
277
max_array_id = array_id;
279
if (max_temp_id < idx)
286
st_dst_reg FakeCodeline::create_array_dst_register(const tuple<int,int,int>& r)
289
int array_id = std::get<0>(r);
290
int idx = std::get<1>(r);
292
st_dst_reg retval = create_dst_register(idx, std::get<2>(r));
295
retval.file = PROGRAM_ARRAY;
296
retval.array_id = array_id;
297
if (max_array_id < array_id)
298
max_array_id = array_id;
300
if (max_temp_id < idx)
306
st_src_reg FakeCodeline::create_src_register(const tuple<int,int,int>& src)
308
int src_idx = std::get<0>(src);
309
int relidx1 = std::get<1>(src);
310
int relidx2 = std::get<2>(src);
312
gl_register_file file = PROGRAM_TEMPORARY;
314
file = PROGRAM_OUTPUT;
315
else if (relidx1 || relidx2) {
316
file = PROGRAM_ARRAY;
319
st_src_reg retval = create_src_register(src_idx, file);
321
if (relidx1 || relidx2) {
325
retval.reladdr = create_rel_src_register(relidx1);
327
retval.reladdr2 = create_rel_src_register(relidx2);
328
retval.has_index2 = true;
336
st_dst_reg FakeCodeline::create_dst_register(int dst_idx,int writemask)
338
gl_register_file file;
341
file = PROGRAM_TEMPORARY;
343
if (max_temp_id < idx)
346
file = PROGRAM_OUTPUT;
349
return st_dst_reg(file, writemask, GLSL_TYPE_INT, idx);
352
st_dst_reg FakeCodeline::create_dst_register(int dst_idx)
354
return create_dst_register(dst_idx, dst_idx < 0 ?
355
PROGRAM_OUTPUT : PROGRAM_TEMPORARY);
358
st_dst_reg FakeCodeline::create_dst_register(int dst_idx, gl_register_file file)
362
retval.index = dst_idx >= 0 ? dst_idx : 1 - dst_idx;
364
if (file == PROGRAM_TEMPORARY) {
365
if (max_temp_id < dst_idx)
366
max_temp_id = dst_idx;
367
} else if (file == PROGRAM_ARRAY) {
369
if (max_array_id < 1)
372
retval.writemask = 0xF;
373
retval.type = GLSL_TYPE_INT;
378
st_dst_reg FakeCodeline::create_dst_register(const tuple<int,int,int>& dst)
380
int dst_idx = std::get<0>(dst);
381
int relidx1 = std::get<1>(dst);
382
int relidx2 = std::get<2>(dst);
384
gl_register_file file = PROGRAM_TEMPORARY;
386
file = PROGRAM_OUTPUT;
387
else if (relidx1 || relidx2) {
388
file = PROGRAM_ARRAY;
390
st_dst_reg retval = create_dst_register(dst_idx, file);
392
if (relidx1 || relidx2) {
394
retval.reladdr = create_rel_src_register(relidx1);
396
retval.reladdr2 = create_rel_src_register(relidx2);
397
retval.has_index2 = true;
404
glsl_to_tgsi_instruction *FakeCodeline::get_codeline() const
406
glsl_to_tgsi_instruction *next_instr = new(mem_ctx) glsl_to_tgsi_instruction();
408
next_instr->info = tgsi_get_opcode_info(op);
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);
414
copy(src.begin(), src.end(), next_instr->src);
415
copy(dst.begin(), dst.end(), next_instr->dst);
417
next_instr->tex_offset_num_offset = tex_offsets.size();
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);
423
next_instr->tex_offsets = nullptr;
428
void FakeCodeline::set_mem_ctx(void *ctx)
433
FakeShader::FakeShader(const vector<FakeCodeline>& source):
438
for (const FakeCodeline& i: source) {
439
int t = i.get_max_reg_id();
443
int a = i.get_max_array_id();
450
FakeShader::FakeShader(exec_list *tgsi_prog):
454
FakeCodeline nop(TGSI_OPCODE_NOP);
455
FakeCodeline& last = nop;
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();
467
int FakeShader::get_num_arrays() const
472
int FakeShader::get_num_temps() const
477
exec_list* FakeShader::get_program(void *ctx) const
479
exec_list *prog = new(ctx) exec_list();
481
for (const FakeCodeline& i: program) {
482
prog->push_tail(i.get_codeline());
488
size_t FakeShader::length() const
490
return program.size();
493
const FakeCodeline& FakeShader::line(unsigned i) const
498
void MesaTestWithMemCtx::SetUp()
500
mem_ctx = ralloc_context(nullptr);
501
FakeCodeline::set_mem_ctx(mem_ctx);
504
void MesaTestWithMemCtx::TearDown()
506
ralloc_free(mem_ctx);
507
FakeCodeline::set_mem_ctx(nullptr);
512
LifetimeEvaluatorTest::life_range_result
513
LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, bool& success)
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()));
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]);
526
void LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, const temp_lt_expect& e)
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);
535
void LifetimeEvaluatorTest::run(const vector<FakeCodeline>& code, const array_lt_expect& e)
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);
544
void LifetimeEvaluatorExactTest::check( const vector<register_live_range>& lifetimes,
545
const temp_lt_expect& e)
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]);
553
void LifetimeEvaluatorExactTest::check(const vector<array_live_range>& lifetimes,
554
const array_lt_expect& e)
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());
563
void LifetimeEvaluatorAtLeastTest::check( const vector<register_live_range>& lifetimes,
564
const temp_lt_expect& e)
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]);
572
void LifetimeEvaluatorAtLeastTest::check(const vector<array_live_range>& lifetimes,
573
const array_lt_expect& e)
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());
579
/* Tests that lifetimes doesn't add unexpected swizzles */
580
EXPECT_EQ(lifetimes[i].access_mask()| e[i].access_mask(),
586
void RegisterRemappingTest::run(const vector<register_live_range>& lt,
587
const vector<int>& expect)
589
rename_reg_pair proto{false,0};
590
vector<rename_reg_pair> result(lt.size(), proto);
592
get_temp_registers_remapping(mem_ctx, lt.size(), <[0], &result[0]);
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;
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;
604
for(unsigned i = 1; i < remap.size(); ++i) {
605
EXPECT_EQ(remap[i], expect[i]);
609
void RegisterLifetimeAndRemappingTest::run(const vector<FakeCodeline>& code,
610
const vector<int>& expect)
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);