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/loop_fusion.h"
16
17 #include <algorithm>
18 #include <vector>
19
20 #include "source/opt/ir_context.h"
21 #include "source/opt/loop_dependence.h"
22 #include "source/opt/loop_descriptor.h"
23
24 namespace spvtools {
25 namespace opt {
26 namespace {
27
28 // Append all the loops nested in |loop| to |loops|.
CollectChildren(Loop * loop,std::vector<const Loop * > * loops)29 void CollectChildren(Loop* loop, std::vector<const Loop*>* loops) {
30 for (auto child : *loop) {
31 loops->push_back(child);
32 if (child->NumImmediateChildren() != 0) {
33 CollectChildren(child, loops);
34 }
35 }
36 }
37
38 // Return the set of locations accessed by |stores| and |loads|.
GetLocationsAccessed(const std::map<Instruction *,std::vector<Instruction * >> & stores,const std::map<Instruction *,std::vector<Instruction * >> & loads)39 std::set<Instruction*> GetLocationsAccessed(
40 const std::map<Instruction*, std::vector<Instruction*>>& stores,
41 const std::map<Instruction*, std::vector<Instruction*>>& loads) {
42 std::set<Instruction*> locations{};
43
44 for (const auto& kv : stores) {
45 locations.insert(std::get<0>(kv));
46 }
47
48 for (const auto& kv : loads) {
49 locations.insert(std::get<0>(kv));
50 }
51
52 return locations;
53 }
54
55 // Append all dependences from |sources| to |destinations| to |dependences|.
GetDependences(std::vector<DistanceVector> * dependences,LoopDependenceAnalysis * analysis,const std::vector<Instruction * > & sources,const std::vector<Instruction * > & destinations,size_t num_entries)56 void GetDependences(std::vector<DistanceVector>* dependences,
57 LoopDependenceAnalysis* analysis,
58 const std::vector<Instruction*>& sources,
59 const std::vector<Instruction*>& destinations,
60 size_t num_entries) {
61 for (auto source : sources) {
62 for (auto destination : destinations) {
63 DistanceVector dist(num_entries);
64 if (!analysis->GetDependence(source, destination, &dist)) {
65 dependences->push_back(dist);
66 }
67 }
68 }
69 }
70
71 // Apped all instructions in |block| to |instructions|.
AddInstructionsInBlock(std::vector<Instruction * > * instructions,BasicBlock * block)72 void AddInstructionsInBlock(std::vector<Instruction*>* instructions,
73 BasicBlock* block) {
74 for (auto& inst : *block) {
75 instructions->push_back(&inst);
76 }
77
78 instructions->push_back(block->GetLabelInst());
79 }
80
81 } // namespace
82
UsedInContinueOrConditionBlock(Instruction * phi_instruction,Loop * loop)83 bool LoopFusion::UsedInContinueOrConditionBlock(Instruction* phi_instruction,
84 Loop* loop) {
85 auto condition_block = loop->FindConditionBlock()->id();
86 auto continue_block = loop->GetContinueBlock()->id();
87 auto not_used = context_->get_def_use_mgr()->WhileEachUser(
88 phi_instruction,
89 [this, condition_block, continue_block](Instruction* instruction) {
90 auto block_id = context_->get_instr_block(instruction)->id();
91 return block_id != condition_block && block_id != continue_block;
92 });
93
94 return !not_used;
95 }
96
RemoveIfNotUsedContinueOrConditionBlock(std::vector<Instruction * > * instructions,Loop * loop)97 void LoopFusion::RemoveIfNotUsedContinueOrConditionBlock(
98 std::vector<Instruction*>* instructions, Loop* loop) {
99 instructions->erase(
100 std::remove_if(std::begin(*instructions), std::end(*instructions),
101 [this, loop](Instruction* instruction) {
102 return !UsedInContinueOrConditionBlock(instruction,
103 loop);
104 }),
105 std::end(*instructions));
106 }
107
AreCompatible()108 bool LoopFusion::AreCompatible() {
109 // Check that the loops are in the same function.
110 if (loop_0_->GetHeaderBlock()->GetParent() !=
111 loop_1_->GetHeaderBlock()->GetParent()) {
112 return false;
113 }
114
115 // Check that both loops have pre-header blocks.
116 if (!loop_0_->GetPreHeaderBlock() || !loop_1_->GetPreHeaderBlock()) {
117 return false;
118 }
119
120 // Check there are no breaks.
121 if (context_->cfg()->preds(loop_0_->GetMergeBlock()->id()).size() != 1 ||
122 context_->cfg()->preds(loop_1_->GetMergeBlock()->id()).size() != 1) {
123 return false;
124 }
125
126 // Check there are no continues.
127 if (context_->cfg()->preds(loop_0_->GetContinueBlock()->id()).size() != 1 ||
128 context_->cfg()->preds(loop_1_->GetContinueBlock()->id()).size() != 1) {
129 return false;
130 }
131
132 // |GetInductionVariables| returns all OpPhi in the header. Check that both
133 // loops have exactly one that is used in the continue and condition blocks.
134 std::vector<Instruction*> inductions_0{}, inductions_1{};
135 loop_0_->GetInductionVariables(inductions_0);
136 RemoveIfNotUsedContinueOrConditionBlock(&inductions_0, loop_0_);
137
138 if (inductions_0.size() != 1) {
139 return false;
140 }
141
142 induction_0_ = inductions_0.front();
143
144 loop_1_->GetInductionVariables(inductions_1);
145 RemoveIfNotUsedContinueOrConditionBlock(&inductions_1, loop_1_);
146
147 if (inductions_1.size() != 1) {
148 return false;
149 }
150
151 induction_1_ = inductions_1.front();
152
153 if (!CheckInit()) {
154 return false;
155 }
156
157 if (!CheckCondition()) {
158 return false;
159 }
160
161 if (!CheckStep()) {
162 return false;
163 }
164
165 // Check adjacency, |loop_0_| should come just before |loop_1_|.
166 // There is always at least one block between loops, even if it's empty.
167 // We'll check at most 2 preceding blocks.
168
169 auto pre_header_1 = loop_1_->GetPreHeaderBlock();
170
171 std::vector<BasicBlock*> block_to_check{};
172 block_to_check.push_back(pre_header_1);
173
174 if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
175 // Follow CFG for one more block.
176 auto preds = context_->cfg()->preds(pre_header_1->id());
177 if (preds.size() == 1) {
178 auto block = &*containing_function_->FindBlock(preds.front());
179 if (block == loop_0_->GetMergeBlock()) {
180 block_to_check.push_back(block);
181 } else {
182 return false;
183 }
184 } else {
185 return false;
186 }
187 }
188
189 // Check that the separating blocks are either empty or only contains a store
190 // to a local variable that is never read (left behind by
191 // '--eliminate-local-multi-store'). Also allow OpPhi, since the loop could be
192 // in LCSSA form.
193 for (auto block : block_to_check) {
194 for (auto& inst : *block) {
195 if (inst.opcode() == spv::Op::OpStore) {
196 // Get the definition of the target to check it's function scope so
197 // there are no observable side effects.
198 auto variable =
199 context_->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
200
201 if (variable->opcode() != spv::Op::OpVariable ||
202 spv::StorageClass(variable->GetSingleWordInOperand(0)) !=
203 spv::StorageClass::Function) {
204 return false;
205 }
206
207 // Check the target is never loaded.
208 auto is_used = false;
209 context_->get_def_use_mgr()->ForEachUse(
210 inst.GetSingleWordInOperand(0),
211 [&is_used](Instruction* use_inst, uint32_t) {
212 if (use_inst->opcode() == spv::Op::OpLoad) {
213 is_used = true;
214 }
215 });
216
217 if (is_used) {
218 return false;
219 }
220 } else if (inst.opcode() == spv::Op::OpPhi) {
221 if (inst.NumInOperands() != 2) {
222 return false;
223 }
224 } else if (inst.opcode() != spv::Op::OpBranch) {
225 return false;
226 }
227 }
228 }
229
230 return true;
231 } // namespace opt
232
ContainsBarriersOrFunctionCalls(Loop * loop)233 bool LoopFusion::ContainsBarriersOrFunctionCalls(Loop* loop) {
234 for (const auto& block : loop->GetBlocks()) {
235 for (const auto& inst : *containing_function_->FindBlock(block)) {
236 auto opcode = inst.opcode();
237 if (opcode == spv::Op::OpFunctionCall ||
238 opcode == spv::Op::OpControlBarrier ||
239 opcode == spv::Op::OpMemoryBarrier ||
240 opcode == spv::Op::OpTypeNamedBarrier ||
241 opcode == spv::Op::OpNamedBarrierInitialize ||
242 opcode == spv::Op::OpMemoryNamedBarrier) {
243 return true;
244 }
245 }
246 }
247
248 return false;
249 }
250
CheckInit()251 bool LoopFusion::CheckInit() {
252 int64_t loop_0_init;
253 if (!loop_0_->GetInductionInitValue(induction_0_, &loop_0_init)) {
254 return false;
255 }
256
257 int64_t loop_1_init;
258 if (!loop_1_->GetInductionInitValue(induction_1_, &loop_1_init)) {
259 return false;
260 }
261
262 if (loop_0_init != loop_1_init) {
263 return false;
264 }
265
266 return true;
267 }
268
CheckCondition()269 bool LoopFusion::CheckCondition() {
270 auto condition_0 = loop_0_->GetConditionInst();
271 auto condition_1 = loop_1_->GetConditionInst();
272
273 if (!loop_0_->IsSupportedCondition(condition_0->opcode()) ||
274 !loop_1_->IsSupportedCondition(condition_1->opcode())) {
275 return false;
276 }
277
278 if (condition_0->opcode() != condition_1->opcode()) {
279 return false;
280 }
281
282 for (uint32_t i = 0; i < condition_0->NumInOperandWords(); ++i) {
283 auto arg_0 = context_->get_def_use_mgr()->GetDef(
284 condition_0->GetSingleWordInOperand(i));
285 auto arg_1 = context_->get_def_use_mgr()->GetDef(
286 condition_1->GetSingleWordInOperand(i));
287
288 if (arg_0 == induction_0_ && arg_1 == induction_1_) {
289 continue;
290 }
291
292 if (arg_0 == induction_0_ && arg_1 != induction_1_) {
293 return false;
294 }
295
296 if (arg_1 == induction_1_ && arg_0 != induction_0_) {
297 return false;
298 }
299
300 if (arg_0 != arg_1) {
301 return false;
302 }
303 }
304
305 return true;
306 }
307
CheckStep()308 bool LoopFusion::CheckStep() {
309 auto scalar_analysis = context_->GetScalarEvolutionAnalysis();
310 SENode* induction_node_0 = scalar_analysis->SimplifyExpression(
311 scalar_analysis->AnalyzeInstruction(induction_0_));
312 if (!induction_node_0->AsSERecurrentNode()) {
313 return false;
314 }
315
316 SENode* induction_step_0 =
317 induction_node_0->AsSERecurrentNode()->GetCoefficient();
318 if (!induction_step_0->AsSEConstantNode()) {
319 return false;
320 }
321
322 SENode* induction_node_1 = scalar_analysis->SimplifyExpression(
323 scalar_analysis->AnalyzeInstruction(induction_1_));
324 if (!induction_node_1->AsSERecurrentNode()) {
325 return false;
326 }
327
328 SENode* induction_step_1 =
329 induction_node_1->AsSERecurrentNode()->GetCoefficient();
330 if (!induction_step_1->AsSEConstantNode()) {
331 return false;
332 }
333
334 if (*induction_step_0 != *induction_step_1) {
335 return false;
336 }
337
338 return true;
339 }
340
LocationToMemOps(const std::vector<Instruction * > & mem_ops)341 std::map<Instruction*, std::vector<Instruction*>> LoopFusion::LocationToMemOps(
342 const std::vector<Instruction*>& mem_ops) {
343 std::map<Instruction*, std::vector<Instruction*>> location_map{};
344
345 for (auto instruction : mem_ops) {
346 auto access_location = context_->get_def_use_mgr()->GetDef(
347 instruction->GetSingleWordInOperand(0));
348
349 while (access_location->opcode() == spv::Op::OpAccessChain) {
350 access_location = context_->get_def_use_mgr()->GetDef(
351 access_location->GetSingleWordInOperand(0));
352 }
353
354 location_map[access_location].push_back(instruction);
355 }
356
357 return location_map;
358 }
359
360 std::pair<std::vector<Instruction*>, std::vector<Instruction*>>
GetLoadsAndStoresInLoop(Loop * loop)361 LoopFusion::GetLoadsAndStoresInLoop(Loop* loop) {
362 std::vector<Instruction*> loads{};
363 std::vector<Instruction*> stores{};
364
365 for (auto block_id : loop->GetBlocks()) {
366 if (block_id == loop->GetContinueBlock()->id()) {
367 continue;
368 }
369
370 for (auto& instruction : *containing_function_->FindBlock(block_id)) {
371 if (instruction.opcode() == spv::Op::OpLoad) {
372 loads.push_back(&instruction);
373 } else if (instruction.opcode() == spv::Op::OpStore) {
374 stores.push_back(&instruction);
375 }
376 }
377 }
378
379 return std::make_pair(loads, stores);
380 }
381
IsUsedInLoop(Instruction * instruction,Loop * loop)382 bool LoopFusion::IsUsedInLoop(Instruction* instruction, Loop* loop) {
383 auto not_used = context_->get_def_use_mgr()->WhileEachUser(
384 instruction, [this, loop](Instruction* user) {
385 auto block_id = context_->get_instr_block(user)->id();
386 return !loop->IsInsideLoop(block_id);
387 });
388
389 return !not_used;
390 }
391
IsLegal()392 bool LoopFusion::IsLegal() {
393 assert(AreCompatible() && "Fusion can't be legal, loops are not compatible.");
394
395 // Bail out if there are function calls as they could have side-effects that
396 // cause dependencies or if there are any barriers.
397 if (ContainsBarriersOrFunctionCalls(loop_0_) ||
398 ContainsBarriersOrFunctionCalls(loop_1_)) {
399 return false;
400 }
401
402 std::vector<Instruction*> phi_instructions{};
403 loop_0_->GetInductionVariables(phi_instructions);
404
405 // Check no OpPhi in |loop_0_| is used in |loop_1_|.
406 for (auto phi_instruction : phi_instructions) {
407 if (IsUsedInLoop(phi_instruction, loop_1_)) {
408 return false;
409 }
410 }
411
412 // Check no LCSSA OpPhi in merge block of |loop_0_| is used in |loop_1_|.
413 auto phi_used = false;
414 loop_0_->GetMergeBlock()->ForEachPhiInst(
415 [this, &phi_used](Instruction* phi_instruction) {
416 phi_used |= IsUsedInLoop(phi_instruction, loop_1_);
417 });
418
419 if (phi_used) {
420 return false;
421 }
422
423 // Grab loads & stores from both loops.
424 auto loads_stores_0 = GetLoadsAndStoresInLoop(loop_0_);
425 auto loads_stores_1 = GetLoadsAndStoresInLoop(loop_1_);
426
427 // Build memory location to operation maps.
428 auto load_locs_0 = LocationToMemOps(std::get<0>(loads_stores_0));
429 auto store_locs_0 = LocationToMemOps(std::get<1>(loads_stores_0));
430
431 auto load_locs_1 = LocationToMemOps(std::get<0>(loads_stores_1));
432 auto store_locs_1 = LocationToMemOps(std::get<1>(loads_stores_1));
433
434 // Get the locations accessed in both loops.
435 auto locations_0 = GetLocationsAccessed(store_locs_0, load_locs_0);
436 auto locations_1 = GetLocationsAccessed(store_locs_1, load_locs_1);
437
438 std::vector<Instruction*> potential_clashes{};
439
440 std::set_intersection(std::begin(locations_0), std::end(locations_0),
441 std::begin(locations_1), std::end(locations_1),
442 std::back_inserter(potential_clashes));
443
444 // If the loops don't access the same variables, the fusion is legal.
445 if (potential_clashes.empty()) {
446 return true;
447 }
448
449 // Find variables that have at least one store.
450 std::vector<Instruction*> potential_clashes_with_stores{};
451 for (auto location : potential_clashes) {
452 if (store_locs_0.find(location) != std::end(store_locs_0) ||
453 store_locs_1.find(location) != std::end(store_locs_1)) {
454 potential_clashes_with_stores.push_back(location);
455 }
456 }
457
458 // If there are only loads to the same variables, the fusion is legal.
459 if (potential_clashes_with_stores.empty()) {
460 return true;
461 }
462
463 // Else if loads and at least one store (across loops) to the same variable
464 // there is a potential dependence and we need to check the dependence
465 // distance.
466
467 // Find all the loops in this loop nest for the dependency analysis.
468 std::vector<const Loop*> loops{};
469
470 // Find the parents.
471 for (auto current_loop = loop_0_; current_loop != nullptr;
472 current_loop = current_loop->GetParent()) {
473 loops.push_back(current_loop);
474 }
475
476 auto this_loop_position = loops.size() - 1;
477 std::reverse(std::begin(loops), std::end(loops));
478
479 // Find the children.
480 CollectChildren(loop_0_, &loops);
481 CollectChildren(loop_1_, &loops);
482
483 // Check that any dependes created are legal. That means the fused loops do
484 // not have any dependencies with dependence distance greater than 0 that did
485 // not exist in the original loops.
486
487 LoopDependenceAnalysis analysis(context_, loops);
488
489 analysis.GetScalarEvolution()->AddLoopsToPretendAreTheSame(
490 {loop_0_, loop_1_});
491
492 for (auto location : potential_clashes_with_stores) {
493 // Analyse dependences from |loop_0_| to |loop_1_|.
494 std::vector<DistanceVector> dependences;
495 // Read-After-Write.
496 GetDependences(&dependences, &analysis, store_locs_0[location],
497 load_locs_1[location], loops.size());
498 // Write-After-Read.
499 GetDependences(&dependences, &analysis, load_locs_0[location],
500 store_locs_1[location], loops.size());
501 // Write-After-Write.
502 GetDependences(&dependences, &analysis, store_locs_0[location],
503 store_locs_1[location], loops.size());
504
505 // Check that the induction variables either don't appear in the subscripts
506 // or the dependence distance is negative.
507 for (const auto& dependence : dependences) {
508 const auto& entry = dependence.GetEntries()[this_loop_position];
509 if ((entry.dependence_information ==
510 DistanceEntry::DependenceInformation::DISTANCE &&
511 entry.distance < 1) ||
512 (entry.dependence_information ==
513 DistanceEntry::DependenceInformation::IRRELEVANT)) {
514 continue;
515 } else {
516 return false;
517 }
518 }
519 }
520
521 return true;
522 }
523
ReplacePhiParentWith(Instruction * inst,uint32_t orig_block,uint32_t new_block)524 void ReplacePhiParentWith(Instruction* inst, uint32_t orig_block,
525 uint32_t new_block) {
526 if (inst->GetSingleWordInOperand(1) == orig_block) {
527 inst->SetInOperand(1, {new_block});
528 } else {
529 inst->SetInOperand(3, {new_block});
530 }
531 }
532
Fuse()533 void LoopFusion::Fuse() {
534 assert(AreCompatible() && "Can't fuse, loops aren't compatible");
535 assert(IsLegal() && "Can't fuse, illegal");
536
537 // Save the pointers/ids, won't be found in the middle of doing modifications.
538 auto header_1 = loop_1_->GetHeaderBlock()->id();
539 auto condition_1 = loop_1_->FindConditionBlock()->id();
540 auto continue_1 = loop_1_->GetContinueBlock()->id();
541 auto continue_0 = loop_0_->GetContinueBlock()->id();
542 auto condition_block_of_0 = loop_0_->FindConditionBlock();
543
544 // Find the blocks whose branches need updating.
545 auto first_block_of_1 = &*(++containing_function_->FindBlock(condition_1));
546 auto last_block_of_1 = &*(--containing_function_->FindBlock(continue_1));
547 auto last_block_of_0 = &*(--containing_function_->FindBlock(continue_0));
548
549 // Update the branch for |last_block_of_loop_0| to go to |first_block_of_1|.
550 last_block_of_0->ForEachSuccessorLabel(
551 [first_block_of_1](uint32_t* succ) { *succ = first_block_of_1->id(); });
552
553 // Update the branch for the |last_block_of_loop_1| to go to the continue
554 // block of |loop_0_|.
555 last_block_of_1->ForEachSuccessorLabel(
556 [this](uint32_t* succ) { *succ = loop_0_->GetContinueBlock()->id(); });
557
558 // Update merge block id in the header of |loop_0_| to the merge block of
559 // |loop_1_|.
560 loop_0_->GetHeaderBlock()->ForEachInst([this](Instruction* inst) {
561 if (inst->opcode() == spv::Op::OpLoopMerge) {
562 inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()});
563 }
564 });
565
566 // Update condition branch target in |loop_0_| to the merge block of
567 // |loop_1_|.
568 condition_block_of_0->ForEachInst([this](Instruction* inst) {
569 if (inst->opcode() == spv::Op::OpBranchConditional) {
570 auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id();
571
572 if (inst->GetSingleWordInOperand(1) == loop_0_merge_block_id) {
573 inst->SetInOperand(1, {loop_1_->GetMergeBlock()->id()});
574 } else {
575 inst->SetInOperand(2, {loop_1_->GetMergeBlock()->id()});
576 }
577 }
578 });
579
580 // Move OpPhi instructions not corresponding to the induction variable from
581 // the header of |loop_1_| to the header of |loop_0_|.
582 std::vector<Instruction*> instructions_to_move{};
583 for (auto& instruction : *loop_1_->GetHeaderBlock()) {
584 if (instruction.opcode() == spv::Op::OpPhi &&
585 &instruction != induction_1_) {
586 instructions_to_move.push_back(&instruction);
587 }
588 }
589
590 for (auto& it : instructions_to_move) {
591 it->RemoveFromList();
592 it->InsertBefore(induction_0_);
593 }
594
595 // Update the OpPhi parents to the correct blocks in |loop_0_|.
596 loop_0_->GetHeaderBlock()->ForEachPhiInst([this](Instruction* i) {
597 ReplacePhiParentWith(i, loop_1_->GetPreHeaderBlock()->id(),
598 loop_0_->GetPreHeaderBlock()->id());
599
600 ReplacePhiParentWith(i, loop_1_->GetContinueBlock()->id(),
601 loop_0_->GetContinueBlock()->id());
602 });
603
604 // Update instruction to block mapping & DefUseManager.
605 for (auto& phi_instruction : instructions_to_move) {
606 context_->set_instr_block(phi_instruction, loop_0_->GetHeaderBlock());
607 context_->get_def_use_mgr()->AnalyzeInstUse(phi_instruction);
608 }
609
610 // Replace the uses of the induction variable of |loop_1_| with that the
611 // induction variable of |loop_0_|.
612 context_->ReplaceAllUsesWith(induction_1_->result_id(),
613 induction_0_->result_id());
614
615 // Replace LCSSA OpPhi in merge block of |loop_0_|.
616 loop_0_->GetMergeBlock()->ForEachPhiInst([this](Instruction* instruction) {
617 context_->ReplaceAllUsesWith(instruction->result_id(),
618 instruction->GetSingleWordInOperand(0));
619 });
620
621 // Update LCSSA OpPhi in merge block of |loop_1_|.
622 loop_1_->GetMergeBlock()->ForEachPhiInst(
623 [condition_block_of_0](Instruction* instruction) {
624 instruction->SetInOperand(1, {condition_block_of_0->id()});
625 });
626
627 // Move the continue block of |loop_0_| after the last block of |loop_1_|.
628 containing_function_->MoveBasicBlockToAfter(continue_0, last_block_of_1);
629
630 // Gather all instructions to be killed from |loop_1_| (induction variable
631 // initialisation, header, condition and continue blocks).
632 std::vector<Instruction*> instr_to_delete{};
633 AddInstructionsInBlock(&instr_to_delete, loop_1_->GetPreHeaderBlock());
634 AddInstructionsInBlock(&instr_to_delete, loop_1_->GetHeaderBlock());
635 AddInstructionsInBlock(&instr_to_delete, loop_1_->FindConditionBlock());
636 AddInstructionsInBlock(&instr_to_delete, loop_1_->GetContinueBlock());
637
638 // There was an additional empty block between the loops, kill that too.
639 if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
640 AddInstructionsInBlock(&instr_to_delete, loop_0_->GetMergeBlock());
641 }
642
643 // Update the CFG, so it wouldn't need invalidating.
644 auto cfg = context_->cfg();
645
646 cfg->ForgetBlock(loop_1_->GetPreHeaderBlock());
647 cfg->ForgetBlock(loop_1_->GetHeaderBlock());
648 cfg->ForgetBlock(loop_1_->FindConditionBlock());
649 cfg->ForgetBlock(loop_1_->GetContinueBlock());
650
651 if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
652 cfg->ForgetBlock(loop_0_->GetMergeBlock());
653 }
654
655 cfg->RemoveEdge(last_block_of_0->id(), loop_0_->GetContinueBlock()->id());
656 cfg->AddEdge(last_block_of_0->id(), first_block_of_1->id());
657
658 cfg->AddEdge(last_block_of_1->id(), loop_0_->GetContinueBlock()->id());
659
660 cfg->AddEdge(loop_0_->GetContinueBlock()->id(),
661 loop_1_->GetHeaderBlock()->id());
662
663 cfg->AddEdge(condition_block_of_0->id(), loop_1_->GetMergeBlock()->id());
664
665 // Update DefUseManager.
666 auto def_use_mgr = context_->get_def_use_mgr();
667
668 // Uses of labels that are in updated branches need analysing.
669 def_use_mgr->AnalyzeInstUse(last_block_of_0->terminator());
670 def_use_mgr->AnalyzeInstUse(last_block_of_1->terminator());
671 def_use_mgr->AnalyzeInstUse(loop_0_->GetHeaderBlock()->GetLoopMergeInst());
672 def_use_mgr->AnalyzeInstUse(condition_block_of_0->terminator());
673
674 // Update the LoopDescriptor, so it wouldn't need invalidating.
675 auto ld = context_->GetLoopDescriptor(containing_function_);
676
677 // Create a copy, so the iterator wouldn't be invalidated.
678 std::vector<Loop*> loops_to_add_remove{};
679 for (auto child_loop : *loop_1_) {
680 loops_to_add_remove.push_back(child_loop);
681 }
682
683 for (auto child_loop : loops_to_add_remove) {
684 loop_1_->RemoveChildLoop(child_loop);
685 loop_0_->AddNestedLoop(child_loop);
686 }
687
688 auto loop_1_blocks = loop_1_->GetBlocks();
689
690 for (auto block : loop_1_blocks) {
691 loop_1_->RemoveBasicBlock(block);
692 if (block != header_1 && block != condition_1 && block != continue_1) {
693 loop_0_->AddBasicBlock(block);
694 if ((*ld)[block] == loop_1_) {
695 ld->SetBasicBlockToLoop(block, loop_0_);
696 }
697 }
698
699 if ((*ld)[block] == loop_1_) {
700 ld->ForgetBasicBlock(block);
701 }
702 }
703
704 loop_1_->RemoveBasicBlock(loop_1_->GetPreHeaderBlock()->id());
705 ld->ForgetBasicBlock(loop_1_->GetPreHeaderBlock()->id());
706
707 if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
708 loop_0_->RemoveBasicBlock(loop_0_->GetMergeBlock()->id());
709 ld->ForgetBasicBlock(loop_0_->GetMergeBlock()->id());
710 }
711
712 loop_0_->SetMergeBlock(loop_1_->GetMergeBlock());
713
714 loop_1_->ClearBlocks();
715
716 ld->RemoveLoop(loop_1_);
717
718 // Kill unnecessary instructions and remove all empty blocks.
719 for (auto inst : instr_to_delete) {
720 context_->KillInst(inst);
721 }
722
723 containing_function_->RemoveEmptyBlocks();
724
725 // Invalidate analyses.
726 context_->InvalidateAnalysesExceptFor(
727 IRContext::Analysis::kAnalysisInstrToBlockMapping |
728 IRContext::Analysis::kAnalysisLoopAnalysis |
729 IRContext::Analysis::kAnalysisDefUse | IRContext::Analysis::kAnalysisCFG);
730 }
731
732 } // namespace opt
733 } // namespace spvtools
734