1/* 2 * Copyright © 2018 Valve Corporation 3 * Copyright © 2018 Google 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 * 24 */ 25 26#include "aco_builder.h" 27#include "aco_ir.h" 28 29#include "common/sid.h" 30 31#include <algorithm> 32#include <cstring> 33#include <map> 34#include <set> 35#include <stack> 36#include <unordered_map> 37#include <unordered_set> 38#include <vector> 39 40namespace std { 41template <> struct hash<aco::Temp> { 42 size_t operator()(aco::Temp temp) const noexcept 43 { 44 uint32_t v; 45 std::memcpy(&v, &temp, sizeof(temp)); 46 return std::hash<uint32_t>{}(v); 47 } 48}; 49} // namespace std 50 51/* 52 * Implements the spilling algorithm on SSA-form from 53 * "Register Spilling and Live-Range Splitting for SSA-Form Programs" 54 * by Matthias Braun and Sebastian Hack. 55 */ 56 57namespace aco { 58 59namespace { 60 61struct remat_info { 62 Instruction* instr; 63}; 64 65struct spill_ctx { 66 RegisterDemand target_pressure; 67 Program* program; 68 std::vector<std::vector<RegisterDemand>> register_demand; 69 std::vector<std::map<Temp, Temp>> renames; 70 std::vector<std::unordered_map<Temp, uint32_t>> spills_entry; 71 std::vector<std::unordered_map<Temp, uint32_t>> spills_exit; 72 73 std::vector<bool> processed; 74 std::stack<Block*, std::vector<Block*>> loop_header; 75 std::vector<std::unordered_map<Temp, std::pair<uint32_t, uint32_t>>> next_use_distances_start; 76 std::vector<std::unordered_map<Temp, std::pair<uint32_t, uint32_t>>> next_use_distances_end; 77 std::vector<std::vector<std::pair<Temp, uint32_t>>> local_next_use_distance; /* Working buffer */ 78 std::vector<std::pair<RegClass, std::unordered_set<uint32_t>>> interferences; 79 std::vector<std::vector<uint32_t>> affinities; 80 std::vector<bool> is_reloaded; 81 std::unordered_map<Temp, remat_info> remat; 82 std::set<Instruction*> unused_remats; 83 unsigned wave_size; 84 85 spill_ctx(const RegisterDemand target_pressure_, Program* program_, 86 std::vector<std::vector<RegisterDemand>> register_demand_) 87 : target_pressure(target_pressure_), program(program_), 88 register_demand(std::move(register_demand_)), renames(program->blocks.size()), 89 spills_entry(program->blocks.size()), spills_exit(program->blocks.size()), 90 processed(program->blocks.size(), false), wave_size(program->wave_size) 91 {} 92 93 void add_affinity(uint32_t first, uint32_t second) 94 { 95 unsigned found_first = affinities.size(); 96 unsigned found_second = affinities.size(); 97 for (unsigned i = 0; i < affinities.size(); i++) { 98 std::vector<uint32_t>& vec = affinities[i]; 99 for (uint32_t entry : vec) { 100 if (entry == first) 101 found_first = i; 102 else if (entry == second) 103 found_second = i; 104 } 105 } 106 if (found_first == affinities.size() && found_second == affinities.size()) { 107 affinities.emplace_back(std::vector<uint32_t>({first, second})); 108 } else if (found_first < affinities.size() && found_second == affinities.size()) { 109 affinities[found_first].push_back(second); 110 } else if (found_second < affinities.size() && found_first == affinities.size()) { 111 affinities[found_second].push_back(first); 112 } else if (found_first != found_second) { 113 /* merge second into first */ 114 affinities[found_first].insert(affinities[found_first].end(), 115 affinities[found_second].begin(), 116 affinities[found_second].end()); 117 affinities.erase(std::next(affinities.begin(), found_second)); 118 } else { 119 assert(found_first == found_second); 120 } 121 } 122 123 void add_interference(uint32_t first, uint32_t second) 124 { 125 if (interferences[first].first.type() != interferences[second].first.type()) 126 return; 127 128 bool inserted = interferences[first].second.insert(second).second; 129 if (inserted) 130 interferences[second].second.insert(first); 131 } 132 133 uint32_t allocate_spill_id(RegClass rc) 134 { 135 interferences.emplace_back(rc, std::unordered_set<uint32_t>()); 136 is_reloaded.push_back(false); 137 return next_spill_id++; 138 } 139 140 uint32_t next_spill_id = 0; 141}; 142 143int32_t 144get_dominator(int idx_a, int idx_b, Program* program, bool is_linear) 145{ 146 147 if (idx_a == -1) 148 return idx_b; 149 if (idx_b == -1) 150 return idx_a; 151 if (is_linear) { 152 while (idx_a != idx_b) { 153 if (idx_a > idx_b) 154 idx_a = program->blocks[idx_a].linear_idom; 155 else 156 idx_b = program->blocks[idx_b].linear_idom; 157 } 158 } else { 159 while (idx_a != idx_b) { 160 if (idx_a > idx_b) 161 idx_a = program->blocks[idx_a].logical_idom; 162 else 163 idx_b = program->blocks[idx_b].logical_idom; 164 } 165 } 166 assert(idx_a != -1); 167 return idx_a; 168} 169 170void 171next_uses_per_block(spill_ctx& ctx, unsigned block_idx, uint32_t& worklist) 172{ 173 Block* block = &ctx.program->blocks[block_idx]; 174 ctx.next_use_distances_start[block_idx] = ctx.next_use_distances_end[block_idx]; 175 auto& next_use_distances_start = ctx.next_use_distances_start[block_idx]; 176 177 /* to compute the next use distance at the beginning of the block, we have to add the block's 178 * size */ 179 for (std::unordered_map<Temp, std::pair<uint32_t, uint32_t>>::iterator it = 180 next_use_distances_start.begin(); 181 it != next_use_distances_start.end(); ++it) 182 it->second.second = it->second.second + block->instructions.size(); 183 184 int idx = block->instructions.size() - 1; 185 while (idx >= 0) { 186 aco_ptr<Instruction>& instr = block->instructions[idx]; 187 188 if (instr->opcode == aco_opcode::p_linear_phi || instr->opcode == aco_opcode::p_phi) 189 break; 190 191 for (const Definition& def : instr->definitions) { 192 if (def.isTemp()) 193 next_use_distances_start.erase(def.getTemp()); 194 } 195 196 for (const Operand& op : instr->operands) { 197 /* omit exec mask */ 198 if (op.isFixed() && op.physReg() == exec) 199 continue; 200 if (op.regClass().type() == RegType::vgpr && op.regClass().is_linear()) 201 continue; 202 if (op.isTemp()) 203 next_use_distances_start[op.getTemp()] = {block_idx, idx}; 204 } 205 idx--; 206 } 207 208 assert(block_idx != 0 || next_use_distances_start.empty()); 209 std::unordered_set<Temp> phi_defs; 210 while (idx >= 0) { 211 aco_ptr<Instruction>& instr = block->instructions[idx]; 212 assert(instr->opcode == aco_opcode::p_linear_phi || instr->opcode == aco_opcode::p_phi); 213 214 std::pair<uint32_t, uint32_t> distance{block_idx, 0}; 215 216 auto it = instr->definitions[0].isTemp() ? next_use_distances_start.find(instr->definitions[0].getTemp()) 217 : next_use_distances_start.end(); 218 if (it != next_use_distances_start.end() && 219 phi_defs.insert(instr->definitions[0].getTemp()).second) { 220 distance = it->second; 221 } 222 223 for (unsigned i = 0; i < instr->operands.size(); i++) { 224 unsigned pred_idx = 225 instr->opcode == aco_opcode::p_phi ? block->logical_preds[i] : block->linear_preds[i]; 226 if (instr->operands[i].isTemp()) { 227 auto insert_result = ctx.next_use_distances_end[pred_idx].insert( 228 std::make_pair(instr->operands[i].getTemp(), distance)); 229 const bool inserted = insert_result.second; 230 std::pair<uint32_t, uint32_t>& entry_distance = insert_result.first->second; 231 if (inserted || entry_distance != distance) 232 worklist = std::max(worklist, pred_idx + 1); 233 entry_distance = distance; 234 } 235 } 236 idx--; 237 } 238 239 /* all remaining live vars must be live-out at the predecessors */ 240 for (std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : next_use_distances_start) { 241 Temp temp = pair.first; 242 if (phi_defs.count(temp)) { 243 continue; 244 } 245 uint32_t distance = pair.second.second; 246 uint32_t dom = pair.second.first; 247 std::vector<unsigned>& preds = temp.is_linear() ? block->linear_preds : block->logical_preds; 248 for (unsigned pred_idx : preds) { 249 if (ctx.program->blocks[pred_idx].loop_nest_depth > block->loop_nest_depth) 250 distance += 0xFFFF; 251 auto insert_result = ctx.next_use_distances_end[pred_idx].insert( 252 std::make_pair(temp, std::pair<uint32_t, uint32_t>{})); 253 const bool inserted = insert_result.second; 254 std::pair<uint32_t, uint32_t>& entry_distance = insert_result.first->second; 255 256 if (!inserted) { 257 dom = get_dominator(dom, entry_distance.first, ctx.program, temp.is_linear()); 258 distance = std::min(entry_distance.second, distance); 259 } 260 if (entry_distance != std::pair<uint32_t, uint32_t>{dom, distance}) { 261 worklist = std::max(worklist, pred_idx + 1); 262 entry_distance = {dom, distance}; 263 } 264 } 265 } 266} 267 268void 269compute_global_next_uses(spill_ctx& ctx) 270{ 271 ctx.next_use_distances_start.resize(ctx.program->blocks.size()); 272 ctx.next_use_distances_end.resize(ctx.program->blocks.size()); 273 274 uint32_t worklist = ctx.program->blocks.size(); 275 while (worklist) { 276 unsigned block_idx = --worklist; 277 next_uses_per_block(ctx, block_idx, worklist); 278 } 279} 280 281bool 282should_rematerialize(aco_ptr<Instruction>& instr) 283{ 284 /* TODO: rematerialization is only supported for VOP1, SOP1 and PSEUDO */ 285 if (instr->format != Format::VOP1 && instr->format != Format::SOP1 && 286 instr->format != Format::PSEUDO && instr->format != Format::SOPK) 287 return false; 288 /* TODO: pseudo-instruction rematerialization is only supported for 289 * p_create_vector/p_parallelcopy */ 290 if (instr->isPseudo() && instr->opcode != aco_opcode::p_create_vector && 291 instr->opcode != aco_opcode::p_parallelcopy) 292 return false; 293 if (instr->isSOPK() && instr->opcode != aco_opcode::s_movk_i32) 294 return false; 295 296 for (const Operand& op : instr->operands) { 297 /* TODO: rematerialization using temporaries isn't yet supported */ 298 if (!op.isConstant()) 299 return false; 300 } 301 302 /* TODO: rematerialization with multiple definitions isn't yet supported */ 303 if (instr->definitions.size() > 1) 304 return false; 305 306 return true; 307} 308 309aco_ptr<Instruction> 310do_reload(spill_ctx& ctx, Temp tmp, Temp new_name, uint32_t spill_id) 311{ 312 std::unordered_map<Temp, remat_info>::iterator remat = ctx.remat.find(tmp); 313 if (remat != ctx.remat.end()) { 314 Instruction* instr = remat->second.instr; 315 assert((instr->isVOP1() || instr->isSOP1() || instr->isPseudo() || instr->isSOPK()) && 316 "unsupported"); 317 assert((instr->format != Format::PSEUDO || instr->opcode == aco_opcode::p_create_vector || 318 instr->opcode == aco_opcode::p_parallelcopy) && 319 "unsupported"); 320 assert(instr->definitions.size() == 1 && "unsupported"); 321 322 aco_ptr<Instruction> res; 323 if (instr->isVOP1()) { 324 res.reset(create_instruction<VOP1_instruction>( 325 instr->opcode, instr->format, instr->operands.size(), instr->definitions.size())); 326 } else if (instr->isSOP1()) { 327 res.reset(create_instruction<SOP1_instruction>( 328 instr->opcode, instr->format, instr->operands.size(), instr->definitions.size())); 329 } else if (instr->isPseudo()) { 330 res.reset(create_instruction<Pseudo_instruction>( 331 instr->opcode, instr->format, instr->operands.size(), instr->definitions.size())); 332 } else if (instr->isSOPK()) { 333 res.reset(create_instruction<SOPK_instruction>( 334 instr->opcode, instr->format, instr->operands.size(), instr->definitions.size())); 335 res->sopk().imm = instr->sopk().imm; 336 } 337 for (unsigned i = 0; i < instr->operands.size(); i++) { 338 res->operands[i] = instr->operands[i]; 339 if (instr->operands[i].isTemp()) { 340 assert(false && "unsupported"); 341 if (ctx.remat.count(instr->operands[i].getTemp())) 342 ctx.unused_remats.erase(ctx.remat[instr->operands[i].getTemp()].instr); 343 } 344 } 345 res->definitions[0] = Definition(new_name); 346 return res; 347 } else { 348 aco_ptr<Pseudo_instruction> reload{ 349 create_instruction<Pseudo_instruction>(aco_opcode::p_reload, Format::PSEUDO, 1, 1)}; 350 reload->operands[0] = Operand::c32(spill_id); 351 reload->definitions[0] = Definition(new_name); 352 ctx.is_reloaded[spill_id] = true; 353 return reload; 354 } 355} 356 357void 358get_rematerialize_info(spill_ctx& ctx) 359{ 360 for (Block& block : ctx.program->blocks) { 361 bool logical = false; 362 for (aco_ptr<Instruction>& instr : block.instructions) { 363 if (instr->opcode == aco_opcode::p_logical_start) 364 logical = true; 365 else if (instr->opcode == aco_opcode::p_logical_end) 366 logical = false; 367 if (logical && should_rematerialize(instr)) { 368 for (const Definition& def : instr->definitions) { 369 if (def.isTemp()) { 370 ctx.remat[def.getTemp()] = remat_info{instr.get()}; 371 ctx.unused_remats.insert(instr.get()); 372 } 373 } 374 } 375 } 376 } 377} 378 379void 380update_local_next_uses(spill_ctx& ctx, Block* block, 381 std::vector<std::vector<std::pair<Temp, uint32_t>>>& local_next_uses) 382{ 383 if (local_next_uses.size() < block->instructions.size()) { 384 /* Allocate more next-use-maps. Note that by never reducing the vector size, we enable 385 * future calls to this function to re-use already allocated map memory. */ 386 local_next_uses.resize(block->instructions.size()); 387 } 388 389 local_next_uses[block->instructions.size() - 1].clear(); 390 for (std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : 391 ctx.next_use_distances_end[block->index]) { 392 local_next_uses[block->instructions.size() - 1].push_back(std::make_pair<Temp, uint32_t>( 393 (Temp)pair.first, pair.second.second + block->instructions.size())); 394 } 395 396 for (int idx = block->instructions.size() - 1; idx >= 0; idx--) { 397 aco_ptr<Instruction>& instr = block->instructions[idx]; 398 if (!instr) 399 break; 400 if (instr->opcode == aco_opcode::p_phi || instr->opcode == aco_opcode::p_linear_phi) 401 break; 402 403 if (idx != (int)block->instructions.size() - 1) { 404 local_next_uses[idx] = local_next_uses[idx + 1]; 405 } 406 407 for (const Operand& op : instr->operands) { 408 if (op.isFixed() && op.physReg() == exec) 409 continue; 410 if (op.regClass().type() == RegType::vgpr && op.regClass().is_linear()) 411 continue; 412 if (op.isTemp()) { 413 auto it = std::find_if(local_next_uses[idx].begin(), local_next_uses[idx].end(), 414 [op](auto& pair) { return pair.first == op.getTemp(); }); 415 if (it == local_next_uses[idx].end()) { 416 local_next_uses[idx].push_back(std::make_pair<Temp, uint32_t>(op.getTemp(), idx)); 417 } else { 418 it->second = idx; 419 } 420 } 421 } 422 for (const Definition& def : instr->definitions) { 423 if (def.isTemp()) { 424 auto it = std::find_if(local_next_uses[idx].begin(), local_next_uses[idx].end(), 425 [def](auto& pair) { return pair.first == def.getTemp(); }); 426 if (it != local_next_uses[idx].end()) { 427 local_next_uses[idx].erase(it); 428 } 429 } 430 } 431 } 432} 433 434RegisterDemand 435get_demand_before(spill_ctx& ctx, unsigned block_idx, unsigned idx) 436{ 437 if (idx == 0) { 438 RegisterDemand demand = ctx.register_demand[block_idx][idx]; 439 aco_ptr<Instruction>& instr = ctx.program->blocks[block_idx].instructions[idx]; 440 aco_ptr<Instruction> instr_before(nullptr); 441 return get_demand_before(demand, instr, instr_before); 442 } else { 443 return ctx.register_demand[block_idx][idx - 1]; 444 } 445} 446 447RegisterDemand 448get_live_in_demand(spill_ctx& ctx, unsigned block_idx) 449{ 450 unsigned idx = 0; 451 RegisterDemand reg_pressure = RegisterDemand(); 452 Block& block = ctx.program->blocks[block_idx]; 453 for (aco_ptr<Instruction>& phi : block.instructions) { 454 if (!is_phi(phi)) 455 break; 456 idx++; 457 458 /* Killed phi definitions increase pressure in the predecessor but not 459 * the block they're in. Since the loops below are both to control 460 * pressure of the start of this block and the ends of it's 461 * predecessors, we need to count killed unspilled phi definitions here. */ 462 if (phi->definitions[0].isTemp() && phi->definitions[0].isKill() && 463 !ctx.spills_entry[block_idx].count(phi->definitions[0].getTemp())) 464 reg_pressure += phi->definitions[0].getTemp(); 465 } 466 467 reg_pressure += get_demand_before(ctx, block_idx, idx); 468 469 /* Consider register pressure from linear predecessors. This can affect 470 * reg_pressure if the branch instructions define sgprs. */ 471 for (unsigned pred : block.linear_preds) 472 reg_pressure.sgpr = 473 std::max<int16_t>(reg_pressure.sgpr, ctx.register_demand[pred].back().sgpr); 474 475 return reg_pressure; 476} 477 478RegisterDemand 479init_live_in_vars(spill_ctx& ctx, Block* block, unsigned block_idx) 480{ 481 RegisterDemand spilled_registers; 482 483 /* first block, nothing was spilled before */ 484 if (block_idx == 0) 485 return {0, 0}; 486 487 /* next use distances at the beginning of the current block */ 488 const auto& next_use_distances = ctx.next_use_distances_start[block_idx]; 489 490 /* loop header block */ 491 if (block->loop_nest_depth > ctx.program->blocks[block_idx - 1].loop_nest_depth) { 492 assert(block->linear_preds[0] == block_idx - 1); 493 assert(block->logical_preds[0] == block_idx - 1); 494 495 /* create new loop_info */ 496 ctx.loop_header.emplace(block); 497 498 /* check how many live-through variables should be spilled */ 499 RegisterDemand reg_pressure = get_live_in_demand(ctx, block_idx); 500 RegisterDemand loop_demand = reg_pressure; 501 unsigned i = block_idx; 502 while (ctx.program->blocks[i].loop_nest_depth >= block->loop_nest_depth) { 503 assert(ctx.program->blocks.size() > i); 504 loop_demand.update(ctx.program->blocks[i++].register_demand); 505 } 506 unsigned loop_end = i; 507 508 for (auto spilled : ctx.spills_exit[block_idx - 1]) { 509 auto it = next_use_distances.find(spilled.first); 510 511 /* variable is not live at loop entry: probably a phi operand */ 512 if (it == next_use_distances.end()) 513 continue; 514 515 /* keep constants and live-through variables spilled */ 516 if (it->second.first >= loop_end || ctx.remat.count(spilled.first)) { 517 ctx.spills_entry[block_idx][spilled.first] = spilled.second; 518 spilled_registers += spilled.first; 519 loop_demand -= spilled.first; 520 } 521 } 522 523 /* select live-through variables and constants */ 524 RegType type = RegType::vgpr; 525 while (loop_demand.exceeds(ctx.target_pressure)) { 526 /* if VGPR demand is low enough, select SGPRs */ 527 if (type == RegType::vgpr && loop_demand.vgpr <= ctx.target_pressure.vgpr) 528 type = RegType::sgpr; 529 /* if SGPR demand is low enough, break */ 530 if (type == RegType::sgpr && loop_demand.sgpr <= ctx.target_pressure.sgpr) 531 break; 532 533 unsigned distance = 0; 534 Temp to_spill; 535 for (const std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : 536 next_use_distances) { 537 if (pair.first.type() == type && 538 (pair.second.first >= loop_end || 539 (ctx.remat.count(pair.first) && type == RegType::sgpr)) && 540 pair.second.second > distance && !ctx.spills_entry[block_idx].count(pair.first)) { 541 to_spill = pair.first; 542 distance = pair.second.second; 543 } 544 } 545 546 /* select SGPRs or break */ 547 if (distance == 0) { 548 if (type == RegType::sgpr) 549 break; 550 type = RegType::sgpr; 551 continue; 552 } 553 554 uint32_t spill_id; 555 if (!ctx.spills_exit[block_idx - 1].count(to_spill)) { 556 spill_id = ctx.allocate_spill_id(to_spill.regClass()); 557 } else { 558 spill_id = ctx.spills_exit[block_idx - 1][to_spill]; 559 } 560 561 ctx.spills_entry[block_idx][to_spill] = spill_id; 562 spilled_registers += to_spill; 563 loop_demand -= to_spill; 564 } 565 566 /* shortcut */ 567 if (!loop_demand.exceeds(ctx.target_pressure)) 568 return spilled_registers; 569 570 /* if reg pressure is too high at beginning of loop, add variables with furthest use */ 571 reg_pressure -= spilled_registers; 572 573 while (reg_pressure.exceeds(ctx.target_pressure)) { 574 unsigned distance = 0; 575 Temp to_spill; 576 type = reg_pressure.vgpr > ctx.target_pressure.vgpr ? RegType::vgpr : RegType::sgpr; 577 578 for (const std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : 579 next_use_distances) { 580 if (pair.first.type() == type && pair.second.second > distance && 581 !ctx.spills_entry[block_idx].count(pair.first)) { 582 to_spill = pair.first; 583 distance = pair.second.second; 584 } 585 } 586 assert(distance != 0); 587 ctx.spills_entry[block_idx][to_spill] = ctx.allocate_spill_id(to_spill.regClass()); 588 spilled_registers += to_spill; 589 reg_pressure -= to_spill; 590 } 591 592 return spilled_registers; 593 } 594 595 /* branch block */ 596 if (block->linear_preds.size() == 1 && !(block->kind & block_kind_loop_exit)) { 597 /* keep variables spilled if they are alive and not used in the current block */ 598 unsigned pred_idx = block->linear_preds[0]; 599 for (std::pair<Temp, uint32_t> pair : ctx.spills_exit[pred_idx]) { 600 if (pair.first.type() != RegType::sgpr) { 601 continue; 602 } 603 auto next_use_distance_it = next_use_distances.find(pair.first); 604 if (next_use_distance_it != next_use_distances.end() && 605 next_use_distance_it->second.first != block_idx) { 606 ctx.spills_entry[block_idx].insert(pair); 607 spilled_registers.sgpr += pair.first.size(); 608 } 609 } 610 if (block->logical_preds.size() == 1) { 611 pred_idx = block->logical_preds[0]; 612 for (std::pair<Temp, uint32_t> pair : ctx.spills_exit[pred_idx]) { 613 if (pair.first.type() != RegType::vgpr) { 614 continue; 615 } 616 auto next_use_distance_it = next_use_distances.find(pair.first); 617 if (next_use_distance_it != next_use_distances.end() && 618 next_use_distance_it->second.first != block_idx) { 619 ctx.spills_entry[block_idx].insert(pair); 620 spilled_registers.vgpr += pair.first.size(); 621 } 622 } 623 } 624 625 /* if register demand is still too high, we just keep all spilled live vars 626 * and process the block */ 627 if (block->register_demand.sgpr - spilled_registers.sgpr > ctx.target_pressure.sgpr) { 628 pred_idx = block->linear_preds[0]; 629 for (std::pair<Temp, uint32_t> pair : ctx.spills_exit[pred_idx]) { 630 if (pair.first.type() == RegType::sgpr && next_use_distances.count(pair.first) && 631 ctx.spills_entry[block_idx].insert(pair).second) { 632 spilled_registers.sgpr += pair.first.size(); 633 } 634 } 635 } 636 if (block->register_demand.vgpr - spilled_registers.vgpr > ctx.target_pressure.vgpr && 637 block->logical_preds.size() == 1) { 638 pred_idx = block->logical_preds[0]; 639 for (std::pair<Temp, uint32_t> pair : ctx.spills_exit[pred_idx]) { 640 if (pair.first.type() == RegType::vgpr && next_use_distances.count(pair.first) && 641 ctx.spills_entry[block_idx].insert(pair).second) { 642 spilled_registers.vgpr += pair.first.size(); 643 } 644 } 645 } 646 647 return spilled_registers; 648 } 649 650 /* else: merge block */ 651 std::set<Temp> partial_spills; 652 653 /* keep variables spilled on all incoming paths */ 654 for (const std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : next_use_distances) { 655 std::vector<unsigned>& preds = 656 pair.first.is_linear() ? block->linear_preds : block->logical_preds; 657 /* If it can be rematerialized, keep the variable spilled if all predecessors do not reload 658 * it. Otherwise, if any predecessor reloads it, ensure it's reloaded on all other 659 * predecessors. The idea is that it's better in practice to rematerialize redundantly than to 660 * create lots of phis. */ 661 /* TODO: test this idea with more than Dawn of War III shaders (the current pipeline-db 662 * doesn't seem to exercise this path much) */ 663 bool remat = ctx.remat.count(pair.first); 664 bool spill = !remat; 665 uint32_t spill_id = 0; 666 for (unsigned pred_idx : preds) { 667 /* variable is not even live at the predecessor: probably from a phi */ 668 if (!ctx.next_use_distances_end[pred_idx].count(pair.first)) { 669 spill = false; 670 break; 671 } 672 if (!ctx.spills_exit[pred_idx].count(pair.first)) { 673 if (!remat) 674 spill = false; 675 } else { 676 partial_spills.insert(pair.first); 677 /* it might be that on one incoming path, the variable has a different spill_id, but 678 * add_couple_code() will take care of that. */ 679 spill_id = ctx.spills_exit[pred_idx][pair.first]; 680 if (remat) 681 spill = true; 682 } 683 } 684 if (spill) { 685 ctx.spills_entry[block_idx][pair.first] = spill_id; 686 partial_spills.erase(pair.first); 687 spilled_registers += pair.first; 688 } 689 } 690 691 /* same for phis */ 692 for (aco_ptr<Instruction>& phi : block->instructions) { 693 if (!is_phi(phi)) 694 break; 695 if (!phi->definitions[0].isTemp()) 696 continue; 697 698 std::vector<unsigned>& preds = 699 phi->opcode == aco_opcode::p_phi ? block->logical_preds : block->linear_preds; 700 bool spill = true; 701 702 for (unsigned i = 0; i < phi->operands.size(); i++) { 703 /* non-temp operands can increase the register pressure */ 704 if (!phi->operands[i].isTemp()) { 705 partial_spills.insert(phi->definitions[0].getTemp()); 706 continue; 707 } 708 709 if (!ctx.spills_exit[preds[i]].count(phi->operands[i].getTemp())) 710 spill = false; 711 else 712 partial_spills.insert(phi->definitions[0].getTemp()); 713 } 714 if (spill) { 715 ctx.spills_entry[block_idx][phi->definitions[0].getTemp()] = 716 ctx.allocate_spill_id(phi->definitions[0].regClass()); 717 partial_spills.erase(phi->definitions[0].getTemp()); 718 spilled_registers += phi->definitions[0].getTemp(); 719 } 720 } 721 722 /* if reg pressure at first instruction is still too high, add partially spilled variables */ 723 RegisterDemand reg_pressure = get_live_in_demand(ctx, block_idx); 724 reg_pressure -= spilled_registers; 725 726 while (reg_pressure.exceeds(ctx.target_pressure)) { 727 assert(!partial_spills.empty()); 728 std::set<Temp>::iterator it = partial_spills.begin(); 729 Temp to_spill = Temp(); 730 unsigned distance = 0; 731 RegType type = reg_pressure.vgpr > ctx.target_pressure.vgpr ? RegType::vgpr : RegType::sgpr; 732 733 while (it != partial_spills.end()) { 734 assert(!ctx.spills_entry[block_idx].count(*it)); 735 736 if (it->type() == type && next_use_distances.at(*it).second > distance) { 737 distance = next_use_distances.at(*it).second; 738 to_spill = *it; 739 } 740 ++it; 741 } 742 assert(distance != 0); 743 744 ctx.spills_entry[block_idx][to_spill] = ctx.allocate_spill_id(to_spill.regClass()); 745 partial_spills.erase(to_spill); 746 spilled_registers += to_spill; 747 reg_pressure -= to_spill; 748 } 749 750 return spilled_registers; 751} 752 753void 754add_coupling_code(spill_ctx& ctx, Block* block, unsigned block_idx) 755{ 756 /* no coupling code necessary */ 757 if (block->linear_preds.size() == 0) 758 return; 759 760 std::vector<aco_ptr<Instruction>> instructions; 761 /* branch block: TODO take other branch into consideration */ 762 if (block->linear_preds.size() == 1 && 763 !(block->kind & (block_kind_loop_exit | block_kind_loop_header))) { 764 assert(ctx.processed[block->linear_preds[0]]); 765 assert(ctx.register_demand[block_idx].size() == block->instructions.size()); 766 std::vector<RegisterDemand> reg_demand; 767 unsigned insert_idx = 0; 768 RegisterDemand demand_before = get_demand_before(ctx, block_idx, 0); 769 770 for (std::pair<const Temp, std::pair<uint32_t, uint32_t>>& live : 771 ctx.next_use_distances_start[block_idx]) { 772 const unsigned pred_idx = block->linear_preds[0]; 773 774 if (!live.first.is_linear()) 775 continue; 776 /* still spilled */ 777 if (ctx.spills_entry[block_idx].count(live.first)) 778 continue; 779 780 /* in register at end of predecessor */ 781 auto spills_exit_it = ctx.spills_exit[pred_idx].find(live.first); 782 if (spills_exit_it == ctx.spills_exit[pred_idx].end()) { 783 std::map<Temp, Temp>::iterator it = ctx.renames[pred_idx].find(live.first); 784 if (it != ctx.renames[pred_idx].end()) 785 ctx.renames[block_idx].insert(*it); 786 continue; 787 } 788 789 /* variable is spilled at predecessor and live at current block: create reload instruction */ 790 Temp new_name = ctx.program->allocateTmp(live.first.regClass()); 791 aco_ptr<Instruction> reload = do_reload(ctx, live.first, new_name, spills_exit_it->second); 792 instructions.emplace_back(std::move(reload)); 793 reg_demand.push_back(demand_before); 794 ctx.renames[block_idx][live.first] = new_name; 795 } 796 797 if (block->logical_preds.size() == 1) { 798 do { 799 assert(insert_idx < block->instructions.size()); 800 instructions.emplace_back(std::move(block->instructions[insert_idx])); 801 reg_demand.push_back(ctx.register_demand[block_idx][insert_idx]); 802 insert_idx++; 803 } while (instructions.back()->opcode != aco_opcode::p_logical_start); 804 805 unsigned pred_idx = block->logical_preds[0]; 806 for (std::pair<const Temp, std::pair<uint32_t, uint32_t>>& live : 807 ctx.next_use_distances_start[block_idx]) { 808 if (live.first.is_linear()) 809 continue; 810 /* still spilled */ 811 if (ctx.spills_entry[block_idx].count(live.first)) 812 continue; 813 814 /* in register at end of predecessor */ 815 auto spills_exit_it = ctx.spills_exit[pred_idx].find(live.first); 816 if (spills_exit_it == ctx.spills_exit[pred_idx].end()) { 817 std::map<Temp, Temp>::iterator it = ctx.renames[pred_idx].find(live.first); 818 if (it != ctx.renames[pred_idx].end()) 819 ctx.renames[block_idx].insert(*it); 820 continue; 821 } 822 823 /* variable is spilled at predecessor and live at current block: 824 * create reload instruction */ 825 Temp new_name = ctx.program->allocateTmp(live.first.regClass()); 826 aco_ptr<Instruction> reload = 827 do_reload(ctx, live.first, new_name, spills_exit_it->second); 828 instructions.emplace_back(std::move(reload)); 829 reg_demand.emplace_back(reg_demand.back()); 830 ctx.renames[block_idx][live.first] = new_name; 831 } 832 } 833 834 /* combine new reload instructions with original block */ 835 if (!instructions.empty()) { 836 reg_demand.insert(reg_demand.end(), 837 std::next(ctx.register_demand[block->index].begin(), insert_idx), 838 ctx.register_demand[block->index].end()); 839 ctx.register_demand[block_idx] = std::move(reg_demand); 840 instructions.insert(instructions.end(), 841 std::move_iterator<std::vector<aco_ptr<Instruction>>::iterator>( 842 std::next(block->instructions.begin(), insert_idx)), 843 std::move_iterator<std::vector<aco_ptr<Instruction>>::iterator>( 844 block->instructions.end())); 845 block->instructions = std::move(instructions); 846 } 847 return; 848 } 849 850 /* loop header and merge blocks: check if all (linear) predecessors have been processed */ 851 for (ASSERTED unsigned pred : block->linear_preds) 852 assert(ctx.processed[pred]); 853 854 /* iterate the phi nodes for which operands to spill at the predecessor */ 855 for (aco_ptr<Instruction>& phi : block->instructions) { 856 if (!is_phi(phi)) 857 break; 858 859 /* if the phi is not spilled, add to instructions */ 860 if (!phi->definitions[0].isTemp() || 861 !ctx.spills_entry[block_idx].count(phi->definitions[0].getTemp())) { 862 instructions.emplace_back(std::move(phi)); 863 continue; 864 } 865 866 std::vector<unsigned>& preds = 867 phi->opcode == aco_opcode::p_phi ? block->logical_preds : block->linear_preds; 868 uint32_t def_spill_id = ctx.spills_entry[block_idx][phi->definitions[0].getTemp()]; 869 870 for (unsigned i = 0; i < phi->operands.size(); i++) { 871 if (phi->operands[i].isUndefined()) 872 continue; 873 874 unsigned pred_idx = preds[i]; 875 Operand spill_op = phi->operands[i]; 876 877 if (spill_op.isTemp()) { 878 assert(phi->operands[i].isKill()); 879 Temp var = phi->operands[i].getTemp(); 880 881 std::map<Temp, Temp>::iterator rename_it = ctx.renames[pred_idx].find(var); 882 /* prevent the definining instruction from being DCE'd if it could be rematerialized */ 883 if (rename_it == ctx.renames[preds[i]].end() && ctx.remat.count(var)) 884 ctx.unused_remats.erase(ctx.remat[var].instr); 885 886 /* check if variable is already spilled at predecessor */ 887 auto spilled = ctx.spills_exit[pred_idx].find(var); 888 if (spilled != ctx.spills_exit[pred_idx].end()) { 889 if (spilled->second != def_spill_id) 890 ctx.add_affinity(def_spill_id, spilled->second); 891 continue; 892 } 893 894 /* rename if necessary */ 895 if (rename_it != ctx.renames[pred_idx].end()) { 896 spill_op.setTemp(rename_it->second); 897 ctx.renames[pred_idx].erase(rename_it); 898 } 899 } 900 901 uint32_t spill_id = ctx.allocate_spill_id(phi->definitions[0].regClass()); 902 903 /* add interferences and affinity */ 904 for (std::pair<Temp, uint32_t> pair : ctx.spills_exit[pred_idx]) 905 ctx.add_interference(spill_id, pair.second); 906 ctx.add_affinity(def_spill_id, spill_id); 907 908 aco_ptr<Pseudo_instruction> spill{ 909 create_instruction<Pseudo_instruction>(aco_opcode::p_spill, Format::PSEUDO, 2, 0)}; 910 spill->operands[0] = spill_op; 911 spill->operands[1] = Operand::c32(spill_id); 912 Block& pred = ctx.program->blocks[pred_idx]; 913 unsigned idx = pred.instructions.size(); 914 do { 915 assert(idx != 0); 916 idx--; 917 } while (phi->opcode == aco_opcode::p_phi && 918 pred.instructions[idx]->opcode != aco_opcode::p_logical_end); 919 std::vector<aco_ptr<Instruction>>::iterator it = std::next(pred.instructions.begin(), idx); 920 pred.instructions.insert(it, std::move(spill)); 921 if (spill_op.isTemp()) 922 ctx.spills_exit[pred_idx][spill_op.getTemp()] = spill_id; 923 } 924 925 /* remove phi from instructions */ 926 phi.reset(); 927 } 928 929 /* iterate all (other) spilled variables for which to spill at the predecessor */ 930 // TODO: would be better to have them sorted: first vgprs and first with longest distance 931 for (std::pair<Temp, uint32_t> pair : ctx.spills_entry[block_idx]) { 932 std::vector<unsigned> preds = 933 pair.first.is_linear() ? block->linear_preds : block->logical_preds; 934 935 for (unsigned pred_idx : preds) { 936 /* variable is already spilled at predecessor */ 937 auto spilled = ctx.spills_exit[pred_idx].find(pair.first); 938 if (spilled != ctx.spills_exit[pred_idx].end()) { 939 if (spilled->second != pair.second) 940 ctx.add_affinity(pair.second, spilled->second); 941 continue; 942 } 943 944 /* variable is dead at predecessor, it must be from a phi: this works because of CSSA form */ 945 if (!ctx.next_use_distances_end[pred_idx].count(pair.first)) 946 continue; 947 948 /* add interferences between spilled variable and predecessors exit spills */ 949 for (std::pair<Temp, uint32_t> exit_spill : ctx.spills_exit[pred_idx]) { 950 if (exit_spill.first == pair.first) 951 continue; 952 ctx.add_interference(exit_spill.second, pair.second); 953 } 954 955 /* variable is in register at predecessor and has to be spilled */ 956 /* rename if necessary */ 957 Temp var = pair.first; 958 std::map<Temp, Temp>::iterator rename_it = ctx.renames[pred_idx].find(var); 959 if (rename_it != ctx.renames[pred_idx].end()) { 960 var = rename_it->second; 961 ctx.renames[pred_idx].erase(rename_it); 962 } 963 964 aco_ptr<Pseudo_instruction> spill{ 965 create_instruction<Pseudo_instruction>(aco_opcode::p_spill, Format::PSEUDO, 2, 0)}; 966 spill->operands[0] = Operand(var); 967 spill->operands[1] = Operand::c32(pair.second); 968 Block& pred = ctx.program->blocks[pred_idx]; 969 unsigned idx = pred.instructions.size(); 970 do { 971 assert(idx != 0); 972 idx--; 973 } while (pair.first.type() == RegType::vgpr && 974 pred.instructions[idx]->opcode != aco_opcode::p_logical_end); 975 std::vector<aco_ptr<Instruction>>::iterator it = std::next(pred.instructions.begin(), idx); 976 pred.instructions.insert(it, std::move(spill)); 977 ctx.spills_exit[pred.index][pair.first] = pair.second; 978 } 979 } 980 981 /* iterate phis for which operands to reload */ 982 for (aco_ptr<Instruction>& phi : instructions) { 983 assert(phi->opcode == aco_opcode::p_phi || phi->opcode == aco_opcode::p_linear_phi); 984 assert(!phi->definitions[0].isTemp() || 985 !ctx.spills_entry[block_idx].count(phi->definitions[0].getTemp())); 986 987 std::vector<unsigned>& preds = 988 phi->opcode == aco_opcode::p_phi ? block->logical_preds : block->linear_preds; 989 for (unsigned i = 0; i < phi->operands.size(); i++) { 990 if (!phi->operands[i].isTemp()) 991 continue; 992 unsigned pred_idx = preds[i]; 993 994 /* if the operand was reloaded, rename */ 995 if (!ctx.spills_exit[pred_idx].count(phi->operands[i].getTemp())) { 996 std::map<Temp, Temp>::iterator it = 997 ctx.renames[pred_idx].find(phi->operands[i].getTemp()); 998 if (it != ctx.renames[pred_idx].end()) { 999 phi->operands[i].setTemp(it->second); 1000 /* prevent the definining instruction from being DCE'd if it could be rematerialized */ 1001 } else { 1002 auto remat_it = ctx.remat.find(phi->operands[i].getTemp()); 1003 if (remat_it != ctx.remat.end()) { 1004 ctx.unused_remats.erase(remat_it->second.instr); 1005 } 1006 } 1007 continue; 1008 } 1009 1010 Temp tmp = phi->operands[i].getTemp(); 1011 1012 /* reload phi operand at end of predecessor block */ 1013 Temp new_name = ctx.program->allocateTmp(tmp.regClass()); 1014 Block& pred = ctx.program->blocks[pred_idx]; 1015 unsigned idx = pred.instructions.size(); 1016 do { 1017 assert(idx != 0); 1018 idx--; 1019 } while (phi->opcode == aco_opcode::p_phi && 1020 pred.instructions[idx]->opcode != aco_opcode::p_logical_end); 1021 std::vector<aco_ptr<Instruction>>::iterator it = std::next(pred.instructions.begin(), idx); 1022 aco_ptr<Instruction> reload = 1023 do_reload(ctx, tmp, new_name, ctx.spills_exit[pred_idx][tmp]); 1024 1025 /* reload spilled exec mask directly to exec */ 1026 if (!phi->definitions[0].isTemp()) { 1027 assert(phi->definitions[0].isFixed() && phi->definitions[0].physReg() == exec); 1028 reload->definitions[0] = phi->definitions[0]; 1029 phi->operands[i] = Operand(exec, ctx.program->lane_mask); 1030 } else { 1031 ctx.spills_exit[pred_idx].erase(tmp); 1032 ctx.renames[pred_idx][tmp] = new_name; 1033 phi->operands[i].setTemp(new_name); 1034 } 1035 1036 pred.instructions.insert(it, std::move(reload)); 1037 } 1038 } 1039 1040 /* iterate live variables for which to reload */ 1041 // TODO: reload at current block if variable is spilled on all predecessors 1042 for (std::pair<const Temp, std::pair<uint32_t, uint32_t>>& pair : 1043 ctx.next_use_distances_start[block_idx]) { 1044 /* skip spilled variables */ 1045 if (ctx.spills_entry[block_idx].count(pair.first)) 1046 continue; 1047 std::vector<unsigned> preds = 1048 pair.first.is_linear() ? block->linear_preds : block->logical_preds; 1049 1050 /* variable is dead at predecessor, it must be from a phi */ 1051 bool is_dead = false; 1052 for (unsigned pred_idx : preds) { 1053 if (!ctx.next_use_distances_end[pred_idx].count(pair.first)) 1054 is_dead = true; 1055 } 1056 if (is_dead) 1057 continue; 1058 for (unsigned pred_idx : preds) { 1059 /* the variable is not spilled at the predecessor */ 1060 if (!ctx.spills_exit[pred_idx].count(pair.first)) 1061 continue; 1062 1063 /* variable is spilled at predecessor and has to be reloaded */ 1064 Temp new_name = ctx.program->allocateTmp(pair.first.regClass()); 1065 Block& pred = ctx.program->blocks[pred_idx]; 1066 unsigned idx = pred.instructions.size(); 1067 do { 1068 assert(idx != 0); 1069 idx--; 1070 } while (pair.first.type() == RegType::vgpr && 1071 pred.instructions[idx]->opcode != aco_opcode::p_logical_end); 1072 std::vector<aco_ptr<Instruction>>::iterator it = std::next(pred.instructions.begin(), idx); 1073 1074 aco_ptr<Instruction> reload = 1075 do_reload(ctx, pair.first, new_name, ctx.spills_exit[pred.index][pair.first]); 1076 pred.instructions.insert(it, std::move(reload)); 1077 1078 ctx.spills_exit[pred.index].erase(pair.first); 1079 ctx.renames[pred.index][pair.first] = new_name; 1080 } 1081 1082 /* check if we have to create a new phi for this variable */ 1083 Temp rename = Temp(); 1084 bool is_same = true; 1085 for (unsigned pred_idx : preds) { 1086 if (!ctx.renames[pred_idx].count(pair.first)) { 1087 if (rename == Temp()) 1088 rename = pair.first; 1089 else 1090 is_same = rename == pair.first; 1091 } else { 1092 if (rename == Temp()) 1093 rename = ctx.renames[pred_idx][pair.first]; 1094 else 1095 is_same = rename == ctx.renames[pred_idx][pair.first]; 1096 } 1097 1098 if (!is_same) 1099 break; 1100 } 1101 1102 if (!is_same) { 1103 /* the variable was renamed differently in the predecessors: we have to create a phi */ 1104 aco_opcode opcode = pair.first.is_linear() ? aco_opcode::p_linear_phi : aco_opcode::p_phi; 1105 aco_ptr<Pseudo_instruction> phi{ 1106 create_instruction<Pseudo_instruction>(opcode, Format::PSEUDO, preds.size(), 1)}; 1107 rename = ctx.program->allocateTmp(pair.first.regClass()); 1108 for (unsigned i = 0; i < phi->operands.size(); i++) { 1109 Temp tmp; 1110 if (ctx.renames[preds[i]].count(pair.first)) { 1111 tmp = ctx.renames[preds[i]][pair.first]; 1112 } else if (preds[i] >= block_idx) { 1113 tmp = rename; 1114 } else { 1115 tmp = pair.first; 1116 /* prevent the definining instruction from being DCE'd if it could be rematerialized */ 1117 if (ctx.remat.count(tmp)) 1118 ctx.unused_remats.erase(ctx.remat[tmp].instr); 1119 } 1120 phi->operands[i] = Operand(tmp); 1121 } 1122 phi->definitions[0] = Definition(rename); 1123 instructions.emplace_back(std::move(phi)); 1124 } 1125 1126 /* the variable was renamed: add new name to renames */ 1127 if (!(rename == Temp() || rename == pair.first)) 1128 ctx.renames[block_idx][pair.first] = rename; 1129 } 1130 1131 /* combine phis with instructions */ 1132 unsigned idx = 0; 1133 while (!block->instructions[idx]) { 1134 idx++; 1135 } 1136 1137 if (!ctx.processed[block_idx]) { 1138 assert(!(block->kind & block_kind_loop_header)); 1139 RegisterDemand demand_before = get_demand_before(ctx, block_idx, idx); 1140 ctx.register_demand[block->index].erase(ctx.register_demand[block->index].begin(), 1141 ctx.register_demand[block->index].begin() + idx); 1142 ctx.register_demand[block->index].insert(ctx.register_demand[block->index].begin(), 1143 instructions.size(), demand_before); 1144 } 1145 1146 std::vector<aco_ptr<Instruction>>::iterator start = std::next(block->instructions.begin(), idx); 1147 instructions.insert( 1148 instructions.end(), std::move_iterator<std::vector<aco_ptr<Instruction>>::iterator>(start), 1149 std::move_iterator<std::vector<aco_ptr<Instruction>>::iterator>(block->instructions.end())); 1150 block->instructions = std::move(instructions); 1151} 1152 1153void 1154process_block(spill_ctx& ctx, unsigned block_idx, Block* block, RegisterDemand spilled_registers) 1155{ 1156 assert(!ctx.processed[block_idx]); 1157 1158 std::vector<aco_ptr<Instruction>> instructions; 1159 unsigned idx = 0; 1160 1161 /* phis are handled separetely */ 1162 while (block->instructions[idx]->opcode == aco_opcode::p_phi || 1163 block->instructions[idx]->opcode == aco_opcode::p_linear_phi) { 1164 instructions.emplace_back(std::move(block->instructions[idx++])); 1165 } 1166 1167 if (block->register_demand.exceeds(ctx.target_pressure)) { 1168 update_local_next_uses(ctx, block, ctx.local_next_use_distance); 1169 } else { 1170 /* We won't use local_next_use_distance, so no initialization needed */ 1171 } 1172 1173 auto& current_spills = ctx.spills_exit[block_idx]; 1174 1175 while (idx < block->instructions.size()) { 1176 aco_ptr<Instruction>& instr = block->instructions[idx]; 1177 1178 std::map<Temp, std::pair<Temp, uint32_t>> reloads; 1179 1180 /* rename and reload operands */ 1181 for (Operand& op : instr->operands) { 1182 if (!op.isTemp()) 1183 continue; 1184 if (!current_spills.count(op.getTemp())) { 1185 /* the Operand is in register: check if it was renamed */ 1186 auto rename_it = ctx.renames[block_idx].find(op.getTemp()); 1187 if (rename_it != ctx.renames[block_idx].end()) { 1188 op.setTemp(rename_it->second); 1189 } else { 1190 /* prevent its definining instruction from being DCE'd if it could be rematerialized */ 1191 auto remat_it = ctx.remat.find(op.getTemp()); 1192 if (remat_it != ctx.remat.end()) { 1193 ctx.unused_remats.erase(remat_it->second.instr); 1194 } 1195 } 1196 continue; 1197 } 1198 /* the Operand is spilled: add it to reloads */ 1199 Temp new_tmp = ctx.program->allocateTmp(op.regClass()); 1200 ctx.renames[block_idx][op.getTemp()] = new_tmp; 1201 reloads[new_tmp] = std::make_pair(op.getTemp(), current_spills[op.getTemp()]); 1202 current_spills.erase(op.getTemp()); 1203 op.setTemp(new_tmp); 1204 spilled_registers -= new_tmp; 1205 } 1206 1207 /* check if register demand is low enough before and after the current instruction */ 1208 if (block->register_demand.exceeds(ctx.target_pressure)) { 1209 1210 RegisterDemand new_demand = ctx.register_demand[block_idx][idx]; 1211 new_demand.update(get_demand_before(ctx, block_idx, idx)); 1212 1213 assert(!ctx.local_next_use_distance.empty()); 1214 1215 /* if reg pressure is too high, spill variable with furthest next use */ 1216 while ((new_demand - spilled_registers).exceeds(ctx.target_pressure)) { 1217 unsigned distance = 0; 1218 Temp to_spill; 1219 bool do_rematerialize = false; 1220 RegType type = RegType::sgpr; 1221 if (new_demand.vgpr - spilled_registers.vgpr > ctx.target_pressure.vgpr) 1222 type = RegType::vgpr; 1223 1224 for (std::pair<Temp, uint32_t> pair : ctx.local_next_use_distance[idx]) { 1225 if (pair.first.type() != type) 1226 continue; 1227 bool can_rematerialize = ctx.remat.count(pair.first); 1228 if (((pair.second > distance && can_rematerialize == do_rematerialize) || 1229 (can_rematerialize && !do_rematerialize && pair.second > idx)) && 1230 !current_spills.count(pair.first)) { 1231 to_spill = pair.first; 1232 distance = pair.second; 1233 do_rematerialize = can_rematerialize; 1234 } 1235 } 1236 1237 assert(distance != 0 && distance > idx); 1238 uint32_t spill_id = ctx.allocate_spill_id(to_spill.regClass()); 1239 1240 /* add interferences with currently spilled variables */ 1241 for (std::pair<Temp, uint32_t> pair : current_spills) 1242 ctx.add_interference(spill_id, pair.second); 1243 for (std::pair<const Temp, std::pair<Temp, uint32_t>>& pair : reloads) 1244 ctx.add_interference(spill_id, pair.second.second); 1245 1246 current_spills[to_spill] = spill_id; 1247 spilled_registers += to_spill; 1248 1249 /* rename if necessary */ 1250 if (ctx.renames[block_idx].count(to_spill)) { 1251 to_spill = ctx.renames[block_idx][to_spill]; 1252 } 1253 1254 /* add spill to new instructions */ 1255 aco_ptr<Pseudo_instruction> spill{ 1256 create_instruction<Pseudo_instruction>(aco_opcode::p_spill, Format::PSEUDO, 2, 0)}; 1257 spill->operands[0] = Operand(to_spill); 1258 spill->operands[1] = Operand::c32(spill_id); 1259 instructions.emplace_back(std::move(spill)); 1260 } 1261 } 1262 1263 /* add reloads and instruction to new instructions */ 1264 for (std::pair<const Temp, std::pair<Temp, uint32_t>>& pair : reloads) { 1265 aco_ptr<Instruction> reload = 1266 do_reload(ctx, pair.second.first, pair.first, pair.second.second); 1267 instructions.emplace_back(std::move(reload)); 1268 } 1269 instructions.emplace_back(std::move(instr)); 1270 idx++; 1271 } 1272 1273 block->instructions = std::move(instructions); 1274} 1275 1276void 1277spill_block(spill_ctx& ctx, unsigned block_idx) 1278{ 1279 Block* block = &ctx.program->blocks[block_idx]; 1280 1281 /* determine set of variables which are spilled at the beginning of the block */ 1282 RegisterDemand spilled_registers = init_live_in_vars(ctx, block, block_idx); 1283 1284 /* add interferences for spilled variables */ 1285 for (auto it = ctx.spills_entry[block_idx].begin(); it != ctx.spills_entry[block_idx].end(); 1286 ++it) { 1287 for (auto it2 = std::next(it); it2 != ctx.spills_entry[block_idx].end(); ++it2) 1288 ctx.add_interference(it->second, it2->second); 1289 } 1290 1291 bool is_loop_header = block->loop_nest_depth && ctx.loop_header.top()->index == block_idx; 1292 if (!is_loop_header) { 1293 /* add spill/reload code on incoming control flow edges */ 1294 add_coupling_code(ctx, block, block_idx); 1295 } 1296 1297 const auto& current_spills = ctx.spills_entry[block_idx]; 1298 1299 /* check conditions to process this block */ 1300 bool process = (block->register_demand - spilled_registers).exceeds(ctx.target_pressure) || 1301 !ctx.renames[block_idx].empty() || ctx.unused_remats.size(); 1302 1303 for (auto it = current_spills.begin(); !process && it != current_spills.end(); ++it) { 1304 if (ctx.next_use_distances_start[block_idx].at(it->first).first == block_idx) 1305 process = true; 1306 } 1307 1308 assert(ctx.spills_exit[block_idx].empty()); 1309 ctx.spills_exit[block_idx] = current_spills; 1310 if (process) { 1311 process_block(ctx, block_idx, block, spilled_registers); 1312 } 1313 1314 ctx.processed[block_idx] = true; 1315 1316 /* check if the next block leaves the current loop */ 1317 if (block->loop_nest_depth == 0 || 1318 ctx.program->blocks[block_idx + 1].loop_nest_depth >= block->loop_nest_depth) 1319 return; 1320 1321 Block* loop_header = ctx.loop_header.top(); 1322 1323 /* preserve original renames at end of loop header block */ 1324 std::map<Temp, Temp> renames = std::move(ctx.renames[loop_header->index]); 1325 1326 /* add coupling code to all loop header predecessors */ 1327 add_coupling_code(ctx, loop_header, loop_header->index); 1328 1329 /* propagate new renames through loop: i.e. repair the SSA */ 1330 renames.swap(ctx.renames[loop_header->index]); 1331 for (std::pair<Temp, Temp> rename : renames) { 1332 for (unsigned idx = loop_header->index; idx <= block_idx; idx++) { 1333 Block& current = ctx.program->blocks[idx]; 1334 std::vector<aco_ptr<Instruction>>::iterator instr_it = current.instructions.begin(); 1335 1336 /* first rename phis */ 1337 while (instr_it != current.instructions.end()) { 1338 aco_ptr<Instruction>& phi = *instr_it; 1339 if (phi->opcode != aco_opcode::p_phi && phi->opcode != aco_opcode::p_linear_phi) 1340 break; 1341 /* no need to rename the loop header phis once again. this happened in 1342 * add_coupling_code() */ 1343 if (idx == loop_header->index) { 1344 instr_it++; 1345 continue; 1346 } 1347 1348 for (Operand& op : phi->operands) { 1349 if (!op.isTemp()) 1350 continue; 1351 if (op.getTemp() == rename.first) 1352 op.setTemp(rename.second); 1353 } 1354 instr_it++; 1355 } 1356 1357 /* variable is not live at beginning of this block */ 1358 if (ctx.next_use_distances_start[idx].count(rename.first) == 0) 1359 continue; 1360 1361 /* if the variable is live at the block's exit, add rename */ 1362 if (ctx.next_use_distances_end[idx].count(rename.first) != 0) 1363 ctx.renames[idx].insert(rename); 1364 1365 /* rename all uses in this block */ 1366 bool renamed = false; 1367 while (!renamed && instr_it != current.instructions.end()) { 1368 aco_ptr<Instruction>& instr = *instr_it; 1369 for (Operand& op : instr->operands) { 1370 if (!op.isTemp()) 1371 continue; 1372 if (op.getTemp() == rename.first) { 1373 op.setTemp(rename.second); 1374 /* we can stop with this block as soon as the variable is spilled */ 1375 if (instr->opcode == aco_opcode::p_spill) 1376 renamed = true; 1377 } 1378 } 1379 instr_it++; 1380 } 1381 } 1382 } 1383 1384 /* remove loop header info from stack */ 1385 ctx.loop_header.pop(); 1386} 1387 1388Temp 1389load_scratch_resource(spill_ctx& ctx, Temp& scratch_offset, 1390 std::vector<aco_ptr<Instruction>>& instructions, unsigned offset, 1391 bool is_top_level) 1392{ 1393 Builder bld(ctx.program); 1394 if (is_top_level) { 1395 bld.reset(&instructions); 1396 } else { 1397 /* find p_logical_end */ 1398 unsigned idx = instructions.size() - 1; 1399 while (instructions[idx]->opcode != aco_opcode::p_logical_end) 1400 idx--; 1401 bld.reset(&instructions, std::next(instructions.begin(), idx)); 1402 } 1403 1404 Temp private_segment_buffer = ctx.program->private_segment_buffer; 1405 if (ctx.program->stage != compute_cs) 1406 private_segment_buffer = 1407 bld.smem(aco_opcode::s_load_dwordx2, bld.def(s2), private_segment_buffer, Operand::zero()); 1408 1409 if (offset) 1410 scratch_offset = bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.def(s1, scc), 1411 scratch_offset, Operand::c32(offset)); 1412 1413 uint32_t rsrc_conf = 1414 S_008F0C_ADD_TID_ENABLE(1) | S_008F0C_INDEX_STRIDE(ctx.program->wave_size == 64 ? 3 : 2); 1415 1416 if (ctx.program->chip_class >= GFX10) { 1417 rsrc_conf |= S_008F0C_FORMAT(V_008F0C_GFX10_FORMAT_32_FLOAT) | 1418 S_008F0C_OOB_SELECT(V_008F0C_OOB_SELECT_RAW) | S_008F0C_RESOURCE_LEVEL(1); 1419 } else if (ctx.program->chip_class <= GFX7) { 1420 /* dfmt modifies stride on GFX8/GFX9 when ADD_TID_EN=1 */ 1421 rsrc_conf |= S_008F0C_NUM_FORMAT(V_008F0C_BUF_NUM_FORMAT_FLOAT) | 1422 S_008F0C_DATA_FORMAT(V_008F0C_BUF_DATA_FORMAT_32); 1423 } 1424 /* older generations need element size = 4 bytes. element size removed in GFX9 */ 1425 if (ctx.program->chip_class <= GFX8) 1426 rsrc_conf |= S_008F0C_ELEMENT_SIZE(1); 1427 1428 return bld.pseudo(aco_opcode::p_create_vector, bld.def(s4), private_segment_buffer, 1429 Operand::c32(-1u), Operand::c32(rsrc_conf)); 1430} 1431 1432void 1433add_interferences(spill_ctx& ctx, std::vector<bool>& is_assigned, std::vector<uint32_t>& slots, 1434 std::vector<bool>& slots_used, unsigned id) 1435{ 1436 for (unsigned other : ctx.interferences[id].second) { 1437 if (!is_assigned[other]) 1438 continue; 1439 1440 RegClass other_rc = ctx.interferences[other].first; 1441 unsigned slot = slots[other]; 1442 std::fill(slots_used.begin() + slot, slots_used.begin() + slot + other_rc.size(), true); 1443 } 1444} 1445 1446unsigned 1447find_available_slot(std::vector<bool>& used, unsigned wave_size, unsigned size, bool is_sgpr, 1448 unsigned* num_slots) 1449{ 1450 unsigned wave_size_minus_one = wave_size - 1; 1451 unsigned slot = 0; 1452 1453 while (true) { 1454 bool available = true; 1455 for (unsigned i = 0; i < size; i++) { 1456 if (slot + i < used.size() && used[slot + i]) { 1457 available = false; 1458 break; 1459 } 1460 } 1461 if (!available) { 1462 slot++; 1463 continue; 1464 } 1465 1466 if (is_sgpr && ((slot & wave_size_minus_one) > wave_size - size)) { 1467 slot = align(slot, wave_size); 1468 continue; 1469 } 1470 1471 std::fill(used.begin(), used.end(), false); 1472 1473 if (slot + size > used.size()) 1474 used.resize(slot + size); 1475 1476 return slot; 1477 } 1478} 1479 1480void 1481assign_spill_slots_helper(spill_ctx& ctx, RegType type, std::vector<bool>& is_assigned, 1482 std::vector<uint32_t>& slots, unsigned* num_slots) 1483{ 1484 std::vector<bool> slots_used(*num_slots); 1485 1486 /* assign slots for ids with affinities first */ 1487 for (std::vector<uint32_t>& vec : ctx.affinities) { 1488 if (ctx.interferences[vec[0]].first.type() != type) 1489 continue; 1490 1491 for (unsigned id : vec) { 1492 if (!ctx.is_reloaded[id]) 1493 continue; 1494 1495 add_interferences(ctx, is_assigned, slots, slots_used, id); 1496 } 1497 1498 unsigned slot = 1499 find_available_slot(slots_used, ctx.wave_size, ctx.interferences[vec[0]].first.size(), 1500 type == RegType::sgpr, num_slots); 1501 1502 for (unsigned id : vec) { 1503 assert(!is_assigned[id]); 1504 1505 if (ctx.is_reloaded[id]) { 1506 slots[id] = slot; 1507 is_assigned[id] = true; 1508 } 1509 } 1510 } 1511 1512 /* assign slots for ids without affinities */ 1513 for (unsigned id = 0; id < ctx.interferences.size(); id++) { 1514 if (is_assigned[id] || !ctx.is_reloaded[id] || ctx.interferences[id].first.type() != type) 1515 continue; 1516 1517 add_interferences(ctx, is_assigned, slots, slots_used, id); 1518 1519 unsigned slot = 1520 find_available_slot(slots_used, ctx.wave_size, ctx.interferences[id].first.size(), 1521 type == RegType::sgpr, num_slots); 1522 1523 slots[id] = slot; 1524 is_assigned[id] = true; 1525 } 1526 1527 *num_slots = slots_used.size(); 1528} 1529 1530void 1531assign_spill_slots(spill_ctx& ctx, unsigned spills_to_vgpr) 1532{ 1533 std::vector<uint32_t> slots(ctx.interferences.size()); 1534 std::vector<bool> is_assigned(ctx.interferences.size()); 1535 1536 /* first, handle affinities: just merge all interferences into both spill ids */ 1537 for (std::vector<uint32_t>& vec : ctx.affinities) { 1538 for (unsigned i = 0; i < vec.size(); i++) { 1539 for (unsigned j = i + 1; j < vec.size(); j++) { 1540 assert(vec[i] != vec[j]); 1541 bool reloaded = ctx.is_reloaded[vec[i]] || ctx.is_reloaded[vec[j]]; 1542 ctx.is_reloaded[vec[i]] = reloaded; 1543 ctx.is_reloaded[vec[j]] = reloaded; 1544 } 1545 } 1546 } 1547 for (ASSERTED uint32_t i = 0; i < ctx.interferences.size(); i++) 1548 for (ASSERTED uint32_t id : ctx.interferences[i].second) 1549 assert(i != id); 1550 1551 /* for each spill slot, assign as many spill ids as possible */ 1552 unsigned sgpr_spill_slots = 0, vgpr_spill_slots = 0; 1553 assign_spill_slots_helper(ctx, RegType::sgpr, is_assigned, slots, &sgpr_spill_slots); 1554 assign_spill_slots_helper(ctx, RegType::vgpr, is_assigned, slots, &vgpr_spill_slots); 1555 1556 for (unsigned id = 0; id < is_assigned.size(); id++) 1557 assert(is_assigned[id] || !ctx.is_reloaded[id]); 1558 1559 for (std::vector<uint32_t>& vec : ctx.affinities) { 1560 for (unsigned i = 0; i < vec.size(); i++) { 1561 for (unsigned j = i + 1; j < vec.size(); j++) { 1562 assert(is_assigned[vec[i]] == is_assigned[vec[j]]); 1563 if (!is_assigned[vec[i]]) 1564 continue; 1565 assert(ctx.is_reloaded[vec[i]] == ctx.is_reloaded[vec[j]]); 1566 assert(ctx.interferences[vec[i]].first.type() == 1567 ctx.interferences[vec[j]].first.type()); 1568 assert(slots[vec[i]] == slots[vec[j]]); 1569 } 1570 } 1571 } 1572 1573 /* hope, we didn't mess up */ 1574 std::vector<Temp> vgpr_spill_temps((sgpr_spill_slots + ctx.wave_size - 1) / ctx.wave_size); 1575 assert(vgpr_spill_temps.size() <= spills_to_vgpr); 1576 1577 /* replace pseudo instructions with actual hardware instructions */ 1578 Temp scratch_offset = ctx.program->scratch_offset, scratch_rsrc = Temp(); 1579 unsigned last_top_level_block_idx = 0; 1580 std::vector<bool> reload_in_loop(vgpr_spill_temps.size()); 1581 for (Block& block : ctx.program->blocks) { 1582 1583 /* after loops, we insert a user if there was a reload inside the loop */ 1584 if (block.loop_nest_depth == 0) { 1585 int end_vgprs = 0; 1586 for (unsigned i = 0; i < vgpr_spill_temps.size(); i++) { 1587 if (reload_in_loop[i]) 1588 end_vgprs++; 1589 } 1590 1591 if (end_vgprs > 0) { 1592 aco_ptr<Instruction> destr{create_instruction<Pseudo_instruction>( 1593 aco_opcode::p_end_linear_vgpr, Format::PSEUDO, end_vgprs, 0)}; 1594 int k = 0; 1595 for (unsigned i = 0; i < vgpr_spill_temps.size(); i++) { 1596 if (reload_in_loop[i]) 1597 destr->operands[k++] = Operand(vgpr_spill_temps[i]); 1598 reload_in_loop[i] = false; 1599 } 1600 /* find insertion point */ 1601 std::vector<aco_ptr<Instruction>>::iterator it = block.instructions.begin(); 1602 while ((*it)->opcode == aco_opcode::p_linear_phi || (*it)->opcode == aco_opcode::p_phi) 1603 ++it; 1604 block.instructions.insert(it, std::move(destr)); 1605 } 1606 } 1607 1608 if (block.kind & block_kind_top_level && !block.linear_preds.empty()) { 1609 last_top_level_block_idx = block.index; 1610 1611 /* check if any spilled variables use a created linear vgpr, otherwise destroy them */ 1612 for (unsigned i = 0; i < vgpr_spill_temps.size(); i++) { 1613 if (vgpr_spill_temps[i] == Temp()) 1614 continue; 1615 1616 bool can_destroy = true; 1617 for (std::pair<Temp, uint32_t> pair : ctx.spills_entry[block.index]) { 1618 1619 if (ctx.interferences[pair.second].first.type() == RegType::sgpr && 1620 slots[pair.second] / ctx.wave_size == i) { 1621 can_destroy = false; 1622 break; 1623 } 1624 } 1625 if (can_destroy) 1626 vgpr_spill_temps[i] = Temp(); 1627 } 1628 } 1629 1630 std::vector<aco_ptr<Instruction>>::iterator it; 1631 std::vector<aco_ptr<Instruction>> instructions; 1632 instructions.reserve(block.instructions.size()); 1633 Builder bld(ctx.program, &instructions); 1634 for (it = block.instructions.begin(); it != block.instructions.end(); ++it) { 1635 1636 if ((*it)->opcode == aco_opcode::p_spill) { 1637 uint32_t spill_id = (*it)->operands[1].constantValue(); 1638 1639 if (!ctx.is_reloaded[spill_id]) { 1640 /* never reloaded, so don't spill */ 1641 } else if (!is_assigned[spill_id]) { 1642 unreachable("No spill slot assigned for spill id"); 1643 } else if (ctx.interferences[spill_id].first.type() == RegType::vgpr) { 1644 /* spill vgpr */ 1645 ctx.program->config->spilled_vgprs += (*it)->operands[0].size(); 1646 uint32_t spill_slot = slots[spill_id]; 1647 bool add_offset_to_sgpr = 1648 ctx.program->config->scratch_bytes_per_wave / ctx.program->wave_size + 1649 vgpr_spill_slots * 4 > 1650 4096; 1651 unsigned base_offset = 1652 add_offset_to_sgpr 1653 ? 0 1654 : ctx.program->config->scratch_bytes_per_wave / ctx.program->wave_size; 1655 1656 /* check if the scratch resource descriptor already exists */ 1657 if (scratch_rsrc == Temp()) { 1658 unsigned offset = 1659 add_offset_to_sgpr ? ctx.program->config->scratch_bytes_per_wave : 0; 1660 scratch_rsrc = load_scratch_resource( 1661 ctx, scratch_offset, 1662 last_top_level_block_idx == block.index 1663 ? instructions 1664 : ctx.program->blocks[last_top_level_block_idx].instructions, 1665 offset, last_top_level_block_idx == block.index); 1666 } 1667 1668 unsigned offset = base_offset + spill_slot * 4; 1669 aco_opcode opcode = aco_opcode::buffer_store_dword; 1670 assert((*it)->operands[0].isTemp()); 1671 Temp temp = (*it)->operands[0].getTemp(); 1672 assert(temp.type() == RegType::vgpr && !temp.is_linear()); 1673 if (temp.size() > 1) { 1674 Instruction* split{create_instruction<Pseudo_instruction>( 1675 aco_opcode::p_split_vector, Format::PSEUDO, 1, temp.size())}; 1676 split->operands[0] = Operand(temp); 1677 for (unsigned i = 0; i < temp.size(); i++) 1678 split->definitions[i] = bld.def(v1); 1679 bld.insert(split); 1680 for (unsigned i = 0; i < temp.size(); i++) { 1681 Instruction* instr = 1682 bld.mubuf(opcode, scratch_rsrc, Operand(v1), scratch_offset, 1683 split->definitions[i].getTemp(), offset + i * 4, false, true); 1684 instr->mubuf().sync = memory_sync_info(storage_vgpr_spill, semantic_private); 1685 } 1686 } else { 1687 Instruction* instr = bld.mubuf(opcode, scratch_rsrc, Operand(v1), scratch_offset, 1688 temp, offset, false, true); 1689 instr->mubuf().sync = memory_sync_info(storage_vgpr_spill, semantic_private); 1690 } 1691 } else { 1692 ctx.program->config->spilled_sgprs += (*it)->operands[0].size(); 1693 1694 uint32_t spill_slot = slots[spill_id]; 1695 1696 /* check if the linear vgpr already exists */ 1697 if (vgpr_spill_temps[spill_slot / ctx.wave_size] == Temp()) { 1698 Temp linear_vgpr = ctx.program->allocateTmp(v1.as_linear()); 1699 vgpr_spill_temps[spill_slot / ctx.wave_size] = linear_vgpr; 1700 aco_ptr<Pseudo_instruction> create{create_instruction<Pseudo_instruction>( 1701 aco_opcode::p_start_linear_vgpr, Format::PSEUDO, 0, 1)}; 1702 create->definitions[0] = Definition(linear_vgpr); 1703 /* find the right place to insert this definition */ 1704 if (last_top_level_block_idx == block.index) { 1705 /* insert right before the current instruction */ 1706 instructions.emplace_back(std::move(create)); 1707 } else { 1708 assert(last_top_level_block_idx < block.index); 1709 /* insert before the branch at last top level block */ 1710 std::vector<aco_ptr<Instruction>>& block_instrs = 1711 ctx.program->blocks[last_top_level_block_idx].instructions; 1712 block_instrs.insert(std::prev(block_instrs.end()), std::move(create)); 1713 } 1714 } 1715 1716 /* spill sgpr: just add the vgpr temp to operands */ 1717 Pseudo_instruction* spill = 1718 create_instruction<Pseudo_instruction>(aco_opcode::p_spill, Format::PSEUDO, 3, 0); 1719 spill->operands[0] = Operand(vgpr_spill_temps[spill_slot / ctx.wave_size]); 1720 spill->operands[1] = Operand::c32(spill_slot % ctx.wave_size); 1721 spill->operands[2] = (*it)->operands[0]; 1722 instructions.emplace_back(aco_ptr<Instruction>(spill)); 1723 } 1724 1725 } else if ((*it)->opcode == aco_opcode::p_reload) { 1726 uint32_t spill_id = (*it)->operands[0].constantValue(); 1727 assert(ctx.is_reloaded[spill_id]); 1728 1729 if (!is_assigned[spill_id]) { 1730 unreachable("No spill slot assigned for spill id"); 1731 } else if (ctx.interferences[spill_id].first.type() == RegType::vgpr) { 1732 /* reload vgpr */ 1733 uint32_t spill_slot = slots[spill_id]; 1734 bool add_offset_to_sgpr = 1735 ctx.program->config->scratch_bytes_per_wave / ctx.program->wave_size + 1736 vgpr_spill_slots * 4 > 1737 4096; 1738 unsigned base_offset = 1739 add_offset_to_sgpr 1740 ? 0 1741 : ctx.program->config->scratch_bytes_per_wave / ctx.program->wave_size; 1742 1743 /* check if the scratch resource descriptor already exists */ 1744 if (scratch_rsrc == Temp()) { 1745 unsigned offset = 1746 add_offset_to_sgpr ? ctx.program->config->scratch_bytes_per_wave : 0; 1747 scratch_rsrc = load_scratch_resource( 1748 ctx, scratch_offset, 1749 last_top_level_block_idx == block.index 1750 ? instructions 1751 : ctx.program->blocks[last_top_level_block_idx].instructions, 1752 offset, last_top_level_block_idx == block.index); 1753 } 1754 1755 unsigned offset = base_offset + spill_slot * 4; 1756 aco_opcode opcode = aco_opcode::buffer_load_dword; 1757 Definition def = (*it)->definitions[0]; 1758 if (def.size() > 1) { 1759 Instruction* vec{create_instruction<Pseudo_instruction>( 1760 aco_opcode::p_create_vector, Format::PSEUDO, def.size(), 1)}; 1761 vec->definitions[0] = def; 1762 for (unsigned i = 0; i < def.size(); i++) { 1763 Temp tmp = bld.tmp(v1); 1764 vec->operands[i] = Operand(tmp); 1765 Instruction* instr = 1766 bld.mubuf(opcode, Definition(tmp), scratch_rsrc, Operand(v1), 1767 scratch_offset, offset + i * 4, false, true); 1768 instr->mubuf().sync = memory_sync_info(storage_vgpr_spill, semantic_private); 1769 } 1770 bld.insert(vec); 1771 } else { 1772 Instruction* instr = bld.mubuf(opcode, def, scratch_rsrc, Operand(v1), 1773 scratch_offset, offset, false, true); 1774 instr->mubuf().sync = memory_sync_info(storage_vgpr_spill, semantic_private); 1775 } 1776 } else { 1777 uint32_t spill_slot = slots[spill_id]; 1778 reload_in_loop[spill_slot / ctx.wave_size] = block.loop_nest_depth > 0; 1779 1780 /* check if the linear vgpr already exists */ 1781 if (vgpr_spill_temps[spill_slot / ctx.wave_size] == Temp()) { 1782 Temp linear_vgpr = ctx.program->allocateTmp(v1.as_linear()); 1783 vgpr_spill_temps[spill_slot / ctx.wave_size] = linear_vgpr; 1784 aco_ptr<Pseudo_instruction> create{create_instruction<Pseudo_instruction>( 1785 aco_opcode::p_start_linear_vgpr, Format::PSEUDO, 0, 1)}; 1786 create->definitions[0] = Definition(linear_vgpr); 1787 /* find the right place to insert this definition */ 1788 if (last_top_level_block_idx == block.index) { 1789 /* insert right before the current instruction */ 1790 instructions.emplace_back(std::move(create)); 1791 } else { 1792 assert(last_top_level_block_idx < block.index); 1793 /* insert before the branch at last top level block */ 1794 std::vector<aco_ptr<Instruction>>& block_instrs = 1795 ctx.program->blocks[last_top_level_block_idx].instructions; 1796 block_instrs.insert(std::prev(block_instrs.end()), std::move(create)); 1797 } 1798 } 1799 1800 /* reload sgpr: just add the vgpr temp to operands */ 1801 Pseudo_instruction* reload = create_instruction<Pseudo_instruction>( 1802 aco_opcode::p_reload, Format::PSEUDO, 2, 1); 1803 reload->operands[0] = Operand(vgpr_spill_temps[spill_slot / ctx.wave_size]); 1804 reload->operands[1] = Operand::c32(spill_slot % ctx.wave_size); 1805 reload->definitions[0] = (*it)->definitions[0]; 1806 instructions.emplace_back(aco_ptr<Instruction>(reload)); 1807 } 1808 } else if (!ctx.unused_remats.count(it->get())) { 1809 instructions.emplace_back(std::move(*it)); 1810 } 1811 } 1812 block.instructions = std::move(instructions); 1813 } 1814 1815 /* update required scratch memory */ 1816 ctx.program->config->scratch_bytes_per_wave += 1817 align(vgpr_spill_slots * 4 * ctx.program->wave_size, 1024); 1818 1819 /* SSA elimination inserts copies for logical phis right before p_logical_end 1820 * So if a linear vgpr is used between that p_logical_end and the branch, 1821 * we need to ensure logical phis don't choose a definition which aliases 1822 * the linear vgpr. 1823 * TODO: Moving the spills and reloads to before p_logical_end might produce 1824 * slightly better code. */ 1825 for (Block& block : ctx.program->blocks) { 1826 /* loops exits are already handled */ 1827 if (block.logical_preds.size() <= 1) 1828 continue; 1829 1830 bool has_logical_phis = false; 1831 for (aco_ptr<Instruction>& instr : block.instructions) { 1832 if (instr->opcode == aco_opcode::p_phi) { 1833 has_logical_phis = true; 1834 break; 1835 } else if (instr->opcode != aco_opcode::p_linear_phi) { 1836 break; 1837 } 1838 } 1839 if (!has_logical_phis) 1840 continue; 1841 1842 std::set<Temp> vgprs; 1843 for (unsigned pred_idx : block.logical_preds) { 1844 Block& pred = ctx.program->blocks[pred_idx]; 1845 for (int i = pred.instructions.size() - 1; i >= 0; i--) { 1846 aco_ptr<Instruction>& pred_instr = pred.instructions[i]; 1847 if (pred_instr->opcode == aco_opcode::p_logical_end) { 1848 break; 1849 } else if (pred_instr->opcode == aco_opcode::p_spill || 1850 pred_instr->opcode == aco_opcode::p_reload) { 1851 vgprs.insert(pred_instr->operands[0].getTemp()); 1852 } 1853 } 1854 } 1855 if (!vgprs.size()) 1856 continue; 1857 1858 aco_ptr<Instruction> destr{create_instruction<Pseudo_instruction>( 1859 aco_opcode::p_end_linear_vgpr, Format::PSEUDO, vgprs.size(), 0)}; 1860 int k = 0; 1861 for (Temp tmp : vgprs) { 1862 destr->operands[k++] = Operand(tmp); 1863 } 1864 /* find insertion point */ 1865 std::vector<aco_ptr<Instruction>>::iterator it = block.instructions.begin(); 1866 while ((*it)->opcode == aco_opcode::p_linear_phi || (*it)->opcode == aco_opcode::p_phi) 1867 ++it; 1868 block.instructions.insert(it, std::move(destr)); 1869 } 1870} 1871 1872} /* end namespace */ 1873 1874void 1875spill(Program* program, live& live_vars) 1876{ 1877 program->config->spilled_vgprs = 0; 1878 program->config->spilled_sgprs = 0; 1879 1880 program->progress = CompilationProgress::after_spilling; 1881 1882 /* no spilling when register pressure is low enough */ 1883 if (program->num_waves > 0) 1884 return; 1885 1886 /* lower to CSSA before spilling to ensure correctness w.r.t. phis */ 1887 lower_to_cssa(program, live_vars); 1888 1889 /* calculate target register demand */ 1890 const RegisterDemand demand = program->max_reg_demand; /* current max */ 1891 const uint16_t sgpr_limit = get_addr_sgpr_from_waves(program, program->min_waves); 1892 const uint16_t vgpr_limit = get_addr_vgpr_from_waves(program, program->min_waves); 1893 uint16_t extra_vgprs = 0; 1894 uint16_t extra_sgprs = 0; 1895 1896 /* calculate extra VGPRs required for spilling SGPRs */ 1897 if (demand.sgpr > sgpr_limit) { 1898 unsigned sgpr_spills = demand.sgpr - sgpr_limit; 1899 extra_vgprs = DIV_ROUND_UP(sgpr_spills, program->wave_size) + 1; 1900 } 1901 /* add extra SGPRs required for spilling VGPRs */ 1902 if (demand.vgpr + extra_vgprs > vgpr_limit) { 1903 extra_sgprs = 5; /* scratch_resource (s4) + scratch_offset (s1) */ 1904 if (demand.sgpr + extra_sgprs > sgpr_limit) { 1905 /* re-calculate in case something has changed */ 1906 unsigned sgpr_spills = demand.sgpr + extra_sgprs - sgpr_limit; 1907 extra_vgprs = DIV_ROUND_UP(sgpr_spills, program->wave_size) + 1; 1908 } 1909 } 1910 /* the spiller has to target the following register demand */ 1911 const RegisterDemand target(vgpr_limit - extra_vgprs, sgpr_limit - extra_sgprs); 1912 1913 /* initialize ctx */ 1914 spill_ctx ctx(target, program, live_vars.register_demand); 1915 compute_global_next_uses(ctx); 1916 get_rematerialize_info(ctx); 1917 1918 /* create spills and reloads */ 1919 for (unsigned i = 0; i < program->blocks.size(); i++) 1920 spill_block(ctx, i); 1921 1922 /* assign spill slots and DCE rematerialized code */ 1923 assign_spill_slots(ctx, extra_vgprs); 1924 1925 /* update live variable information */ 1926 live_vars = live_var_analysis(program); 1927 1928 assert(program->num_waves > 0); 1929} 1930 1931} // namespace aco 1932