Coverage Report

Created: 2025-07-23 06:18

/src/spirv-tools/source/opt/basic_block.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2016 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
// This file defines the language constructs for representing a SPIR-V
16
// module in memory.
17
18
#ifndef SOURCE_OPT_BASIC_BLOCK_H_
19
#define SOURCE_OPT_BASIC_BLOCK_H_
20
21
#include <functional>
22
#include <iterator>
23
#include <memory>
24
#include <ostream>
25
#include <string>
26
#include <utility>
27
#include <vector>
28
29
#include "source/opt/instruction.h"
30
#include "source/opt/instruction_list.h"
31
#include "source/opt/iterator.h"
32
33
namespace spvtools {
34
namespace opt {
35
36
class Function;
37
class IRContext;
38
39
// A SPIR-V basic block.
40
class BasicBlock {
41
 public:
42
  using iterator = InstructionList::iterator;
43
  using const_iterator = InstructionList::const_iterator;
44
  using reverse_iterator = std::reverse_iterator<InstructionList::iterator>;
45
  using const_reverse_iterator =
46
      std::reverse_iterator<InstructionList::const_iterator>;
47
48
  // Creates a basic block with the given starting |label|.
49
  inline explicit BasicBlock(std::unique_ptr<Instruction> label);
50
51
  explicit BasicBlock(const BasicBlock& bb) = delete;
52
53
  // Creates a clone of the basic block in the given |context|
54
  //
55
  // The parent function will default to null and needs to be explicitly set by
56
  // the user.
57
  //
58
  // If the inst-to-block map in |context| is valid, then the new instructions
59
  // will be inserted into the map.
60
  BasicBlock* Clone(IRContext*) const;
61
62
  // Sets the enclosing function for this basic block.
63
2.21M
  void SetParent(Function* function) { function_ = function; }
64
65
  // Return the enclosing function
66
4.18M
  inline Function* GetParent() const { return function_; }
67
68
  // Appends an instruction to this basic block.
69
  inline void AddInstruction(std::unique_ptr<Instruction> i);
70
71
  // Appends all of block's instructions (except label) to this block
72
  inline void AddInstructions(BasicBlock* bp);
73
74
  // The pointer to the label starting this basic block.
75
18.1k
  std::unique_ptr<Instruction>& GetLabel() { return label_; }
76
77
  // The label starting this basic block.
78
15.7M
  Instruction* GetLabelInst() { return label_.get(); }
79
834k
  const Instruction* GetLabelInst() const { return label_.get(); }
80
81
  // Returns the merge instruction in this basic block, if it exists.
82
  // Otherwise return null.  May be used whenever tail() can be used.
83
  const Instruction* GetMergeInst() const;
84
  Instruction* GetMergeInst();
85
86
  // Returns the OpLoopMerge instruction in this basic block, if it exists.
87
  // Otherwise return null.  May be used whenever tail() can be used.
88
  const Instruction* GetLoopMergeInst() const;
89
  Instruction* GetLoopMergeInst();
90
91
  // Returns the id of the label at the top of this block
92
205M
  inline uint32_t id() const { return label_->result_id(); }
93
94
56.8M
  iterator begin() { return insts_.begin(); }
95
155M
  iterator end() { return insts_.end(); }
96
463k
  const_iterator begin() const { return insts_.cbegin(); }
97
463k
  const_iterator end() const { return insts_.cend(); }
98
49.2M
  const_iterator cbegin() const { return insts_.cbegin(); }
99
26.0M
  const_iterator cend() const { return insts_.cend(); }
100
101
0
  reverse_iterator rbegin() { return reverse_iterator(end()); }
102
0
  reverse_iterator rend() { return reverse_iterator(begin()); }
103
0
  const_reverse_iterator rbegin() const {
104
0
    return const_reverse_iterator(cend());
105
0
  }
106
0
  const_reverse_iterator rend() const {
107
0
    return const_reverse_iterator(cbegin());
108
0
  }
109
0
  const_reverse_iterator crbegin() const {
110
0
    return const_reverse_iterator(cend());
111
0
  }
112
0
  const_reverse_iterator crend() const {
113
0
    return const_reverse_iterator(cbegin());
114
0
  }
115
116
  // Returns an iterator pointing to the last instruction.  This may only
117
  // be used if this block has an instruction other than the OpLabel
118
  // that defines it.
119
51.2M
  iterator tail() {
120
51.2M
    assert(!insts_.empty());
121
51.2M
    return --end();
122
51.2M
  }
123
124
  // Returns a const iterator, but othewrise similar to tail().
125
24.2M
  const_iterator ctail() const {
126
24.2M
    assert(!insts_.empty());
127
24.2M
    return --insts_.cend();
128
24.2M
  }
129
130
  // Returns true if the basic block has at least one successor.
131
0
  inline bool hasSuccessor() const { return ctail()->IsBranch(); }
132
133
  // Runs the given function |f| on each instruction in this basic block, and
134
  // optionally on the debug line instructions that might precede them.
135
  inline void ForEachInst(const std::function<void(Instruction*)>& f,
136
                          bool run_on_debug_line_insts = false);
137
  inline void ForEachInst(const std::function<void(const Instruction*)>& f,
138
                          bool run_on_debug_line_insts = false) const;
139
140
  // Runs the given function |f| on each instruction in this basic block, and
141
  // optionally on the debug line instructions that might precede them. If |f|
142
  // returns false, iteration is terminated and this function returns false.
143
  inline bool WhileEachInst(const std::function<bool(Instruction*)>& f,
144
                            bool run_on_debug_line_insts = false);
145
  inline bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
146
                            bool run_on_debug_line_insts = false) const;
147
148
  // Runs the given function |f| on each Phi instruction in this basic block,
149
  // and optionally on the debug line instructions that might precede them.
150
  inline void ForEachPhiInst(const std::function<void(Instruction*)>& f,
151
                             bool run_on_debug_line_insts = false);
152
153
  // Runs the given function |f| on each Phi instruction in this basic block,
154
  // and optionally on the debug line instructions that might precede them. If
155
  // |f| returns false, iteration is terminated and this function return false.
156
  inline bool WhileEachPhiInst(const std::function<bool(Instruction*)>& f,
157
                               bool run_on_debug_line_insts = false);
158
159
  // Runs the given function |f| on each label id of each successor block
160
  void ForEachSuccessorLabel(
161
      const std::function<void(const uint32_t)>& f) const;
162
163
  // Runs the given function |f| on each label id of each successor block.  If
164
  // |f| returns false, iteration is terminated and this function returns false.
165
  bool WhileEachSuccessorLabel(
166
      const std::function<bool(const uint32_t)>& f) const;
167
168
  // Runs the given function |f| on each label id of each successor block.
169
  // Modifying the pointed value will change the branch taken by the basic
170
  // block. It is the caller responsibility to update or invalidate the CFG.
171
  void ForEachSuccessorLabel(const std::function<void(uint32_t*)>& f);
172
173
  // Returns true if |block| is a direct successor of |this|.
174
  bool IsSuccessor(const BasicBlock* block) const;
175
176
  // Runs the given function |f| on the merge and continue label, if any
177
  void ForMergeAndContinueLabel(const std::function<void(const uint32_t)>& f);
178
179
  // Returns true if this basic block has any Phi instructions.
180
0
  bool HasPhiInstructions() {
181
0
    return !WhileEachPhiInst([](Instruction*) { return false; });
182
0
  }
183
184
  // Return true if this block is a loop header block.
185
23.6M
  bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; }
186
187
  // Returns the ID of the merge block declared by a merge instruction in this
188
  // block, if any.  If none, returns zero.
189
  uint32_t MergeBlockIdIfAny() const;
190
191
  // Returns MergeBlockIdIfAny() and asserts that it is non-zero.
192
  uint32_t MergeBlockId() const;
193
194
  // Returns the ID of the continue block declared by a merge instruction in
195
  // this block, if any.  If none, returns zero.
196
  uint32_t ContinueBlockIdIfAny() const;
197
198
  // Returns ContinueBlockIdIfAny() and asserts that it is non-zero.
199
  uint32_t ContinueBlockId() const;
200
201
  // Returns the terminator instruction.  Assumes the terminator exists.
202
20.6M
  Instruction* terminator() { return &*tail(); }
203
0
  const Instruction* terminator() const { return &*ctail(); }
204
205
  // Returns true if this basic block exits this function and returns to its
206
  // caller.
207
0
  bool IsReturn() const { return ctail()->IsReturn(); }
208
209
  // Returns true if this basic block exits this function or aborts execution.
210
364k
  bool IsReturnOrAbort() const { return ctail()->IsReturnOrAbort(); }
211
212
  // Kill all instructions in this block. Whether or not to kill the label is
213
  // indicated by |killLabel|.
214
  void KillAllInsts(bool killLabel);
215
216
  // Splits this basic block into two. Returns a new basic block with label
217
  // |label_id| containing the instructions from |iter| onwards. Instructions
218
  // prior to |iter| remain in this basic block.  The new block will be added
219
  // to the function immediately after the original block.
220
  BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id,
221
                              iterator iter);
