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