/src/spirv-tools/source/opt/loop_descriptor.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2017 Google Inc. |
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/loop_descriptor.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <limits> |
19 | | #include <stack> |
20 | | #include <utility> |
21 | | #include <vector> |
22 | | |
23 | | #include "source/opt/cfg.h" |
24 | | #include "source/opt/constants.h" |
25 | | #include "source/opt/dominator_tree.h" |
26 | | #include "source/opt/ir_context.h" |
27 | | #include "source/opt/iterator.h" |
28 | | #include "source/opt/tree_iterator.h" |
29 | | #include "source/util/make_unique.h" |
30 | | |
31 | | namespace spvtools { |
32 | | namespace opt { |
33 | | |
34 | | // Takes in a phi instruction |induction| and the loop |header| and returns the |
35 | | // step operation of the loop. |
36 | | Instruction* Loop::GetInductionStepOperation( |
37 | 22.9k | const Instruction* induction) const { |
38 | | // Induction must be a phi instruction. |
39 | 22.9k | assert(induction->opcode() == spv::Op::OpPhi); |
40 | | |
41 | 22.9k | Instruction* step = nullptr; |
42 | | |
43 | 22.9k | analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); |
44 | | |
45 | | // Traverse the incoming operands of the phi instruction. |
46 | 45.9k | for (uint32_t operand_id = 1; operand_id < induction->NumInOperands(); |
47 | 45.9k | operand_id += 2) { |
48 | | // Incoming edge. |
49 | 45.9k | BasicBlock* incoming_block = |
50 | 45.9k | context_->cfg()->block(induction->GetSingleWordInOperand(operand_id)); |
51 | | |
52 | | // Check if the block is dominated by header, and thus coming from within |
53 | | // the loop. |
54 | 45.9k | if (IsInsideLoop(incoming_block)) { |
55 | 22.9k | step = def_use_manager->GetDef( |
56 | 22.9k | induction->GetSingleWordInOperand(operand_id - 1)); |
57 | 22.9k | break; |
58 | 22.9k | } |
59 | 45.9k | } |
60 | | |
61 | 22.9k | if (!step || !IsSupportedStepOp(step->opcode())) { |
62 | 85 | return nullptr; |
63 | 85 | } |
64 | | |
65 | | // The induction variable which binds the loop must only be modified once. |
66 | 22.8k | uint32_t lhs = step->GetSingleWordInOperand(0); |
67 | 22.8k | uint32_t rhs = step->GetSingleWordInOperand(1); |
68 | | |
69 | | // One of the left hand side or right hand side of the step instruction must |
70 | | // be the induction phi and the other must be an OpConstant. |
71 | 22.8k | if (lhs != induction->result_id() && rhs != induction->result_id()) { |
72 | 89 | return nullptr; |
73 | 89 | } |
74 | | |
75 | 22.8k | if (def_use_manager->GetDef(lhs)->opcode() != spv::Op::OpConstant && |
76 | 22.8k | def_use_manager->GetDef(rhs)->opcode() != spv::Op::OpConstant) { |
77 | 9 | return nullptr; |
78 | 9 | } |
79 | | |
80 | 22.7k | return step; |
81 | 22.8k | } |
82 | | |
83 | | // Returns true if the |step| operation is an induction variable step operation |
84 | | // which is currently handled. |
85 | 22.9k | bool Loop::IsSupportedStepOp(spv::Op step) const { |
86 | 22.9k | switch (step) { |
87 | 7.93k | case spv::Op::OpISub: |
88 | 22.8k | case spv::Op::OpIAdd: |
89 | 22.8k | return true; |
90 | 85 | default: |
91 | 85 | return false; |
92 | 22.9k | } |
93 | 22.9k | } |
94 | | |
95 | 32.2k | bool Loop::IsSupportedCondition(spv::Op condition) const { |
96 | 32.2k | switch (condition) { |
97 | | // < |
98 | 2.02k | case spv::Op::OpULessThan: |
99 | 19.2k | case spv::Op::OpSLessThan: |
100 | | // > |
101 | 21.4k | case spv::Op::OpUGreaterThan: |
102 | 26.6k | case spv::Op::OpSGreaterThan: |
103 | | |
104 | | // >= |
105 | 29.8k | case spv::Op::OpSGreaterThanEqual: |
106 | 30.4k | case spv::Op::OpUGreaterThanEqual: |
107 | | // <= |
108 | 31.7k | case spv::Op::OpSLessThanEqual: |
109 | 32.0k | case spv::Op::OpULessThanEqual: |
110 | | |
111 | 32.0k | return true; |
112 | 196 | default: |
113 | 196 | return false; |
114 | 32.2k | } |
115 | 32.2k | } |
116 | | |
117 | | int64_t Loop::GetResidualConditionValue(spv::Op condition, |
118 | | int64_t initial_value, |
119 | | int64_t step_value, |
120 | | size_t number_of_iterations, |
121 | 0 | size_t factor) { |
122 | 0 | int64_t remainder = |
123 | 0 | initial_value + (number_of_iterations % factor) * step_value; |
124 | | |
125 | | // We subtract or add one as the above formula calculates the remainder if the |
126 | | // loop where just less than or greater than. Adding or subtracting one should |
127 | | // give a functionally equivalent value. |
128 | 0 | switch (condition) { |
129 | 0 | case spv::Op::OpSGreaterThanEqual: |
130 | 0 | case spv::Op::OpUGreaterThanEqual: { |
131 | 0 | remainder -= 1; |
132 | 0 | break; |
133 | 0 | } |
134 | 0 | case spv::Op::OpSLessThanEqual: |
135 | 0 | case spv::Op::OpULessThanEqual: { |
136 | 0 | remainder += 1; |
137 | 0 | break; |
138 | 0 | } |
139 | | |
140 | 0 | default: |
141 | 0 | break; |
142 | 0 | } |
143 | 0 | return remainder; |
144 | 0 | } |
145 | | |
146 | 0 | Instruction* Loop::GetConditionInst() const { |
147 | 0 | BasicBlock* condition_block = FindConditionBlock(); |
148 | 0 | if (!condition_block) { |
149 | 0 | return nullptr; |
150 | 0 | } |
151 | 0 | Instruction* branch_conditional = &*condition_block->tail(); |
152 | 0 | if (!branch_conditional || |
153 | 0 | branch_conditional->opcode() != spv::Op::OpBranchConditional) { |
154 | 0 | return nullptr; |
155 | 0 | } |
156 | 0 | Instruction* condition_inst = context_->get_def_use_mgr()->GetDef( |
157 | 0 | branch_conditional->GetSingleWordInOperand(0)); |
158 | 0 | if (IsSupportedCondition(condition_inst->opcode())) { |
159 | 0 | return condition_inst; |
160 | 0 | } |
161 | | |
162 | 0 | return nullptr; |
163 | 0 | } |
164 | | |
165 | | // Extract the initial value from the |induction| OpPhi instruction and store it |
166 | | // in |value|. If the function couldn't find the initial value of |induction| |
167 | | // return false. |
168 | | bool Loop::GetInductionInitValue(const Instruction* induction, |
169 | 22.7k | int64_t* value) const { |
170 | 22.7k | Instruction* constant_instruction = nullptr; |
171 | 22.7k | analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); |
172 | | |
173 | 68.3k | for (uint32_t operand_id = 0; operand_id < induction->NumInOperands(); |
174 | 45.5k | operand_id += 2) { |
175 | 45.5k | BasicBlock* bb = context_->cfg()->block( |
176 | 45.5k | induction->GetSingleWordInOperand(operand_id + 1)); |
177 | | |
178 | 45.5k | if (!IsInsideLoop(bb)) { |
179 | 22.7k | constant_instruction = def_use_manager->GetDef( |
180 | 22.7k | induction->GetSingleWordInOperand(operand_id)); |
181 | 22.7k | } |
182 | 45.5k | } |
183 | | |
184 | 22.7k | if (!constant_instruction) return false; |
185 | | |
186 | 22.7k | const analysis::Constant* constant = |
187 | 22.7k | context_->get_constant_mgr()->FindDeclaredConstant( |
188 | 22.7k | constant_instruction->result_id()); |
189 | 22.7k | if (!constant) return false; |
190 | | |
191 | 22.7k | if (value) { |
192 | 22.7k | const analysis::Integer* type = constant->type()->AsInteger(); |
193 | 22.7k | if (!type) { |
194 | 0 | return false; |
195 | 0 | } |
196 | | |
197 | 22.7k | *value = type->IsSigned() ? constant->GetSignExtendedValue() |
198 | 22.7k | : constant->GetZeroExtendedValue(); |
199 | 22.7k | } |
200 | | |
201 | 22.7k | return true; |
202 | 22.7k | } |
203 | | |
204 | | Loop::Loop(IRContext* context, DominatorAnalysis* dom_analysis, |
205 | | BasicBlock* header, BasicBlock* continue_target, |
206 | | BasicBlock* merge_target) |
207 | | : context_(context), |
208 | | loop_header_(header), |
209 | | loop_continue_(continue_target), |
210 | | loop_merge_(merge_target), |
211 | | loop_preheader_(nullptr), |
212 | | parent_(nullptr), |
213 | 18.9k | loop_is_marked_for_removal_(false) { |
214 | 18.9k | assert(context); |
215 | 18.9k | assert(dom_analysis); |
216 | 18.9k | loop_preheader_ = FindLoopPreheader(dom_analysis); |
217 | 18.9k | loop_latch_ = FindLatchBlock(); |
218 | 18.9k | } |
219 | | |
220 | 18.9k | BasicBlock* Loop::FindLoopPreheader(DominatorAnalysis* dom_analysis) { |
221 | 18.9k | CFG* cfg = context_->cfg(); |
222 | 18.9k | DominatorTree& dom_tree = dom_analysis->GetDomTree(); |
223 | 18.9k | DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_); |
224 | | |
225 | | // The loop predecessor. |
226 | 18.9k | BasicBlock* loop_pred = nullptr; |
227 | | |
228 | 18.9k | auto header_pred = cfg->preds(loop_header_->id()); |
229 | 38.0k | for (uint32_t p_id : header_pred) { |
230 | 38.0k | DominatorTreeNode* node = dom_tree.GetTreeNode(p_id); |
231 | 38.0k | if (node && !dom_tree.Dominates(header_node, node)) { |
232 | | // The predecessor is not part of the loop, so potential loop preheader. |
233 | 19.0k | if (loop_pred && node->bb_ != loop_pred) { |
234 | | // If we saw 2 distinct predecessors that are outside the loop, we don't |
235 | | // have a loop preheader. |
236 | 14 | return nullptr; |
237 | 14 | } |
238 | 18.9k | loop_pred = node->bb_; |
239 | 18.9k | } |
240 | 38.0k | } |
241 | | // Safe guard against invalid code, SPIR-V spec forbids loop with the entry |
242 | | // node as header. |
243 | 18.9k | assert(loop_pred && "The header node is the entry block ?"); |
244 | | |
245 | | // So we have a unique basic block that can enter this loop. |
246 | | // If this loop is the unique successor of this block, then it is a loop |
247 | | // preheader. |
248 | 18.9k | bool is_preheader = true; |
249 | 18.9k | uint32_t loop_header_id = loop_header_->id(); |
250 | 18.9k | const auto* const_loop_pred = loop_pred; |
251 | 18.9k | const_loop_pred->ForEachSuccessorLabel( |
252 | 18.9k | [&is_preheader, loop_header_id](const uint32_t id) { |
253 | 18.9k | if (id != loop_header_id) is_preheader = false; |
254 | 18.9k | }); |
255 | 18.9k | if (is_preheader) return loop_pred; |
256 | 1 | return nullptr; |
257 | 18.9k | } |
258 | | |
259 | 0 | bool Loop::IsInsideLoop(Instruction* inst) const { |
260 | 0 | const BasicBlock* parent_block = context_->get_instr_block(inst); |
261 | 0 | if (!parent_block) return false; |
262 | 0 | return IsInsideLoop(parent_block); |
263 | 0 | } |
264 | | |
265 | 0 | bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) { |
266 | 0 | assert(bb->GetParent() && "The basic block does not belong to a function"); |
267 | 0 | DominatorAnalysis* dom_analysis = |
268 | 0 | context_->GetDominatorAnalysis(bb->GetParent()); |
269 | 0 | if (dom_analysis->IsReachable(bb) && |
270 | 0 | !dom_analysis->Dominates(GetHeaderBlock(), bb)) |
271 | 0 | return false; |
272 | | |
273 | 0 | return true; |
274 | 0 | } |
275 | | |
276 | 0 | BasicBlock* Loop::GetOrCreatePreHeaderBlock() { |
277 | 0 | if (loop_preheader_) return loop_preheader_; |
278 | | |
279 | 0 | CFG* cfg = context_->cfg(); |
280 | 0 | loop_header_ = cfg->SplitLoopHeader(loop_header_); |
281 | 0 | return loop_preheader_; |
282 | 0 | } |
283 | | |
284 | 0 | void Loop::SetContinueBlock(BasicBlock* continue_block) { |
285 | 0 | assert(IsInsideLoop(continue_block)); |
286 | 0 | loop_continue_ = continue_block; |
287 | 0 | } |
288 | | |
289 | 0 | void Loop::SetLatchBlock(BasicBlock* latch) { |
290 | 0 | #ifndef NDEBUG |
291 | 0 | assert(latch->GetParent() && "The basic block does not belong to a function"); |
292 | | |
293 | 0 | const auto* const_latch = latch; |
294 | 0 | const_latch->ForEachSuccessorLabel([this](uint32_t id) { |
295 | 0 | assert((!IsInsideLoop(id) || id == GetHeaderBlock()->id()) && |
296 | 0 | "A predecessor of the continue block does not belong to the loop"); |
297 | 0 | }); |
298 | 0 | #endif // NDEBUG |
299 | 0 | assert(IsInsideLoop(latch) && "The continue block is not in the loop"); |
300 | | |
301 | 0 | SetLatchBlockImpl(latch); |
302 | 0 | } |
303 | | |
304 | 0 | void Loop::SetMergeBlock(BasicBlock* merge) { |
305 | 0 | #ifndef NDEBUG |
306 | 0 | assert(merge->GetParent() && "The basic block does not belong to a function"); |
307 | 0 | #endif // NDEBUG |
308 | 0 | assert(!IsInsideLoop(merge) && "The merge block is in the loop"); |
309 | | |
310 | 0 | SetMergeBlockImpl(merge); |
311 | 0 | if (GetHeaderBlock()->GetLoopMergeInst()) { |
312 | 0 | UpdateLoopMergeInst(); |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | 0 | void Loop::SetPreHeaderBlock(BasicBlock* preheader) { |
317 | 0 | if (preheader) { |
318 | 0 | assert(!IsInsideLoop(preheader) && "The preheader block is in the loop"); |
319 | 0 | assert(preheader->tail()->opcode() == spv::Op::OpBranch && |
320 | 0 | "The preheader block does not unconditionally branch to the header " |
321 | 0 | "block"); |
322 | 0 | assert(preheader->tail()->GetSingleWordOperand(0) == |
323 | 0 | GetHeaderBlock()->id() && |
324 | 0 | "The preheader block does not unconditionally branch to the header " |
325 | 0 | "block"); |
326 | 0 | } |
327 | 0 | loop_preheader_ = preheader; |
328 | 0 | } |
329 | | |
330 | 18.9k | BasicBlock* Loop::FindLatchBlock() { |
331 | 18.9k | CFG* cfg = context_->cfg(); |
332 | | |
333 | 18.9k | DominatorAnalysis* dominator_analysis = |
334 | 18.9k | context_->GetDominatorAnalysis(loop_header_->GetParent()); |
335 | | |
336 | | // Look at the predecessors of the loop header to find a predecessor block |
337 | | // which is dominated by the loop continue target. There should only be one |
338 | | // block which meets this criteria and this is the latch block, as per the |
339 | | // SPIR-V spec. |
340 | 38.0k | for (uint32_t block_id : cfg->preds(loop_header_->id())) { |
341 | 38.0k | if (dominator_analysis->Dominates(loop_continue_->id(), block_id)) { |
342 | 18.9k | return cfg->block(block_id); |
343 | 18.9k | } |
344 | 38.0k | } |
345 | | |
346 | 0 | assert( |
347 | 0 | false && |
348 | 0 | "Every loop should have a latch block dominated by the continue target"); |
349 | 0 | return nullptr; |
350 | 0 | } |
351 | | |
352 | 0 | void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const { |
353 | 0 | CFG* cfg = context_->cfg(); |
354 | 0 | exit_blocks->clear(); |
355 | |
|
356 | 0 | for (uint32_t bb_id : GetBlocks()) { |
357 | 0 | const BasicBlock* bb = cfg->block(bb_id); |
358 | 0 | bb->ForEachSuccessorLabel([exit_blocks, this](uint32_t succ) { |
359 | 0 | if (!IsInsideLoop(succ)) { |
360 | 0 | exit_blocks->insert(succ); |
361 | 0 | } |
362 | 0 | }); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | void Loop::GetMergingBlocks( |
367 | 0 | std::unordered_set<uint32_t>* merging_blocks) const { |
368 | 0 | assert(GetMergeBlock() && "This loop is not structured"); |
369 | 0 | CFG* cfg = context_->cfg(); |
370 | 0 | merging_blocks->clear(); |
371 | |
|
372 | 0 | std::stack<const BasicBlock*> to_visit; |
373 | 0 | to_visit.push(GetMergeBlock()); |
374 | 0 | while (!to_visit.empty()) { |
375 | 0 | const BasicBlock* bb = to_visit.top(); |
376 | 0 | to_visit.pop(); |
377 | 0 | merging_blocks->insert(bb->id()); |
378 | 0 | for (uint32_t pred_id : cfg->preds(bb->id())) { |
379 | 0 | if (!IsInsideLoop(pred_id) && !merging_blocks->count(pred_id)) { |
380 | 0 | to_visit.push(cfg->block(pred_id)); |
381 | 0 | } |
382 | 0 | } |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | namespace { |
387 | | |
388 | 0 | inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) { |
389 | 0 | for (Instruction& inst : *bb) { |
390 | 0 | if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst)) |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | 0 | return true; |
395 | 0 | } |
396 | | |
397 | | } // namespace |
398 | | |
399 | 0 | bool Loop::IsSafeToClone() const { |
400 | 0 | CFG& cfg = *context_->cfg(); |
401 | |
|
402 | 0 | for (uint32_t bb_id : GetBlocks()) { |
403 | 0 | BasicBlock* bb = cfg.block(bb_id); |
404 | 0 | assert(bb); |
405 | 0 | if (!IsBasicBlockSafeToClone(context_, bb)) return false; |
406 | 0 | } |
407 | | |
408 | | // Look at the merge construct. |
409 | 0 | if (GetHeaderBlock()->GetLoopMergeInst()) { |
410 | 0 | std::unordered_set<uint32_t> blocks; |
411 | 0 | GetMergingBlocks(&blocks); |
412 | 0 | blocks.erase(GetMergeBlock()->id()); |
413 | 0 | for (uint32_t bb_id : blocks) { |
414 | 0 | BasicBlock* bb = cfg.block(bb_id); |
415 | 0 | assert(bb); |
416 | 0 | if (!IsBasicBlockSafeToClone(context_, bb)) return false; |
417 | 0 | } |
418 | 0 | } |
419 | | |
420 | 0 | return true; |
421 | 0 | } |
422 | | |
423 | 0 | bool Loop::IsLCSSA() const { |
424 | 0 | CFG* cfg = context_->cfg(); |
425 | 0 | analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); |
426 | |
|
427 | 0 | std::unordered_set<uint32_t> exit_blocks; |
428 | 0 | GetExitBlocks(&exit_blocks); |
429 | | |
430 | | // Declare ir_context so we can capture context_ in the below lambda |
431 | 0 | IRContext* ir_context = context_; |
432 | |
|
433 | 0 | for (uint32_t bb_id : GetBlocks()) { |
434 | 0 | for (Instruction& insn : *cfg->block(bb_id)) { |
435 | | // All uses must be either: |
436 | | // - In the loop; |
437 | | // - In an exit block and in a phi instruction. |
438 | 0 | if (!def_use_mgr->WhileEachUser( |
439 | 0 | &insn, |
440 | 0 | [&exit_blocks, ir_context, this](Instruction* use) -> bool { |
441 | 0 | BasicBlock* parent = ir_context->get_instr_block(use); |
442 | 0 | assert(parent && "Invalid analysis"); |
443 | 0 | if (IsInsideLoop(parent)) return true; |
444 | 0 | if (use->opcode() != spv::Op::OpPhi) return false; |
445 | 0 | return exit_blocks.count(parent->id()); |
446 | 0 | })) |
447 | 0 | return false; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | return true; |
451 | 0 | } |
452 | | |
453 | 0 | bool Loop::ShouldHoistInstruction(const Instruction& inst) const { |
454 | 0 | return inst.IsOpcodeCodeMotionSafe() && AreAllOperandsOutsideLoop(inst) && |
455 | 0 | (!inst.IsLoad() || inst.IsReadOnlyLoad()); |
456 | 0 | } |
457 | | |
458 | 0 | bool Loop::AreAllOperandsOutsideLoop(const Instruction& inst) const { |
459 | 0 | analysis::DefUseManager* def_use_mgr = GetContext()->get_def_use_mgr(); |
460 | |
|
461 | 0 | const std::function<bool(const uint32_t*)> operand_outside_loop = |
462 | 0 | [this, &def_use_mgr](const uint32_t* id) { |
463 | 0 | return !this->IsInsideLoop(def_use_mgr->GetDef(*id)); |
464 | 0 | }; |
465 | |
|
466 | 0 | return inst.WhileEachInId(operand_outside_loop); |
467 | 0 | } |
468 | | |
469 | | void Loop::ComputeLoopStructuredOrder( |
470 | | std::vector<BasicBlock*>* ordered_loop_blocks, bool include_pre_header, |
471 | 2.74k | bool include_merge) const { |
472 | 2.74k | CFG& cfg = *context_->cfg(); |
473 | | |
474 | | // Reserve the memory: all blocks in the loop + extra if needed. |
475 | 2.74k | ordered_loop_blocks->reserve(GetBlocks().size() + include_pre_header + |
476 | 2.74k | include_merge); |
477 | | |
478 | 2.74k | if (include_pre_header && GetPreHeaderBlock()) |
479 | 0 | ordered_loop_blocks->push_back(loop_preheader_); |
480 | | |
481 | 2.74k | bool is_shader = |
482 | 2.74k | context_->get_feature_mgr()->HasCapability(spv::Capability::Shader); |
483 | 2.74k | if (!is_shader) { |
484 | 0 | cfg.ForEachBlockInReversePostOrder( |
485 | 0 | loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) { |
486 | 0 | if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb); |
487 | 0 | }); |
488 | 2.74k | } else { |
489 | | // If this is a shader, it is possible that there are unreachable merge and |
490 | | // continue blocks that must be copied to retain the structured order. |
491 | | // The structured order will include these. |
492 | 2.74k | std::list<BasicBlock*> order; |
493 | 2.74k | cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, |
494 | 2.74k | loop_merge_, &order); |
495 | 88.4k | for (BasicBlock* bb : order) { |
496 | 88.4k | if (bb == GetMergeBlock()) { |
497 | 2.74k | break; |
498 | 2.74k | } |
499 | 85.7k | ordered_loop_blocks->push_back(bb); |
500 | 85.7k | } |
501 | 2.74k | } |
502 | 2.74k | if (include_merge && GetMergeBlock()) |
503 | 0 | ordered_loop_blocks->push_back(loop_merge_); |
504 | 2.74k | } |
505 | | |
506 | | LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f) |
507 | 9.89k | : loops_(), placeholder_top_loop_(nullptr) { |
508 | 9.89k | PopulateList(context, f); |
509 | 9.89k | } |
510 | | |
511 | 29.6k | LoopDescriptor::~LoopDescriptor() { ClearLoops(); } |
512 | | |
513 | 9.89k | void LoopDescriptor::PopulateList(IRContext* context, const Function* f) { |
514 | 9.89k | DominatorAnalysis* dom_analysis = context->GetDominatorAnalysis(f); |
515 | | |
516 | 9.89k | ClearLoops(); |
517 | | |
518 | | // Post-order traversal of the dominator tree to find all the OpLoopMerge |
519 | | // instructions. |
520 | 9.89k | DominatorTree& dom_tree = dom_analysis->GetDomTree(); |
521 | 9.89k | for (DominatorTreeNode& node : |
522 | 219k | make_range(dom_tree.post_begin(), dom_tree.post_end())) { |
523 | 219k | Instruction* merge_inst = node.bb_->GetLoopMergeInst(); |
524 | 219k | if (merge_inst) { |
525 | 22.4k | bool all_backedge_unreachable = true; |
526 | 44.9k | for (uint32_t pid : context->cfg()->preds(node.bb_->id())) { |
527 | 44.9k | if (dom_analysis->IsReachable(pid) && |
528 | 44.9k | dom_analysis->Dominates(node.bb_->id(), pid)) { |
529 | 18.9k | all_backedge_unreachable = false; |
530 | 18.9k | break; |
531 | 18.9k | } |
532 | 44.9k | } |
533 | 22.4k | if (all_backedge_unreachable) |
534 | 3.49k | continue; // ignore this one, we actually never branch back. |
535 | | |
536 | | // The id of the merge basic block of this loop. |
537 | 18.9k | uint32_t merge_bb_id = merge_inst->GetSingleWordOperand(0); |
538 | | |
539 | | // The id of the continue basic block of this loop. |
540 | 18.9k | uint32_t continue_bb_id = merge_inst->GetSingleWordOperand(1); |
541 | | |
542 | | // The merge target of this loop. |
543 | 18.9k | BasicBlock* merge_bb = context->cfg()->block(merge_bb_id); |
544 | | |
545 | | // The continue target of this loop. |
546 | 18.9k | BasicBlock* continue_bb = context->cfg()->block(continue_bb_id); |
547 | | |
548 | | // The basic block containing the merge instruction. |
549 | 18.9k | BasicBlock* header_bb = context->get_instr_block(merge_inst); |
550 | | |
551 | | // Add the loop to the list of all the loops in the function. |
552 | 18.9k | Loop* current_loop = |
553 | 18.9k | new Loop(context, dom_analysis, header_bb, continue_bb, merge_bb); |
554 | 18.9k | loops_.push_back(current_loop); |
555 | | |
556 | | // We have a bottom-up construction, so if this loop has nested-loops, |
557 | | // they are by construction at the tail of the loop list. |
558 | 98.0k | for (auto itr = loops_.rbegin() + 1; itr != loops_.rend(); ++itr) { |
559 | 79.0k | Loop* previous_loop = *itr; |
560 | | |
561 | | // If the loop already has a parent, then it has been processed. |
562 | 79.0k | if (previous_loop->HasParent()) continue; |
563 | | |
564 | | // If the current loop does not dominates the previous loop then it is |
565 | | // not nested loop. |
566 | 68.7k | if (!dom_analysis->Dominates(header_bb, |
567 | 68.7k | previous_loop->GetHeaderBlock())) |
568 | 19.6k | continue; |
569 | | // If the current loop merge dominates the previous loop then it is |
570 | | // not nested loop. |
571 | 49.1k | if (dom_analysis->Dominates(merge_bb, previous_loop->GetHeaderBlock())) |
572 | 46.1k | continue; |
573 | | |
574 | 2.96k | current_loop->AddNestedLoop(previous_loop); |
575 | 2.96k | } |
576 | 18.9k | DominatorTreeNode* dom_merge_node = dom_tree.GetTreeNode(merge_bb); |
577 | 18.9k | for (DominatorTreeNode& loop_node : |
578 | 805k | make_range(node.df_begin(), node.df_end())) { |
579 | | // Check if we are in the loop. |
580 | 805k | if (dom_tree.Dominates(dom_merge_node, &loop_node)) continue; |
581 | 131k | current_loop->AddBasicBlock(loop_node.bb_); |
582 | 131k | basic_block_to_loop_.insert( |
583 | 131k | std::make_pair(loop_node.bb_->id(), current_loop)); |
584 | 131k | } |
585 | 18.9k | } |
586 | 219k | } |
587 | 18.9k | for (Loop* loop : loops_) { |
588 | 18.9k | if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); |
589 | 18.9k | } |
590 | 9.89k | } |
591 | | |
592 | 0 | std::vector<Loop*> LoopDescriptor::GetLoopsInBinaryLayoutOrder() { |
593 | 0 | std::vector<uint32_t> ids{}; |
594 | |
|
595 | 0 | for (size_t i = 0; i < NumLoops(); ++i) { |
596 | 0 | ids.push_back(GetLoopByIndex(i).GetHeaderBlock()->id()); |
597 | 0 | } |
598 | |
|
599 | 0 | std::vector<Loop*> loops{}; |
600 | 0 | if (!ids.empty()) { |
601 | 0 | auto function = GetLoopByIndex(0).GetHeaderBlock()->GetParent(); |
602 | 0 | for (const auto& block : *function) { |
603 | 0 | auto block_id = block.id(); |
604 | |
|
605 | 0 | auto element = std::find(std::begin(ids), std::end(ids), block_id); |
606 | 0 | if (element != std::end(ids)) { |
607 | 0 | loops.push_back(&GetLoopByIndex(element - std::begin(ids))); |
608 | 0 | } |
609 | 0 | } |
610 | 0 | } |
611 | |
|
612 | 0 | return loops; |
613 | 0 | } |
614 | | |
615 | 9.82k | BasicBlock* Loop::FindConditionBlock() const { |
616 | 9.82k | if (!loop_merge_) { |
617 | 0 | return nullptr; |
618 | 0 | } |
619 | 9.82k | BasicBlock* condition_block = nullptr; |
620 | | |
621 | 9.82k | uint32_t in_loop_pred = 0; |
622 | 10.1k | for (uint32_t p : context_->cfg()->preds(loop_merge_->id())) { |
623 | 10.1k | if (IsInsideLoop(p)) { |
624 | 9.99k | if (in_loop_pred) { |
625 | | // 2 in-loop predecessors. |
626 | 394 | return nullptr; |
627 | 394 | } |
628 | 9.60k | in_loop_pred = p; |
629 | 9.60k | } |
630 | 10.1k | } |
631 | 9.42k | if (!in_loop_pred) { |
632 | | // Merge block is unreachable. |
633 | 218 | return nullptr; |
634 | 218 | } |
635 | | |
636 | 9.20k | BasicBlock* bb = context_->cfg()->block(in_loop_pred); |
637 | | |
638 | 9.20k | if (!bb) return nullptr; |
639 | | |
640 | 9.20k | const Instruction& branch = *bb->ctail(); |
641 | | |
642 | | // Make sure the branch is a conditional branch. |
643 | 9.20k | if (branch.opcode() != spv::Op::OpBranchConditional) return nullptr; |
644 | | |
645 | | // Make sure one of the two possible branches is to the merge block. |
646 | 9.18k | if (branch.GetSingleWordInOperand(1) == loop_merge_->id() || |
647 | 9.18k | branch.GetSingleWordInOperand(2) == loop_merge_->id()) { |
648 | 9.18k | condition_block = bb; |
649 | 9.18k | } |
650 | | |
651 | 9.18k | return condition_block; |
652 | 9.20k | } |
653 | | |
654 | | bool Loop::FindNumberOfIterations(const Instruction* induction, |
655 | | const Instruction* branch_inst, |
656 | | size_t* iterations_out, |
657 | | int64_t* step_value_out, |
658 | 23.0k | int64_t* init_value_out) const { |
659 | | // From the branch instruction find the branch condition. |
660 | 23.0k | analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); |
661 | | |
662 | | // Condition instruction from the OpConditionalBranch. |
663 | 23.0k | Instruction* condition = |
664 | 23.0k | def_use_manager->GetDef(branch_inst->GetSingleWordOperand(0)); |
665 | | |
666 | 23.0k | assert(IsSupportedCondition(condition->opcode())); |
667 | | |
668 | | // Get the constant manager from the ir context. |
669 | 23.0k | analysis::ConstantManager* const_manager = context_->get_constant_mgr(); |
670 | | |
671 | | // Find the constant value used by the condition variable. Exit out if it |
672 | | // isn't a constant int. |
673 | 23.0k | const analysis::Constant* upper_bound = |
674 | 23.0k | const_manager->FindDeclaredConstant(condition->GetSingleWordOperand(3)); |
675 | 23.0k | if (!upper_bound) return false; |
676 | | |
677 | | // Must be integer because of the opcode on the condition. |
678 | 22.9k | const analysis::Integer* type = upper_bound->type()->AsInteger(); |
679 | | |
680 | 22.9k | if (!type || type->width() > 64) { |
681 | 0 | return false; |
682 | 0 | } |
683 | | |
684 | 22.9k | int64_t condition_value = type->IsSigned() |
685 | 22.9k | ? upper_bound->GetSignExtendedValue() |
686 | 22.9k | : upper_bound->GetZeroExtendedValue(); |
687 | | |
688 | | // Find the instruction which is stepping through the loop. |
689 | | // |
690 | | // GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull. |
691 | 22.9k | Instruction* step_inst = GetInductionStepOperation(induction); |
692 | 22.9k | if (!step_inst) return false; |
693 | | |
694 | | // Find the constant value used by the condition variable. |
695 | 22.7k | const analysis::Constant* step_constant = |
696 | 22.7k | const_manager->FindDeclaredConstant(step_inst->GetSingleWordOperand(3)); |
697 | 22.7k | if (!step_constant) return false; |
698 | | |
699 | | // Must be integer because of the opcode on the condition. |
700 | 22.7k | int64_t step_value = 0; |
701 | | |
702 | 22.7k | const analysis::Integer* step_type = |
703 | 22.7k | step_constant->AsIntConstant()->type()->AsInteger(); |
704 | | |
705 | 22.7k | if (step_type->IsSigned()) { |
706 | 20.8k | step_value = step_constant->AsIntConstant()->GetS32BitValue(); |
707 | 20.8k | } else { |
708 | 1.94k | step_value = step_constant->AsIntConstant()->GetU32BitValue(); |
709 | 1.94k | } |
710 | | |
711 | | // If this is a subtraction step we should negate the step value. |
712 | 22.7k | if (step_inst->opcode() == spv::Op::OpISub) { |
713 | 7.91k | step_value = -step_value; |
714 | 7.91k | } |
715 | | |
716 | | // Find the initial value of the loop and make sure it is a constant integer. |
717 | 22.7k | int64_t init_value = 0; |
718 | 22.7k | if (!GetInductionInitValue(induction, &init_value)) return false; |
719 | | |
720 | | // If iterations is non null then store the value in that. |
721 | 22.7k | int64_t num_itrs = GetIterations(condition->opcode(), condition_value, |
722 | 22.7k | init_value, step_value); |
723 | | |
724 | | // If the loop body will not be reached return false. |
725 | 22.7k | if (num_itrs <= 0) { |
726 | 110 | return false; |
727 | 110 | } |
728 | | |
729 | 22.6k | if (iterations_out) { |
730 | 8.45k | assert(static_cast<size_t>(num_itrs) <= std::numeric_limits<size_t>::max()); |
731 | 8.45k | *iterations_out = static_cast<size_t>(num_itrs); |
732 | 8.45k | } |
733 | | |
734 | 22.6k | if (step_value_out) { |
735 | 2.74k | *step_value_out = step_value; |
736 | 2.74k | } |
737 | | |
738 | 22.6k | if (init_value_out) { |
739 | 2.74k | *init_value_out = init_value; |
740 | 2.74k | } |
741 | | |
742 | 22.6k | return true; |
743 | 22.6k | } |
744 | | |
745 | | // We retrieve the number of iterations using the following formula, diff / |
746 | | // |step_value| where diff is calculated differently according to the |
747 | | // |condition| and uses the |condition_value| and |init_value|. If diff / |
748 | | // |step_value| is NOT cleanly divisible then we add one to the sum. |
749 | | int64_t Loop::GetIterations(spv::Op condition, int64_t condition_value, |
750 | 22.7k | int64_t init_value, int64_t step_value) const { |
751 | 22.7k | if (step_value == 0) { |
752 | 4 | return 0; |
753 | 4 | } |
754 | | |
755 | 22.7k | int64_t diff = 0; |
756 | | |
757 | 22.7k | switch (condition) { |
758 | 12.2k | case spv::Op::OpSLessThan: |
759 | 13.6k | case spv::Op::OpULessThan: { |
760 | | // If the condition is not met to begin with the loop will never iterate. |
761 | 13.6k | if (!(init_value < condition_value)) return 0; |
762 | | |
763 | 13.6k | diff = condition_value - init_value; |
764 | | |
765 | | // If the operation is a less then operation then the diff and step must |
766 | | // have the same sign otherwise the induction will never cross the |
767 | | // condition (either never true or always true). |
768 | 13.6k | if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { |
769 | 37 | return 0; |
770 | 37 | } |
771 | | |
772 | 13.5k | break; |
773 | 13.6k | } |
774 | 13.5k | case spv::Op::OpSGreaterThan: |
775 | 5.28k | case spv::Op::OpUGreaterThan: { |
776 | | // If the condition is not met to begin with the loop will never iterate. |
777 | 5.28k | if (!(init_value > condition_value)) return 0; |
778 | | |
779 | 5.27k | diff = init_value - condition_value; |
780 | | |
781 | | // If the operation is a greater than operation then the diff and step |
782 | | // must have opposite signs. Otherwise the condition will always be true |
783 | | // or will never be true. |
784 | 5.27k | if ((diff < 0 && step_value < 0) || (diff > 0 && step_value > 0)) { |
785 | 11 | return 0; |
786 | 11 | } |
787 | | |
788 | 5.26k | break; |
789 | 5.27k | } |
790 | | |
791 | 5.26k | case spv::Op::OpSGreaterThanEqual: |
792 | 2.71k | case spv::Op::OpUGreaterThanEqual: { |
793 | | // If the condition is not met to begin with the loop will never iterate. |
794 | 2.71k | if (!(init_value >= condition_value)) return 0; |
795 | | |
796 | | // We subtract one to make it the same as spv::Op::OpGreaterThan as it is |
797 | | // functionally equivalent. |
798 | 2.70k | diff = init_value - (condition_value - 1); |
799 | | |
800 | | // If the operation is a greater than operation then the diff and step |
801 | | // must have opposite signs. Otherwise the condition will always be true |
802 | | // or will never be true. |
803 | 2.70k | if ((diff > 0 && step_value > 0) || (diff < 0 && step_value < 0)) { |
804 | 7 | return 0; |
805 | 7 | } |
806 | | |
807 | 2.70k | break; |
808 | 2.70k | } |
809 | | |
810 | 2.70k | case spv::Op::OpSLessThanEqual: |
811 | 1.08k | case spv::Op::OpULessThanEqual: { |
812 | | // If the condition is not met to begin with the loop will never iterate. |
813 | 1.08k | if (!(init_value <= condition_value)) return 0; |
814 | | |
815 | | // We add one to make it the same as spv::Op::OpLessThan as it is |
816 | | // functionally equivalent. |
817 | 1.08k | diff = (condition_value + 1) - init_value; |
818 | | |
819 | | // If the operation is a less than operation then the diff and step must |
820 | | // have the same sign otherwise the induction will never cross the |
821 | | // condition (either never true or always true). |
822 | 1.08k | if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { |
823 | 10 | return 0; |
824 | 10 | } |
825 | | |
826 | 1.07k | break; |
827 | 1.08k | } |
828 | | |
829 | 1.07k | default: |
830 | 0 | assert(false && |
831 | 22.7k | "Could not retrieve number of iterations from the loop condition. " |
832 | 22.7k | "Condition is not supported."); |
833 | 22.7k | } |
834 | | |
835 | | // Take the abs of - step values. |
836 | 22.6k | step_value = llabs(step_value); |
837 | 22.6k | diff = llabs(diff); |
838 | 22.6k | int64_t result = diff / step_value; |
839 | | |
840 | 22.6k | if (diff % step_value != 0) { |
841 | 13.3k | result += 1; |
842 | 13.3k | } |
843 | 22.6k | return result; |
844 | 22.7k | } |
845 | | |
846 | | // Returns the list of induction variables within the loop. |
847 | | void Loop::GetInductionVariables( |
848 | 129k | std::vector<Instruction*>& induction_variables) const { |
849 | 541k | for (Instruction& inst : *loop_header_) { |
850 | 541k | if (inst.opcode() == spv::Op::OpPhi) { |
851 | 212k | induction_variables.push_back(&inst); |
852 | 212k | } |
853 | 541k | } |
854 | 129k | } |
855 | | |
856 | | Instruction* Loop::FindConditionVariable( |
857 | 9.18k | const BasicBlock* condition_block) const { |
858 | | // Find the branch instruction. |
859 | 9.18k | const Instruction& branch_inst = *condition_block->ctail(); |
860 | | |
861 | 9.18k | Instruction* induction = nullptr; |
862 | | // Verify that the branch instruction is a conditional branch. |
863 | 9.18k | if (branch_inst.opcode() == spv::Op::OpBranchConditional) { |
864 | | // From the branch instruction find the branch condition. |
865 | 9.18k | analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); |
866 | | |
867 | | // Find the instruction representing the condition used in the conditional |
868 | | // branch. |
869 | 9.18k | Instruction* condition = |
870 | 9.18k | def_use_manager->GetDef(branch_inst.GetSingleWordOperand(0)); |
871 | | |
872 | | // Ensure that the condition is a less than operation. |
873 | 9.18k | if (condition && IsSupportedCondition(condition->opcode())) { |
874 | | // The left hand side operand of the operation. |
875 | 8.98k | Instruction* variable_inst = |
876 | 8.98k | def_use_manager->GetDef(condition->GetSingleWordOperand(2)); |
877 | | |
878 | | // Make sure the variable instruction used is a phi. |
879 | 8.98k | if (!variable_inst || variable_inst->opcode() != spv::Op::OpPhi) |
880 | 54 | return nullptr; |
881 | | |
882 | | // Make sure the phi instruction only has two incoming blocks. Each |
883 | | // incoming block will be represented by two in operands in the phi |
884 | | // instruction, the value and the block which that value came from. We |
885 | | // assume the cannocalised phi will have two incoming values, one from the |
886 | | // preheader and one from the continue block. |
887 | 8.93k | size_t max_supported_operands = 4; |
888 | 8.93k | if (variable_inst->NumInOperands() == max_supported_operands) { |
889 | | // The operand index of the first incoming block label. |
890 | 8.93k | uint32_t operand_label_1 = 1; |
891 | | |
892 | | // The operand index of the second incoming block label. |
893 | 8.93k | uint32_t operand_label_2 = 3; |
894 | | |
895 | | // Make sure one of them is the preheader. |
896 | 8.93k | if (!IsInsideLoop( |
897 | 8.93k | variable_inst->GetSingleWordInOperand(operand_label_1)) && |
898 | 8.93k | !IsInsideLoop( |
899 | 8.93k | variable_inst->GetSingleWordInOperand(operand_label_2))) { |
900 | 55 | return nullptr; |
901 | 55 | } |
902 | | |
903 | | // And make sure that the other is the latch block. |
904 | 8.87k | if (variable_inst->GetSingleWordInOperand(operand_label_1) != |
905 | 8.87k | loop_latch_->id() && |
906 | 8.87k | variable_inst->GetSingleWordInOperand(operand_label_2) != |
907 | 8.87k | loop_latch_->id()) { |
908 | 0 | return nullptr; |
909 | 0 | } |
910 | 8.87k | } else { |
911 | 0 | return nullptr; |
912 | 0 | } |
913 | | |
914 | 8.87k | if (!FindNumberOfIterations(variable_inst, &branch_inst, nullptr)) |
915 | 424 | return nullptr; |
916 | 8.45k | induction = variable_inst; |
917 | 8.45k | } |
918 | 9.18k | } |
919 | | |
920 | 8.65k | return induction; |
921 | 9.18k | } |
922 | | |
923 | 0 | bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() { |
924 | 0 | auto modified = false; |
925 | |
|
926 | 0 | for (auto& loop : *this) { |
927 | 0 | if (!loop.GetPreHeaderBlock()) { |
928 | 0 | modified = true; |
929 | | // TODO(1841): Handle failure to create pre-header. |
930 | 0 | loop.GetOrCreatePreHeaderBlock(); |
931 | 0 | } |
932 | 0 | } |
933 | |
|
934 | 0 | return modified; |
935 | 0 | } |
936 | | |
937 | | // Add and remove loops which have been marked for addition and removal to |
938 | | // maintain the state of the loop descriptor class. |
939 | 9.89k | void LoopDescriptor::PostModificationCleanup() { |
940 | 9.89k | LoopContainerType loops_to_remove_; |
941 | 18.9k | for (Loop* loop : loops_) { |
942 | 18.9k | if (loop->IsMarkedForRemoval()) { |
943 | 2.74k | loops_to_remove_.push_back(loop); |
944 | 2.74k | if (loop->HasParent()) { |
945 | 789 | loop->GetParent()->RemoveChildLoop(loop); |
946 | 789 | } |
947 | 2.74k | } |
948 | 18.9k | } |
949 | | |
950 | 9.89k | for (Loop* loop : loops_to_remove_) { |
951 | 2.74k | loops_.erase(std::find(loops_.begin(), loops_.end(), loop)); |
952 | 2.74k | delete loop; |
953 | 2.74k | } |
954 | | |
955 | 9.89k | for (auto& pair : loops_to_add_) { |
956 | 0 | Loop* parent = pair.first; |
957 | 0 | std::unique_ptr<Loop> loop = std::move(pair.second); |
958 | |
|
959 | 0 | if (parent) { |
960 | 0 | loop->SetParent(nullptr); |
961 | 0 | parent->AddNestedLoop(loop.get()); |
962 | |
|
963 | 0 | for (uint32_t block_id : loop->GetBlocks()) { |
964 | 0 | parent->AddBasicBlock(block_id); |
965 | 0 | } |
966 | 0 | } |
967 | |
|
968 | 0 | loops_.emplace_back(loop.release()); |
969 | 0 | } |
970 | | |
971 | 9.89k | loops_to_add_.clear(); |
972 | 9.89k | } |
973 | | |
974 | 39.5k | void LoopDescriptor::ClearLoops() { |
975 | 39.5k | for (Loop* loop : loops_) { |
976 | 16.2k | delete loop; |
977 | 16.2k | } |
978 | 39.5k | loops_.clear(); |
979 | 39.5k | } |
980 | | |
981 | | // Adds a new loop nest to the descriptor set. |
982 | 0 | Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) { |
983 | 0 | Loop* loop = new_loop.release(); |
984 | 0 | if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); |
985 | | // Iterate from inner to outer most loop, adding basic block to loop mapping |
986 | | // as we go. |
987 | 0 | for (Loop& current_loop : |
988 | 0 | make_range(iterator::begin(loop), iterator::end(nullptr))) { |
989 | 0 | loops_.push_back(¤t_loop); |
990 | 0 | for (uint32_t bb_id : current_loop.GetBlocks()) |
991 | 0 | basic_block_to_loop_.insert(std::make_pair(bb_id, ¤t_loop)); |
992 | 0 | } |
993 | |
|
994 | 0 | return loop; |
995 | 0 | } |
996 | | |
997 | 0 | void LoopDescriptor::RemoveLoop(Loop* loop) { |
998 | 0 | Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_; |
999 | 0 | parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(), |
1000 | 0 | parent->nested_loops_.end(), loop)); |
1001 | 0 | std::for_each( |
1002 | 0 | loop->nested_loops_.begin(), loop->nested_loops_.end(), |
1003 | 0 | [loop](Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); }); |
1004 | 0 | parent->nested_loops_.insert(parent->nested_loops_.end(), |
1005 | 0 | loop->nested_loops_.begin(), |
1006 | 0 | loop->nested_loops_.end()); |
1007 | 0 | for (uint32_t bb_id : loop->GetBlocks()) { |
1008 | 0 | Loop* l = FindLoopForBasicBlock(bb_id); |
1009 | 0 | if (l == loop) { |
1010 | 0 | SetBasicBlockToLoop(bb_id, l->GetParent()); |
1011 | 0 | } else { |
1012 | 0 | ForgetBasicBlock(bb_id); |
1013 | 0 | } |
1014 | 0 | } |
1015 | |
|
1016 | 0 | LoopContainerType::iterator it = |
1017 | 0 | std::find(loops_.begin(), loops_.end(), loop); |
1018 | 0 | assert(it != loops_.end()); |
1019 | 0 | delete loop; |
1020 | 0 | loops_.erase(it); |
1021 | 0 | } |
1022 | | |
1023 | | } // namespace opt |
1024 | | } // namespace spvtools |