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
23namespace spvtools {
24namespace val {
25
26spv_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