Coverage Report

Created: 2023-03-01 07:33

/src/spirv-tools/source/opt/register_pressure.cpp
Line
Count
Source (jump to first uncovered line)
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:
34
  ExcludePhiDefinedInBlock(IRContext* context, const BasicBlock* bb)
35
0
      : context_(context), bb_(bb) {}
36
37
0
  bool operator()(Instruction* insn) const {
38
0
    return !(insn->opcode() == spv::Op::OpPhi &&
39
0
             context_->get_instr_block(insn) == bb_);
40
0
  }
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.
49
0
bool CreatesRegisterUsage(Instruction* insn) {
50
0
  if (!insn->HasResultId()) return false;
51
0
  if (insn->opcode() == spv::Op::OpUndef) return false;
52
0
  if (IsConstantInst(insn->opcode())) return false;
53
0
  if (insn->opcode() == spv::Op::OpLabel) return false;
54
0
  return true;
55
0
}
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:
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
0
        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).
79
0
  void Compute() {
80
0
    for (BasicBlock& start_bb : *function_) {
81
0
      if (reg_pressure_->Get(start_bb.id()) != nullptr) {
82
0
        continue;
83
0
      }
84
0
      cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
85
0
        if (reg_pressure_->Get(bb->id()) == nullptr) {
86
0
          ComputePartialLiveness(bb);
87
0
        }
88
0
      });
89
0
    }
90
0
    DoLoopLivenessUnification();
91
0
    EvaluateRegisterRequirements();
92
0
  }
93
94
 private:
95
  // Registers all SSA register used by successors of |bb| in their phi
96
  // instructions.
97
  void ComputePhiUses(const BasicBlock& bb,
98
0
                      RegisterLiveness::RegionRegisterLiveness::LiveSet* live) {
99
0
    uint32_t bb_id = bb.id();
100
0
    bb.ForEachSuccessorLabel([live, bb_id, this](uint32_t sid) {
101
0
      BasicBlock* succ_bb = cfg_.block(sid);
102
0
      succ_bb->ForEachPhiInst([live, bb_id, this](const Instruction* phi) {
103
0
        for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
104
0
          if (phi->GetSingleWordInOperand(i + 1) == bb_id) {
105
0
            Instruction* insn_op =
106
0
                def_use_manager_.GetDef(phi->GetSingleWordInOperand(i));
107
0
            if (CreatesRegisterUsage(insn_op)) {
108
0
              live->insert(insn_op);
109
0
              break;
110
0
            }
111
0
          }
112
0
        }
113
0
      });
114
0
    });
115
0
  }
116
117
  // Computes register liveness for each basic blocks but ignores all
118
  // back-edges.
119
0
  void ComputePartialLiveness(BasicBlock* bb) {
120
0
    assert(reg_pressure_->Get(bb) == nullptr &&
121
0
           "Basic block already processed");
122
123
0
    RegisterLiveness::RegionRegisterLiveness* live_inout =
124
0
        reg_pressure_->GetOrInsert(bb->id());
125
0
    ComputePhiUses(*bb, &live_inout->live_out_);
126
127
0
    const BasicBlock* cbb = bb;
128
0
    cbb->ForEachSuccessorLabel([&live_inout, bb, this](uint32_t sid) {
129
      // Skip back edges.
130
0
      if (dom_tree_.Dominates(sid, bb->id())) {
131
0
        return;
132
0
      }
133
134
0
      BasicBlock* succ_bb = cfg_.block(sid);
135
0
      RegisterLiveness::RegionRegisterLiveness* succ_live_inout =
136
0
          reg_pressure_->Get(succ_bb);
137
0
      assert(succ_live_inout &&
138
0
             "Successor liveness analysis was not performed");
139
140
0
      ExcludePhiDefinedInBlock predicate(context_, succ_bb);
141
0
      auto filter =
142
0
          MakeFilterIteratorRange(succ_live_inout->live_in_.begin(),
143
0
                                  succ_live_inout->live_in_.end(), predicate);
144
0
      live_inout->live_out_.insert(filter.begin(), filter.end());
145
0
    });
146
147
0
    live_inout->live_in_ = live_inout->live_out_;
148
0
    for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
149
0
      if (insn.opcode() == spv::Op::OpPhi) {
150
0
        live_inout->live_in_.insert(&insn);
151
0
        break;
152
0
      }
153
0
      live_inout->live_in_.erase(&insn);
154
0
      insn.ForEachInId([live_inout, this](uint32_t* id) {
155
0
        Instruction* insn_op = def_use_manager_.GetDef(*id);
156
0
        if (CreatesRegisterUsage(insn_op)) {
157
0
          live_inout->live_in_.insert(insn_op);
158
0
        }
159
0
      });
160
0
    }
