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(), <[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(), <[0], 617 shader.get_num_arrays(), &alt[0]); 618 this->run(lt, expect); 619} 620