Coverage Report

Created: 2024-09-11 07:09

/src/spirv-tools/source/opt/cfg.cpp
Line
Count
Source (jump to first uncovered line)
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
    : module_(module),
38
      pseudo_entry_block_(std::unique_ptr<Instruction>(
39
          new Instruction(module->context(), spv::Op::OpLabel, 0, 0, {}))),
40
      pseudo_exit_block_(std::unique_ptr<Instruction>(new Instruction(
41
51.0k
          module->context(), spv::Op::OpLabel, 0, kMaxResultId, {}))) {
42
76.9k
  for (auto& fn : *module) {
43
9.28M
    for (auto& blk : fn) {
44
9.28M
      RegisterBlock(&blk);
45
9.28M
    }
46
76.9k
  }
47
51.0k
}
48
49
9.30M
void CFG::AddEdges(BasicBlock* blk) {
50
9.30M
  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
9.30M
  label2preds_[blk_id];
54
9.30M
  const auto* const_blk = blk;
55
9.30M
  const_blk->ForEachSuccessorLabel(
56
9.85M
      [blk_id, this](const uint32_t succ_id) { AddEdge(blk_id, succ_id); });
57
9.30M
}
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
115k
                                 std::list<BasicBlock*>* order) {
77
115k
  ComputeStructuredOrder(func, root, nullptr, order);
78
115k
}
79
80
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
81
                                 BasicBlock* end,
82
118k
                                 std::list<BasicBlock*>* order) {
83
118k
  assert(module_->context()->get_feature_mgr()->HasCapability(
84
118k
             spv::Capability::Shader) &&
85
118k
         "This only works on structured control flow");
86
87
  // Compute structured successors and do DFS.
88
118k
  ComputeStructuredSuccessors(func);
89
11.7M
  auto ignore_block = [](cbb_ptr) {};
90
25.8M
  auto terminal = [end](cbb_ptr bb) { return bb == end; };
91
92
37.6M
  auto get_structured_successors = [this](const BasicBlock* b) {
93
37.6M
    return &(block2structured_succs_[b]);
94
37.6M
  };
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
11.7M
  auto post_order = [&](cbb_ptr b) {
99
11.7M
    order->push_front(const_cast<BasicBlock*>(b));
100
11.7M
  };
101
118k
  CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
102
118k
                                       ignore_block, post_order, terminal);
103
118k
}
104
105
void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
106
3.36k
                                  const std::function<void(BasicBlock*)>& f) {
107
3.36k
  std::vector<BasicBlock*> po;
108
3.36k
  std::unordered_set<BasicBlock*> seen;
109
3.36k
  ComputePostOrderTraversal(bb, &po, &seen);
110
111
55.2k
  for (BasicBlock* current_bb : po) {
112
55.2k
    if (!IsPseudoExitBlock(current_bb) && !IsPseudoEntryBlock(current_bb)) {
113
55.2k
      f(current_bb);
114
55.2k
    }
115
55.2k
  }
116
3.36k
}
117
118
void CFG::ForEachBlockInReversePostOrder(
119
24.2k
    BasicBlock* bb, const std::function<void(BasicBlock*)>& f) {
120
5.23M
  WhileEachBlockInReversePostOrder(bb, [f](BasicBlock* b) {
121
5.23M
    f(b);
122
5.23M
    return true;
123
5.23M
  });
124
24.2k
}
125
126
bool CFG::WhileEachBlockInReversePostOrder(
127
40.4k
    BasicBlock* bb, const std::function<bool(BasicBlock*)>& f) {
128
40.4k
  std::vector<BasicBlock*> po;
129
40.4k
  std::unordered_set<BasicBlock*> seen;
130
40.4k
  ComputePostOrderTraversal(bb, &po, &seen);
131
132
5.65M
  for (auto current_bb = po.rbegin(); current_bb != po.rend(); ++current_bb) {
133
5.61M
    if (!IsPseudoExitBlock(*current_bb) && !IsPseudoEntryBlock(*current_bb)) {
134
5.61M
      if (!f(*current_bb)) {
135
0
        return false;
136
0
      }
137
5.61M
    }
138
5.61M
  }
139
40.4k
  return true;
140
40.4k
}
141
142
118k
void CFG::ComputeStructuredSuccessors(Function* func) {
143
118k
  block2structured_succs_.clear();
144
11.7M
  for (auto& blk : *func) {
145
    // If no predecessors in function, make successor to pseudo entry.
146
11.7M
    if (label2preds_[blk.id()].size() == 0)
147
324k
      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
11.7M
    uint32_t mbid = blk.MergeBlockIdIfAny();
152
11.7M
    if (mbid != 0) {
153
1.20M
      block2structured_succs_[&blk].push_back(block(mbid));
154
1.20M
      uint32_t cbid = blk.ContinueBlockIdIfAny();
155
1.20M
      if (cbid != 0) {
156
289k
        block2structured_succs_[&blk].push_back(block(cbid));
157
289k
      }
158
1.20M
    }
159
160
    // Add true successors.
161
11.7M
    const auto& const_blk = blk;
162
12.6M
    const_blk.ForEachSuccessorLabel([&blk, this](const uint32_t sbid) {
163
12.6M
      block2structured_succs_[&blk].push_back(block(sbid));
164
12.6M
    });
165
11.7M
  }
166
118k
}
167
168
void CFG::ComputePostOrderTraversal(BasicBlock* bb,
169
                                    std::vector<BasicBlock*>* order,
170
43.8k
                                    std::unordered_set<BasicBlock*>* seen) {
171
43.8k
  std::vector<BasicBlock*> stack;
172
43.8k
  stack.push_back(bb);
173
11.3M
  while (!stack.empty()) {
174
11.3M
    bb = stack.back();
175
11.3M
    seen->insert(bb);
176
11.3M
    static_cast<const BasicBlock*>(bb)->WhileEachSuccessorLabel(
177
11.8M
        [&seen, &stack, this](const uint32_t sbid) {
178
11.8M
          BasicBlock* succ_bb = id2block_[sbid];
179
11.8M
          if (!seen->count(succ_bb)) {
180
5.63M
            stack.push_back(succ_bb);
181
5.63M
            return false;
182
5.63M
          }
183
6.20M
          return true;
184
11.8M
        });
185
11.3M
    if (stack.back() == bb) {
186
5.67M
      order->push_back(bb);
187
5.67M
      stack.pop_back();
188
5.67M
    }
189
11.3M
  }
190
43.8k
}
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
  bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
