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