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
LocalSingleStoreElim(Function * func)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
AllExtensionsSupported() const48 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
ProcessImpl()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
Process()76 Pass::Status LocalSingleStoreElimPass::Process() {
77 InitExtensionWhiteList();
78 return ProcessImpl();
79 }
80
InitExtensionWhiteList()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 // Currently do not support extended pointer expressions
103 "SPV_AMD_gpu_shader_int16",
104 "SPV_KHR_post_depth_coverage",
105 "SPV_KHR_shader_atomic_counter_ops",
106 "SPV_EXT_shader_stencil_export",
107 "SPV_EXT_shader_viewport_index_layer",
108 "SPV_AMD_shader_image_load_store_lod",
109 "SPV_AMD_shader_fragment_mask",
110 "SPV_EXT_fragment_fully_covered",
111 "SPV_AMD_gpu_shader_half_float_fetch",
112 "SPV_GOOGLE_decorate_string",
113 "SPV_GOOGLE_hlsl_functionality1",
114 "SPV_NV_shader_subgroup_partitioned",
115 "SPV_EXT_descriptor_indexing",
116 "SPV_NV_fragment_shader_barycentric",
117 "SPV_NV_compute_shader_derivatives",
118 "SPV_NV_shader_image_footprint",
119 "SPV_NV_shading_rate",
120 "SPV_NV_mesh_shader",
121 "SPV_NV_ray_tracing",
122 "SPV_EXT_fragment_invocation_density",
123 });
124 }
ProcessVariable(Instruction * var_inst)125 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
126 std::vector<Instruction*> users;
127 FindUses(var_inst, &users);
128
129 Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users);
130
131 if (store_inst == nullptr) {
132 return false;
133 }
134
135 return RewriteLoads(store_inst, users);
136 }
137
FindSingleStoreAndCheckUses(Instruction * var_inst,const std::vector<Instruction * > & users) const138 Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
139 Instruction* var_inst, const std::vector<Instruction*>& users) const {
140 // Make sure there is exactly 1 store.
141 Instruction* store_inst = nullptr;
142
143 // If |var_inst| has an initializer, then that will count as a store.
144 if (var_inst->NumInOperands() > 1) {
145 store_inst = var_inst;
146 }
147
148 for (Instruction* user : users) {
149 switch (user->opcode()) {
150 case SpvOpStore:
151 // Since we are in the relaxed addressing mode, the use has to be the
152 // base address of the store, and not the value being store. Otherwise,
153 // we would have a pointer to a pointer to function scope memory, which
154 // is not allowed.
155 if (store_inst == nullptr) {
156 store_inst = user;
157 } else {
158 // More than 1 store.
159 return nullptr;
160 }
161 break;
162 case SpvOpAccessChain:
163 case SpvOpInBoundsAccessChain:
164 if (FeedsAStore(user)) {
165 // Has a partial store. Cannot propagate that.
166 return nullptr;
167 }
168 break;
169 case SpvOpLoad:
170 case SpvOpImageTexelPointer:
171 case SpvOpName:
172 case SpvOpCopyObject:
173 break;
174 default:
175 if (!user->IsDecoration()) {
176 // Don't know if this instruction modifies the variable.
177 // Conservatively assume it is a store.
178 return nullptr;
179 }
180 break;
181 }
182 }
183 return store_inst;
184 }
185
FindUses(const Instruction * var_inst,std::vector<Instruction * > * users) const186 void LocalSingleStoreElimPass::FindUses(
187 const Instruction* var_inst, std::vector<Instruction*>* users) const {
188 analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
189 def_use_mgr->ForEachUser(var_inst, [users, this](Instruction* user) {
190 users->push_back(user);
191 if (user->opcode() == SpvOpCopyObject) {
192 FindUses(user, users);
193 }
194 });
195 }
196
FeedsAStore(Instruction * inst) const197 bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const {
198 analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
199 return !def_use_mgr->WhileEachUser(inst, [this](Instruction* user) {
200 switch (user->opcode()) {
201 case SpvOpStore:
202 return false;
203 case SpvOpAccessChain:
204 case SpvOpInBoundsAccessChain:
205 case SpvOpCopyObject:
206 return !FeedsAStore(user);
207 case SpvOpLoad:
208 case SpvOpImageTexelPointer:
209 case SpvOpName:
210 return true;
211 default:
212 // Don't know if this instruction modifies the variable.
213 // Conservatively assume it is a store.
214 return user->IsDecoration();
215 }
216 });
217 }
218
RewriteLoads(Instruction * store_inst,const std::vector<Instruction * > & uses)219 bool LocalSingleStoreElimPass::RewriteLoads(
220 Instruction* store_inst, const std::vector<Instruction*>& uses) {
221 BasicBlock* store_block = context()->get_instr_block(store_inst);
222 DominatorAnalysis* dominator_analysis =
223 context()->GetDominatorAnalysis(store_block->GetParent());
224
225 uint32_t stored_id;
226 if (store_inst->opcode() == SpvOpStore)
227 stored_id = store_inst->GetSingleWordInOperand(kStoreValIdInIdx);
228 else
229 stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
230
231 std::vector<Instruction*> uses_in_store_block;
232 bool modified = false;
233 for (Instruction* use : uses) {
234 if (use->opcode() == SpvOpLoad) {
235 if (dominator_analysis->Dominates(store_inst, use)) {
236 modified = true;
237 context()->KillNamesAndDecorates(use->result_id());
238 context()->ReplaceAllUsesWith(use->result_id(), stored_id);
239 context()->KillInst(use);
240 }
241 }
242 }
243
244 return modified;
245 }
246
247 } // namespace opt
248 } // namespace spvtools
249