Coverage Report

Created: 2025-10-10 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/spirv-tools/source/opt/cfg.cpp
Line
Count
Source
1
// Copyright (c) 2017 Google Inc.
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/cfg.h"
16
17
#include <memory>
18
#include <utility>
19
20
#include "source/cfa.h"
21
#include "source/opt/ir_builder.h"
22
#include "source/opt/ir_context.h"
23
#include "source/opt/module.h"
24
25
namespace spvtools {
26
namespace opt {
27
namespace {
28
29
using cbb_ptr = const opt::BasicBlock*;
30
31
// Universal Limit of ResultID + 1
32
constexpr int kMaxResultId = 0x400000;
33
34
}  // namespace
35
36
CFG::CFG(Module* module)
37
4.33k
    : module_(module),
38
4.33k
      pseudo_entry_block_(std::unique_ptr<Instruction>(
39
4.33k
          new Instruction(module->context(), spv::Op::OpLabel, 0, 0, {}))),
40
4.33k
      pseudo_exit_block_(std::unique_ptr<Instruction>(new Instruction(
41
4.33k
          module->context(), spv::Op::OpLabel, 0, kMaxResultId, {}))) {
42
5.38k
  for (auto& fn : *module) {
43
140k
    for (auto& blk : fn) {
44
140k
      RegisterBlock(&blk);
45
140k
    }
46
5.38k
  }
47
4.33k
}
48
49
140k
void CFG::AddEdges(BasicBlock* blk) {
50
140k
  uint32_t blk_id = blk->id();
51
  // Force the creation of an entry, not all basic block have predecessors
52
  // (such as the entry blocks and some unreachables).
53
140k
  label2preds_[blk_id];
54
140k
  const auto* const_blk = blk;
55
140k
  const_blk->ForEachSuccessorLabel(
56
184k
      [blk_id, this](const uint32_t succ_id) { AddEdge(blk_id, succ_id); });
57
140k
}
58
59
0
void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
60
0
  std::vector<uint32_t> updated_pred_list;
61
0
  for (uint32_t id : preds(blk_id)) {
62
0
    const BasicBlock* pred_blk = block(id);
63
0
    bool has_branch = false;
64
0
    pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) {
65
0
      if (succ == blk_id) {
66
0
        has_branch = true;
67
0
      }
68
0
    });
69
0
    if (has_branch) updated_pred_list.push_back(id);
70
0
  }
71
72
0
  label2preds_.at(blk_id) = std::move(updated_pred_list);
73
0
}
74
75
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
76
11.1k
                                 std::list<BasicBlock*>* order) {
77
11.1k
  ComputeStructuredOrder(func, root, nullptr, order);
78
11.1k
}
79
80
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
81
                                 BasicBlock* end,
82
11.1k
                                 std::list<BasicBlock*>* order) {
83
11.1k
  assert(module_->context()->get_feature_mgr()->HasCapability(
84
11.1k
             spv::Capability::Shader) &&
85
11.1k
         "This only works on structured control flow");
86
87
  // Compute structured successors and do DFS.
88
11.1k
  ComputeStructuredSuccessors(func);
89
231k
  auto ignore_block = [](cbb_ptr) {};
90
622k
  auto terminal = [end](cbb_ptr bb) { return bb == end; };
91
92
854k
  auto get_structured_successors = [this](const BasicBlock* b) {
93
854k
    return &(block2structured_succs_[b]);
94
854k
  };
95
96
  // TODO(greg-lunarg): Get rid of const_cast by making moving const
97
  // out of the cfa.h prototypes and into the invoking code.
98
231k
  auto post_order = [&](cbb_ptr b) {
99
231k
    order->push_front(const_cast<BasicBlock*>(b));
100
231k
  };
101
11.1k
  CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
102
11.1k
                                       ignore_block, post_order, terminal);