161
0
  }
162
163
  // Propagates the register liveness information of each loop iterators.
164
0
  void DoLoopLivenessUnification() {
165
0
    for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) {
166
0
      DoLoopLivenessUnification(*loop);
167
0
    }
168
0
  }
169
170
  // Propagates the register liveness information of loop iterators trough-out
171
  // the loop body.
172
0
  void DoLoopLivenessUnification(const Loop& loop) {
173
0
    auto blocks_in_loop = MakeFilterIteratorRange(
174
0
        loop.GetBlocks().begin(), loop.GetBlocks().end(),
175
0
        [&loop, this](uint32_t bb_id) {
176
0
          return bb_id != loop.GetHeaderBlock()->id() &&
177
0
                 loop_desc_[bb_id] == &loop;
178
0
        });
179
180
0
    RegisterLiveness::RegionRegisterLiveness* header_live_inout =
181
0
        reg_pressure_->Get(loop.GetHeaderBlock());
182
0
    assert(header_live_inout &&
183
0
           "Liveness analysis was not performed for the current block");
184
185
0
    ExcludePhiDefinedInBlock predicate(context_, loop.GetHeaderBlock());
186
0
    auto live_loop =
187
0
        MakeFilterIteratorRange(header_live_inout->live_in_.begin(),
188
0
                                header_live_inout->live_in_.end(), predicate);
189
190
0
    for (uint32_t bb_id : blocks_in_loop) {
191
0
      BasicBlock* bb = cfg_.block(bb_id);
192
193
0
      RegisterLiveness::RegionRegisterLiveness* live_inout =
194
0
          reg_pressure_->Get(bb);
195
0
      live_inout->live_in_.insert(live_loop.begin(), live_loop.end());
196
0
      live_inout->live_out_.insert(live_loop.begin(), live_loop.end());
197
0
    }
198
199
0
    for (const Loop* inner_loop : loop) {
200
0
      RegisterLiveness::RegionRegisterLiveness* live_inout =
201
0
          reg_pressure_->Get(inner_loop->GetHeaderBlock());
202
0
      live_inout->live_in_.insert(live_loop.begin(), live_loop.end());
203
0
      live_inout->live_out_.insert(live_loop.begin(), live_loop.end());
204
205
0
      DoLoopLivenessUnification(*inner_loop);
206
0
    }
207
0
  }
208
209
  // Get the number of required registers for this each basic block.
