Coverage Report

Created: 2026-08-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/loop_utils.cpp
Line
Count
Source
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 <algorithm>
16
#include <memory>
17
#include <unordered_map>
18
#include <unordered_set>
19
#include <utility>
20
#include <vector>
21
22
#include "source/cfa.h"
23
#include "source/opt/cfg.h"
24
#include "source/opt/ir_builder.h"
25
#include "source/opt/ir_context.h"
26
#include "source/opt/loop_descriptor.h"
27
#include "source/opt/loop_utils.h"
28
29
namespace spvtools {
30
namespace opt {
31
namespace {
32
// Return true if |bb| is dominated by at least one block in |exits|
33
inline bool DominatesAnExit(BasicBlock* bb,
34
                            const std::unordered_set<BasicBlock*>& exits,
35
0
                            const DominatorTree& dom_tree) {
36
0
  for (BasicBlock* e_bb : exits)
37
0
    if (dom_tree.Dominates(bb, e_bb)) return true;
38
0
  return false;
39
0
}
40
41
// Utility class to rewrite out-of-loop uses of an in-loop definition in terms
42
// of phi instructions to achieve a LCSSA form.
43
// For a given definition, the class user registers phi instructions using that
44
// definition in all loop exit blocks by which the definition escapes.
45
// Then, when rewriting a use of the definition, the rewriter walks the
46
// paths from the use the loop exits. At each step, it will insert a phi
47
// instruction to merge the incoming value according to exit blocks definition.
48
class LCSSARewriter {
49
 public:
50
  LCSSARewriter(IRContext* context, const DominatorTree& dom_tree,
51
                const std::unordered_set<BasicBlock*>& exit_bb,
52
                BasicBlock* merge_block)
53
0
      : context_(context),
54
0
        cfg_(context_->cfg()),
55
0
        dom_tree_(dom_tree),
56
0
        exit_bb_(exit_bb),
57
0
        merge_block_id_(merge_block ? merge_block->id() : 0) {}
58
59
  struct UseRewriter {
60
    explicit UseRewriter(LCSSARewriter* base, const Instruction& def_insn)
61
0
        : base_(base), def_insn_(def_insn) {}
62
    // Rewrites the use of |def_insn_| by the instruction |user| at the index
63
    // |operand_index| in terms of phi instruction. This recursively builds new
64
    // phi instructions from |user| to the loop exit blocks' phis. The use of
65
    // |def_insn_| in |user| is replaced by the relevant phi instruction at the
66
    // end of the operation.
67
    // It is assumed that |user| does not dominates any of the loop exit basic
68
    // block. This operation does not update the def/use manager, instead it
69
    // records what needs to be updated. The actual update is performed by
70
    // UpdateManagers.
71
0
    bool RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
72
0
      assert(
73
0
          (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) &&
74
0
          "The root basic block must be the incoming edge if |user| is a phi "
75
0
          "instruction");
76
0
      assert((user->opcode() == spv::Op::OpPhi || bb == GetParent(user)) &&
77
0
             "The root basic block must be the instruction parent if |user| is "
78
0
             "not "
79
0
             "phi instruction");
80
81
0
      Instruction* new_def = GetOrBuildIncoming(bb->id());
82
0
      if (!new_def) {
83
0
        return false;
84
0
      }
85
86
0
      user->SetOperand(operand_index, {new_def->result_id()});
87
0
      rewritten_.insert(user);
88
0
      return true;
89
0
    }
90
91
    // In-place update of some managers (avoid full invalidation).
92
0
    inline void UpdateManagers() {
93
0
      analysis::DefUseManager* def_use_mgr = base_->context_->get_def_use_mgr();
94
      // Register all new definitions.
95
0
      for (Instruction* insn : rewritten_) {
96
0
        def_use_mgr->AnalyzeInstDef(insn);
97
0
      }
98
      // Register all new uses.
99
0
      for (Instruction* insn : rewritten_) {
100
0
        def_use_mgr->AnalyzeInstUse(insn);
101
0
      }
102
0
    }
103
104
   private:
105
    // Return the basic block that |instr| belongs to.
106
0
    BasicBlock* GetParent(Instruction* instr) {
107
0
      return base_->context_->get_instr_block(instr);
108
0
    }
109
110
    // Builds a phi instruction for the basic block |bb|. The function assumes
111
    // that |defining_blocks| contains the list of basic block that define the
112
    // usable value for each predecessor of |bb|.
113
    inline Instruction* CreatePhiInstruction(
114
0
        BasicBlock* bb, const std::vector<uint32_t>& defining_blocks) {
115
0
      std::vector<uint32_t> incomings;
116
0
      const std::vector<uint32_t>& bb_preds = base_->cfg_->preds(bb->id());
117
0
      assert(bb_preds.size() == defining_blocks.size());
118
0
      for (size_t i = 0; i < bb_preds.size(); i++) {
119
0
        incomings.push_back(
120
0
            GetOrBuildIncoming(defining_blocks[i])->result_id());
121
0
        incomings.push_back(bb_preds[i]);
122
0
      }
123
0
      InstructionBuilder builder(base_->context_, &*bb->begin(),
124
0
                                 IRContext::kAnalysisInstrToBlockMapping);
125
0
      Instruction* incoming_phi =
126
0
          builder.AddPhi(def_insn_.type_id(), incomings);
127
0
      if (!incoming_phi) {
128
0
        return nullptr;
129
0
      }
130
131
0
      rewritten_.insert(incoming_phi);
132
0
      return incoming_phi;
133
0
    }
134
135
    // Builds a phi instruction for the basic block |bb|, all incoming values
136
    // will be |value|.
137
    inline Instruction* CreatePhiInstruction(BasicBlock* bb,
138
0
                                             const Instruction& value) {
139
0
      std::vector<uint32_t> incomings;
140
0
      const std::vector<uint32_t>& bb_preds = base_->cfg_->preds(bb->id());
141
0
      for (size_t i = 0; i < bb_preds.size(); i++) {
142
0
        incomings.push_back(value.result_id());
143
0
        incomings.push_back(bb_preds[i]);
144
0
      }
145
0
      InstructionBuilder builder(base_->context_, &*bb->begin(),
146
0
                                 IRContext::kAnalysisInstrToBlockMapping);
147
0
      Instruction* incoming_phi =
148
0
          builder.AddPhi(def_insn_.type_id(), incomings);
149
0
      if (!incoming_phi) {
150
0
        return nullptr;
151
0
      }
152
153
0
      rewritten_.insert(incoming_phi);
154
0
      return incoming_phi;
155
0
    }
156
157
    // Return the new def to use for the basic block |bb_id|.
158
    // If |bb_id| does not have a suitable def to use then we:
159
    //   - return the common def used by all predecessors;
160
    //   - if there is no common def, then we build a new phi instr at the
161
    //     beginning of |bb_id| and return this new instruction.
162
0
    Instruction* GetOrBuildIncoming(uint32_t bb_id) {
163
0
      assert(base_->cfg_->block(bb_id) != nullptr && "Unknown basic block");
164
165
0
      Instruction*& incoming_phi = bb_to_phi_[bb_id];
166
0
      if (incoming_phi) {
167
0
        return incoming_phi;
168
0
      }
169
170
0
      BasicBlock* bb = &*base_->cfg_->block(bb_id);
171
      // If this is an exit basic block, look if there already is an eligible
172
      // phi instruction. An eligible phi has |def_insn_| as all incoming
173
      // values.
174
0
      if (base_->exit_bb_.count(bb)) {
175
        // Look if there is an eligible phi in this block.
176
0
        if (!bb->WhileEachPhiInst([&incoming_phi, this](Instruction* phi) {
177
0
              for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
178
0
                if (phi->GetSingleWordInOperand(i) != def_insn_.result_id())
179
0
                  return true;
180
0
              }
181
0
              incoming_phi = phi;
182
0
              rewritten_.insert(incoming_phi);
183
0
              return false;
184
0
            })) {
185
0
          return incoming_phi;
186
0
        }
187
0
        incoming_phi = CreatePhiInstruction(bb, def_insn_);
188
0
        return incoming_phi;
189
0
      }
190
191
      // Get the block that defines the value to use for each predecessor.
192
      // If the vector has 1 value, then it means that this block does not need
193
      // to build a phi instruction unless |bb_id| is the loop merge block.
194
0
      const std::vector<uint32_t>& defining_blocks =
195
0
          base_->GetDefiningBlocks(bb_id);
196
197
      // Special case for structured loops: merge block might be different from
198
      // the exit block set. To maintain structured properties it will ease
199
      // transformations if the merge block also holds a phi instruction like
200
      // the exit ones.
201
0
      if (defining_blocks.size() > 1 || bb_id == base_->merge_block_id_) {
202
0
        if (defining_blocks.size() > 1) {
203
0
          incoming_phi = CreatePhiInstruction(bb, defining_blocks);
204
0
        } else {
205
0
          assert(bb_id == base_->merge_block_id_);
206
0
          incoming_phi =
207
0
              CreatePhiInstruction(bb, *GetOrBuildIncoming(defining_blocks[0]));
208
0
        }
209
0
      } else {
210
0
        incoming_phi = GetOrBuildIncoming(defining_blocks[0]);
211
0
      }
212
213
0
      return incoming_phi;
214
0
    }
215
216
    LCSSARewriter* base_;
217
    const Instruction& def_insn_;
218
    std::unordered_map<uint32_t, Instruction*> bb_to_phi_;
219
    std::unordered_set<Instruction*> rewritten_;
220
  };
221
222
 private:
223
  // Return the new def to use for the basic block |bb_id|.
224
  // If |bb_id| does not have a suitable def to use then we:
225
  //   - return the common def used by all predecessors;
226
  //   - if there is no common def, then we build a new phi instr at the
227
  //     beginning of |bb_id| and return this new instruction.
228
0
  const std::vector<uint32_t>& GetDefiningBlocks(uint32_t bb_id) {
229
0
    assert(cfg_->block(bb_id) != nullptr && "Unknown basic block");
230
0
    std::vector<uint32_t>& defining_blocks = bb_to_defining_blocks_[bb_id];
231
232
0
    if (defining_blocks.size()) return defining_blocks;
233
234
    // Check if one of the loop exit basic block dominates |bb_id|.
235
0
    for (const BasicBlock* e_bb : exit_bb_) {
236
0
      if (dom_tree_.Dominates(e_bb->id(), bb_id)) {
237
0
        defining_blocks.push_back(e_bb->id());
238
0
        return defining_blocks;
239
0
      }
240
0
    }
241
242
    // Process parents, they will returns their suitable blocks.
243
    // If they are all the same, this means this basic block is dominated by a
244
    // common block, so we won't need to build a phi instruction.
245
0
    for (uint32_t pred_id : cfg_->preds(bb_id)) {
246
0
      const std::vector<uint32_t>& pred_blocks = GetDefiningBlocks(pred_id);
247
0
      if (pred_blocks.size() == 1)
248
0
        defining_blocks.push_back(pred_blocks[0]);
249
0
      else
250
0
        defining_blocks.push_back(pred_id);
251
0
    }
252
0
    assert(defining_blocks.size());
253
0
    if (std::all_of(defining_blocks.begin(), defining_blocks.end(),
254
0
                    [&defining_blocks](uint32_t id) {
255
0
                      return id == defining_blocks[0];
256
0
                    })) {
257
      // No need for a phi.
258
0
      defining_blocks.resize(1);
259
0
    }
260
261
0
    return defining_blocks;
262
0
  }
263
264
  IRContext* context_;
265
  CFG* cfg_;
266
  const DominatorTree& dom_tree_;
267
  const std::unordered_set<BasicBlock*>& exit_bb_;
268
  uint32_t merge_block_id_;
269
  // This map represent the set of known paths. For each key, the vector
270
  // represent the set of blocks holding the definition to be used to build the
271
  // phi instruction.
272
  // If the vector has 0 value, then the path is unknown yet, and must be built.
273
  // If the vector has 1 value, then the value defined by that basic block
274
  //   should be used.
275
  // If the vector has more than 1 value, then a phi node must be created, the
276
  //   basic block ordering is the same as the predecessor ordering.
277
  std::unordered_map<uint32_t, std::vector<uint32_t>> bb_to_defining_blocks_;
278
};
279
280
// Make the set |blocks| closed SSA. The set is closed SSA if all the uses
281
// outside the set are phi instructions in exiting basic block set (hold by
282
// |lcssa_rewriter|).
283
inline bool MakeSetClosedSSA(IRContext* context, Function* function,
284
                             const std::unordered_set<uint32_t>& blocks,
285
                             const std::unordered_set<BasicBlock*>& exit_bb,
286
0
                             LCSSARewriter* lcssa_rewriter) {
287
0
  CFG& cfg = *context->cfg();
288
0
  DominatorTree& dom_tree =
289
0
      context->GetDominatorAnalysis(function)->GetDomTree();
290
0
  analysis::DefUseManager* def_use_manager = context->get_def_use_mgr();
291
292
0
  for (uint32_t bb_id : blocks) {
293
0
    BasicBlock* bb = cfg.block(bb_id);
294
    // If bb does not dominate an exit block, then it cannot have escaping defs.
295
0
    if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue;
296
0
    for (Instruction& inst : *bb) {
297
0
      LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst);
298
0
      bool success = def_use_manager->WhileEachUse(
299
0
          &inst, [&blocks, &rewriter, &exit_bb, context](
300
0
                     Instruction* use, uint32_t operand_index) {
301
0
            BasicBlock* use_parent = context->get_instr_block(use);
302
0
            assert(use_parent);
303
0
            if (blocks.count(use_parent->id())) return true;
304
305
0
            if (use->opcode() == spv::Op::OpPhi) {
306
              // If the use is a Phi instruction and the incoming block is
307
              // coming from the loop, then that's consistent with LCSSA form.
308
0
              if (exit_bb.count(use_parent)) {
309
0
                return true;
310
0
              } else {
311
                // That's not an exit block, but the user is a phi instruction.
312
                // Consider the incoming branch only.
313
0
                use_parent = context->get_instr_block(
314
0
                    use->GetSingleWordOperand(operand_index + 1));
315
0
              }
316
0
            }
317
            // Rewrite the use. Note that this call does not invalidate the
318
            // def/use manager. So this operation is safe.
319
0
            return rewriter.RewriteUse(use_parent, use, operand_index);
320
0
          });
321
0
      if (!success) {
322
0
        return false;
323
0
      }
324
0
      rewriter.UpdateManagers();
325
0
    }
326
0
  }
327
0
  return true;
328
0
}
329
330
}  // namespace
331
332
0
bool LoopUtils::CreateLoopDedicatedExits() {
333
0
  Function* function = loop_->GetHeaderBlock()->GetParent();
334
0
  LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function);
