Coverage Report

Created: 2025-07-04 07:23

/src/shaderc/third_party/spirv-tools/source/opt/function.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
#ifndef SOURCE_OPT_FUNCTION_H_
16
#define SOURCE_OPT_FUNCTION_H_
17
18
#include <algorithm>
19
#include <functional>
20
#include <iterator>
21
#include <memory>
22
#include <string>
23
#include <unordered_set>
24
#include <utility>
25
#include <vector>
26
27
#include "source/opt/basic_block.h"
28
#include "source/opt/instruction.h"
29
#include "source/opt/iterator.h"
30
31
namespace spvtools {
32
namespace opt {
33
34
class CFG;
35
class IRContext;
36
class Module;
37
38
// A SPIR-V function.
39
class Function {
40
 public:
41
  using iterator = UptrVectorIterator<BasicBlock>;
42
  using const_iterator = UptrVectorIterator<BasicBlock, true>;
43
  using ParamList = std::vector<std::unique_ptr<Instruction>>;
44
45
  // Creates a function instance declared by the given OpFunction instruction
46
  // |def_inst|.
47
  inline explicit Function(std::unique_ptr<Instruction> def_inst);
48
49
  explicit Function(const Function& f) = delete;
50
51
  // Creates a clone of the instruction in the given |context|
52
  //
53
  // The parent module will default to null and needs to be explicitly set by
54
  // the user.
55
  Function* Clone(IRContext*) const;
56
  // The OpFunction instruction that begins the definition of this function.
57
15.1k
  Instruction& DefInst() { return *def_inst_; }
58
0
  const Instruction& DefInst() const { return *def_inst_; }
59
60
  // Appends a parameter to this function.
61
  inline void AddParameter(std::unique_ptr<Instruction> p);
62
  // Appends a debug instruction in function header to this function.
63
  inline void AddDebugInstructionInHeader(std::unique_ptr<Instruction> p);
64
  // Appends a basic block to this function.
65
  inline void AddBasicBlock(std::unique_ptr<BasicBlock> b);
66
  // Appends a basic block to this function at the position |ip|.
67
  inline void AddBasicBlock(std::unique_ptr<BasicBlock> b, iterator ip);
68
  template <typename T>
69
  inline void AddBasicBlocks(T begin, T end, iterator ip);
70
71
  // Move basic block with |id| to the position after |ip|. Both have to be
72
  // contained in this function.
73
  inline void MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip);
74
75
  // Delete all basic blocks that contain no instructions.
76
  inline void RemoveEmptyBlocks();
77
78
  // Removes a parameter from the function with result id equal to |id|.
79
  // Does nothing if the function doesn't have such a parameter.
80
  inline void RemoveParameter(uint32_t id);
81
82
  // Rewrites the function parameters by calling a replacer callback.
83
  // The replacer takes two parameters: an expiring unique pointer to a current
84
  // instruction, and a back-inserter into a new list of unique pointers to
85
  // instructions.  The replacer is called for each current parameter, in order.
86
  // Not valid to call while also iterating through the parameter list, e.g.
87
  // within the ForEachParam method.
88
  using RewriteParamFn = std::function<void(
89
      std::unique_ptr<Instruction>&&, std::back_insert_iterator<ParamList>&)>;
90
0
  void RewriteParams(RewriteParamFn& replacer) {
91
0
    ParamList new_params;
92
0
    auto appender = std::back_inserter(new_params);
93
0
    for (auto& param : params_) {
94
0
      replacer(std::move(param), appender);
95
0
    }
96
0
    params_ = std::move(new_params);
97
0
  }
98
99
  // Saves the given function end instruction.
100
  inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
101
102
  // Add a non-semantic instruction that succeeds this function in the module.
103
  // These instructions are maintained in the order they are added.
104
  inline void AddNonSemanticInstruction(
105
      std::unique_ptr<Instruction> non_semantic);
106
107
  // Returns the given function end instruction.
108
0
  inline Instruction* EndInst() { return end_inst_.get(); }
109
0
  inline const Instruction* EndInst() const { return end_inst_.get(); }
110
111
  // Returns function's id
112
36.1k
  inline uint32_t result_id() const { return def_inst_->result_id(); }
113
114
  // Returns function's return type id
115
6.36k
  inline uint32_t type_id() const { return def_inst_->type_id(); }
116
117
  // Returns the function's control mask
118
2.90k
  inline uint32_t control_mask() const { return def_inst_->GetSingleWordInOperand(0); }
119
120
  // Returns the entry basic block for this function.
121
84.3k
  const std::unique_ptr<BasicBlock>& entry() const { return blocks_.front(); }
122
123
  // Returns the last basic block in this function.
124
8.69k
  BasicBlock* tail() { return blocks_.back().get(); }
125
0
  const BasicBlock* tail() const { return blocks_.back().get(); }
126
127
259k
  iterator begin() { return iterator(&blocks_, blocks_.begin()); }
128
798k
  iterator end() { return iterator(&blocks_, blocks_.end()); }
129
44.7k
  const_iterator begin() const { return cbegin(); }
130
644k
  const_iterator end() const { return cend(); }
131
52.4k
  const_iterator cbegin() const {
132
52.4k
    return const_iterator(&blocks_, blocks_.cbegin());
133
52.4k
  }
134
652k
  const_iterator cend() const {
135
652k
    return const_iterator(&blocks_, blocks_.cend());
136
652k
  }
137
138
  // Returns an iterator to the basic block |id|.
139
0
  iterator FindBlock(uint32_t bb_id) {
140
0
    return std::find_if(begin(), end(), [bb_id](const BasicBlock& it_bb) {
141
0
      return bb_id == it_bb.id();
142
0
    });
143
0
  }
144
145
  // Runs the given function |f| on instructions in this function, in order,
146
  // and optionally on debug line instructions that might precede them and
147
  // non-semantic instructions that succceed the function.
148
  void ForEachInst(const std::function<void(Instruction*)>& f,
149
                   bool run_on_debug_line_insts = false,
150
                   bool run_on_non_semantic_insts = false);
151
  void ForEachInst(const std::function<void(const Instruction*)>& f,
152
                   bool run_on_debug_line_insts = false,
153
                   bool run_on_non_semantic_insts = false) const;
154
  // Runs the given function |f| on instructions in this function, in order,
155
  // and optionally on debug line instructions that might precede them and
156
  // non-semantic instructions that succeed the function.  If |f| returns
157
  // false, iteration is terminated and this function returns false.
158
  bool WhileEachInst(const std::function<bool(Instruction*)>& f,
159
                     bool run_on_debug_line_insts = false,
160
                     bool run_on_non_semantic_insts = false);
161
  bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
162
                     bool run_on_debug_line_insts = false,
163
                     bool run_on_non_semantic_insts = false) const;
164
165
  // Runs the given function |f| on each parameter instruction in this function,
166
  // in order, and optionally on debug line instructions that might precede
167
  // them.
168
  void ForEachParam(const std::function<void(const Instruction*)>& f,
169
                    bool run_on_debug_line_insts = false) const;
170
  void ForEachParam(const std::function<void(Instruction*)>& f,
171
                    bool run_on_debug_line_insts = false);
172
173
  // Runs the given function |f| on each debug instruction in this function's
174
  // header in order.
175
  void ForEachDebugInstructionsInHeader(
176
      const std::function<void(Instruction*)>& f);
177
178
  BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
179
                                    BasicBlock* position);