103
11.1k
}
104
105
void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
106
146
                                  const std::function<void(BasicBlock*)>& f) {
107
146
  std::vector<BasicBlock*> po;
108
146
  std::unordered_set<BasicBlock*> seen;
109
146
  ComputePostOrderTraversal(bb, &po, &seen);
110
111
2.30k
  for (BasicBlock* current_bb : po) {
112
2.30k
    if (!IsPseudoExitBlock(current_bb) && !IsPseudoEntryBlock(current_bb)) {
113
2.30k
      f(current_bb);
114
2.30k
    }
115
2.30k
  }
116
146
}
117
118
void CFG::ForEachBlockInReversePostOrder(
119
3.09k
    BasicBlock* bb, const std::function<void(BasicBlock*)>& f) {
120
44.2k
  WhileEachBlockInReversePostOrder(bb, [f](BasicBlock* b) {
121
44.2k
    f(b);
122
44.2k
    return true;
123
44.2k
  });
124
3.09k
}
125
126
bool CFG::WhileEachBlockInReversePostOrder(
127
4.80k
    BasicBlock* bb, const std::function<bool(BasicBlock*)>& f) {
128
4.80k
  std::vector<BasicBlock*> po;
129
4.80k
  std::unordered_set<BasicBlock*> seen;
130
4.80k
  ComputePostOrderTraversal(bb, &po, &seen);
131
132
72.8k
  for (auto current_bb = po.rbegin(); current_bb != po.rend(); ++current_bb) {
133
68.0k
    if (!IsPseudoExitBlock(*current_bb) && !IsPseudoEntryBlock(*current_bb)) {
134
68.0k
      if (!f(*current_bb)) {
135
0
        return false;
136
0
      }
137
68.0k
    }
138
68.0k
  }
139
4.80k
  return true;
140
4.80k
}
141
142
11.1k
void CFG::ComputeStructuredSuccessors(Function* func) {
143
11.1k
  block2structured_succs_.clear();
144
231k
  for (auto& blk : *func) {
145
    // If no predecessors in function, make successor to pseudo entry.
146
231k
    if (label2preds_[blk.id()].size() == 0)
147
11.4k
      block2structured_succs_[&pseudo_entry_block_].push_back(&blk);
148
149
    // If header, make merge block first successor and continue block second
150
    // successor if there is one.
151
231k
    uint32_t mbid = blk.MergeBlockIdIfAny();
152
231k
    if (mbid != 0) {
153
69.3k
      block2structured_succs_[&blk].push_back(block(mbid));
154
69.3k
      uint32_t cbid = blk.ContinueBlockIdIfAny();
155
69.3k
      if (cbid != 0) {
156
21.4k
        block2structured_succs_[&blk].push_back(block(cbid));
157
21.4k
      }
158
69.3k
    }
159
160
    // Add true successors.
161
231k
    const auto& const_blk = blk;
162
300k
    const_blk.ForEachSuccessorLabel([&blk, this](const uint32_t sbid) {
163
300k
      block2structured_succs_[&blk].push_back(block(sbid));
164
300k
    });
165
231k
  }
166
11.1k
}
167
168
void CFG::ComputePostOrderTraversal(BasicBlock* bb,
169
                                    std::vector<BasicBlock*>* order,
170
4.94k
                                    std::unordered_set<BasicBlock*>* seen) {
171
4.94k
  std::vector<BasicBlock*> stack;
172
4.94k
  stack.push_back(bb);
173
140k
  while (!stack.empty()) {
174
135k
    bb = stack.back();
175
135k
    seen->insert(bb);
176
135k
    static_cast<const BasicBlock*>(bb)->WhileEachSuccessorLabel(
177
179k
        [&seen, &stack, this](const uint32_t sbid) {
178
179k
          BasicBlock* succ_bb = id2block_[sbid];
179
179k
          if (!seen->count(succ_bb)) {
180
65.4k
            stack.push_back(succ_bb);
181
65.4k
            return false;
182
65.4k
          }
183
113k
          return true;
184
179k
        });
185
135k
    if (stack.back() == bb) {
186
70.3k
      order->push_back(bb);
187
70.3k
      stack.pop_back();
188
70.3k
    }
189
135k
  }
190
4.94k
}
191
192
0
BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
193
0
  assert(bb->GetLoopMergeInst() && "Expecting bb to be the header of a loop.");
194
195
0
  Function* fn = bb->GetParent();
196
0
  IRContext* context = module_->context();
197
198
  // Get the new header id up front.  If we are out of ids, then we cannot split
199
  // the loop.
200
0
  uint32_t new_header_id = context->TakeNextId();
201
0
  if (new_header_id == 0) {
202
0
    return nullptr;
203
0
  }
204
205
  // Find the insertion point for the new bb.
206
0
  Function::iterator header_it = std::find_if(
207
0
      fn->begin(), fn->end(),
208
0
      [bb](BasicBlock& block_in_func) { return &block_in_func == bb; });
209
0
  assert(header_it != fn->end());
210
211
0
  const std::vector<uint32_t>& pred = preds(bb->id());
212
  // Find the back edge
213
0
  BasicBlock* latch_block = nullptr;
214
0
  Function::iterator latch_block_iter = header_it;
215
0
  for (; latch_block_iter != fn->end(); ++latch_block_iter) {
216
    // If blocks are in the proper order, then the only branch that appears
217
    // after the header is the latch.
218
0
    if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) !=
219
0
        pred.end()) {
220
0
      break;
221
0
    }
222
0
  }
223
0
  assert(latch_block_iter != fn->end() && "Could not find the latch.");
224
0
  latch_block = &*latch_block_iter;
225
226
0
  RemoveSuccessorEdges(bb);
227
228
  // Create the new header bb basic bb.
229
  // Leave the phi instructions behind.
230
0
  auto iter = bb->begin();
231
0
  while (iter->opcode() == spv::Op::OpPhi) {
232
0
    ++iter;
233
0
  }
234
235
0
  BasicBlock* new_header = bb->SplitBasicBlock(context, new_header_id, iter);
236
0
  context->AnalyzeDefUse(new_header->GetLabelInst());
237
238
  // Update cfg
239
0
  RegisterBlock(new_header);
240
241
  // Update bb mappings.
