• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_block_elim_pass.h"
18 
19 #include <vector>
20 
21 #include "source/util/string_utils.h"
22 
23 namespace spvtools {
24 namespace opt {
25 namespace {
26 constexpr uint32_t kStoreValIdInIdx = 1;
27 }  // namespace
28 
HasOnlySupportedRefs(uint32_t ptrId)29 bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
30   if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
31   if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
32         auto dbg_op = user->GetCommonDebugOpcode();
33         if (dbg_op == CommonDebugInfoDebugDeclare ||
34             dbg_op == CommonDebugInfoDebugValue) {
35           return true;
36         }
37         spv::Op op = user->opcode();
38         if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) {
39           if (!HasOnlySupportedRefs(user->result_id())) {
40             return false;
41           }
42         } else if (op != spv::Op::OpStore && op != spv::Op::OpLoad &&
43                    op != spv::Op::OpName && !IsNonTypeDecorate(op)) {
44           return false;
45         }
46         return true;
47       })) {
48     supported_ref_ptrs_.insert(ptrId);
49     return true;
50   }
51   return false;
52 }
53 
LocalSingleBlockLoadStoreElim(Function * func)54 bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
55     Function* func) {
56   // Perform local store/load, load/load and store/store elimination
57   // on each block
58   bool modified = false;
59   std::vector<Instruction*> instructions_to_kill;
60   std::unordered_set<Instruction*> instructions_to_save;
61   for (auto bi = func->begin(); bi != func->end(); ++bi) {
62     var2store_.clear();
63     var2load_.clear();
64     auto next = bi->begin();
65     for (auto ii = next; ii != bi->end(); ii = next) {
66       ++next;
67       switch (ii->opcode()) {
68         case spv::Op::OpStore: {
69           // Verify store variable is target type
70           uint32_t varId;
71           Instruction* ptrInst = GetPtr(&*ii, &varId);
72           if (!IsTargetVar(varId)) continue;
73           if (!HasOnlySupportedRefs(varId)) continue;
74           // If a store to the whole variable, remember it for succeeding
75           // loads and stores. Otherwise forget any previous store to that
76           // variable.
77           if (ptrInst->opcode() == spv::Op::OpVariable) {
78             // If a previous store to same variable, mark the store
79             // for deletion if not still used. Don't delete store
80             // if debugging; let ssa-rewrite and DCE handle it
81             auto prev_store = var2store_.find(varId);
82             if (prev_store != var2store_.end() &&
83                 instructions_to_save.count(prev_store->second) == 0 &&
84                 !context()->get_debug_info_mgr()->IsVariableDebugDeclared(
85                     varId)) {
86               instructions_to_kill.push_back(prev_store->second);
87               modified = true;
88             }
89 
90             bool kill_store = false;
91             auto li = var2load_.find(varId);
92             if (li != var2load_.end()) {
93               if (ii->GetSingleWordInOperand(kStoreValIdInIdx) ==
94                   li->second->result_id()) {
95                 // We are storing the same value that already exists in the
96                 // memory location.  The store does nothing.
97                 kill_store = true;
98               }
99             }
100 
101             if (!kill_store) {
102               var2store_[varId] = &*ii;
103               var2load_.erase(varId);
104             } else {
105               instructions_to_kill.push_back(&*ii);
106               modified = true;
107             }
108           } else {
109             assert(IsNonPtrAccessChain(ptrInst->opcode()));
110             var2store_.erase(varId);
111             var2load_.erase(varId);
112           }
113         } break;
114         case spv::Op::OpLoad: {
115           // Verify store variable is target type
116           uint32_t varId;
117           Instruction* ptrInst = GetPtr(&*ii, &varId);
118           if (!IsTargetVar(varId)) continue;
119           if (!HasOnlySupportedRefs(varId)) continue;
120           uint32_t replId = 0;
121           if (ptrInst->opcode() == spv::Op::OpVariable) {
122             // If a load from a variable, look for a previous store or
123             // load from that variable and use its value.
124             auto si = var2store_.find(varId);
125             if (si != var2store_.end()) {
126               replId = si->second->GetSingleWordInOperand(kStoreValIdInIdx);
127             } else {
128               auto li = var2load_.find(varId);
129               if (li != var2load_.end()) {
130                 replId = li->second->result_id();
131               }
132             }
133           } else {
134             // If a partial load of a previously seen store, remember
135             // not to delete the store.
136             auto si = var2store_.find(varId);
137             if (si != var2store_.end()) instructions_to_save.insert(si->second);
138           }
139           if (replId != 0) {
140             // replace load's result id and delete load
141             context()->KillNamesAndDecorates(&*ii);
142             context()->ReplaceAllUsesWith(ii->result_id(), replId);
143             instructions_to_kill.push_back(&*ii);
144             modified = true;
145           } else {
146             if (ptrInst->opcode() == spv::Op::OpVariable)
147               var2load_[varId] = &*ii;  // register load
148           }
149         } break;
150         case spv::Op::OpFunctionCall: {
151           // Conservatively assume all locals are redefined for now.
152           // TODO(): Handle more optimally
153           var2store_.clear();
154           var2load_.clear();
155         } break;
156         default:
157           break;
158       }
159     }
160   }
161 
162   for (Instruction* inst : instructions_to_kill) {
163     context()->KillInst(inst);
164   }
165 
166   return modified;
167 }
168 
Initialize()169 void LocalSingleBlockLoadStoreElimPass::Initialize() {
170   // Initialize Target Type Caches
171   seen_target_vars_.clear();
172   seen_non_target_vars_.clear();
173 
174   // Clear collections
175   supported_ref_ptrs_.clear();
176 
177   // Initialize extensions allowlist
178   InitExtensions();
179 }
180 
AllExtensionsSupported() const181 bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
182   // If any extension not in allowlist, return false
183   for (auto& ei : get_module()->extensions()) {
184     const std::string extName = ei.GetInOperand(0).AsString();
185     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
186       return false;
187   }
188   // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
189   // around unknown extended
190   // instruction sets even if they are non-semantic
191   for (auto& inst : context()->module()->ext_inst_imports()) {
192     assert(inst.opcode() == spv::Op::OpExtInstImport &&
193            "Expecting an import of an extension's instruction set.");
194     const std::string extension_name = inst.GetInOperand(0).AsString();
195     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
196         extension_name != "NonSemantic.Shader.DebugInfo.100") {
197       return false;
198     }
199   }
200   return true;
201 }
202 
ProcessImpl()203 Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
204   // Assumes relaxed logical addressing only (see instruction.h).
205   if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
206     return Status::SuccessWithoutChange;
207 
208   // Do not process if module contains OpGroupDecorate. Additional
209   // support required in KillNamesAndDecorates().
210   // TODO(greg-lunarg): Add support for OpGroupDecorate
211   for (auto& ai : get_module()->annotations())
212     if (ai.opcode() == spv::Op::OpGroupDecorate)
213       return Status::SuccessWithoutChange;
214   // If any extensions in the module are not explicitly supported,
215   // return unmodified.
216   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
217   // Process all entry point functions
218   ProcessFunction pfn = [this](Function* fp) {
219     return LocalSingleBlockLoadStoreElim(fp);
220   };
221 
222   bool modified = context()->ProcessReachableCallTree(pfn);
223   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
224 }
225 
226 LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass() =
227     default;
228 
Process()229 Pass::Status LocalSingleBlockLoadStoreElimPass::Process() {
230   Initialize();
231   return ProcessImpl();
232 }
233 
InitExtensions()234 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
235   extensions_allowlist_.clear();
236   extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
237                                 "SPV_AMD_shader_trinary_minmax",
238                                 "SPV_AMD_gcn_shader",
239                                 "SPV_KHR_shader_ballot",
240                                 "SPV_AMD_shader_ballot",
241                                 "SPV_AMD_gpu_shader_half_float",
242                                 "SPV_KHR_shader_draw_parameters",
243                                 "SPV_KHR_subgroup_vote",
244                                 "SPV_KHR_8bit_storage",
245                                 "SPV_KHR_16bit_storage",
246                                 "SPV_KHR_device_group",
247                                 "SPV_KHR_multiview",
248                                 "SPV_NVX_multiview_per_view_attributes",
249                                 "SPV_NV_viewport_array2",
250                                 "SPV_NV_stereo_view_rendering",
251                                 "SPV_NV_sample_mask_override_coverage",
252                                 "SPV_NV_geometry_shader_passthrough",
253                                 "SPV_AMD_texture_gather_bias_lod",
254                                 "SPV_KHR_storage_buffer_storage_class",
255                                 "SPV_KHR_variable_pointers",
256                                 "SPV_AMD_gpu_shader_int16",
257                                 "SPV_KHR_post_depth_coverage",
258                                 "SPV_KHR_shader_atomic_counter_ops",
259                                 "SPV_EXT_shader_stencil_export",
260                                 "SPV_EXT_shader_viewport_index_layer",
261                                 "SPV_AMD_shader_image_load_store_lod",
262                                 "SPV_AMD_shader_fragment_mask",
263                                 "SPV_EXT_fragment_fully_covered",
264                                 "SPV_AMD_gpu_shader_half_float_fetch",
265                                 "SPV_GOOGLE_decorate_string",
266                                 "SPV_GOOGLE_hlsl_functionality1",
267                                 "SPV_GOOGLE_user_type",
268                                 "SPV_NV_shader_subgroup_partitioned",
269                                 "SPV_EXT_demote_to_helper_invocation",
270                                 "SPV_EXT_descriptor_indexing",
271                                 "SPV_NV_fragment_shader_barycentric",
272                                 "SPV_NV_compute_shader_derivatives",
273                                 "SPV_NV_shader_image_footprint",
274                                 "SPV_NV_shading_rate",
275                                 "SPV_NV_mesh_shader",
276                                 "SPV_NV_ray_tracing",
277                                 "SPV_KHR_ray_tracing",
278                                 "SPV_KHR_ray_query",
279                                 "SPV_EXT_fragment_invocation_density",
280                                 "SPV_EXT_physical_storage_buffer",
281                                 "SPV_KHR_physical_storage_buffer",
282                                 "SPV_KHR_terminate_invocation",
283                                 "SPV_KHR_subgroup_uniform_control_flow",
284                                 "SPV_KHR_integer_dot_product",
285                                 "SPV_EXT_shader_image_int64",
286                                 "SPV_KHR_non_semantic_info",
287                                 "SPV_KHR_uniform_group_instructions",
288                                 "SPV_KHR_fragment_shader_barycentric",
289                                 "SPV_KHR_vulkan_memory_model",
290                                 "SPV_NV_bindless_texture",
291                                 "SPV_EXT_shader_atomic_float_add",
292                                 "SPV_EXT_fragment_shader_interlock",
293                                 "SPV_NV_compute_shader_derivatives"});
294 }
295 
296 }  // namespace opt
297 }  // namespace spvtools
298