| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // Copyright (c) 2023 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #ifndef SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
- #define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
- #include <algorithm>
- #include <array>
- #include <functional>
- #include <optional>
- #include <unordered_map>
- #include <unordered_set>
- #include "source/enum_set.h"
- #include "source/extensions.h"
- #include "source/opt/ir_context.h"
- #include "source/opt/module.h"
- #include "source/opt/pass.h"
- #include "source/spirv_target_env.h"
- namespace spvtools {
- namespace opt {
- // This pass will ensure that an entry point will only have at most one
- // OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that
- // order
- class InvocationInterlockPlacementPass : public Pass {
- public:
- InvocationInterlockPlacementPass() {}
- InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) =
- delete;
- InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete;
- const char* name() const override { return "dedupe-interlock-invocation"; }
- Status Process() override;
- private:
- using BlockSet = std::unordered_set<uint32_t>;
- // Specifies whether a function originally had a begin or end instruction.
- struct ExtractionResult {
- bool had_begin : 1;
- bool had_end : 2;
- };
- // Check if a block has only a single next block, depending on the directing
- // that we are traversing the CFG. If reverse_cfg is true, we are walking
- // forward through the CFG, and will return if the block has only one
- // successor. Otherwise, we are walking backward through the CFG, and will
- // return if the block has only one predecessor.
- bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg);
- // Iterate over each of a block's predecessors or successors, depending on
- // direction. If reverse_cfg is true, we are walking forward through the CFG,
- // and need to iterate over the successors. Otherwise, we are walking backward
- // through the CFG, and need to iterate over the predecessors.
- void forEachNext(uint32_t block_id, bool reverse_cfg,
- std::function<void(uint32_t)> f);
- // Add either a begin or end instruction to the edge of the basic block. If
- // at_end is true, add the instruction to the end of the block; otherwise add
- // the instruction to the beginning of the basic block.
- void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode,
- bool at_end);
- // Remove every OpBeginInvocationInterlockEXT instruction in block after the
- // first. Returns whether any instructions were removed.
- bool killDuplicateBegin(BasicBlock* block);
- // Remove every OpBeginInvocationInterlockEXT instruction in block before the
- // last. Returns whether any instructions were removed.
- bool killDuplicateEnd(BasicBlock* block);
- // Records whether a function will potentially execute a begin or end
- // instruction.
- void recordBeginOrEndInFunction(Function* func);
- // Recursively removes any begin or end instructions from func and any
- // function func calls. Returns whether any instructions were removed.
- bool removeBeginAndEndInstructionsFromFunction(Function* func);
- // For every function call in any of the passed blocks, move any begin or end
- // instructions outside of the function call. Returns whether any extractions
- // occurred.
- bool extractInstructionsFromCalls(std::vector<BasicBlock*> blocks);
- // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and
- // OpEndInvocationInterlockEXT, storing them in the member variables begin_
- // and end_ respectively.
- void recordExistingBeginAndEndBlock(std::vector<BasicBlock*> blocks);
- // Compute the set of blocks including or after the barrier instruction, and
- // the set of blocks with any previous blocks inside the barrier instruction.
- // If reverse_cfg is true, move forward through the CFG, computing
- // after_begin_ and predecessors_after_begin_computing after_begin_ and
- // predecessors_after_begin_, otherwise, move backward through the CFG,
- // computing before_end_ and successors_before_end_.
- BlockSet computeReachableBlocks(BlockSet& in_set,
- const BlockSet& starting_nodes,
- bool reverse_cfg);
- // Remove unneeded begin and end instructions in block.
- bool removeUnneededInstructions(BasicBlock* block);
- // Given a block which branches to multiple successors, and a specific
- // successor, creates a new empty block, and update the branch instruction to
- // branch to the new block instead.
- BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id);
- // For the edge from block to next_id, places a begin or end instruction on
- // the edge, based on the direction we are walking the CFG, specified in
- // reverse_cfg.
- bool placeInstructionsForEdge(BasicBlock* block, uint32_t next_id,
- BlockSet& inside, BlockSet& previous_inside,
- spv::Op opcode, bool reverse_cfg);
- // Calls placeInstructionsForEdge for each edge in block.
- bool placeInstructions(BasicBlock* block);
- // Processes a single fragment shader entry function.
- bool processFragmentShaderEntry(Function* entry_func);
- // Returns whether the module has the SPV_EXT_fragment_shader_interlock
- // extension and one of the FragmentShader*InterlockEXT capabilities.
- bool isFragmentShaderInterlockEnabled();
- // Maps a function to whether that function originally held a begin or end
- // instruction.
- std::unordered_map<Function*, ExtractionResult> extracted_functions_;
- // The set of blocks which have an OpBeginInvocationInterlockEXT instruction.
- BlockSet begin_;
- // The set of blocks which have an OpEndInvocationInterlockEXT instruction.
- BlockSet end_;
- // The set of blocks which either have a begin instruction, or have a
- // predecessor which has a begin instruction.
- BlockSet after_begin_;
- // The set of blocks which either have an end instruction, or have a successor
- // which have an end instruction.
- BlockSet before_end_;
- // The set of blocks which have a predecessor in after_begin_.
- BlockSet predecessors_after_begin_;
- // The set of blocks which have a successor in before_end_.
- BlockSet successors_before_end_;
- };
- } // namespace opt
- } // namespace spvtools
- #endif // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
|