180
181
  BasicBlock* InsertBasicBlockBefore(std::unique_ptr<BasicBlock>&& new_block,
182
                                     BasicBlock* position);
183
184
  // Returns true if the function has a return block other than the exit block.
185
  bool HasEarlyReturn() const;
186
187
  // Returns true if the function calls itself either directly or indirectly.
188
  bool IsRecursive() const;
189
190
  // Pretty-prints all the basic blocks in this function into a std::string.
191
  //
192
  // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
193
  // is always added to |options|.
194
  std::string PrettyPrint(uint32_t options = 0u) const;
195
196
  // Dump this function on stderr.  Useful when running interactive
197
  // debuggers.
198
  void Dump() const;
199
200
  // Returns true is a function declaration and not a function definition.
201
49.6k
  bool IsDeclaration() { return begin() == end(); }
202
203
  // Reorders the basic blocks in the function to match the structured order.
204
  void ReorderBasicBlocksInStructuredOrder();
205
206
 private:
207
  // Reorders the basic blocks in the function to match the order given by the
208
  // range |{begin,end}|.  The range must contain every basic block in the
209
  // function, and no extras.
210
  template <class It>
211
  void ReorderBasicBlocks(It begin, It end);
212
213
  template <class It>
214
  bool ContainsAllBlocksInTheFunction(It begin, It end);
215
216
  // The OpFunction instruction that begins the definition of this function.
217
  std::unique_ptr<Instruction> def_inst_;
218
  // All parameters to this function.
219
  ParamList params_;
220
  // All debug instructions in this function's header.
221
  InstructionList debug_insts_in_header_;
