159 lines
6.8 KiB
C++
159 lines
6.8 KiB
C++
// 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_
|