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