1 // Copyright (c) 2017 The Khronos Group Inc. 2 // Copyright (c) 2017 Valve Corporation 3 // Copyright (c) 2017 LunarG Inc. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #ifndef SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ 18 #define SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ 19 20 #include <algorithm> 21 #include <map> 22 #include <memory> 23 #include <queue> 24 #include <string> 25 #include <unordered_map> 26 #include <unordered_set> 27 #include <utility> 28 #include <vector> 29 30 #include "source/opt/basic_block.h" 31 #include "source/opt/def_use_manager.h" 32 #include "source/opt/mem_pass.h" 33 #include "source/opt/module.h" 34 35 namespace spvtools { 36 namespace opt { 37 38 // See optimizer.hpp for documentation. 39 class LocalAccessChainConvertPass : public MemPass { 40 public: 41 LocalAccessChainConvertPass(); 42 name()43 const char* name() const override { return "convert-local-access-chains"; } 44 Status Process() override; 45 GetPreservedAnalyses()46 IRContext::Analysis GetPreservedAnalyses() override { 47 return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | 48 IRContext::kAnalysisTypes; 49 } 50 51 using ProcessFunction = std::function<bool(Function*)>; 52 53 private: 54 // Return true if all refs through |ptrId| are only loads or stores and 55 // cache ptrId in supported_ref_ptrs_. TODO(dnovillo): This function is 56 // replicated in other passes and it's slightly different in every pass. Is it 57 // possible to make one common implementation? 58 bool HasOnlySupportedRefs(uint32_t ptrId); 59 60 // Search |func| and cache function scope variables of target type that are 61 // not accessed with non-constant-index access chains. Also cache non-target 62 // variables. 63 void FindTargetVars(Function* func); 64 65 // Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|. 66 // Append to |newInsts|. 67 void BuildAndAppendInst(SpvOp opcode, uint32_t typeId, uint32_t resultId, 68 const std::vector<Operand>& in_opnds, 69 std::vector<std::unique_ptr<Instruction>>* newInsts); 70 71 // Build load of variable in |ptrInst| and append to |newInsts|. 72 // Return var in |varId| and its pointee type in |varPteTypeId|. 73 uint32_t BuildAndAppendVarLoad( 74 const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, 75 std::vector<std::unique_ptr<Instruction>>* newInsts); 76 77 // Append literal integer operands to |in_opnds| corresponding to constant 78 // integer operands from access chain |ptrInst|. Assumes all indices in 79 // access chains are OpConstant. 80 void AppendConstantOperands(const Instruction* ptrInst, 81 std::vector<Operand>* in_opnds); 82 83 // Create a load/insert/store equivalent to a store of 84 // |valId| through (constant index) access chain |ptrInst|. 85 // Append to |newInsts|. Returns true if successful. 86 bool GenAccessChainStoreReplacement( 87 const Instruction* ptrInst, uint32_t valId, 88 std::vector<std::unique_ptr<Instruction>>* newInsts); 89 90 // For the (constant index) access chain |address_inst|, create an 91 // equivalent load and extract that replaces |original_load|. The result id 92 // of the extract will be the same as the original result id of 93 // |original_load|. Returns true if successful. 94 bool ReplaceAccessChainLoad(const Instruction* address_inst, 95 Instruction* original_load); 96 97 // Return true if all indices of the access chain |acp| are OpConstant 98 // integers whose signed values can be represented as unsigned 32-bit values. 99 bool Is32BitConstantIndexAccessChain(const Instruction* acp) const; 100 101 // Identify all function scope variables of target type which are 102 // accessed only with loads, stores and access chains with constant 103 // indices. Convert all loads and stores of such variables into equivalent 104 // loads, stores, extracts and inserts. This unifies access to these 105 // variables to a single mode and simplifies analysis and optimization. 106 // See IsTargetType() for targeted types. 107 // 108 // Nested access chains and pointer access chains are not currently 109 // converted. 110 // 111 // Returns a status to indicate success or failure, and change or no change. 112 Status ConvertLocalAccessChains(Function* func); 113 114 // Returns true one of the indexes in the |access_chain_inst| is definitly out 115 // of bounds. If the size of the type or the value of the index is unknown, 116 // then it will be considered in-bounds. 117 bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst); 118 119 // Returns true if getting element |index| from |type| would be out-of-bounds. 120 // If |index| is nullptr or the size of the type are unknown, then it will be 121 // considered in-bounds. 122 bool IsIndexOutOfBounds(const analysis::Constant* index, 123 const analysis::Type* type) const; 124 125 // Initialize extensions allowlist 126 void InitExtensions(); 127 128 // Return true if all extensions in this module are allowed by this pass. 129 bool AllExtensionsSupported() const; 130 131 void Initialize(); 132 Pass::Status ProcessImpl(); 133 134 // Variables with only supported references, ie. loads and stores using 135 // variable directly or through non-ptr access chains. 136 std::unordered_set<uint32_t> supported_ref_ptrs_; 137 138 // Extensions supported by this pass. 139 std::unordered_set<std::string> extensions_allowlist_; 140 }; 141 142 } // namespace opt 143 } // namespace spvtools 144 145 #endif // SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ 146