210
0
  void EvaluateRegisterRequirements() {
211
0
    for (BasicBlock& bb : *function_) {
212
0
      RegisterLiveness::RegionRegisterLiveness* live_inout =
213
0
          reg_pressure_->Get(bb.id());
214
0
      assert(live_inout != nullptr && "Basic block not processed");
215
216
0
      size_t reg_count = live_inout->live_out_.size();
217
0
      for (Instruction* insn : live_inout->live_out_) {
218
0
        live_inout->AddRegisterClass(insn);
219
0
      }
220
0
      live_inout->used_registers_ = reg_count;
221
222
0
      std::unordered_set<uint32_t> die_in_block;
223
0
      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
0
        if (insn.opcode() == spv::Op::OpPhi) {
227
0
          break;
228
0
        }
229
230
0
        insn.ForEachInId(
231
0
            [live_inout, &die_in_block, &reg_count, this](uint32_t* id) {
232
0
              Instruction* op_insn = def_use_manager_.GetDef(*id);
233
0
              if (!CreatesRegisterUsage(op_insn) ||
234
0
                  live_inout->live_out_.count(op_insn)) {
235
                // already taken into account.
236
0
                return;
237
0
              }
238
0
              if (!die_in_block.count(*id)) {
239
0
                live_inout->AddRegisterClass(def_use_manager_.GetDef(*id));
240
0
                reg_count++;
241
0
                die_in_block.insert(*id);
242
0
              }
243
0
            });
244
0
        live_inout->used_registers_ =
245
0
            std::max(live_inout->used_registers_, reg_count);
246
0
        if (CreatesRegisterUsage(&insn)) {
247
0
          reg_count--;
248
0
        }
249
0
      }
250
0
    }
251
0
  }
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.
264
void RegisterLiveness::RegionRegisterLiveness::AddRegisterClass(
265
0
    Instruction* insn) {
266
0
  assert(CreatesRegisterUsage(insn) && "Instruction does not use a register");
267
0
  analysis::Type* type =
268
0
      insn->context()->get_type_mgr()->GetType(insn->type_id());
269
270
0
  RegisterLiveness::RegisterClass reg_class{type, false};
271
272
0
  insn->context()->get_decoration_mgr()->WhileEachDecoration(
273
0
      insn->result_id(), uint32_t(spv::Decoration::Uniform),
274
0
      [&reg_class](const Instruction&) {
275
0
        reg_class.is_uniform_ = true;
276
0
        return false;
277
0
      });
278
279
0
  AddRegisterClass(reg_class);
280
0
}
281
282
0
void RegisterLiveness::Analyze(Function* f) {
283
0
  block_pressure_.clear();
284
0
  ComputeRegisterLiveness(this, f).Compute();
285
0
}
286
287
void RegisterLiveness::ComputeLoopRegisterPressure(
288
0
    const Loop& loop, RegionRegisterLiveness* loop_reg_pressure) const {
289
0
  loop_reg_pressure->Clear();
290
291
0
  const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock());
292
0
  loop_reg_pressure->live_in_ = header_live_inout->live_in_;
293
294
0
  std::unordered_set<uint32_t> exit_blocks;
295
0
  loop.GetExitBlocks(&exit_blocks);
296
297
0
  for (uint32_t bb_id : exit_blocks) {
298
0
    const RegionRegisterLiveness* live_inout = Get(bb_id);
299
0
    loop_reg_pressure->live_out_.insert(live_inout->live_in_.begin(),
300
0
                                        live_inout->live_in_.end());
301
0
  }
302
303
0
  std::unordered_set<uint32_t> seen_insn;
304
0
  for (Instruction* insn : loop_reg_pressure->live_out_) {
305
0
    loop_reg_pressure->AddRegisterClass(insn);
306
0
    seen_insn.insert(insn->result_id());
307
0
  }
308
0
  for (Instruction* insn : loop_reg_pressure->live_in_) {
309
0
    if (!seen_insn.count(insn->result_id())) {
310
0
      continue;
311
0
    }
312
0
    loop_reg_pressure->AddRegisterClass(insn);
313
0
    seen_insn.insert(insn->result_id());
314
0
  }
315
316
0
  loop_reg_pressure->used_registers_ = 0;