335
0
  CFG& cfg = *context_->cfg();
336
0
  analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
337
338
0
  const IRContext::Analysis PreservedAnalyses =
339
0
      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
340
341
  // Gathers the set of basic block that are not in this loop and have at least
342
  // one predecessor in the loop and one not in the loop.
343
0
  std::unordered_set<uint32_t> exit_bb_set;
344
0
  loop_->GetExitBlocks(&exit_bb_set);
345
346
0
  std::unordered_set<BasicBlock*> new_loop_exits;
347
0
  bool made_change = false;
348
  // For each block, we create a new one that gathers all branches from
349
  // the loop and fall into the block.
350
0
  for (uint32_t non_dedicate_id : exit_bb_set) {
351
0
    BasicBlock* non_dedicate = cfg.block(non_dedicate_id);
352
0
    const std::vector<uint32_t>& bb_pred = cfg.preds(non_dedicate_id);
353
    // Ignore the block if all the predecessors are in the loop.
354
0
    if (std::all_of(bb_pred.begin(), bb_pred.end(),
355
0
                    [this](uint32_t id) { return loop_->IsInsideLoop(id); })) {
356
0
      new_loop_exits.insert(non_dedicate);
357
0
      continue;
358
0
    }
359
360
0
    made_change = true;
361
0
    Function::iterator insert_pt = function->begin();
362
0
    for (; insert_pt != function->end() && &*insert_pt != non_dedicate;
363
0
         ++insert_pt) {
364
0
    }
365
0
    assert(insert_pt != function->end() && "Basic Block not found");
366
367
    // Create the dedicate exit basic block.
368
0
    uint32_t exit_id = context_->TakeNextId();
369
0
    if (exit_id == 0) {
370
0
      return false;
371
0
    }
372
0
    BasicBlock& exit = *insert_pt.InsertBefore(
373
0
        std::unique_ptr<BasicBlock>(new BasicBlock(std::unique_ptr<Instruction>(
374
0
            new Instruction(context_, spv::Op::OpLabel, 0, exit_id, {})))));
375
0
    exit.SetParent(function);
376
377
    // Redirect in loop predecessors to |exit| block.
378
0
    for (uint32_t exit_pred_id : bb_pred) {
379
0
      if (loop_->IsInsideLoop(exit_pred_id)) {
380
0
        BasicBlock* pred_block = cfg.block(exit_pred_id);
381
0
        pred_block->ForEachSuccessorLabel([non_dedicate, &exit](uint32_t* id) {
382
0
          if (*id == non_dedicate->id()) *id = exit.id();
383
0
        });
384
        // Update the CFG.
385
        // |non_dedicate|'s predecessor list will be updated at the end of the
386
        // loop.
387
0
        cfg.RegisterBlock(pred_block);
388
0
      }
389
0
    }
390
391
    // Register the label to the def/use manager, requires for the phi patching.
392
0
    def_use_mgr->AnalyzeInstDefUse(exit.GetLabelInst());
393
0
    context_->set_instr_block(exit.GetLabelInst(), &exit);
394
395
0
    InstructionBuilder builder(context_, &exit, PreservedAnalyses);
396
    // Now jump from our dedicate basic block to the old exit.
397
    // We also reset the insert point so all instructions are inserted before
398
    // the branch.
399
0
    builder.SetInsertPoint(builder.AddBranch(non_dedicate->id()));
400
0
    bool succeeded = non_dedicate->WhileEachPhiInst(
401
0
        [&builder, &exit, def_use_mgr, this](Instruction* phi) {
402
          // New phi operands for this instruction.
403
0
          std::vector<uint32_t> new_phi_op;
404
          // Phi operands for the dedicated exit block.
405
0
          std::vector<uint32_t> exit_phi_op;
406
0
          for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
407
0
            uint32_t def_id = phi->GetSingleWordInOperand(i);
408
0
            uint32_t incoming_id = phi->GetSingleWordInOperand(i + 1);
409
0
            if (loop_->IsInsideLoop(incoming_id)) {
410
0
              exit_phi_op.push_back(def_id);
411
0
              exit_phi_op.push_back(incoming_id);
412
0
            } else {
413
0
              new_phi_op.push_back(def_id);
414
0
              new_phi_op.push_back(incoming_id);
415
0
            }
416
0
          }
417
418
          // Build the new phi instruction dedicated exit block.
419
0
          Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op);
420
0
          if (!exit_phi) {
421
0
            return false;
422
0
          }
423
          // Build the new incoming branch.
424
0
          new_phi_op.push_back(exit_phi->result_id());
425
0
          new_phi_op.push_back(exit.id());
426
          // Rewrite operands.
427
0
          uint32_t idx = 0;
428
0
          for (; idx < new_phi_op.size(); idx++)
429
0
            phi->SetInOperand(idx, {new_phi_op[idx]});
430
          // Remove extra operands, from last to first (more efficient).
431
0
          for (uint32_t j = phi->NumInOperands() - 1; j >= idx; j--)
432
0
            phi->RemoveInOperand(j);
433
          // Update the def/use manager for this |phi|.
434
0
          def_use_mgr->AnalyzeInstUse(phi);
435
0
          return true;
436
0
        });
