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,uint32_t memory_scope)26 spv_result_t ValidateMemorySemantics(ValidationState_t& _,
27                                      const Instruction* inst,
28                                      uint32_t operand_index,
29                                      uint32_t memory_scope) {
30   const SpvOp opcode = inst->opcode();
31   const auto id = inst->GetOperandAs<const uint32_t>(operand_index);
32   bool is_int32 = false, is_const_int32 = false;
33   uint32_t value = 0;
34   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
35 
36   if (!is_int32) {
37     return _.diag(SPV_ERROR_INVALID_DATA, inst)
38            << spvOpcodeString(opcode)
39            << ": expected Memory Semantics to be a 32-bit int";
40   }
41 
42   if (!is_const_int32) {
43     if (_.HasCapability(SpvCapabilityShader) &&
44         !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
45       return _.diag(SPV_ERROR_INVALID_DATA, inst)
46              << "Memory Semantics ids must be OpConstant when Shader "
47                 "capability is present";
48     }
49 
50     if (_.HasCapability(SpvCapabilityShader) &&
51         _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
52         !spvOpcodeIsConstant(_.GetIdOpcode(id))) {
53       return _.diag(SPV_ERROR_INVALID_DATA, inst)
54              << "Memory Semantics must be a constant instruction when "
55                 "CooperativeMatrixNV capability is present";
56     }
57     return SPV_SUCCESS;
58   }
59 
60   const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
61       value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
62                SpvMemorySemanticsAcquireReleaseMask |
63                SpvMemorySemanticsSequentiallyConsistentMask));
64 
65   if (num_memory_order_set_bits > 1) {
66     return _.diag(SPV_ERROR_INVALID_DATA, inst)
67            << spvOpcodeString(opcode)
68            << ": Memory Semantics can have at most one of the following "
69               "bits "
70               "set: Acquire, Release, AcquireRelease or "
71               "SequentiallyConsistent";
72   }
73 
74   if (_.memory_model() == SpvMemoryModelVulkanKHR &&
75       value & SpvMemorySemanticsSequentiallyConsistentMask) {
76     return _.diag(SPV_ERROR_INVALID_DATA, inst)
77            << "SequentiallyConsistent memory "
78               "semantics cannot be used with "
79               "the VulkanKHR memory model.";
80   }
81 
82   if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
83       !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
84     return _.diag(SPV_ERROR_INVALID_DATA, inst)
85            << spvOpcodeString(opcode)
86            << ": Memory Semantics MakeAvailableKHR requires capability "
87            << "VulkanMemoryModelKHR";
88   }
89 
90   if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
91       !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
92     return _.diag(SPV_ERROR_INVALID_DATA, inst)
93            << spvOpcodeString(opcode)
94            << ": Memory Semantics MakeVisibleKHR requires capability "
95            << "VulkanMemoryModelKHR";
96   }
97 
98   if (value & SpvMemorySemanticsOutputMemoryKHRMask &&
99       !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
100     return _.diag(SPV_ERROR_INVALID_DATA, inst)
101            << spvOpcodeString(opcode)
102            << ": Memory Semantics OutputMemoryKHR requires capability "
103            << "VulkanMemoryModelKHR";
104   }
105 
106   if (value & SpvMemorySemanticsVolatileMask) {
107     if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
108       return _.diag(SPV_ERROR_INVALID_DATA, inst)
109              << spvOpcodeString(opcode)
110              << ": Memory Semantics Volatile requires capability "
111                 "VulkanMemoryModelKHR";
112     }
113 
114     if (!spvOpcodeIsAtomicOp(inst->opcode())) {
115       return _.diag(SPV_ERROR_INVALID_DATA, inst)
116              << "Memory Semantics Volatile can only be used with atomic "
117                 "instructions";
118     }
119   }
120 
121   if (value & SpvMemorySemanticsUniformMemoryMask &&
122       !_.HasCapability(SpvCapabilityShader)) {
123     return _.diag(SPV_ERROR_INVALID_DATA, inst)
124            << spvOpcodeString(opcode)
125            << ": Memory Semantics UniformMemory requires capability Shader";
126   }
127 
128   // Checking for SpvCapabilityAtomicStorage is intentionally not done here. See
129   // https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why.
130 
131   if (value & (SpvMemorySemanticsMakeAvailableKHRMask |
132                SpvMemorySemanticsMakeVisibleKHRMask)) {
133     const bool includes_storage_class =
134         value & (SpvMemorySemanticsUniformMemoryMask |
135                  SpvMemorySemanticsSubgroupMemoryMask |
136                  SpvMemorySemanticsWorkgroupMemoryMask |
137                  SpvMemorySemanticsCrossWorkgroupMemoryMask |
138                  SpvMemorySemanticsAtomicCounterMemoryMask |
139                  SpvMemorySemanticsImageMemoryMask |
140                  SpvMemorySemanticsOutputMemoryKHRMask);
141 
142     if (!includes_storage_class) {
143       return _.diag(SPV_ERROR_INVALID_DATA, inst)
144              << spvOpcodeString(opcode)
145              << ": expected Memory Semantics to include a storage class";
146     }
147   }
148 
149   if (value & SpvMemorySemanticsMakeVisibleKHRMask &&
150       !(value & (SpvMemorySemanticsAcquireMask |
151                  SpvMemorySemanticsAcquireReleaseMask))) {
152     return _.diag(SPV_ERROR_INVALID_DATA, inst)
153            << spvOpcodeString(opcode)
154            << ": MakeVisibleKHR Memory Semantics also requires either Acquire "
155               "or AcquireRelease Memory Semantics";
156   }
157 
158   if (value & SpvMemorySemanticsMakeAvailableKHRMask &&
159       !(value & (SpvMemorySemanticsReleaseMask |
160                  SpvMemorySemanticsAcquireReleaseMask))) {
161     return _.diag(SPV_ERROR_INVALID_DATA, inst)
162            << spvOpcodeString(opcode)
163            << ": MakeAvailableKHR Memory Semantics also requires either "
164               "Release or AcquireRelease Memory Semantics";
165   }
166 
167   if (spvIsVulkanEnv(_.context()->target_env)) {
168     const bool includes_storage_class =
169         value & (SpvMemorySemanticsUniformMemoryMask |
170                  SpvMemorySemanticsWorkgroupMemoryMask |
171                  SpvMemorySemanticsImageMemoryMask |
172                  SpvMemorySemanticsOutputMemoryKHRMask);
173 
174     if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) {
175       return _.diag(SPV_ERROR_INVALID_DATA, inst)
176              << _.VkErrorID(4732) << spvOpcodeString(opcode)
177              << ": Vulkan specification requires Memory Semantics to have "
178                 "one "
179                 "of the following bits set: Acquire, Release, "
180                 "AcquireRelease "
181                 "or SequentiallyConsistent";
182     } else if (opcode != SpvOpMemoryBarrier && num_memory_order_set_bits) {
183       // should leave only atomics and control barriers for Vulkan env
184       bool memory_is_int32 = false, memory_is_const_int32 = false;
185       uint32_t memory_value = 0;
186       std::tie(memory_is_int32, memory_is_const_int32, memory_value) =
187           _.EvalInt32IfConst(memory_scope);
188       if (memory_is_int32 && memory_value == SpvScopeInvocation) {
189         return _.diag(SPV_ERROR_INVALID_DATA, inst)
190                << _.VkErrorID(4641) << spvOpcodeString(opcode)
191                << ": Vulkan specification requires Memory Semantics to be None "
192                   "if used with Invocation Memory Scope";
193       }
194     }
195 
196     if (opcode == SpvOpMemoryBarrier && !includes_storage_class) {
197       return _.diag(SPV_ERROR_INVALID_DATA, inst)
198              << _.VkErrorID(4733) << spvOpcodeString(opcode)
199              << ": expected Memory Semantics to include a Vulkan-supported "
200                 "storage class";
201     }
202 
203 #if 0
204     // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed.
205     if (opcode == SpvOpControlBarrier && value && !includes_storage_class) {
206       return _.diag(SPV_ERROR_INVALID_DATA, inst)
207              << spvOpcodeString(opcode)
208              << ": expected Memory Semantics to include a Vulkan-supported "
209                 "storage class if Memory Semantics is not None";
210     }
211 #endif
212   }
213 
214   if (opcode == SpvOpAtomicFlagClear &&
215       (value & SpvMemorySemanticsAcquireMask ||
216        value & SpvMemorySemanticsAcquireReleaseMask)) {
217     return _.diag(SPV_ERROR_INVALID_DATA, inst)
218            << "Memory Semantics Acquire and AcquireRelease cannot be used "
219               "with "
220            << spvOpcodeString(opcode);
221   }
222 
223   if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&
224       (value & SpvMemorySemanticsReleaseMask ||
225        value & SpvMemorySemanticsAcquireReleaseMask)) {
226     return _.diag(SPV_ERROR_INVALID_DATA, inst)
227            << spvOpcodeString(opcode)
228            << ": Memory Semantics Release and AcquireRelease cannot be "
229               "used "
230               "for operand Unequal";
231   }
232 
233   if (spvIsVulkanEnv(_.context()->target_env)) {
234     if (opcode == SpvOpAtomicLoad &&
235         (value & SpvMemorySemanticsReleaseMask ||
236          value & SpvMemorySemanticsAcquireReleaseMask ||
237          value & SpvMemorySemanticsSequentiallyConsistentMask)) {
238       return _.diag(SPV_ERROR_INVALID_DATA, inst)
239              << _.VkErrorID(4731)
240              << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
241                 "Release, AcquireRelease and SequentiallyConsistent";
242     }
243 
244     if (opcode == SpvOpAtomicStore &&
245         (value & SpvMemorySemanticsAcquireMask ||
246          value & SpvMemorySemanticsAcquireReleaseMask ||
247          value & SpvMemorySemanticsSequentiallyConsistentMask)) {
248       return _.diag(SPV_ERROR_INVALID_DATA, inst)
249              << _.VkErrorID(4730)
250              << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
251                 "Acquire, AcquireRelease and SequentiallyConsistent";
252     }
253   }
254 
255   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
256 
257   return SPV_SUCCESS;
258 }
259 
260 }  // namespace val
261 }  // namespace spvtools
262