317
318
0
  for (uint32_t bb_id : loop.GetBlocks()) {
319
0
    BasicBlock* bb = context_->cfg()->block(bb_id);
320
321
0
    const RegionRegisterLiveness* live_inout = Get(bb_id);
322
0
    assert(live_inout != nullptr && "Basic block not processed");
323
0
    loop_reg_pressure->used_registers_ = std::max(
324
0
        loop_reg_pressure->used_registers_, live_inout->used_registers_);
325
326
0
    for (Instruction& insn : *bb) {
327
0
      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
328
0
          seen_insn.count(insn.result_id())) {
329
0
        continue;
330
0
      }
331
0
      loop_reg_pressure->AddRegisterClass(&insn);
332
0
    }
333
0
  }
334
0
}
335
336
void RegisterLiveness::SimulateFusion(
337
0
    const Loop& l1, const Loop& l2, RegionRegisterLiveness* sim_result) const {
338
0
  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
0
  const RegionRegisterLiveness* l1_header_live_inout = Get(l1.GetHeaderBlock());
345
0
  sim_result->live_in_ = l1_header_live_inout->live_in_;
346
347
0
  const RegionRegisterLiveness* l2_header_live_inout = Get(l2.GetHeaderBlock());
348
0
  sim_result->live_in_.insert(l2_header_live_inout->live_in_.begin(),
349
0
                              l2_header_live_inout->live_in_.end());
350
351
  // The live-out set of the fused loop is the l2 live-out set.
352
0
  std::unordered_set<uint32_t> exit_blocks;
353
0
  l2.GetExitBlocks(&exit_blocks);
354
355
0
  for (uint32_t bb_id : exit_blocks) {
356
0
    const RegionRegisterLiveness* live_inout = Get(bb_id);
357
0
    sim_result->live_out_.insert(live_inout->live_in_.begin(),
358
0
                                 live_inout->live_in_.end());
359
0
  }
360
361
  // Compute the register usage information.
362
0
  std::unordered_set<uint32_t> seen_insn;
363
0
  for (Instruction* insn : sim_result->live_out_) {
364
0
    sim_result->AddRegisterClass(insn);
365
0
    seen_insn.insert(insn->result_id());
366
0
  }
367
0
  for (Instruction* insn : sim_result->live_in_) {
368
0
    if (!seen_insn.count(insn->result_id())) {
369
0
      continue;
370
0
    }
371
0
    sim_result->AddRegisterClass(insn);
372
0
    seen_insn.insert(insn->result_id());
373
0
  }
374
375
0
  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
0
  auto live_loop = MakeFilterIteratorRange(
384
0
      sim_result->live_in_.begin(), sim_result->live_in_.end(),
385
0
      [&l1, &l2](Instruction* insn) {
386
0
        BasicBlock* bb = insn->context()->get_instr_block(insn);
387
0
        return insn->HasResultId() &&
388
0
               !(insn->opcode() == spv::Op::OpPhi &&
389
0
                 (bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock()));
390
0
      });
391
392
0
  for (uint32_t bb_id : l1.GetBlocks()) {
393
0
    BasicBlock* bb = context_->cfg()->block(bb_id);
394
395
0
    const RegionRegisterLiveness* live_inout_info = Get(bb_id);
396
0
    assert(live_inout_info != nullptr && "Basic block not processed");
397
0
    RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_;
398
0
    live_out.insert(live_loop.begin(), live_loop.end());
399
0
    sim_result->used_registers_ =
400
0
        std::max(sim_result->used_registers_,
401
0
                 live_inout_info->used_registers_ + live_out.size() -
402
0
                     live_inout_info->live_out_.size());
403
404
0
    for (Instruction& insn : *bb) {
405
0
      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
406
0
          seen_insn.count(insn.result_id())) {
407
0
        continue;
408
0
      }
409
0
      sim_result->AddRegisterClass(&insn);
410
0
    }
411
0
  }
412
413
0
  const RegionRegisterLiveness* l1_latch_live_inout_info =
414
0
      Get(l1.GetLatchBlock()->id());
415
0
  assert(l1_latch_live_inout_info != nullptr && "Basic block not processed");