437
0
    if (!succeeded) return false;
438
    // Update the CFG.
439
0
    cfg.RegisterBlock(&exit);
440
0
    cfg.RemoveNonExistingEdges(non_dedicate->id());
441
0
    new_loop_exits.insert(&exit);
442
    // If non_dedicate is in a loop, add the new dedicated exit in that loop.
443
0
    if (Loop* parent_loop = loop_desc[non_dedicate])
444
0
      parent_loop->AddBasicBlock(&exit);
445
0
  }
446
447
0
  if (new_loop_exits.size() == 1) {
448
0
    loop_->SetMergeBlock(*new_loop_exits.begin());
449
0
  }
450
451
0
  if (made_change) {
452
0
    context_->InvalidateAnalysesExceptFor(
453
0
        PreservedAnalyses | IRContext::kAnalysisCFG |
454
0
        IRContext::Analysis::kAnalysisLoopAnalysis);
455
0
  }
456
0
  return true;
457
0
}
458
459
0
bool LoopUtils::MakeLoopClosedSSA() {
460
0
  if (!CreateLoopDedicatedExits()) {
461
0
    return false;
462
0
  }
463
464
0
  Function* function = loop_->GetHeaderBlock()->GetParent();
465
0
  CFG& cfg = *context_->cfg();
466
0
  DominatorTree& dom_tree =
467
0
      context_->GetDominatorAnalysis(function)->GetDomTree();
468
469
0
  std::unordered_set<BasicBlock*> exit_bb;
470
0
  {
471
0
    std::unordered_set<uint32_t> exit_bb_id;
472
0
    loop_->GetExitBlocks(&exit_bb_id);
473
0
    for (uint32_t bb_id : exit_bb_id) {
474
0
      exit_bb.insert(cfg.block(bb_id));
475
0
    }
476
0
  }
477
478
0
  LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb,
479
0
                               loop_->GetMergeBlock());
