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
);