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