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(unsigned _op
, const vector
<int>& _dst
,
47 const vector
<int>& _src
, const vector
<int>&_to
):
51 transform(_dst
.begin(), _dst
.end(), std::back_inserter(dst
),
52 [this](int i
) { return create_dst_register(i
);});
54 transform(_src
.begin(), _src
.end(), std::back_inserter(src
),
55 [this](int i
) { return create_src_register(i
);});
57 transform(_to
.begin(), _to
.end(), std::back_inserter(tex_offsets
),
58 [this](int i
) { return create_src_register(i
);});
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
,
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
);
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
);
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
);
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
):
95 transform(_dst
.begin(), _dst
.end(), std::back_inserter(dst
),
96 [this](const tuple
<int,int,int>& r
) {
97 return create_dst_register(r
);
100 transform(_src
.begin(), _src
.end(), std::back_inserter(src
),
101 [this](const tuple
<int,int,int>& r
) {
102 return create_src_register(r
);
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
);
111 FakeCodeline::FakeCodeline(const glsl_to_tgsi_instruction
& instr
):
115 int nsrc
= num_inst_src_regs(&instr
);
116 int ndst
= num_inst_dst_regs(&instr
);
118 copy(instr
.src
, instr
.src
+ nsrc
, std::back_inserter(src
));
119 copy(instr
.dst
, instr
.dst
+ ndst
, std::back_inserter(dst
));
129 template <typename st_reg
>
130 void FakeCodeline::read_reg(const st_reg
& s
)
132 if (s
.file
== PROGRAM_TEMPORARY
) {
133 if (s
.index
> max_temp_id
)
134 max_temp_id
= s
.index
;
138 void FakeCodeline::print(std::ostream
& os
) const
140 const struct tgsi_opcode_info
*info
= tgsi_get_opcode_info(op
);
141 os
<< tgsi_get_opcode_name(info
->opcode
) << " ";
153 bool operator == (const FakeCodeline
& lhs
, const FakeCodeline
& rhs
)
155 if ((lhs
.op
!= rhs
.op
) ||
156 (lhs
.src
.size() != rhs
.src
.size()) ||
157 (lhs
.dst
.size() != rhs
.dst
.size()))
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());
164 st_src_reg
FakeCodeline::create_src_register(int src_idx
)
166 return create_src_register(src_idx
,
167 src_idx
< 0 ? PROGRAM_INPUT
: PROGRAM_TEMPORARY
);
170 static int swizzle_from_char(const char *sw
)
173 if (!sw
|| sw
[0] == 0)
176 const char *isw
= sw
;
177 for (int i
= 0; i
< 4; ++i
) {
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;
184 assert(!"This test uses an unknown swizzle character");
192 st_src_reg
FakeCodeline::create_src_register(int src_idx
, const char *sw
)
194 st_src_reg result
= create_src_register(src_idx
);
195 result
.swizzle
= swizzle_from_char(sw
);
199 st_src_reg
FakeCodeline::create_src_register(int src_idx
, gl_register_file file
)
203 retval
.index
= src_idx
>= 0 ? src_idx
: 1 - src_idx
;
205 if (file
== PROGRAM_TEMPORARY
) {
206 if (max_temp_id
< src_idx
)
207 max_temp_id
= src_idx
;
208 } else if (file
== PROGRAM_ARRAY
) {
211 retval
.swizzle
= SWIZZLE_XYZW
;
212 retval
.type
= GLSL_TYPE_INT
;
217 st_src_reg
*FakeCodeline::create_rel_src_register(int idx
)
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
)
226 st_src_reg
FakeCodeline::create_src_register(const tuple
<int,int,int>& src
)
228 int src_idx
= std::get
<0>(src
);
229 int relidx1
= std::get
<1>(src
);
230 int relidx2
= std::get
<2>(src
);
232 gl_register_file file
= PROGRAM_TEMPORARY
;
234 file
= PROGRAM_OUTPUT
;
235 else if (relidx1
|| relidx2
) {
236 file
= PROGRAM_ARRAY
;
239 st_src_reg retval
= create_src_register(src_idx
, file
);
241 if (relidx1
|| relidx2
) {
245 retval
.reladdr
= create_rel_src_register(relidx1
);
247 retval
.reladdr2
= create_rel_src_register(relidx2
);
248 retval
.has_index2
= true;
256 st_dst_reg
FakeCodeline::create_dst_register(int dst_idx
,int writemask
)
258 gl_register_file file
;
261 file
= PROGRAM_TEMPORARY
;
263 if (max_temp_id
< idx
)
266 file
= PROGRAM_OUTPUT
;
269 return st_dst_reg(file
, writemask
, GLSL_TYPE_INT
, idx
);
272 st_dst_reg
FakeCodeline::create_dst_register(int dst_idx
)
274 return create_dst_register(dst_idx
, dst_idx
< 0 ?
275 PROGRAM_OUTPUT
: PROGRAM_TEMPORARY
);
278 st_dst_reg
FakeCodeline::create_dst_register(int dst_idx
, gl_register_file file
)
282 retval
.index
= dst_idx
>= 0 ? dst_idx
: 1 - dst_idx
;
284 if (file
== PROGRAM_TEMPORARY
) {
285 if (max_temp_id
< dst_idx
)
286 max_temp_id
= dst_idx
;
287 } else if (file
== PROGRAM_ARRAY
) {
290 retval
.writemask
= 0xF;
291 retval
.type
= GLSL_TYPE_INT
;
296 st_dst_reg
FakeCodeline::create_dst_register(const tuple
<int,int,int>& dst
)
298 int dst_idx
= std::get
<0>(dst
);
299 int relidx1
= std::get
<1>(dst
);
300 int relidx2
= std::get
<2>(dst
);
302 gl_register_file file
= PROGRAM_TEMPORARY
;
304 file
= PROGRAM_OUTPUT
;
305 else if (relidx1
|| relidx2
) {
306 file
= PROGRAM_ARRAY
;
308 st_dst_reg retval
= create_dst_register(dst_idx
, file
);
310 if (relidx1
|| relidx2
) {
312 retval
.reladdr
= create_rel_src_register(relidx1
);
314 retval
.reladdr2
= create_rel_src_register(relidx2
);
315 retval
.has_index2
= true;
322 glsl_to_tgsi_instruction
*FakeCodeline::get_codeline() const
324 glsl_to_tgsi_instruction
*next_instr
= new(mem_ctx
) glsl_to_tgsi_instruction();
326 next_instr
->info
= tgsi_get_opcode_info(op
);
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);
332 copy(src
.begin(), src
.end(), next_instr
->src
);
333 copy(dst
.begin(), dst
.end(), next_instr
->dst
);
335 next_instr
->tex_offset_num_offset
= tex_offsets
.size();
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
);
341 next_instr
->tex_offsets
= nullptr;
346 void FakeCodeline::set_mem_ctx(void *ctx
)
351 FakeShader::FakeShader(const vector
<FakeCodeline
>& source
):
355 for (const FakeCodeline
& i
: source
) {
356 int t
= i
.get_max_reg_id();
363 FakeShader::FakeShader(exec_list
*tgsi_prog
):
366 FakeCodeline
nop(TGSI_OPCODE_NOP
);
367 FakeCodeline
& last
= nop
;
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();
377 int FakeShader::get_num_temps() const
382 exec_list
* FakeShader::get_program(void *ctx
) const
384 exec_list
*prog
= new(ctx
) exec_list();
386 for (const FakeCodeline
& i
: program
) {
387 prog
->push_tail(i
.get_codeline());
393 void MesaTestWithMemCtx::SetUp()
395 mem_ctx
= ralloc_context(nullptr);
396 FakeCodeline::set_mem_ctx(mem_ctx
);
399 void MesaTestWithMemCtx::TearDown()
401 ralloc_free(mem_ctx
);
402 FakeCodeline::set_mem_ctx(nullptr);
407 LifetimeEvaluatorTest::lifetime_result
408 LifetimeEvaluatorTest::run(const vector
<FakeCodeline
>& code
, bool& success
)
410 FakeShader
shader(code
);
411 lifetime_result
result(shader
.get_num_temps());
414 get_temp_registers_required_lifetimes(mem_ctx
, shader
.get_program(mem_ctx
),
415 shader
.get_num_temps(),
421 void LifetimeEvaluatorTest::run(const vector
<FakeCodeline
>& code
, const temp_lt_expect
& e
)
423 FakeShader
shader(code
);
424 lifetime_result
result(shader
.get_num_temps());
426 get_temp_registers_required_lifetimes(mem_ctx
, shader
.get_program(mem_ctx
),
427 shader
.get_num_temps(),
429 ASSERT_TRUE(success
);
430 ASSERT_EQ(result
.size(), e
.size());
434 void LifetimeEvaluatorExactTest::check( const vector
<lifetime
>& lifetimes
,
435 const temp_lt_expect
& e
)
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]);
443 void LifetimeEvaluatorAtLeastTest::check( const vector
<lifetime
>& lifetimes
,
444 const temp_lt_expect
& e
)
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]);
452 void RegisterRemappingTest::run(const vector
<lifetime
>& lt
,
453 const vector
<int>& expect
)
455 rename_reg_pair proto
{false,0};
456 vector
<rename_reg_pair
> result(lt
.size(), proto
);
458 get_temp_registers_remapping(mem_ctx
, lt
.size(), <
[0], &result
[0]);
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
;
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
;
470 for(unsigned i
= 1; i
< remap
.size(); ++i
) {
471 EXPECT_EQ(remap
[i
], expect
[i
]);
475 void RegisterLifetimeAndRemappingTest::run(const vector
<FakeCodeline
>& code
,
476 const vector
<int>& expect
)
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(), <
[0]);
482 this->run(lt
, expect
);