222
223
  // Pretty-prints this basic block into a std::string by printing every
224
  // instruction in it.
225
  //
226
  // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
227
  // is always added to |options|.
228
  std::string PrettyPrint(uint32_t options = 0u) const;
229
230
  // Dump this basic block on stderr.  Useful when running interactive
231
  // debuggers.
232
  void Dump() const;
233
234
 private:
235
  // The enclosing function.
236
  Function* function_;
237
  // The label starting this basic block.
238
  std::unique_ptr<Instruction> label_;
239
  // Instructions inside this basic block, but not the OpLabel.
240
  InstructionList insts_;
241
};
242
243
// Pretty-prints |block| to |str|. Returns |str|.
244
std::ostream& operator<<(std::ostream& str, const BasicBlock& block);
245
246
inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
247
1.78M
    : function_(nullptr), label_(std::move(label)) {}
248
249
8.99M
inline void BasicBlock::AddInstruction(std::unique_ptr<Instruction> i) {
250
8.99M
  insts_.push_back(std::move(i));
251
8.99M
}
252
253
459k
inline void BasicBlock::AddInstructions(BasicBlock* bp) {
254
459k
  auto bEnd = end();
255
459k
  (void)bEnd.MoveBefore(&bp->insts_);
256
459k
}
257
258
inline bool BasicBlock::WhileEachInst(
259
68.0M
    const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
260
68.0M
  if (label_) {
261
68.0M
    if (!label_->WhileEachInst(f, run_on_debug_line_insts)) return false;
262
68.0M
  }
263
68.0M
  if (insts_.empty()) {
264
9.71k
    return true;
265
9.71k
  }
266
267
68.0M
  Instruction* inst = &insts_.front();
268
338M
  while (inst != nullptr) {
269
270M
    Instruction* next_instruction = inst->NextNode();
270
270M
    if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false;
271
270M
    inst = next_instruction;
272
270M
  }
273
68.0M
  return true;
274
68.0M
}
275
276
inline bool BasicBlock::WhileEachInst(
277
    const std::function<bool(const Instruction*)>& f,
278
1.28M
    bool run_on_debug_line_insts) const {
279
1.28M
  if (label_) {
280
1.28M
    if (!static_cast<const Instruction*>(label_.get())
281
1.28M
             ->WhileEachInst(f, run_on_debug_line_insts))
282
0
      return false;
283
1.28M
  }
284
4.31M
  for (const auto& inst : insts_) {
285
4.31M
    if (!static_cast<const Instruction*>(&inst)->WhileEachInst(
286
4.31M
            f, run_on_debug_line_insts))
287
0
      return false;
288
4.31M
  }
289
1.28M
  return true;
290
1.28M
}
291
292
inline void BasicBlock::ForEachInst(const std::function<void(Instruction*)>& f,
293
6.19M
                                    bool run_on_debug_line_insts) {
294
6.19M
  WhileEachInst(
295
33.7M
      [&f](Instruction* inst) {
296
33.7M
        f(inst);
297
33.7M
        return true;
298
33.7M
      },
299
6.19M
      run_on_debug_line_insts);
300
6.19M
}
301
302
inline void BasicBlock::ForEachInst(
303
    const std::function<void(const Instruction*)>& f,
304
0
    bool run_on_debug_line_insts) const {
305
0
  WhileEachInst(
306
0
      [&f](const Instruction* inst) {
307
0
        f(inst);
308
0
        return true;
309
0
      },
310
0
      run_on_debug_line_insts);
311
0
}
312
313
inline bool BasicBlock::WhileEachPhiInst(
314
3.63M
    const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
315
3.63M
  if (insts_.empty()) {
316
2
    return true;
317
2
  }
318
319
3.63M
  Instruction* inst = &insts_.front();
320
4.38M
  while (inst != nullptr) {
321
4.38M
    Instruction* next_instruction = inst->NextNode();
322
4.38M
    if (inst->opcode() != spv::Op::OpPhi) break;
323
751k
    if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false;
324
751k
    inst = next_instruction;
325
751k
  }
326
3.63M
  return true;
327
3.63M
}
328
329
inline void BasicBlock::ForEachPhiInst(
330
3.63M
    const std::function<void(Instruction*)>& f, bool run_on_debug_line_insts) {
331
3.63M
  WhileEachPhiInst(
332
3.63M
      [&f](Instruction* inst) {
333
751k
        f(inst);
334
751k
        return true;
335
751k
      },
336
3.63M
      run_on_debug_line_insts);
337
3.63M
}
338
339
}  // namespace opt
340
}  // namespace spvtools
341
342
#endif  // SOURCE_OPT_BASIC_BLOCK_H_