480
0
  if (!MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
481
0
                        &lcssa_rewriter)) {
482
0
    return false;
483
0
  }
484
485
  // Make sure all defs post-dominated by the merge block have their last use no
486
  // further than the merge block.
487
0
  if (loop_->GetMergeBlock()) {
488
0
    std::unordered_set<uint32_t> merging_bb_id;
489
0
    loop_->GetMergingBlocks(&merging_bb_id);
490
0
    merging_bb_id.erase(loop_->GetMergeBlock()->id());
491
    // Reset the exit set, now only the merge block is the exit.
492
0
    exit_bb.clear();
493
0
    exit_bb.insert(loop_->GetMergeBlock());
494
    // LCSSARewriter is reusable here only because it forces the creation of a
495
    // phi instruction in the merge block.
496
0
    if (!MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
497
0
                          &lcssa_rewriter)) {
498
0
      return false;
499
0
    }
500
0
  }
501
502
0
  context_->InvalidateAnalysesExceptFor(
503
0
      IRContext::Analysis::kAnalysisCFG |
504
0
      IRContext::Analysis::kAnalysisDominatorAnalysis |
505
0
      IRContext::Analysis::kAnalysisLoopAnalysis);
506
0
  return true;
507
0
}
508
509
0
Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const {
510
  // Compute the structured order of the loop basic blocks and store it in the
511
  // vector ordered_loop_blocks.
512
0
  std::vector<BasicBlock*> ordered_loop_blocks;
513
0
  loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks);