416
0
  RegionRegisterLiveness::LiveSet l1_latch_live_out =
417
0
      l1_latch_live_inout_info->live_out_;
418
0
  l1_latch_live_out.insert(live_loop.begin(), live_loop.end());
419
420
0
  auto live_loop_l2 =
421
0
      make_range(l1_latch_live_out.begin(), l1_latch_live_out.end());
422
423
0
  for (uint32_t bb_id : l2.GetBlocks()) {
424
0
    BasicBlock* bb = context_->cfg()->block(bb_id);
425
426
0
    const RegionRegisterLiveness* live_inout_info = Get(bb_id);
427
0
    assert(live_inout_info != nullptr && "Basic block not processed");
428
0
    RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_;
429
0
    live_out.insert(live_loop_l2.begin(), live_loop_l2.end());
430
0
    sim_result->used_registers_ =
431
0
        std::max(sim_result->used_registers_,
432
0
                 live_inout_info->used_registers_ + live_out.size() -
433
0
                     live_inout_info->live_out_.size());
434
435
0
    for (Instruction& insn : *bb) {
436
0
      if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) ||
437
0
          seen_insn.count(insn.result_id())) {
438
0
        continue;
439
0
      }
440
0
      sim_result->AddRegisterClass(&insn);
441
0
    }
442
0
  }
443
0
}
444
445
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
0
    RegionRegisterLiveness* l2_sim_result) const {
450
0
  l1_sim_result->Clear();
451
0
  l2_sim_result->Clear();
452
453
  // Filter predicates: consider instructions that only belong to the first and
454
  // second loop.
455
0
  auto belong_to_loop1 = [&moved_inst, &copied_inst, &loop](Instruction* insn) {
456
0
    return moved_inst.count(insn) || copied_inst.count(insn) ||
457
0
           !loop.IsInsideLoop(insn);
458
0
  };
459
0
  auto belong_to_loop2 = [&moved_inst](Instruction* insn) {
460
0
    return !moved_inst.count(insn);
461
0
  };
462
463
0
  const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock());
464
  // l1 live-in
465
0
  {
466
0
    auto live_loop = MakeFilterIteratorRange(
467
0
        header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(),
468
0
        belong_to_loop1);
469
0
    l1_sim_result->live_in_.insert(live_loop.begin(), live_loop.end());
470
0
  }
471
  // l2 live-in
472
0
  {
473
0
    auto live_loop = MakeFilterIteratorRange(
474
0
        header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(),
475
0
        belong_to_loop2);
476
0
    l2_sim_result->live_in_.insert(live_loop.begin(), live_loop.end());
477
0
  }
478
479
0
  std::unordered_set<uint32_t> exit_blocks;
480
0
  loop.GetExitBlocks(&exit_blocks);
481
482
  // l2 live-out.
483
0
  for (uint32_t bb_id : exit_blocks) {
484
0
    const RegionRegisterLiveness* live_inout = Get(bb_id);
485
0
    l2_sim_result->live_out_.insert(live_inout->live_in_.begin(),
486
0
                                    live_inout->live_in_.end());
487
0
  }
488
  // l1 live-out.
489
0
  {
490
0
    auto live_out = MakeFilterIteratorRange(l2_sim_result->live_out_.begin(),
491
0
                                            l2_sim_result->live_out_.end(),
492
0
                                            belong_to_loop1);
493
0
    l1_sim_result->live_out_.insert(live_out.begin(), live_out.end());
494
0
  }
495
0
  {
496
0
    auto live_out =
497
0
        MakeFilterIteratorRange(l2_sim_result->live_in_.begin(),
498
0
                                l2_sim_result->live_in_.end(), belong_to_loop1);
499
0
    l1_sim_result->live_out_.insert(live_out.begin(), live_out.end());
500
0
  }
501
  // Lives out of l1 are live out of l2 so are live in of l2 as well.
