| // Copyright (c) 2019 Google LLC | |
| // | |
| // 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_CODE_SINK_H_ | |
| #define SOURCE_OPT_CODE_SINK_H_ | |
| #include <unordered_map> | |
| #include "source/opt/ir_context.h" | |
| #include "source/opt/module.h" | |
| #include "source/opt/pass.h" | |
| namespace spvtools { | |
| namespace opt { | |
| // This pass does code sinking for OpAccessChain and OpLoad on variables in | |
| // uniform storage or in read only memory. Code sinking is a transformation | |
| // where an instruction is moved into a more deeply nested construct. | |
| // | |
| // The goal is to move these instructions as close as possible to their uses | |
| // without having to execute them more often or to replicate the instruction. | |
| // Moving the instruction in this way can lead to shorter live ranges, which can | |
| // lead to less register pressure. It can also cause instructions to be | |
| // executed less often because they could be moved into one path of a selection | |
| // construct. | |
| // | |
| // This optimization can cause register pressure to rise if the operands of the | |
| // instructions go dead after the instructions being moved. That is why we only | |
| // move certain OpLoad and OpAccessChain instructions. They generally have | |
| // constants, loop induction variables, and global pointers as operands. The | |
| // operands are live for a longer time in most cases. | |
| class CodeSinkingPass : public Pass { | |
| public: | |
| const char* name() const override { return "code-sink"; } | |
| Status Process() override; | |
| // Return the mask of preserved Analyses. | |
| IRContext::Analysis GetPreservedAnalyses() override { | |
| return IRContext::kAnalysisDefUse | | |
| IRContext::kAnalysisInstrToBlockMapping | | |
| IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | | |
| IRContext::kAnalysisDominatorAnalysis | | |
| IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | | |
| IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; | |
| } | |
| private: | |
| // Sinks the instructions in |bb| as much as possible. Returns true if | |
| // something changes. | |
| bool SinkInstructionsInBB(BasicBlock* bb); | |
| // Tries the sink |inst| as much as possible. Returns true if the instruction | |
| // is moved. | |
| bool SinkInstruction(Instruction* inst); | |
| // Returns the basic block in which to move |inst| to move is as close as | |
| // possible to the uses of |inst| without increasing the number of times | |
| // |inst| will be executed. Return |nullptr| if there is no need to move | |
| // |inst|. | |
| BasicBlock* FindNewBasicBlockFor(Instruction* inst); | |
| // Return true if |inst| reference memory and it is possible that the data in | |
| // the memory changes at some point. | |
| bool ReferencesMutableMemory(Instruction* inst); | |
| // Returns true if the module contains an instruction that has a memory | |
| // semantics id as an operand, and the memory semantics enforces a | |
| // synchronization of uniform memory. See section 3.25 of the SPIR-V | |
| // specification. | |
| bool HasUniformMemorySync(); | |
| // Returns true if there may be a store to the variable |var_inst|. | |
| bool HasPossibleStore(Instruction* var_inst); | |
| // Returns true if one of the basic blocks in |set| exists on a path from the | |
| // basic block |start| to |end|. | |
| bool IntersectsPath(uint32_t start, uint32_t end, | |
| const std::unordered_set<uint32_t>& set); | |
| // Returns true if |mem_semantics_id| is the id of a constant that, when | |
| // interpreted as a memory semantics mask enforces synchronization of uniform | |
| // memory. See section 3.25 of the SPIR-V specification. | |
| bool IsSyncOnUniform(uint32_t mem_semantics_id) const; | |
| // True if a check has for uniform storage has taken place. | |
| bool checked_for_uniform_sync_; | |
| // Cache of whether or not the module has a memory sync on uniform storage. | |
| // only valid if |check_for_uniform_sync_| is true. | |
| bool has_uniform_sync_; | |
| }; | |
| } // namespace opt | |
| } // namespace spvtools | |
| #endif // SOURCE_OPT_CODE_SINK_H_ |