|  | // Copyright (c) 2017 The Khronos Group Inc. | 
|  | // Copyright (c) 2017 Valve Corporation | 
|  | // Copyright (c) 2017 LunarG 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_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ | 
|  | #define SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <queue> | 
|  | #include <string> | 
|  | #include <unordered_map> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "source/opt/basic_block.h" | 
|  | #include "source/opt/def_use_manager.h" | 
|  | #include "source/opt/mem_pass.h" | 
|  | #include "source/opt/module.h" | 
|  |  | 
|  | namespace spvtools { | 
|  | namespace opt { | 
|  |  | 
|  | // See optimizer.hpp for documentation. | 
|  | class LocalAccessChainConvertPass : public MemPass { | 
|  | public: | 
|  | LocalAccessChainConvertPass(); | 
|  |  | 
|  | const char* name() const override { return "convert-local-access-chains"; } | 
|  | Status Process() override; | 
|  |  | 
|  | IRContext::Analysis GetPreservedAnalyses() override { | 
|  | return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | | 
|  | IRContext::kAnalysisTypes; | 
|  | } | 
|  |  | 
|  | using ProcessFunction = std::function<bool(Function*)>; | 
|  |  | 
|  | private: | 
|  | // Return true if all refs through |ptrId| are only loads or stores and | 
|  | // cache ptrId in supported_ref_ptrs_. TODO(dnovillo): This function is | 
|  | // replicated in other passes and it's slightly different in every pass. Is it | 
|  | // possible to make one common implementation? | 
|  | bool HasOnlySupportedRefs(uint32_t ptrId); | 
|  |  | 
|  | // Search |func| and cache function scope variables of target type that are | 
|  | // not accessed with non-constant-index access chains. Also cache non-target | 
|  | // variables. | 
|  | void FindTargetVars(Function* func); | 
|  |  | 
|  | // Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|. | 
|  | // Append to |newInsts|. | 
|  | void BuildAndAppendInst(SpvOp opcode, uint32_t typeId, uint32_t resultId, | 
|  | const std::vector<Operand>& in_opnds, | 
|  | std::vector<std::unique_ptr<Instruction>>* newInsts); | 
|  |  | 
|  | // Build load of variable in |ptrInst| and append to |newInsts|. | 
|  | // Return var in |varId| and its pointee type in |varPteTypeId|. | 
|  | uint32_t BuildAndAppendVarLoad( | 
|  | const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, | 
|  | std::vector<std::unique_ptr<Instruction>>* newInsts); | 
|  |  | 
|  | // Append literal integer operands to |in_opnds| corresponding to constant | 
|  | // integer operands from access chain |ptrInst|. Assumes all indices in | 
|  | // access chains are OpConstant. | 
|  | void AppendConstantOperands(const Instruction* ptrInst, | 
|  | std::vector<Operand>* in_opnds); | 
|  |  | 
|  | // Create a load/insert/store equivalent to a store of | 
|  | // |valId| through (constant index) access chain |ptrInst|. | 
|  | // Append to |newInsts|.  Returns true if successful. | 
|  | bool GenAccessChainStoreReplacement( | 
|  | const Instruction* ptrInst, uint32_t valId, | 
|  | std::vector<std::unique_ptr<Instruction>>* newInsts); | 
|  |  | 
|  | // For the (constant index) access chain |address_inst|, create an | 
|  | // equivalent load and extract that replaces |original_load|.  The result id | 
|  | // of the extract will be the same as the original result id of | 
|  | // |original_load|.  Returns true if successful. | 
|  | bool ReplaceAccessChainLoad(const Instruction* address_inst, | 
|  | Instruction* original_load); | 
|  |  | 
|  | // Return true if all indices of access chain |acp| are OpConstant integers | 
|  | // whose values can fit into an unsigned 32-bit value. | 
|  | bool Is32BitConstantIndexAccessChain(const Instruction* acp) const; | 
|  |  | 
|  | // Identify all function scope variables of target type which are | 
|  | // accessed only with loads, stores and access chains with constant | 
|  | // indices. Convert all loads and stores of such variables into equivalent | 
|  | // loads, stores, extracts and inserts. This unifies access to these | 
|  | // variables to a single mode and simplifies analysis and optimization. | 
|  | // See IsTargetType() for targeted types. | 
|  | // | 
|  | // Nested access chains and pointer access chains are not currently | 
|  | // converted. | 
|  | // | 
|  | // Returns a status to indicate success or failure, and change or no change. | 
|  | Status ConvertLocalAccessChains(Function* func); | 
|  |  | 
|  | // Returns true one of the indexes in the |access_chain_inst| is definitly out | 
|  | // of bounds.  If the size of the type or the value of the index is unknown, | 
|  | // then it will be considered in-bounds. | 
|  | bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst); | 
|  |  | 
|  | // Returns true if getting element |index| from |type| would be out-of-bounds. | 
|  | // If |index| is nullptr or the size of the type are unknown, then it will be | 
|  | // considered in-bounds. | 
|  | bool IsIndexOutOfBounds(const analysis::Constant* index, | 
|  | const analysis::Type* type) const; | 
|  |  | 
|  | // Initialize extensions allowlist | 
|  | void InitExtensions(); | 
|  |  | 
|  | // Return true if all extensions in this module are allowed by this pass. | 
|  | bool AllExtensionsSupported() const; | 
|  |  | 
|  | void Initialize(); | 
|  | Pass::Status ProcessImpl(); | 
|  |  | 
|  | // Variables with only supported references, ie. loads and stores using | 
|  | // variable directly or through non-ptr access chains. | 
|  | std::unordered_set<uint32_t> supported_ref_ptrs_; | 
|  |  | 
|  | // Extensions supported by this pass. | 
|  | std::unordered_set<std::string> extensions_allowlist_; | 
|  | }; | 
|  |  | 
|  | }  // namespace opt | 
|  | }  // namespace spvtools | 
|  |  | 
|  | #endif  // SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ |