1 | // Copyright (c) 2018 Google LLC. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #include "source/val/validate_memory_semantics.h" |
16 | |
17 | #include "source/diagnostic.h" |
18 | #include "source/spirv_target_env.h" |
19 | #include "source/util/bitutils.h" |
20 | #include "source/val/instruction.h" |
21 | #include "source/val/validation_state.h" |
22 | |
23 | namespace spvtools { |
24 | namespace val { |
25 | |
26 | spv_result_t ValidateMemorySemantics(ValidationState_t& _, |
27 | const Instruction* inst, |
28 | uint32_t operand_index) { |
29 | const SpvOp opcode = inst->opcode(); |
30 | const auto id = inst->GetOperandAs<const uint32_t>(operand_index); |
31 | bool is_int32 = false, is_const_int32 = false; |
32 | uint32_t value = 0; |
33 | std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id); |
34 | |
35 | if (!is_int32) { |
36 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
37 | << spvOpcodeString(opcode) |
38 | << ": expected Memory Semantics to be a 32-bit int" ; |
39 | } |
40 | |
41 | if (!is_const_int32) { |
42 | if (_.HasCapability(SpvCapabilityShader) && |
43 | !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) { |
44 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
45 | << "Memory Semantics ids must be OpConstant when Shader " |
46 | "capability is present" ; |
47 | } |
48 | |
49 | if (_.HasCapability(SpvCapabilityShader) && |
50 | _.HasCapability(SpvCapabilityCooperativeMatrixNV) && |
51 | !spvOpcodeIsConstant(_.GetIdOpcode(id))) { |
52 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
53 | << "Memory Semantics must be a constant instruction when " |
54 | "CooperativeMatrixNV capability is present" ; |
55 | } |
56 | return SPV_SUCCESS; |
57 | } |
58 | |
59 | if (spvIsWebGPUEnv(_.context()->target_env)) { |
60 | uint32_t valid_bits; |
61 | switch (inst->opcode()) { |
62 | case SpvOpControlBarrier: |
63 | if (!(value & SpvMemorySemanticsAcquireReleaseMask)) { |
64 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
65 | << "For WebGPU, AcquireRelease must be set for Memory " |
66 | "Semantics of OpControlBarrier." ; |
67 | } |
68 | |
69 | if (!(value & SpvMemorySemanticsWorkgroupMemoryMask)) { |
70 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
71 | << "For WebGPU, WorkgroupMemory must be set for Memory " |
72 | "Semantics of OpControlBarrier." ; |
73 | } |
74 | |
75 | valid_bits = SpvMemorySemanticsAcquireReleaseMask | |
76 | SpvMemorySemanticsWorkgroupMemoryMask; |
77 | if (value & ~valid_bits) { |
78 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
79 | << "For WebGPU only WorkgroupMemory and AcquireRelease may be " |
80 | "set for Memory Semantics of OpControlBarrier." ; |
81 | } |
82 | break; |
83 | case SpvOpMemoryBarrier: |
84 | if (!(value & SpvMemorySemanticsImageMemoryMask)) { |
85 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
86 | << "For WebGPU, ImageMemory must be set for Memory Semantics " |
87 | "of OpMemoryBarrier." ; |
88 | } |
89 | valid_bits = SpvMemorySemanticsImageMemoryMask; |
90 | if (value & ~valid_bits) { |
91 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
92 | << "For WebGPU only ImageMemory may be set for Memory " |
93 | "Semantics of OpMemoryBarrier." ; |
94 | } |
95 | break; |
96 | default: |
97 | if (spvOpcodeIsAtomicOp(inst->opcode())) { |
98 | if (value != 0) { |
99 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
100 | << "For WebGPU Memory no bits may be set for Memory " |
101 | "Semantics of OpAtomic* instructions." ; |
102 | } |
103 | } |
104 | break; |
105 | } |
106 | } |
107 | |
108 | const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits( |
109 | value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask | |
110 | SpvMemorySemanticsAcquireReleaseMask | |
111 | SpvMemorySemanticsSequentiallyConsistentMask)); |
112 | |
113 | if (num_memory_order_set_bits > 1) { |
114 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
115 | << spvOpcodeString(opcode) |
116 | << ": Memory Semantics can have at most one of the following " |
117 | "bits " |
118 | "set: Acquire, Release, AcquireRelease or " |
119 | "SequentiallyConsistent" ; |
120 | } |
121 | |
122 | if (_.memory_model() == SpvMemoryModelVulkanKHR && |
123 | value & SpvMemorySemanticsSequentiallyConsistentMask) { |
124 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
125 | << "SequentiallyConsistent memory " |
126 | "semantics cannot be used with " |
127 | "the VulkanKHR memory model." ; |
128 | } |
129 | |
130 | if (value & SpvMemorySemanticsMakeAvailableKHRMask && |
131 | !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { |
132 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
133 | << spvOpcodeString(opcode) |
134 | << ": Memory Semantics MakeAvailableKHR requires capability " |
135 | << "VulkanMemoryModelKHR" ; |
136 | } |
137 | |
138 | if (value & SpvMemorySemanticsMakeVisibleKHRMask && |
139 | !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { |
140 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
141 | << spvOpcodeString(opcode) |
142 | << ": Memory Semantics MakeVisibleKHR requires capability " |
143 | << "VulkanMemoryModelKHR" ; |
144 | } |
145 | |
146 | if (value & SpvMemorySemanticsOutputMemoryKHRMask && |
147 | !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { |
148 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
149 | << spvOpcodeString(opcode) |
150 | << ": Memory Semantics OutputMemoryKHR requires capability " |
151 | << "VulkanMemoryModelKHR" ; |
152 | } |
153 | |
154 | if (value & SpvMemorySemanticsVolatileMask) { |
155 | if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { |
156 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
157 | << spvOpcodeString(opcode) |
158 | << ": Memory Semantics Volatile requires capability " |
159 | "VulkanMemoryModelKHR" ; |
160 | } |
161 | |
162 | if (!spvOpcodeIsAtomicOp(inst->opcode())) { |
163 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
164 | << "Memory Semantics Volatile can only be used with atomic " |
165 | "instructions" ; |
166 | } |
167 | } |
168 | |
169 | if (value & SpvMemorySemanticsUniformMemoryMask && |
170 | !_.HasCapability(SpvCapabilityShader)) { |
171 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
172 | << spvOpcodeString(opcode) |
173 | << ": Memory Semantics UniformMemory requires capability Shader" ; |
174 | } |
175 | |
176 | // Checking for SpvCapabilityAtomicStorage is intentionally not done here. See |
177 | // https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why. |
178 | |
179 | if (value & (SpvMemorySemanticsMakeAvailableKHRMask | |
180 | SpvMemorySemanticsMakeVisibleKHRMask)) { |
181 | const bool includes_storage_class = |
182 | value & (SpvMemorySemanticsUniformMemoryMask | |
183 | SpvMemorySemanticsSubgroupMemoryMask | |
184 | SpvMemorySemanticsWorkgroupMemoryMask | |
185 | SpvMemorySemanticsCrossWorkgroupMemoryMask | |
186 | SpvMemorySemanticsAtomicCounterMemoryMask | |
187 | SpvMemorySemanticsImageMemoryMask | |
188 | SpvMemorySemanticsOutputMemoryKHRMask); |
189 | |
190 | if (!includes_storage_class) { |
191 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
192 | << spvOpcodeString(opcode) |
193 | << ": expected Memory Semantics to include a storage class" ; |
194 | } |
195 | } |
196 | |
197 | if (value & SpvMemorySemanticsMakeVisibleKHRMask && |
198 | !(value & (SpvMemorySemanticsAcquireMask | |
199 | SpvMemorySemanticsAcquireReleaseMask))) { |
200 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
201 | << spvOpcodeString(opcode) |
202 | << ": MakeVisibleKHR Memory Semantics also requires either Acquire " |
203 | "or AcquireRelease Memory Semantics" ; |
204 | } |
205 | |
206 | if (value & SpvMemorySemanticsMakeAvailableKHRMask && |
207 | !(value & (SpvMemorySemanticsReleaseMask | |
208 | SpvMemorySemanticsAcquireReleaseMask))) { |
209 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
210 | << spvOpcodeString(opcode) |
211 | << ": MakeAvailableKHR Memory Semantics also requires either " |
212 | "Release or AcquireRelease Memory Semantics" ; |
213 | } |
214 | |
215 | if (spvIsVulkanEnv(_.context()->target_env)) { |
216 | const bool includes_storage_class = |
217 | value & (SpvMemorySemanticsUniformMemoryMask | |
218 | SpvMemorySemanticsWorkgroupMemoryMask | |
219 | SpvMemorySemanticsImageMemoryMask | |
220 | SpvMemorySemanticsOutputMemoryKHRMask); |
221 | |
222 | if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) { |
223 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
224 | << spvOpcodeString(opcode) |
225 | << ": Vulkan specification requires Memory Semantics to have " |
226 | "one " |
227 | "of the following bits set: Acquire, Release, " |
228 | "AcquireRelease " |
229 | "or SequentiallyConsistent" ; |
230 | } |
231 | |
232 | if (opcode == SpvOpMemoryBarrier && !includes_storage_class) { |
233 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
234 | << spvOpcodeString(opcode) |
235 | << ": expected Memory Semantics to include a Vulkan-supported " |
236 | "storage class" ; |
237 | } |
238 | |
239 | #if 0 |
240 | // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed. |
241 | if (opcode == SpvOpControlBarrier && value && !includes_storage_class) { |
242 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
243 | << spvOpcodeString(opcode) |
244 | << ": expected Memory Semantics to include a Vulkan-supported " |
245 | "storage class if Memory Semantics is not None" ; |
246 | } |
247 | #endif |
248 | } |
249 | |
250 | if (opcode == SpvOpAtomicFlagClear && |
251 | (value & SpvMemorySemanticsAcquireMask || |
252 | value & SpvMemorySemanticsAcquireReleaseMask)) { |
253 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
254 | << "Memory Semantics Acquire and AcquireRelease cannot be used " |
255 | "with " |
256 | << spvOpcodeString(opcode); |
257 | } |
258 | |
259 | if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 && |
260 | (value & SpvMemorySemanticsReleaseMask || |
261 | value & SpvMemorySemanticsAcquireReleaseMask)) { |
262 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
263 | << spvOpcodeString(opcode) |
264 | << ": Memory Semantics Release and AcquireRelease cannot be " |
265 | "used " |
266 | "for operand Unequal" ; |
267 | } |
268 | |
269 | if (spvIsVulkanEnv(_.context()->target_env)) { |
270 | if (opcode == SpvOpAtomicLoad && |
271 | (value & SpvMemorySemanticsReleaseMask || |
272 | value & SpvMemorySemanticsAcquireReleaseMask || |
273 | value & SpvMemorySemanticsSequentiallyConsistentMask)) { |
274 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
275 | << "Vulkan spec disallows OpAtomicLoad with Memory Semantics " |
276 | "Release, AcquireRelease and SequentiallyConsistent" ; |
277 | } |
278 | |
279 | if (opcode == SpvOpAtomicStore && |
280 | (value & SpvMemorySemanticsAcquireMask || |
281 | value & SpvMemorySemanticsAcquireReleaseMask || |
282 | value & SpvMemorySemanticsSequentiallyConsistentMask)) { |
283 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
284 | << "Vulkan spec disallows OpAtomicStore with Memory Semantics " |
285 | "Acquire, AcquireRelease and SequentiallyConsistent" ; |
286 | } |
287 | } |
288 | |
289 | // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. |
290 | |
291 | return SPV_SUCCESS; |
292 | } |
293 | |
294 | } // namespace val |
295 | } // namespace spvtools |
296 | |