514
515
  // Clone the loop.
516
0
  return CloneLoop(cloning_result, ordered_loop_blocks);
517
0
}
518
519
0
Loop* LoopUtils::CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result) {
520
  // Clone the loop.
521
0
  Loop* cloned_loop = CloneLoop(cloning_result);
522
0
  if (!cloned_loop) {
523
0
    return nullptr;
524
0
  }
525
526
  // Create a new exit block/label for the new loop.
527
0
  uint32_t new_label_id = context_->TakeNextId();
528
0
  if (new_label_id == 0) {
529
0
    return nullptr;
530
0
  }
531
0
  std::unique_ptr<Instruction> new_label{
532
0
      new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})};
533
0
  std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
534
0
  new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent());
535
536
  // Create an unconditional branch to the header block.
537
0
  InstructionBuilder builder{context_, new_exit_bb.get()};
538
0
  builder.AddBranch(loop_->GetHeaderBlock()->id());
539
540
  // Save the ids of the new and old merge block.
541
0
  const uint32_t old_merge_block = loop_->GetMergeBlock()->id();
542
0
  const uint32_t new_merge_block = new_exit_bb->id();
543
544
  // Replace the uses of the old merge block in the new loop with the new merge
545
  // block.
546
0
  for (std::unique_ptr<BasicBlock>& basic_block : cloning_result->cloned_bb_) {
547
0
    for (Instruction& inst : *basic_block) {
548
      // For each operand in each instruction check if it is using the old merge
549
      // block and change it to be the new merge block.
550
0
      auto replace_merge_use = [old_merge_block,
551
0
                                new_merge_block](uint32_t* id) {
552
0
        if (*id == old_merge_block) *id = new_merge_block;
553
0
      };
554
0
      inst.ForEachInOperand(replace_merge_use);
555
0
    }
556
0
  }