222
  // All basic blocks inside this function in specification order
223
  std::vector<std::unique_ptr<BasicBlock>> blocks_;
224
  // The OpFunctionEnd instruction.
225
  std::unique_ptr<Instruction> end_inst_;
226
  // Non-semantic instructions succeeded by this function.
227
  std::vector<std::unique_ptr<Instruction>> non_semantic_;
228
};
229
230
// Pretty-prints |func| to |str|. Returns |str|.
231
std::ostream& operator<<(std::ostream& str, const Function& func);
232
233
inline Function::Function(std::unique_ptr<Instruction> def_inst)
234
2.90k
    : def_inst_(std::move(def_inst)), end_inst_() {}
235
236
2.38k
inline void Function::AddParameter(std::unique_ptr<Instruction> p) {
237
2.38k
  params_.emplace_back(std::move(p));
238
2.38k
}
239
240
inline void Function::AddDebugInstructionInHeader(
241
0
    std::unique_ptr<Instruction> p) {
242
0
  debug_insts_in_header_.push_back(std::move(p));
243
0
}
244
245
23.7k
inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b) {
246
23.7k
  AddBasicBlock(std::move(b), end());
247
23.7k
}
248
249
inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b,
250
23.7k
                                    iterator ip) {
251
23.7k
  b->SetParent(this);
252
23.7k
  ip.InsertBefore(std::move(b));
253
23.7k
}
254
255
template <typename T>
256
0
inline void Function::AddBasicBlocks(T src_begin, T src_end, iterator ip) {
257
0
  blocks_.insert(ip.Get(), std::make_move_iterator(src_begin),
258
0
                 std::make_move_iterator(src_end));
259
0
}
260
261
0
inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) {
262
0
  std::unique_ptr<BasicBlock> block_to_move = std::move(*FindBlock(id).Get());
263
0
  blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
264
265
0
  assert(block_to_move->GetParent() == ip->GetParent() &&
266
0
         "Both blocks have to be in the same function.");
267
268
0
  InsertBasicBlockAfter(std::move(block_to_move), ip);
269
0
}
270
271
0
inline void Function::RemoveEmptyBlocks() {
272
0
  auto first_empty =
273
0
      std::remove_if(std::begin(blocks_), std::end(blocks_),
274
0
                     [](const std::unique_ptr<BasicBlock>& bb) -> bool {
275
0
                       return bb->GetLabelInst()->opcode() == spv::Op::OpNop;
276
0
                     });
277
0
  blocks_.erase(first_empty, std::end(blocks_));
278
0
}
279
280
0
inline void Function::RemoveParameter(uint32_t id) {
281
0
  params_.erase(std::remove_if(params_.begin(), params_.end(),
282
0
                               [id](const std::unique_ptr<Instruction>& param) {
283
0
                                 return param->result_id() == id;
284
0
                               }),
285
0
                params_.end());
286
0
}
287
288
2.90k
inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
289
2.90k
  end_inst_ = std::move(end_inst);
290
2.90k
}
291
292
inline void Function::AddNonSemanticInstruction(
293
0
    std::unique_ptr<Instruction> non_semantic) {
294
0
  non_semantic_.emplace_back(std::move(non_semantic));
295
0
}
296
297
template <class It>
298
380
void Function::ReorderBasicBlocks(It begin, It end) {
299
  // Asserts to make sure every node in the function is in new_order.
300
380
  assert(ContainsAllBlocksInTheFunction(begin, end));
301
302
  // We have a pointer to all the elements in order, so we can release all
303
  // pointers in |block_|, and then create the new unique pointers from |{begin,
304
  // end}|.
305
380
  std::for_each(blocks_.begin(), blocks_.end(),
306
3.67k
                [](std::unique_ptr<BasicBlock>& bb) { bb.release(); });
307
3.67k
  std::transform(begin, end, blocks_.begin(), [](BasicBlock* bb) {
308
3.67k
    return std::unique_ptr<BasicBlock>(bb);
309
3.67k
  });
310
380
}
311
312
template <class It>
313
bool Function::ContainsAllBlocksInTheFunction(It begin, It end) {
314
  std::unordered_multiset<BasicBlock*> range(begin, end);
315
  if (range.size() != blocks_.size()) {
316
    return false;
317
  }
318
319
  for (auto& bb : blocks_) {
320
    if (range.count(bb.get()) == 0) return false;
321
  }
322
  return true;
323
}
324
325
}  // namespace opt
326
}  // namespace spvtools
327
328
#endif  // SOURCE_OPT_FUNCTION_H_