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 const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
60 value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
61 SpvMemorySemanticsAcquireReleaseMask |
62 SpvMemorySemanticsSequentiallyConsistentMask));
63
64 if (num_memory_order_set_bits > 1) {
65 return _.diag(SPV_ERROR_INVALID_DATA, inst)
66 << spvOpcodeString(opcode)
67 << ": Memory Semantics can have at most one of the following "
68 "bits "
69 "set: Acquire, Release, AcquireRelease or "
70 "SequentiallyConsistent";
71 }
72
73 if (_.memory_model() == SpvMemoryModelVulkanKHR &&
74 value & SpvMemorySemanticsSequentiallyConsistentMask) {
75 return _.diag(SPV_ERROR_INVALID_DATA, inst)
76 << "SequentiallyConsistent memory "
77 "semantics cannot be used with "
78 "the VulkanKHR memory model.";
79 }
80
81 if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
82 !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
83 return _.diag(SPV_ERROR_INVALID_DATA, inst)
84 << spvOpcodeString(opcode)
85 << ": Memory Semantics MakeAvailableKHR requires capability "
86 << "VulkanMemoryModelKHR";
87 }
88
89 if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
90 !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
91 return _.diag(SPV_ERROR_INVALID_DATA, inst)
92 << spvOpcodeString(opcode)
93 << ": Memory Semantics MakeVisibleKHR requires capability "
94 << "VulkanMemoryModelKHR";
95 }
96
97 if (value & SpvMemorySemanticsOutputMemoryKHRMask &&
98 !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
99 return _.diag(SPV_ERROR_INVALID_DATA, inst)
100 << spvOpcodeString(opcode)
101 << ": Memory Semantics OutputMemoryKHR requires capability "
102 << "VulkanMemoryModelKHR";
103 }
104
105 if (value & SpvMemorySemanticsVolatileMask) {
106 if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
107 return _.diag(SPV_ERROR_INVALID_DATA, inst)
108 << spvOpcodeString(opcode)
109 << ": Memory Semantics Volatile requires capability "
110 "VulkanMemoryModelKHR";
111 }
112
113 if (!spvOpcodeIsAtomicOp(inst->opcode())) {
114 return _.diag(SPV_ERROR_INVALID_DATA, inst)
115 << "Memory Semantics Volatile can only be used with atomic "
116 "instructions";
117 }
118 }
119
120 if (value & SpvMemorySemanticsUniformMemoryMask &&
121 !_.HasCapability(SpvCapabilityShader)) {
122 return _.diag(SPV_ERROR_INVALID_DATA, inst)
123 << spvOpcodeString(opcode)
124 << ": Memory Semantics UniformMemory requires capability Shader";
125 }
126
127 // Checking for SpvCapabilityAtomicStorage is intentionally not done here. See
128 // https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why.
129
130 if (value & (SpvMemorySemanticsMakeAvailableKHRMask |
131 SpvMemorySemanticsMakeVisibleKHRMask)) {
132 const bool includes_storage_class =
133 value & (SpvMemorySemanticsUniformMemoryMask |
134 SpvMemorySemanticsSubgroupMemoryMask |
135 SpvMemorySemanticsWorkgroupMemoryMask |
136 SpvMemorySemanticsCrossWorkgroupMemoryMask |
137 SpvMemorySemanticsAtomicCounterMemoryMask |
138 SpvMemorySemanticsImageMemoryMask |
139 SpvMemorySemanticsOutputMemoryKHRMask);
140
141 if (!includes_storage_class) {
142 return _.diag(SPV_ERROR_INVALID_DATA, inst)
143 << spvOpcodeString(opcode)
144 << ": expected Memory Semantics to include a storage class";
145 }
146 }
147
148 if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
149 !(value & (SpvMemorySemanticsAcquireMask |
150 SpvMemorySemanticsAcquireReleaseMask))) {
151 return _.diag(SPV_ERROR_INVALID_DATA, inst)
152 << spvOpcodeString(opcode)
153 << ": MakeVisibleKHR Memory Semantics also requires either Acquire "
154 "or AcquireRelease Memory Semantics";
155 }
156
157 if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
158 !(value & (SpvMemorySemanticsReleaseMask |
159 SpvMemorySemanticsAcquireReleaseMask))) {
160 return _.diag(SPV_ERROR_INVALID_DATA, inst)
161 << spvOpcodeString(opcode)
162 << ": MakeAvailableKHR Memory Semantics also requires either "
163 "Release or AcquireRelease Memory Semantics";
164 }
165
166 if (spvIsVulkanEnv(_.context()->target_env)) {
167 const bool includes_storage_class =
168 value & (SpvMemorySemanticsUniformMemoryMask |
169 SpvMemorySemanticsWorkgroupMemoryMask |
170 SpvMemorySemanticsImageMemoryMask |
171 SpvMemorySemanticsOutputMemoryKHRMask);
172
173 if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) {
174 return _.diag(SPV_ERROR_INVALID_DATA, inst)
175 << _.VkErrorID(4649) << spvOpcodeString(opcode)
176 << ": Vulkan specification requires Memory Semantics to have "
177 "one "
178 "of the following bits set: Acquire, Release, "
179 "AcquireRelease "
180 "or SequentiallyConsistent";
181 }
182
183 if (opcode == SpvOpMemoryBarrier && !includes_storage_class) {
184 return _.diag(SPV_ERROR_INVALID_DATA, inst)
185 << _.VkErrorID(4649) << spvOpcodeString(opcode)
186 << ": expected Memory Semantics to include a Vulkan-supported "
187 "storage class";
188 }
189
190 #if 0
191 // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed.
192 if (opcode == SpvOpControlBarrier && value && !includes_storage_class) {
193 return _.diag(SPV_ERROR_INVALID_DATA, inst)
194 << spvOpcodeString(opcode)
195 << ": expected Memory Semantics to include a Vulkan-supported "
196 "storage class if Memory Semantics is not None";
197 }
198 #endif
199 }
200
201 if (opcode == SpvOpAtomicFlagClear &&
202 (value & SpvMemorySemanticsAcquireMask ||
203 value & SpvMemorySemanticsAcquireReleaseMask)) {
204 return _.diag(SPV_ERROR_INVALID_DATA, inst)
205 << "Memory Semantics Acquire and AcquireRelease cannot be used "
206 "with "
207 << spvOpcodeString(opcode);
208 }
209
210 if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&
211 (value & SpvMemorySemanticsReleaseMask ||
212 value & SpvMemorySemanticsAcquireReleaseMask)) {
213 return _.diag(SPV_ERROR_INVALID_DATA, inst)
214 << spvOpcodeString(opcode)
215 << ": Memory Semantics Release and AcquireRelease cannot be "
216 "used "
217 "for operand Unequal";
218 }
219
220 if (spvIsVulkanEnv(_.context()->target_env)) {
221 if (opcode == SpvOpAtomicLoad &&
222 (value & SpvMemorySemanticsReleaseMask ||
223 value & SpvMemorySemanticsAcquireReleaseMask ||
224 value & SpvMemorySemanticsSequentiallyConsistentMask)) {
225 return _.diag(SPV_ERROR_INVALID_DATA, inst)
226 << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
227 "Release, AcquireRelease and SequentiallyConsistent";
228 }
229
230 if (opcode == SpvOpAtomicStore &&
231 (value & SpvMemorySemanticsAcquireMask ||
232 value & SpvMemorySemanticsAcquireReleaseMask ||
233 value & SpvMemorySemanticsSequentiallyConsistentMask)) {
234 return _.diag(SPV_ERROR_INVALID_DATA, inst)
235 << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
236 "Acquire, AcquireRelease and SequentiallyConsistent";
237 }
238 }
239
240 // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
241
242 return SPV_SUCCESS;
243 }
244
245 } // namespace val
246 } // namespace spvtools
247