557
558
0
  const uint32_t old_header = loop_->GetHeaderBlock()->id();
559
0
  const uint32_t new_header = cloned_loop->GetHeaderBlock()->id();
560
0
  analysis::DefUseManager* def_use = context_->get_def_use_mgr();
561
562
0
  def_use->ForEachUse(old_header,
563
0
                      [new_header, this](Instruction* inst, uint32_t operand) {
564
0
                        if (!this->loop_->IsInsideLoop(inst))
565
0
                          inst->SetOperand(operand, {new_header});
566
0
                      });
567
568
0
  BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock();
569
0
  if (!pre_header) {
570
0
    return nullptr;
571
0
  }
572
0
  def_use->ForEachUse(
573
0
      pre_header->id(),
574
0
      [new_merge_block, this](Instruction* inst, uint32_t operand) {
575
0
        if (this->loop_->IsInsideLoop(inst))
576
0
          inst->SetOperand(operand, {new_merge_block});
577
0
      });
578
0
  cloned_loop->SetMergeBlock(new_exit_bb.get());
579
580
0
  cloned_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock());
581
582
  // Add the new block into the cloned instructions.
583
0
  cloning_result->cloned_bb_.push_back(std::move(new_exit_bb));
584
585
0
  return cloned_loop;
586
0
}
587
588
Loop* LoopUtils::CloneLoop(
589
    LoopCloningResult* cloning_result,
590
0
    const std::vector<BasicBlock*>& ordered_loop_blocks) const {
591
0
  analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
592
593
0
  std::unique_ptr<Loop> new_loop = MakeUnique<Loop>(context_);
594
595
0
  CFG& cfg = *context_->cfg();
596
597
  // Clone and place blocks in a SPIR-V compliant order (dominators first).
598
0
  for (BasicBlock* old_bb : ordered_loop_blocks) {
599
    // For each basic block in the loop, we clone it and register the mapping
600
    // between old and new ids.
601
0
    BasicBlock* new_bb = old_bb->Clone(context_);
602
0
    if (!new_bb) return nullptr;
603
0
    new_bb->SetParent(&function_);
604
0
    uint32_t new_label_id = context_->TakeNextId();
605
0
    if (new_label_id == 0) {
606
0
      return nullptr;
607
0
    }
608
0
    new_bb->GetLabelInst()->SetResultId(new_label_id);
609
0
    def_use_mgr->AnalyzeInstDef(new_bb->GetLabelInst());
610
0
    context_->set_instr_block(new_bb->GetLabelInst(), new_bb);
611
0
    cloning_result->cloned_bb_.emplace_back(new_bb);
612
613
0
    cloning_result->old_to_new_bb_[old_bb->id()] = new_bb;
614
0
    cloning_result->new_to_old_bb_[new_bb->id()] = old_bb;
615
0
    cloning_result->value_map_[old_bb->id()] = new_bb->id();
616
617
0
    if (loop_->IsInsideLoop(old_bb)) new_loop->AddBasicBlock(new_bb);
618
619
0
    for (auto new_inst = new_bb->begin(), old_inst = old_bb->begin();
620
0
         new_inst != new_bb->end(); ++new_inst, ++old_inst) {
621
0
      cloning_result->ptr_map_[&*new_inst] = &*old_inst;
622
0
      if (new_inst->HasResultId()) {
623
0
        uint32_t new_result_id = context_->TakeNextId();
624
0
        if (new_result_id == 0) {
625
0
          return nullptr;
626
0
        }
627
0
        new_inst->SetResultId(new_result_id);
628
0
        cloning_result->value_map_[old_inst->result_id()] =
629
0
            new_inst->result_id();
630
631
        // Only look at the defs for now, uses are not updated yet.
632
0
        def_use_mgr->AnalyzeInstDef(&*new_inst);
633
0
      }
634
0
    }
635
0
  }
636
637
  // All instructions (including all labels) have been cloned,
638
  // remap instruction operands id with the new ones.
639
0
  for (std::unique_ptr<BasicBlock>& bb_ref : cloning_result->cloned_bb_) {
640
0
    BasicBlock* bb = bb_ref.get();
641
642
0
    for (Instruction& insn : *bb) {
643
0
      insn.ForEachInId([cloning_result](uint32_t* old_id) {
644
        // If the operand is defined in the loop, remap the id.
645
0
        auto id_it = cloning_result->value_map_.find(*old_id);
646
0
        if (id_it != cloning_result->value_map_.end()) {
647
0
          *old_id = id_it->second;
648
0
        }
649
0
      });
650
      // Only look at what the instruction uses. All defs are register, so all
651
      // should be fine now.
652
0
      def_use_mgr->AnalyzeInstUse(&insn);
653
0
      context_->set_instr_block(&insn, bb);
654
0
    }
655
0
    cfg.RegisterBlock(bb);
656
0
  }
657
658
0
  PopulateLoopNest(new_loop.get(), *cloning_result);
659
660
0
  return new_loop.release();
661
0
}
662
663
void LoopUtils::PopulateLoopNest(
664
0
    Loop* new_loop, const LoopCloningResult& cloning_result) const {
665
0
  std::unordered_map<Loop*, Loop*> loop_mapping;
666
0
  loop_mapping[loop_] = new_loop;
667
668
0
  if (loop_->HasParent()) loop_->GetParent()->AddNestedLoop(new_loop);
669
0
  PopulateLoopDesc(new_loop, loop_, cloning_result);
670
671
0
  for (Loop& sub_loop :
672
0
       make_range(++TreeDFIterator<Loop>(loop_), TreeDFIterator<Loop>())) {
673
0
    Loop* cloned = new Loop(context_);
674
0
    if (Loop* parent = loop_mapping[sub_loop.GetParent()])
675
0
      parent->AddNestedLoop(cloned);
676
0
    loop_mapping[&sub_loop] = cloned;
677
0
    PopulateLoopDesc(cloned, &sub_loop, cloning_result);
678
0
  }
679
680
0
  loop_desc_->AddLoopNest(std::unique_ptr<Loop>(new_loop));
681
0
}
682
683
// Populates |new_loop| descriptor according to |old_loop|'s one.
684
void LoopUtils::PopulateLoopDesc(
685
    Loop* new_loop, Loop* old_loop,
686
0
    const LoopCloningResult& cloning_result) const {
687
0
  for (uint32_t bb_id : old_loop->GetBlocks()) {
688
0
    BasicBlock* bb = cloning_result.old_to_new_bb_.at(bb_id);
689
0
    new_loop->AddBasicBlock(bb);
690
0
  }
691
0
  new_loop->SetHeaderBlock(
692
0
      cloning_result.old_to_new_bb_.at(old_loop->GetHeaderBlock()->id()));
693
0
  if (old_loop->GetLatchBlock())
694
0
    new_loop->SetLatchBlock(
695
0
        cloning_result.old_to_new_bb_.at(old_loop->GetLatchBlock()->id()));
696
0
  if (old_loop->GetContinueBlock())
697
0
    new_loop->SetContinueBlock(
698
0
        cloning_result.old_to_new_bb_.at(old_loop->GetContinueBlock()->id()));
699
0
  if (old_loop->GetMergeBlock()) {
700
0
    auto it =
701
0
        cloning_result.old_to_new_bb_.find(old_loop->GetMergeBlock()->id());
702
0
    BasicBlock* bb = it != cloning_result.old_to_new_bb_.end()
703
0
                         ? it->second
704
0
                         : old_loop->GetMergeBlock();
705
0
    new_loop->SetMergeBlock(bb);
706
0
  }
707
0
  if (old_loop->GetPreHeaderBlock()) {
708
0
    auto it =
709
0
        cloning_result.old_to_new_bb_.find(old_loop->GetPreHeaderBlock()->id());
710
0
    if (it != cloning_result.old_to_new_bb_.end()) {
711
0
      new_loop->SetPreHeaderBlock(it->second);
712
0
    }
713
0
  }
714
0
}
715
716
// Class to gather some metrics about a region of interest.
717
0
void CodeMetrics::Analyze(const Loop& loop) {
718
0
  CFG& cfg = *loop.GetContext()->cfg();
719
720
0
  roi_size_ = 0;
721
0
  block_sizes_.clear();
722
723
0
  for (uint32_t id : loop.GetBlocks()) {
724
0
    const BasicBlock* bb = cfg.block(id);
725
0
    size_t bb_size = 0;
726
0
    bb->ForEachInst([&bb_size](const Instruction* insn) {
727
0
      if (insn->opcode() == spv::Op::OpLabel) return;
728
0
      if (insn->IsNop()) return;
729
0
      if (insn->opcode() == spv::Op::OpPhi) return;
730
0
      bb_size++;
731
0
    });
732
0
    block_sizes_[bb->id()] = bb_size;
733
0
    roi_size_ += bb_size;
734
0
  }
735
0
}
736
737
}  // namespace opt
738
}  // namespace spvtools