/src/spirv-tools/source/opt/register_pressure.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2018 Google LLC. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include "source/opt/register_pressure.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <iterator> |
19 | | |
20 | | #include "source/opt/cfg.h" |
21 | | #include "source/opt/def_use_manager.h" |
22 | | #include "source/opt/dominator_tree.h" |
23 | | #include "source/opt/function.h" |
24 | | #include "source/opt/ir_context.h" |
25 | | #include "source/opt/iterator.h" |
26 | | |
27 | | namespace spvtools { |
28 | | namespace opt { |
29 | | namespace { |
30 | | // Predicate for the FilterIterator to only consider instructions that are not |
31 | | // phi instructions defined in the basic block |bb|. |
32 | | class ExcludePhiDefinedInBlock { |
33 | | public: |
34 | | ExcludePhiDefinedInBlock(IRContext* context, const BasicBlock* bb) |
35 | 0 | : context_(context), bb_(bb) {} |
36 | | |
37 | 0 | bool operator()(Instruction* insn) const { |
38 | 0 | return !(insn->opcode() == spv::Op::OpPhi && |
39 | 0 | context_->get_instr_block(insn) == bb_); |
40 | 0 | } |
41 | | |
42 | | private: |
43 | | IRContext* context_; |
44 | | const BasicBlock* bb_; |
45 | | }; |
46 | | |
47 | | // Returns true if |insn| generates a SSA register that is likely to require a |
48 | | // physical register. |
49 | 0 | bool CreatesRegisterUsage(Instruction* insn) { |
50 | 0 | if (!insn->HasResultId()) return false; |
51 | 0 | if (insn->opcode() == spv::Op::OpUndef) return false; |
52 | 0 | if (IsConstantInst(insn->opcode())) return false; |
53 | 0 | if (insn->opcode() == spv::Op::OpLabel) return false; |
54 | 0 | return true; |
55 | 0 | } |
56 | | |
57 | | // Compute the register liveness for each basic block of a function. This also |
58 | | // fill-up some information about the pick register usage and a break down of |
59 | | // register usage. This implements: "A non-iterative data-flow algorithm for |
60 | | // computing liveness sets in strict ssa programs" from Boissinot et al. |
61 | | class ComputeRegisterLiveness { |
62 | | public: |
63 | | ComputeRegisterLiveness(RegisterLiveness* reg_pressure, Function* f) |
64 | | : reg_pressure_(reg_pressure), |
65 | | context_(reg_pressure->GetContext()), |
66 | | function_(f), |
67 | | cfg_(*reg_pressure->GetContext()->cfg()), |
68 | | def_use_manager_(*reg_pressure->GetContext()->get_def_use_mgr()), |
69 | | dom_tree_( |
70 | | reg_pressure->GetContext()->GetDominatorAnalysis(f)->GetDomTree()), |
71 | 0 | loop_desc_(*reg_pressure->GetContext()->GetLoopDescriptor(f)) {} |
72 | | |
73 | | // Computes the register liveness for |function_| and then estimate the |
74 | | // register usage. The liveness algorithm works in 2 steps: |
75 | | // - First, compute the liveness for each basic blocks, but will ignore any |
76 | | // back-edge; |
77 | | // - Second, walk loop forest to propagate registers crossing back-edges |
78 | | // (add iterative values into the liveness set). |
79 | 0 | void Compute() { |
80 | 0 | for (BasicBlock& start_bb : *function_) { |
81 | 0 | if (reg_pressure_->Get(start_bb.id()) != nullptr) { |
82 | 0 | continue; |
83 | 0 | } |
84 | 0 | cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) { |
85 | 0 | if (reg_pressure_->Get(bb->id()) == nullptr) { |
86 | 0 | ComputePartialLiveness(bb); |
87 | 0 | } |
88 | 0 | }); |
89 | 0 | } |
90 | 0 | DoLoopLivenessUnification(); |
91 | 0 | EvaluateRegisterRequirements(); |
92 | 0 | } |
93 | | |
94 | | private: |
95 | | // Registers all SSA register used by successors of |bb| in their phi |
96 | | // instructions. |
97 | | void ComputePhiUses(const BasicBlock& bb, |
98 | 0 | RegisterLiveness::RegionRegisterLiveness::LiveSet* live) { |
99 | 0 | uint32_t bb_id = bb.id(); |
100 | 0 | bb.ForEachSuccessorLabel([live, bb_id, this](uint32_t sid) { |
101 | 0 | BasicBlock* succ_bb = cfg_.block(sid); |
102 | 0 | succ_bb->ForEachPhiInst([live, bb_id, this](const Instruction* phi) { |
103 | 0 | for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { |
104 | 0 | if (phi->GetSingleWordInOperand(i + 1) == bb_id) { |
105 | 0 | Instruction* insn_op = |
106 | 0 | def_use_manager_.GetDef(phi->GetSingleWordInOperand(i)); |
107 | 0 | if (CreatesRegisterUsage(insn_op)) { |
108 | 0 | live->insert(insn_op); |
109 | 0 | break; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | } |
113 | 0 | }); |
114 | 0 | }); |
115 | 0 | } |
116 | | |
117 | | // Computes register liveness for each basic blocks but ignores all |
118 | | // back-edges. |
119 | 0 | void ComputePartialLiveness(BasicBlock* bb) { |
120 | 0 | assert(reg_pressure_->Get(bb) == nullptr && |
121 | 0 | "Basic block already processed"); |
122 | | |
123 | 0 | RegisterLiveness::RegionRegisterLiveness* live_inout = |
124 | 0 | reg_pressure_->GetOrInsert(bb->id()); |
125 | 0 | ComputePhiUses(*bb, &live_inout->live_out_); |
126 | |
|
127 | 0 | const BasicBlock* cbb = bb; |
128 | 0 | cbb->ForEachSuccessorLabel([&live_inout, bb, this](uint32_t sid) { |
129 | | // Skip back edges. |
130 | 0 | if (dom_tree_.Dominates(sid, bb->id())) { |
131 | 0 | return; |
132 | 0 | } |
133 | | |
134 | 0 | BasicBlock* succ_bb = cfg_.block(sid); |
135 | 0 | RegisterLiveness::RegionRegisterLiveness* succ_live_inout = |
136 | 0 | reg_pressure_->Get(succ_bb); |
137 | 0 | assert(succ_live_inout && |
138 | 0 | "Successor liveness analysis was not performed"); |
139 | | |
140 | 0 | ExcludePhiDefinedInBlock predicate(context_, succ_bb); |
141 | 0 | auto filter = |
142 | 0 | MakeFilterIteratorRange(succ_live_inout->live_in_.begin(), |
143 | 0 | succ_live_inout->live_in_.end(), predicate); |
144 | 0 | live_inout->live_out_.insert(filter.begin(), filter.end()); |
145 | 0 | }); |
146 | |
|
147 | 0 | live_inout->live_in_ = live_inout->live_out_; |
148 | 0 | for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { |
149 | 0 | if (insn.opcode() == spv::Op::OpPhi) { |
150 | 0 | live_inout->live_in_.insert(&insn); |
151 | 0 | break; |
152 | 0 | } |
153 | 0 | live_inout->live_in_.erase(&insn); |
154 | 0 | insn.ForEachInId([live_inout, this](uint32_t* id) { |
155 | 0 | Instruction* insn_op = def_use_manager_.GetDef(*id); |
156 | 0 | if (CreatesRegisterUsage(insn_op)) { |
157 | 0 | live_inout->live_in_.insert(insn_op); |
158 | 0 | } |
159 | 0 | }); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | // Propagates the register liveness information of each loop iterators. |
164 | 0 | void DoLoopLivenessUnification() { |
165 | 0 | for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) { |
166 | 0 | DoLoopLivenessUnification(*loop); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | // Propagates the register liveness information of loop iterators trough-out |
171 | | // the loop body. |
172 | 0 | void DoLoopLivenessUnification(const Loop& loop) { |
173 | 0 | auto blocks_in_loop = MakeFilterIteratorRange( |
174 | 0 | loop.GetBlocks().begin(), loop.GetBlocks().end(), |
175 | 0 | [&loop, this](uint32_t bb_id) { |
176 | 0 | return bb_id != loop.GetHeaderBlock()->id() && |
177 | 0 | loop_desc_[bb_id] == &loop; |
178 | 0 | }); |
179 | |
|
180 | 0 | RegisterLiveness::RegionRegisterLiveness* header_live_inout = |
181 | 0 | reg_pressure_->Get(loop.GetHeaderBlock()); |
182 | 0 | assert(header_live_inout && |
183 | 0 | "Liveness analysis was not performed for the current block"); |
184 | | |
185 | 0 | ExcludePhiDefinedInBlock predicate(context_, loop.GetHeaderBlock()); |
186 | 0 | auto live_loop = |
187 | 0 | MakeFilterIteratorRange(header_live_inout->live_in_.begin(), |
188 | 0 | header_live_inout->live_in_.end(), predicate); |
189 | |
|
190 | 0 | for (uint32_t bb_id : blocks_in_loop) { |
191 | 0 | BasicBlock* bb = cfg_.block(bb_id); |
192 | |
|
193 | 0 | RegisterLiveness::RegionRegisterLiveness* live_inout = |
194 | 0 | reg_pressure_->Get(bb); |
195 | 0 | live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); |
196 | 0 | live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); |
197 | 0 | } |
198 | |
|
199 | 0 | for (const Loop* inner_loop : loop) { |
200 | 0 | RegisterLiveness::RegionRegisterLiveness* live_inout = |
201 | 0 | reg_pressure_->Get(inner_loop->GetHeaderBlock()); |
202 | 0 | live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); |
203 | 0 | live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); |
204 | |
|
205 | 0 | DoLoopLivenessUnification(*inner_loop); |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | // Get the number of required registers for this each basic block. |
210 | 0 | void EvaluateRegisterRequirements() { |
211 | 0 | for (BasicBlock& bb : *function_) { |
212 | 0 | RegisterLiveness::RegionRegisterLiveness* live_inout = |
213 | 0 | reg_pressure_->Get(bb.id()); |
214 | 0 | assert(live_inout != nullptr && "Basic block not processed"); |
215 | | |
216 | 0 | size_t reg_count = live_inout->live_out_.size(); |
217 | 0 | for (Instruction* insn : live_inout->live_out_) { |
218 | 0 | live_inout->AddRegisterClass(insn); |
219 | 0 | } |
220 | 0 | live_inout->used_registers_ = reg_count; |
221 | |
|
222 | 0 | std::unordered_set<uint32_t> die_in_block; |
223 | 0 | for (Instruction& insn : make_range(bb.rbegin(), bb.rend())) { |
224 | | // If it is a phi instruction, the register pressure will not change |
225 | | // anymore. |
226 | 0 | if (insn.opcode() == spv::Op::OpPhi) { |
227 | 0 | break; |
228 | 0 | } |
229 | | |
230 | 0 | insn.ForEachInId( |
231 | 0 | [live_inout, &die_in_block, ®_count, this](uint32_t* id) { |
232 | 0 | Instruction* op_insn = def_use_manager_.GetDef(*id); |
233 | 0 | if (!CreatesRegisterUsage(op_insn) || |
234 | 0 | live_inout->live_out_.count(op_insn)) { |
235 | | // already taken into account. |
236 | 0 | return; |
237 | 0 | } |
238 | 0 | if (!die_in_block.count(*id)) { |
239 | 0 | live_inout->AddRegisterClass(def_use_manager_.GetDef(*id)); |
240 | 0 | reg_count++; |
241 | 0 | die_in_block.insert(*id); |
242 | 0 | } |
243 | 0 | }); |
244 | 0 | live_inout->used_registers_ = |
245 | 0 | std::max(live_inout->used_registers_, reg_count); |
246 | 0 | if (CreatesRegisterUsage(&insn)) { |
247 | 0 | reg_count--; |
248 | 0 | } |
249 | 0 | } |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | RegisterLiveness* reg_pressure_; |
254 | | IRContext* context_; |
255 | | Function* function_; |
256 | | CFG& cfg_; |
257 | | analysis::DefUseManager& def_use_manager_; |
258 | | DominatorTree& dom_tree_; |
259 | | LoopDescriptor& loop_desc_; |
260 | | }; |
261 | | } // namespace |
262 | | |
263 | | // Get the number of required registers for each basic block. |
264 | | void RegisterLiveness::RegionRegisterLiveness::AddRegisterClass( |
265 | 0 | Instruction* insn) { |
266 | 0 | assert(CreatesRegisterUsage(insn) && "Instruction does not use a register"); |
267 | 0 | analysis::Type* type = |
268 | 0 | insn->context()->get_type_mgr()->GetType(insn->type_id()); |
269 | |
|
270 | 0 | RegisterLiveness::RegisterClass reg_class{type, false}; |
271 | |
|
272 | 0 | insn->context()->get_decoration_mgr()->WhileEachDecoration( |
273 | 0 | insn->result_id(), uint32_t(spv::Decoration::Uniform), |
274 | 0 | [®_class](const Instruction&) { |
275 | 0 | reg_class.is_uniform_ = true; |
276 | 0 | return false; |
277 | 0 | }); |
278 | |
|
279 | 0 | AddRegisterClass(reg_class); |
280 | 0 | } |
281 | | |
282 | 0 | void RegisterLiveness::Analyze(Function* f) { |
283 | 0 | block_pressure_.clear(); |
284 | 0 | ComputeRegisterLiveness(this, f).Compute(); |
285 | 0 | } |
286 | | |
287 | | void RegisterLiveness::ComputeLoopRegisterPressure( |
288 | 0 | const Loop& loop, RegionRegisterLiveness* loop_reg_pressure) const { |
289 | 0 | loop_reg_pressure->Clear(); |
290 | |
|
291 | 0 | const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); |
292 | 0 | loop_reg_pressure->live_in_ = header_live_inout->live_in_; |
293 | |
|
294 | 0 | std::unordered_set<uint32_t> exit_blocks; |
295 | 0 | loop.GetExitBlocks(&exit_blocks); |
296 | |
|
297 | 0 | for (uint32_t bb_id : exit_blocks) { |
298 | 0 | const RegionRegisterLiveness* live_inout = Get(bb_id); |
299 | 0 | loop_reg_pressure->live_out_.insert(live_inout->live_in_.begin(), |
300 | 0 | live_inout->live_in_.end()); |
301 | 0 | } |
302 | |
|
303 | 0 | std::unordered_set<uint32_t> seen_insn; |
304 | 0 | for (Instruction* insn : loop_reg_pressure->live_out_) { |
305 | 0 | loop_reg_pressure->AddRegisterClass(insn); |
306 | 0 | seen_insn.insert(insn->result_id()); |
307 | 0 | } |
308 | 0 | for (Instruction* insn : loop_reg_pressure->live_in_) { |
309 | 0 | if (!seen_insn.count(insn->result_id())) { |
310 | 0 | continue; |
311 | 0 | } |
312 | 0 | loop_reg_pressure->AddRegisterClass(insn); |
313 | 0 | seen_insn.insert(insn->result_id()); |
314 | 0 | } |
315 | |
|
316 | 0 | loop_reg_pressure->used_registers_ = 0; |
317 | |
|
318 | 0 | for (uint32_t bb_id : loop.GetBlocks()) { |
319 | 0 | BasicBlock* bb = context_->cfg()->block(bb_id); |
320 | |
|
321 | 0 | const RegionRegisterLiveness* live_inout = Get(bb_id); |
322 | 0 | assert(live_inout != nullptr && "Basic block not processed"); |
323 | 0 | loop_reg_pressure->used_registers_ = std::max( |
324 | 0 | loop_reg_pressure->used_registers_, live_inout->used_registers_); |
325 | |
|
326 | 0 | for (Instruction& insn : *bb) { |
327 | 0 | if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || |
328 | 0 | seen_insn.count(insn.result_id())) { |
329 | 0 | continue; |
330 | 0 | } |
331 | 0 | loop_reg_pressure->AddRegisterClass(&insn); |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | void RegisterLiveness::SimulateFusion( |
337 | 0 | const Loop& l1, const Loop& l2, RegionRegisterLiveness* sim_result) const { |
338 | 0 | sim_result->Clear(); |
339 | | |
340 | | // Compute the live-in state: |
341 | | // sim_result.live_in = l1.live_in U l2.live_in |
342 | | // This assumes that |l1| does not generated register that is live-out for |
343 | | // |l1|. |
344 | 0 | const RegionRegisterLiveness* l1_header_live_inout = Get(l1.GetHeaderBlock()); |
345 | 0 | sim_result->live_in_ = l1_header_live_inout->live_in_; |
346 | |
|
347 | 0 | const RegionRegisterLiveness* l2_header_live_inout = Get(l2.GetHeaderBlock()); |
348 | 0 | sim_result->live_in_.insert(l2_header_live_inout->live_in_.begin(), |
349 | 0 | l2_header_live_inout->live_in_.end()); |
350 | | |
351 | | // The live-out set of the fused loop is the l2 live-out set. |
352 | 0 | std::unordered_set<uint32_t> exit_blocks; |
353 | 0 | l2.GetExitBlocks(&exit_blocks); |
354 | |
|
355 | 0 | for (uint32_t bb_id : exit_blocks) { |
356 | 0 | const RegionRegisterLiveness* live_inout = Get(bb_id); |
357 | 0 | sim_result->live_out_.insert(live_inout->live_in_.begin(), |
358 | 0 | live_inout->live_in_.end()); |
359 | 0 | } |
360 | | |
361 | | // Compute the register usage information. |
362 | 0 | std::unordered_set<uint32_t> seen_insn; |
363 | 0 | for (Instruction* insn : sim_result->live_out_) { |
364 | 0 | sim_result->AddRegisterClass(insn); |
365 | 0 | seen_insn.insert(insn->result_id()); |
366 | 0 | } |
367 | 0 | for (Instruction* insn : sim_result->live_in_) { |
368 | 0 | if (!seen_insn.count(insn->result_id())) { |
369 | 0 | continue; |
370 | 0 | } |
371 | 0 | sim_result->AddRegisterClass(insn); |
372 | 0 | seen_insn.insert(insn->result_id()); |
373 | 0 | } |
374 | |
|
375 | 0 | sim_result->used_registers_ = 0; |
376 | | |
377 | | // The loop fusion is injecting the l1 before the l2, the latch of l1 will be |
378 | | // connected to the header of l2. |
379 | | // To compute the register usage, we inject the loop live-in (union of l1 and |
380 | | // l2 live-in header blocks) into the live in/out of each basic block of |
381 | | // l1 to get the peak register usage. We then repeat the operation to for l2 |
382 | | // basic blocks but in this case we inject the live-out of the latch of l1. |
383 | 0 | auto live_loop = MakeFilterIteratorRange( |
384 | 0 | sim_result->live_in_.begin(), sim_result->live_in_.end(), |
385 | 0 | [&l1, &l2](Instruction* insn) { |
386 | 0 | BasicBlock* bb = insn->context()->get_instr_block(insn); |
387 | 0 | return insn->HasResultId() && |
388 | 0 | !(insn->opcode() == spv::Op::OpPhi && |
389 | 0 | (bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock())); |
390 | 0 | }); |
391 | |
|
392 | 0 | for (uint32_t bb_id : l1.GetBlocks()) { |
393 | 0 | BasicBlock* bb = context_->cfg()->block(bb_id); |
394 | |
|
395 | 0 | const RegionRegisterLiveness* live_inout_info = Get(bb_id); |
396 | 0 | assert(live_inout_info != nullptr && "Basic block not processed"); |
397 | 0 | RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; |
398 | 0 | live_out.insert(live_loop.begin(), live_loop.end()); |
399 | 0 | sim_result->used_registers_ = |
400 | 0 | std::max(sim_result->used_registers_, |
401 | 0 | live_inout_info->used_registers_ + live_out.size() - |
402 | 0 | live_inout_info->live_out_.size()); |
403 | |
|
404 | 0 | for (Instruction& insn : *bb) { |
405 | 0 | if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || |
406 | 0 | seen_insn.count(insn.result_id())) { |
407 | 0 | continue; |
408 | 0 | } |
409 | 0 | sim_result->AddRegisterClass(&insn); |
410 | 0 | } |
411 | 0 | } |
412 | |
|
413 | 0 | const RegionRegisterLiveness* l1_latch_live_inout_info = |
414 | 0 | Get(l1.GetLatchBlock()->id()); |
415 | 0 | assert(l1_latch_live_inout_info != nullptr && "Basic block not processed"); |
416 | 0 | RegionRegisterLiveness::LiveSet l1_latch_live_out = |
417 | 0 | l1_latch_live_inout_info->live_out_; |
418 | 0 | l1_latch_live_out.insert(live_loop.begin(), live_loop.end()); |
419 | |
|
420 | 0 | auto live_loop_l2 = |
421 | 0 | make_range(l1_latch_live_out.begin(), l1_latch_live_out.end()); |
422 | |
|
423 | 0 | for (uint32_t bb_id : l2.GetBlocks()) { |
424 | 0 | BasicBlock* bb = context_->cfg()->block(bb_id); |
425 | |
|
426 | 0 | const RegionRegisterLiveness* live_inout_info = Get(bb_id); |
427 | 0 | assert(live_inout_info != nullptr && "Basic block not processed"); |
428 | 0 | RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; |
429 | 0 | live_out.insert(live_loop_l2.begin(), live_loop_l2.end()); |
430 | 0 | sim_result->used_registers_ = |
431 | 0 | std::max(sim_result->used_registers_, |
432 | 0 | live_inout_info->used_registers_ + live_out.size() - |
433 | 0 | live_inout_info->live_out_.size()); |
434 | |
|
435 | 0 | for (Instruction& insn : *bb) { |
436 | 0 | if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || |
437 | 0 | seen_insn.count(insn.result_id())) { |
438 | 0 | continue; |
439 | 0 | } |
440 | 0 | sim_result->AddRegisterClass(&insn); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | void RegisterLiveness::SimulateFission( |
446 | | const Loop& loop, const std::unordered_set<Instruction*>& moved_inst, |
447 | | const std::unordered_set<Instruction*>& copied_inst, |
448 | | RegionRegisterLiveness* l1_sim_result, |
449 | 0 | RegionRegisterLiveness* l2_sim_result) const { |
450 | 0 | l1_sim_result->Clear(); |
451 | 0 | l2_sim_result->Clear(); |
452 | | |
453 | | // Filter predicates: consider instructions that only belong to the first and |
454 | | // second loop. |
455 | 0 | auto belong_to_loop1 = [&moved_inst, &copied_inst, &loop](Instruction* insn) { |
456 | 0 | return moved_inst.count(insn) || copied_inst.count(insn) || |
457 | 0 | !loop.IsInsideLoop(insn); |
458 | 0 | }; |
459 | 0 | auto belong_to_loop2 = [&moved_inst](Instruction* insn) { |
460 | 0 | return !moved_inst.count(insn); |
461 | 0 | }; |
462 | |
|
463 | 0 | const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); |
464 | | // l1 live-in |
465 | 0 | { |
466 | 0 | auto live_loop = MakeFilterIteratorRange( |
467 | 0 | header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), |
468 | 0 | belong_to_loop1); |
469 | 0 | l1_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); |
470 | 0 | } |
471 | | // l2 live-in |
472 | 0 | { |
473 | 0 | auto live_loop = MakeFilterIteratorRange( |
474 | 0 | header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), |
475 | 0 | belong_to_loop2); |
476 | 0 | l2_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); |
477 | 0 | } |
478 | |
|
479 | 0 | std::unordered_set<uint32_t> exit_blocks; |
480 | 0 | loop.GetExitBlocks(&exit_blocks); |
481 | | |
482 | | // l2 live-out. |
483 | 0 | for (uint32_t bb_id : exit_blocks) { |
484 | 0 | const RegionRegisterLiveness* live_inout = Get(bb_id); |
485 | 0 | l2_sim_result->live_out_.insert(live_inout->live_in_.begin(), |
486 | 0 | live_inout->live_in_.end()); |
487 | 0 | } |
488 | | // l1 live-out. |
489 | 0 | { |
490 | 0 | auto live_out = MakeFilterIteratorRange(l2_sim_result->live_out_.begin(), |
491 | 0 | l2_sim_result->live_out_.end(), |
492 | 0 | belong_to_loop1); |
493 | 0 | l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); |
494 | 0 | } |
495 | 0 | { |
496 | 0 | auto live_out = |
497 | 0 | MakeFilterIteratorRange(l2_sim_result->live_in_.begin(), |
498 | 0 | l2_sim_result->live_in_.end(), belong_to_loop1); |
499 | 0 | l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); |
500 | 0 | } |
501 | | // Lives out of l1 are live out of l2 so are live in of l2 as well. |
502 | 0 | l2_sim_result->live_in_.insert(l1_sim_result->live_out_.begin(), |
503 | 0 | l1_sim_result->live_out_.end()); |
504 | |
|
505 | 0 | for (Instruction* insn : l1_sim_result->live_in_) { |
506 | 0 | l1_sim_result->AddRegisterClass(insn); |
507 | 0 | } |
508 | 0 | for (Instruction* insn : l2_sim_result->live_in_) { |
509 | 0 | l2_sim_result->AddRegisterClass(insn); |
510 | 0 | } |
511 | |
|
512 | 0 | l1_sim_result->used_registers_ = 0; |
513 | 0 | l2_sim_result->used_registers_ = 0; |
514 | |
|
515 | 0 | for (uint32_t bb_id : loop.GetBlocks()) { |
516 | 0 | BasicBlock* bb = context_->cfg()->block(bb_id); |
517 | |
|
518 | 0 | const RegisterLiveness::RegionRegisterLiveness* live_inout = Get(bb_id); |
519 | 0 | assert(live_inout != nullptr && "Basic block not processed"); |
520 | 0 | auto l1_block_live_out = |
521 | 0 | MakeFilterIteratorRange(live_inout->live_out_.begin(), |
522 | 0 | live_inout->live_out_.end(), belong_to_loop1); |
523 | 0 | auto l2_block_live_out = |
524 | 0 | MakeFilterIteratorRange(live_inout->live_out_.begin(), |
525 | 0 | live_inout->live_out_.end(), belong_to_loop2); |
526 | |
|
527 | 0 | size_t l1_reg_count = |
528 | 0 | std::distance(l1_block_live_out.begin(), l1_block_live_out.end()); |
529 | 0 | size_t l2_reg_count = |
530 | 0 | std::distance(l2_block_live_out.begin(), l2_block_live_out.end()); |
531 | |
|
532 | 0 | std::unordered_set<uint32_t> die_in_block; |
533 | 0 | for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { |
534 | 0 | if (insn.opcode() == spv::Op::OpPhi) { |
535 | 0 | break; |
536 | 0 | } |
537 | | |
538 | 0 | bool does_belong_to_loop1 = belong_to_loop1(&insn); |
539 | 0 | bool does_belong_to_loop2 = belong_to_loop2(&insn); |
540 | 0 | insn.ForEachInId([live_inout, &die_in_block, &l1_reg_count, &l2_reg_count, |
541 | 0 | does_belong_to_loop1, does_belong_to_loop2, |
542 | 0 | this](uint32_t* id) { |
543 | 0 | Instruction* op_insn = context_->get_def_use_mgr()->GetDef(*id); |
544 | 0 | if (!CreatesRegisterUsage(op_insn) || |
545 | 0 | live_inout->live_out_.count(op_insn)) { |
546 | | // already taken into account. |
547 | 0 | return; |
548 | 0 | } |
549 | 0 | if (!die_in_block.count(*id)) { |
550 | 0 | if (does_belong_to_loop1) { |
551 | 0 | l1_reg_count++; |
552 | 0 | } |
553 | 0 | if (does_belong_to_loop2) { |
554 | 0 | l2_reg_count++; |
555 | 0 | } |
556 | 0 | die_in_block.insert(*id); |
557 | 0 | } |
558 | 0 | }); |
559 | 0 | l1_sim_result->used_registers_ = |
560 | 0 | std::max(l1_sim_result->used_registers_, l1_reg_count); |
561 | 0 | l2_sim_result->used_registers_ = |
562 | 0 | std::max(l2_sim_result->used_registers_, l2_reg_count); |
563 | 0 | if (CreatesRegisterUsage(&insn)) { |
564 | 0 | if (does_belong_to_loop1) { |
565 | 0 | if (!l1_sim_result->live_in_.count(&insn)) { |
566 | 0 | l1_sim_result->AddRegisterClass(&insn); |
567 | 0 | } |
568 | 0 | l1_reg_count--; |
569 | 0 | } |
570 | 0 | if (does_belong_to_loop2) { |
571 | 0 | if (!l2_sim_result->live_in_.count(&insn)) { |
572 | 0 | l2_sim_result->AddRegisterClass(&insn); |
573 | 0 | } |
574 | 0 | l2_reg_count--; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | } |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | | } // namespace opt |
582 | | } // namespace spvtools |