242
0
  context->set_instr_block(new_header->GetLabelInst(), new_header);
243
0
  new_header->ForEachInst([new_header, context](Instruction* inst) {
244
0
    context->set_instr_block(inst, new_header);
245
0
  });
246
247
  // If |bb| was the latch block, the branch back to the header is not in
248
  // |new_header|.
249
0
  if (latch_block == bb) {
250
0
    if (new_header->ContinueBlockId() == bb->id()) {
251
0
      new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id});
252
0
    }
253
0
    latch_block = new_header;
254
0
  }
255
256
  // Adjust the OpPhi instructions as needed.
257
0
  bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header,
258
0
                                  context](Instruction* phi) -> bool {
259
0
    std::vector<uint32_t> preheader_phi_ops;
260
0
    std::vector<Operand> header_phi_ops;
261
262
    // Identify where the original inputs to original OpPhi belong: header
263
    // or preheader.
264
0
    for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
265
0
      uint32_t def_id = phi->GetSingleWordInOperand(i);
266
0
      uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
267
0
      if (branch_id == latch_block->id()) {
268
0
        header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {def_id}});
269
0
        header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {branch_id}});
270
0
      } else {
271
0
        preheader_phi_ops.push_back(def_id);
272
0
        preheader_phi_ops.push_back(branch_id);
273
0
      }
274
0
    }
275
276
    // Create a phi instruction if and only if the preheader_phi_ops has
277
    // more than one pair.
278
0
    if (preheader_phi_ops.size() > 2) {
279
0
      InstructionBuilder builder(
280
0
          context, &*bb->begin(),
281
0
          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
282
283
0
      Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
284
0
      if (!new_phi) {
285
0
        return false;
286
0
      }
287
288
      // Add the OpPhi to the header bb.
289
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
290
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
291
0
    } else {
292
      // An OpPhi with a single entry is just a copy.  In this case use the
293
      // same instruction in the new header.
294
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
295
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
296
0
    }
297
298
0
    phi->RemoveFromList();
299
0
    std::unique_ptr<Instruction> phi_owner(phi);
300
0
    phi->SetInOperands(std::move(header_phi_ops));
301
0
    new_header->begin()->InsertBefore(std::move(phi_owner));
302
0
    context->set_instr_block(phi, new_header);
303
0
    context->AnalyzeUses(phi);
304
0
    return true;
305
0
  });
306
307
0
  if (!ok) {
308
0
    return nullptr;
309
0
  }
310
311
  // Add a branch to the new header.
312
0
  InstructionBuilder branch_builder(
313
0
      context, bb,
314
0
      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
315
0
  bb->AddInstruction(
316
0
      MakeUnique<Instruction>(context, spv::Op::OpBranch, 0, 0,
317
0
                              std::initializer_list<Operand>{
318
0
                                  {SPV_OPERAND_TYPE_ID, {new_header->id()}}}));
319
0
  context->AnalyzeUses(bb->terminator());
320
0
  context->set_instr_block(bb->terminator(), bb);
321
0
  label2preds_[new_header->id()].push_back(bb->id());
322
323
  // Update the latch to branch to the new header.
324
0
  latch_block->ForEachSuccessorLabel([bb, new_header_id](uint32_t* id) {
325
0
    if (*id == bb->id()) {
326
0
      *id = new_header_id;
327
0
    }
328
0
  });
329
0
  Instruction* latch_branch = latch_block->terminator();
330
0
  context->AnalyzeUses(latch_branch);
331
0
  label2preds_[new_header->id()].push_back(latch_block->id());
332
333
0
  auto& block_preds = label2preds_[bb->id()];
334
0
  auto latch_pos =
335
0
      std::find(block_preds.begin(), block_preds.end(), latch_block->id());
336
0
  assert(latch_pos != block_preds.end() && "The cfg was invalid.");
337
0
  block_preds.erase(latch_pos);
338
339
  // Update the loop descriptors
340
0
  if (context->AreAnalysesValid(IRContext::kAnalysisLoopAnalysis)) {
341
0
    LoopDescriptor* loop_desc = context->GetLoopDescriptor(bb->GetParent());
342
0
    Loop* loop = (*loop_desc)[bb->id()];
343
344
0
    loop->AddBasicBlock(new_header_id);
345
0
    loop->SetHeaderBlock(new_header);
346
0
    loop_desc->SetBasicBlockToLoop(new_header_id, loop);
347
348
0
    loop->RemoveBasicBlock(bb->id());
349
0
    loop->SetPreHeaderBlock(bb);
350
351
0
    Loop* parent_loop = loop->GetParent();
352
0
    if (parent_loop != nullptr) {
353
0
      parent_loop->AddBasicBlock(bb->id());
354
0
      loop_desc->SetBasicBlockToLoop(bb->id(), parent_loop);
355
0
    } else {
356
0
      loop_desc->SetBasicBlockToLoop(bb->id(), nullptr);
357
0
    }
358
0
  }
359
0
  return new_header;
360
0
}
361
362
}  // namespace opt
363
}  // namespace spvtools