258
0
    std::vector<uint32_t> preheader_phi_ops;
259
0
    std::vector<Operand> header_phi_ops;
260
261
    // Identify where the original inputs to original OpPhi belong: header or
262
    // preheader.
263
0
    for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
264
0
      uint32_t def_id = phi->GetSingleWordInOperand(i);
265
0
      uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
266
0
      if (branch_id == latch_block->id()) {
267
0
        header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {def_id}});
268
0
        header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {branch_id}});
269
0
      } else {
270
0
        preheader_phi_ops.push_back(def_id);
271
0
        preheader_phi_ops.push_back(branch_id);
272
0
      }
273
0
    }
274
275
    // Create a phi instruction if and only if the preheader_phi_ops has more
276
    // than one pair.
277
0
    if (preheader_phi_ops.size() > 2) {
278
0
      InstructionBuilder builder(
279
0
          context, &*bb->begin(),
280
0
          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
281
282
0
      Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
283
284
      // Add the OpPhi to the header bb.
285
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
286
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
287
0
    } else {
288
      // An OpPhi with a single entry is just a copy.  In this case use the same
289
      // instruction in the new header.
290
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
291
0
      header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
292
0
    }
293
294
0
    phi->RemoveFromList();
295
0
    std::unique_ptr<Instruction> phi_owner(phi);
296
0
    phi->SetInOperands(std::move(header_phi_ops));
297
0
    new_header->begin()->InsertBefore(std::move(phi_owner));
298
0
    context->set_instr_block(phi, new_header);
299
0
    context->AnalyzeUses(phi);
300
0
  });
301
302
  // Add a branch to the new header.
303
0
  InstructionBuilder branch_builder(
304
0
      context, bb,
305
0
      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
306
0
  bb->AddInstruction(
307
0
      MakeUnique<Instruction>(context, spv::Op::OpBranch, 0, 0,
308
0
                              std::initializer_list<Operand>{
309
0
                                  {SPV_OPERAND_TYPE_ID, {new_header->id()}}}));
310
0
  context->AnalyzeUses(bb->terminator());
311
0
  context->set_instr_block(bb->terminator(), bb);
312
0
  label2preds_[new_header->id()].push_back(bb->id());
313
314
  // Update the latch to branch to the new header.
315
0
  latch_block->ForEachSuccessorLabel([bb, new_header_id](uint32_t* id) {
316
0
    if (*id == bb->id()) {
317
0
      *id = new_header_id;
318
0
    }
319
0
  });
320
0
  Instruction* latch_branch = latch_block->terminator();
321
0
  context->AnalyzeUses(latch_branch);
322
0
  label2preds_[new_header->id()].push_back(latch_block->id());
323
324
0
  auto& block_preds = label2preds_[bb->id()];
325
0
  auto latch_pos =
326
0
      std::find(block_preds.begin(), block_preds.end(), latch_block->id());
327
0
  assert(latch_pos != block_preds.end() && "The cfg was invalid.");
328
0
  block_preds.erase(latch_pos);
329
330
  // Update the loop descriptors
331
0
  if (context->AreAnalysesValid(IRContext::kAnalysisLoopAnalysis)) {
332
0
    LoopDescriptor* loop_desc = context->GetLoopDescriptor(bb->GetParent());
333
0
    Loop* loop = (*loop_desc)[bb->id()];
334
335
0
    loop->AddBasicBlock(new_header_id);
336
0
    loop->SetHeaderBlock(new_header);
337
0
    loop_desc->SetBasicBlockToLoop(new_header_id, loop);
338
339
0
    loop->RemoveBasicBlock(bb->id());
340
0
    loop->SetPreHeaderBlock(bb);
341
342
0
    Loop* parent_loop = loop->GetParent();
343
0
    if (parent_loop != nullptr) {
344
0
      parent_loop->AddBasicBlock(bb->id());
345
0
      loop_desc->SetBasicBlockToLoop(bb->id(), parent_loop);
346
0
    } else {
347
0
      loop_desc->SetBasicBlockToLoop(bb->id(), nullptr);
348
0
    }
349
0
  }
350
0
  return new_header;
351
0
}
352
353
}  // namespace opt
354
}  // namespace spvtools