Coverage Report

Created: 2026-01-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/invocation_interlock_placement_pass.h
Line
Count
Source
1
// Copyright (c) 2023 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_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
16
#define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
17
18
#include <algorithm>
19
#include <array>
20
#include <functional>
21
#include <optional>
22
#include <unordered_map>
23
#include <unordered_set>
24
25
#include "source/enum_set.h"
26
#include "source/extensions.h"
27
#include "source/opt/ir_context.h"
28
#include "source/opt/module.h"
29
#include "source/opt/pass.h"
30
#include "source/spirv_target_env.h"
31
32
namespace spvtools {
33
namespace opt {
34
35
// This pass will ensure that an entry point will only have at most one
36
// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that
37
// order
38
class InvocationInterlockPlacementPass : public Pass {
39
 public:
40
13.4k
  InvocationInterlockPlacementPass() {}
41
  InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) =
42
      delete;
43
  InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete;
44
45
6.74k
  const char* name() const override { return "dedupe-interlock-invocation"; }
46
  Status Process() override;
47
48
 private:
49
  using BlockSet = std::unordered_set<uint32_t>;
50
51
  // Specifies whether a function originally had a begin or end instruction.
52
  struct ExtractionResult {
53
    bool had_begin : 1;
54
    bool had_end : 2;
55
  };
56
57
  // Check if a block has only a single next block, depending on the directing
58
  // that we are traversing the CFG. If reverse_cfg is true, we are walking
59
  // forward through the CFG, and will return if the block has only one
60
  // successor. Otherwise, we are walking backward through the CFG, and will
61
  // return if the block has only one predecessor.
62
  bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg);
63
64
  // Iterate over each of a block's predecessors or successors, depending on
65
  // direction. If reverse_cfg is true, we are walking forward through the CFG,
66
  // and need to iterate over the successors. Otherwise, we are walking backward
67
  // through the CFG, and need to iterate over the predecessors.
68
  void forEachNext(uint32_t block_id, bool reverse_cfg,
69
                   std::function<void(uint32_t)> f);
70
71
  // Add either a begin or end instruction to the edge of the basic block. If
72
  // at_end is true, add the instruction to the end of the block; otherwise add
73
  // the instruction to the beginning of the basic block.
74
  void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode,
75
                                     bool at_end);
76
77
  // Remove every OpBeginInvocationInterlockEXT instruction in block after the
78
  // first. Returns whether any instructions were removed.
79
  bool killDuplicateBegin(BasicBlock* block);
80
  // Remove every OpBeginInvocationInterlockEXT instruction in block before the
81
  // last. Returns whether any instructions were removed.
82
  bool killDuplicateEnd(BasicBlock* block);
83
84
  // Records whether a function will potentially execute a begin or end
85
  // instruction.
86
  void recordBeginOrEndInFunction(Function* func);
87
88
  // Recursively removes any begin or end instructions from func and any
89
  // function func calls. Returns whether any instructions were removed.
90
  bool removeBeginAndEndInstructionsFromFunction(Function* func);
91
92
  // For every function call in any of the passed blocks, move any begin or end
93
  // instructions outside of the function call. Returns whether any extractions
94
  // occurred.
95
  bool extractInstructionsFromCalls(std::vector<BasicBlock*> blocks);
96
97
  // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and
98
  // OpEndInvocationInterlockEXT, storing them in the member variables begin_
99
  // and end_ respectively.
100
  void recordExistingBeginAndEndBlock(std::vector<BasicBlock*> blocks);
101
102
  // Compute the set of blocks including or after the barrier instruction, and
103
  // the set of blocks with any previous blocks inside the barrier instruction.
104
  // If reverse_cfg is true, move forward through the CFG, computing
105
  // after_begin_ and predecessors_after_begin_computing after_begin_ and
106
  // predecessors_after_begin_, otherwise, move backward through the CFG,
107
  // computing before_end_ and successors_before_end_.
108
  BlockSet computeReachableBlocks(BlockSet& in_set,
109
                                  const BlockSet& starting_nodes,
110
                                  bool reverse_cfg);
111
112
  // Remove unneeded begin and end instructions in block.
113
  bool removeUnneededInstructions(BasicBlock* block);
114
115
  // Given a block which branches to multiple successors, and a specific
116
  // successor, creates a new empty block, and update the branch instruction to
117
  // branch to the new block instead.
118
  BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id);
119
120
  // For the edge from block to next_id, places a begin or end instruction on
121
  // the edge, based on the direction we are walking the CFG, specified in
122
  // reverse_cfg.
123
  Status placeInstructionsForEdge(BasicBlock* block, uint32_t next_id,
124
                                  BlockSet& inside, BlockSet& previous_inside,
125
                                  spv::Op opcode, bool reverse_cfg);
126
  // Calls placeInstructionsForEdge for each edge in block.
127
  Status placeInstructions(BasicBlock* block);
128
129
  // Processes a single fragment shader entry function.
130
  Status processFragmentShaderEntry(Function* entry_func);
131
132
  // Returns whether the module has the SPV_EXT_fragment_shader_interlock
133
  // extension and one of the FragmentShader*InterlockEXT capabilities.
134
  bool isFragmentShaderInterlockEnabled();
135
136
  // Maps a function to whether that function originally held a begin or end
137
  // instruction.
138
  std::unordered_map<Function*, ExtractionResult> extracted_functions_;
139
140
  // The set of blocks which have an OpBeginInvocationInterlockEXT instruction.
141
  BlockSet begin_;
142
  // The set of blocks which have an OpEndInvocationInterlockEXT instruction.
143
  BlockSet end_;
144
  // The set of blocks which either have a begin instruction, or have a
145
  // predecessor which has a begin instruction.
146
  BlockSet after_begin_;
147
  // The set of blocks which either have an end instruction, or have a successor
148
  // which have an end instruction.
149
  BlockSet before_end_;
150
  // The set of blocks which have a predecessor in after_begin_.
151
  BlockSet predecessors_after_begin_;
152
  // The set of blocks which have a successor in before_end_.
153
  BlockSet successors_before_end_;
154
};
155
156
}  // namespace opt
157
}  // namespace spvtools
158
#endif  // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_