502
0
  l2_sim_result->live_in_.insert(l1_sim_result->live_out_.begin(),
503
0
                                 l1_sim_result->live_out_.end());
504
505
0
  for (Instruction* insn : l1_sim_result->live_in_) {
506
0
    l1_sim_result->AddRegisterClass(insn);
507
0
  }
508
0
  for (Instruction* insn : l2_sim_result->live_in_) {
509
0
    l2_sim_result->AddRegisterClass(insn);
510
0
  }
511
512
0
  l1_sim_result->used_registers_ = 0;
513
0
  l2_sim_result->used_registers_ = 0;
514
515
0
  for (uint32_t bb_id : loop.GetBlocks()) {
516
0
    BasicBlock* bb = context_->cfg()->block(bb_id);
517
518
0
    const RegisterLiveness::RegionRegisterLiveness* live_inout = Get(bb_id);
519
0
    assert(live_inout != nullptr && "Basic block not processed");
520
0
    auto l1_block_live_out =
521
0
        MakeFilterIteratorRange(live_inout->live_out_.begin(),
522
0
                                live_inout->live_out_.end(), belong_to_loop1);
523
0
    auto l2_block_live_out =
524
0
        MakeFilterIteratorRange(live_inout->live_out_.begin(),
525
0
                                live_inout->live_out_.end(), belong_to_loop2);
526
527
0
    size_t l1_reg_count =
528
0
        std::distance(l1_block_live_out.begin(), l1_block_live_out.end());
529
0
    size_t l2_reg_count =
530
0
        std::distance(l2_block_live_out.begin(), l2_block_live_out.end());
531
532
0
    std::unordered_set<uint32_t> die_in_block;
533
0
    for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) {
534
0
      if (insn.opcode() == spv::Op::OpPhi) {
535
0
        break;
536
0
      }
537
538
0
      bool does_belong_to_loop1 = belong_to_loop1(&insn);
539
0
      bool does_belong_to_loop2 = belong_to_loop2(&insn);
540
0
      insn.ForEachInId([live_inout, &die_in_block, &l1_reg_count, &l2_reg_count,
541
0
                        does_belong_to_loop1, does_belong_to_loop2,
542
0
                        this](uint32_t* id) {
543
0
        Instruction* op_insn = context_->get_def_use_mgr()->GetDef(*id);
544
0
        if (!CreatesRegisterUsage(op_insn) ||
545
0
            live_inout->live_out_.count(op_insn)) {
546
          // already taken into account.
547
0
          return;
548
0
        }
549
0
        if (!die_in_block.count(*id)) {
550
0
          if (does_belong_to_loop1) {
551
0
            l1_reg_count++;
552
0
          }
553
0
          if (does_belong_to_loop2) {
554
0
            l2_reg_count++;
555
0
          }
556
0
          die_in_block.insert(*id);
557
0
        }
558
0
      });
559
0
      l1_sim_result->used_registers_ =
560
0
          std::max(l1_sim_result->used_registers_, l1_reg_count);
561
0
      l2_sim_result->used_registers_ =
562
0
          std::max(l2_sim_result->used_registers_, l2_reg_count);
563
0
      if (CreatesRegisterUsage(&insn)) {
564
0
        if (does_belong_to_loop1) {
565
0
          if (!l1_sim_result->live_in_.count(&insn)) {
566
0
            l1_sim_result->AddRegisterClass(&insn);
567
0
          }
568
0
          l1_reg_count--;
569
0
        }
570
0
        if (does_belong_to_loop2) {
571
0
          if (!l2_sim_result->live_in_.count(&insn)) {
572
0
            l2_sim_result->AddRegisterClass(&insn);
573
0
          }
574
0
          l2_reg_count--;
575
0
        }
576
0
      }
577
0
    }
578
0
  }
579
0
}
580
581
}  // namespace opt
582
}  // namespace spvtools