| 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 | #include "source/opt/local_single_store_elim_pass.h" | 
|---|
| 18 |  | 
|---|
| 19 | #include "source/cfa.h" | 
|---|
| 20 | #include "source/latest_version_glsl_std_450_header.h" | 
|---|
| 21 | #include "source/opt/iterator.h" | 
|---|
| 22 |  | 
|---|
| 23 | namespace spvtools { | 
|---|
| 24 | namespace opt { | 
|---|
| 25 |  | 
|---|
| 26 | namespace { | 
|---|
| 27 |  | 
|---|
| 28 | const uint32_t kStoreValIdInIdx = 1; | 
|---|
| 29 | const uint32_t kVariableInitIdInIdx = 1; | 
|---|
| 30 |  | 
|---|
| 31 | }  // anonymous namespace | 
|---|
| 32 |  | 
|---|
| 33 | bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { | 
|---|
| 34 | bool modified = false; | 
|---|
| 35 |  | 
|---|
| 36 | // Check all function scope variables in |func|. | 
|---|
| 37 | BasicBlock* entry_block = &*func->begin(); | 
|---|
| 38 | for (Instruction& inst : *entry_block) { | 
|---|
| 39 | if (inst.opcode() != SpvOpVariable) { | 
|---|
| 40 | break; | 
|---|
| 41 | } | 
|---|
| 42 |  | 
|---|
| 43 | modified |= ProcessVariable(&inst); | 
|---|
| 44 | } | 
|---|
| 45 | return modified; | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 | bool LocalSingleStoreElimPass::AllExtensionsSupported() const { | 
|---|
| 49 | // If any extension not in whitelist, return false | 
|---|
| 50 | for (auto& ei : get_module()->extensions()) { | 
|---|
| 51 | const char* extName = | 
|---|
| 52 | reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); | 
|---|
| 53 | if (extensions_whitelist_.find(extName) == extensions_whitelist_.end()) | 
|---|
| 54 | return false; | 
|---|
| 55 | } | 
|---|
| 56 | return true; | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | Pass::Status LocalSingleStoreElimPass::ProcessImpl() { | 
|---|
| 60 | // Assumes relaxed logical addressing only (see instruction.h) | 
|---|
| 61 | if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) | 
|---|
| 62 | return Status::SuccessWithoutChange; | 
|---|
| 63 |  | 
|---|
| 64 | // Do not process if any disallowed extensions are enabled | 
|---|
| 65 | if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; | 
|---|
| 66 | // Process all entry point functions | 
|---|
| 67 | ProcessFunction pfn = [this](Function* fp) { | 
|---|
| 68 | return LocalSingleStoreElim(fp); | 
|---|
| 69 | }; | 
|---|
| 70 | bool modified = context()->ProcessEntryPointCallTree(pfn); | 
|---|
| 71 | return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 | LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default; | 
|---|
| 75 |  | 
|---|
| 76 | Pass::Status LocalSingleStoreElimPass::Process() { | 
|---|
| 77 | InitExtensionWhiteList(); | 
|---|
| 78 | return ProcessImpl(); | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | void LocalSingleStoreElimPass::InitExtensionWhiteList() { | 
|---|
| 82 | extensions_whitelist_.insert({ | 
|---|
| 83 | "SPV_AMD_shader_explicit_vertex_parameter", | 
|---|
| 84 | "SPV_AMD_shader_trinary_minmax", | 
|---|
| 85 | "SPV_AMD_gcn_shader", | 
|---|
| 86 | "SPV_KHR_shader_ballot", | 
|---|
| 87 | "SPV_AMD_shader_ballot", | 
|---|
| 88 | "SPV_AMD_gpu_shader_half_float", | 
|---|
| 89 | "SPV_KHR_shader_draw_parameters", | 
|---|
| 90 | "SPV_KHR_subgroup_vote", | 
|---|
| 91 | "SPV_KHR_16bit_storage", | 
|---|
| 92 | "SPV_KHR_device_group", | 
|---|
| 93 | "SPV_KHR_multiview", | 
|---|
| 94 | "SPV_NVX_multiview_per_view_attributes", | 
|---|
| 95 | "SPV_NV_viewport_array2", | 
|---|
| 96 | "SPV_NV_stereo_view_rendering", | 
|---|
| 97 | "SPV_NV_sample_mask_override_coverage", | 
|---|
| 98 | "SPV_NV_geometry_shader_passthrough", | 
|---|
| 99 | "SPV_AMD_texture_gather_bias_lod", | 
|---|
| 100 | "SPV_KHR_storage_buffer_storage_class", | 
|---|
| 101 | "SPV_KHR_variable_pointers", | 
|---|
| 102 | "SPV_AMD_gpu_shader_int16", | 
|---|
| 103 | "SPV_KHR_post_depth_coverage", | 
|---|
| 104 | "SPV_KHR_shader_atomic_counter_ops", | 
|---|
| 105 | "SPV_EXT_shader_stencil_export", | 
|---|
| 106 | "SPV_EXT_shader_viewport_index_layer", | 
|---|
| 107 | "SPV_AMD_shader_image_load_store_lod", | 
|---|
| 108 | "SPV_AMD_shader_fragment_mask", | 
|---|
| 109 | "SPV_EXT_fragment_fully_covered", | 
|---|
| 110 | "SPV_AMD_gpu_shader_half_float_fetch", | 
|---|
| 111 | "SPV_GOOGLE_decorate_string", | 
|---|
| 112 | "SPV_GOOGLE_hlsl_functionality1", | 
|---|
| 113 | "SPV_NV_shader_subgroup_partitioned", | 
|---|
| 114 | "SPV_EXT_descriptor_indexing", | 
|---|
| 115 | "SPV_NV_fragment_shader_barycentric", | 
|---|
| 116 | "SPV_NV_compute_shader_derivatives", | 
|---|
| 117 | "SPV_NV_shader_image_footprint", | 
|---|
| 118 | "SPV_NV_shading_rate", | 
|---|
| 119 | "SPV_NV_mesh_shader", | 
|---|
| 120 | "SPV_NV_ray_tracing", | 
|---|
| 121 | "SPV_KHR_ray_query", | 
|---|
| 122 | "SPV_EXT_fragment_invocation_density", | 
|---|
| 123 | "SPV_EXT_physical_storage_buffer", | 
|---|
| 124 | }); | 
|---|
| 125 | } | 
|---|
| 126 | bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { | 
|---|
| 127 | std::vector<Instruction*> users; | 
|---|
| 128 | FindUses(var_inst, &users); | 
|---|
| 129 |  | 
|---|
| 130 | Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users); | 
|---|
| 131 |  | 
|---|
| 132 | if (store_inst == nullptr) { | 
|---|
| 133 | return false; | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | return RewriteLoads(store_inst, users); | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( | 
|---|
| 140 | Instruction* var_inst, const std::vector<Instruction*>& users) const { | 
|---|
| 141 | // Make sure there is exactly 1 store. | 
|---|
| 142 | Instruction* store_inst = nullptr; | 
|---|
| 143 |  | 
|---|
| 144 | // If |var_inst| has an initializer, then that will count as a store. | 
|---|
| 145 | if (var_inst->NumInOperands() > 1) { | 
|---|
| 146 | store_inst = var_inst; | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | for (Instruction* user : users) { | 
|---|
| 150 | switch (user->opcode()) { | 
|---|
| 151 | case SpvOpStore: | 
|---|
| 152 | // Since we are in the relaxed addressing mode, the use has to be the | 
|---|
| 153 | // base address of the store, and not the value being store.  Otherwise, | 
|---|
| 154 | // we would have a pointer to a pointer to function scope memory, which | 
|---|
| 155 | // is not allowed. | 
|---|
| 156 | if (store_inst == nullptr) { | 
|---|
| 157 | store_inst = user; | 
|---|
| 158 | } else { | 
|---|
| 159 | // More than 1 store. | 
|---|
| 160 | return nullptr; | 
|---|
| 161 | } | 
|---|
| 162 | break; | 
|---|
| 163 | case SpvOpAccessChain: | 
|---|
| 164 | case SpvOpInBoundsAccessChain: | 
|---|
| 165 | if (FeedsAStore(user)) { | 
|---|
| 166 | // Has a partial store.  Cannot propagate that. | 
|---|
| 167 | return nullptr; | 
|---|
| 168 | } | 
|---|
| 169 | break; | 
|---|
| 170 | case SpvOpLoad: | 
|---|
| 171 | case SpvOpImageTexelPointer: | 
|---|
| 172 | case SpvOpName: | 
|---|
| 173 | case SpvOpCopyObject: | 
|---|
| 174 | break; | 
|---|
| 175 | default: | 
|---|
| 176 | if (!user->IsDecoration()) { | 
|---|
| 177 | // Don't know if this instruction modifies the variable. | 
|---|
| 178 | // Conservatively assume it is a store. | 
|---|
| 179 | return nullptr; | 
|---|
| 180 | } | 
|---|
| 181 | break; | 
|---|
| 182 | } | 
|---|
| 183 | } | 
|---|
| 184 | return store_inst; | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | void LocalSingleStoreElimPass::FindUses( | 
|---|
| 188 | const Instruction* var_inst, std::vector<Instruction*>* users) const { | 
|---|
| 189 | analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); | 
|---|
| 190 | def_use_mgr->ForEachUser(var_inst, [users, this](Instruction* user) { | 
|---|
| 191 | users->push_back(user); | 
|---|
| 192 | if (user->opcode() == SpvOpCopyObject) { | 
|---|
| 193 | FindUses(user, users); | 
|---|
| 194 | } | 
|---|
| 195 | }); | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const { | 
|---|
| 199 | analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); | 
|---|
| 200 | return !def_use_mgr->WhileEachUser(inst, [this](Instruction* user) { | 
|---|
| 201 | switch (user->opcode()) { | 
|---|
| 202 | case SpvOpStore: | 
|---|
| 203 | return false; | 
|---|
| 204 | case SpvOpAccessChain: | 
|---|
| 205 | case SpvOpInBoundsAccessChain: | 
|---|
| 206 | case SpvOpCopyObject: | 
|---|
| 207 | return !FeedsAStore(user); | 
|---|
| 208 | case SpvOpLoad: | 
|---|
| 209 | case SpvOpImageTexelPointer: | 
|---|
| 210 | case SpvOpName: | 
|---|
| 211 | return true; | 
|---|
| 212 | default: | 
|---|
| 213 | // Don't know if this instruction modifies the variable. | 
|---|
| 214 | // Conservatively assume it is a store. | 
|---|
| 215 | return user->IsDecoration(); | 
|---|
| 216 | } | 
|---|
| 217 | }); | 
|---|
| 218 | } | 
|---|
| 219 |  | 
|---|
| 220 | bool LocalSingleStoreElimPass::RewriteLoads( | 
|---|
| 221 | Instruction* store_inst, const std::vector<Instruction*>& uses) { | 
|---|
| 222 | BasicBlock* store_block = context()->get_instr_block(store_inst); | 
|---|
| 223 | DominatorAnalysis* dominator_analysis = | 
|---|
| 224 | context()->GetDominatorAnalysis(store_block->GetParent()); | 
|---|
| 225 |  | 
|---|
| 226 | uint32_t stored_id; | 
|---|
| 227 | if (store_inst->opcode() == SpvOpStore) | 
|---|
| 228 | stored_id = store_inst->GetSingleWordInOperand(kStoreValIdInIdx); | 
|---|
| 229 | else | 
|---|
| 230 | stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx); | 
|---|
| 231 |  | 
|---|
| 232 | std::vector<Instruction*> uses_in_store_block; | 
|---|
| 233 | bool modified = false; | 
|---|
| 234 | for (Instruction* use : uses) { | 
|---|
| 235 | if (use->opcode() == SpvOpLoad) { | 
|---|
| 236 | if (dominator_analysis->Dominates(store_inst, use)) { | 
|---|
| 237 | modified = true; | 
|---|
| 238 | context()->KillNamesAndDecorates(use->result_id()); | 
|---|
| 239 | context()->ReplaceAllUsesWith(use->result_id(), stored_id); | 
|---|
| 240 | context()->KillInst(use); | 
|---|
| 241 | } | 
|---|
| 242 | } | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | return modified; | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|
| 248 | }  // namespace opt | 
|---|
| 249 | }  // namespace spvtools | 
|---|
| 250 |  | 
|---|