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 | |