• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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