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 
ValidateMemorySemantics(ValidationState_t & _,const Instruction * inst,uint32_t operand_index)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