xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/source/val/validate_memory_semantics.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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/spirv_target_env.h"
18 #include "source/util/bitutils.h"
19 #include "source/val/instruction.h"
20 #include "source/val/validation_state.h"
21 
22 namespace spvtools {
23 namespace val {
24 
ValidateMemorySemantics(ValidationState_t & _,const Instruction * inst,uint32_t operand_index,uint32_t memory_scope)25 spv_result_t ValidateMemorySemantics(ValidationState_t& _,
26                                      const Instruction* inst,
27                                      uint32_t operand_index,
28                                      uint32_t memory_scope) {
29   const spv::Op 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(spv::Capability::Shader) &&
43         !_.HasCapability(spv::Capability::CooperativeMatrixNV)) {
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(spv::Capability::Shader) &&
50         _.HasCapability(spv::Capability::CooperativeMatrixNV) &&
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 & uint32_t(spv::MemorySemanticsMask::Acquire |
61                        spv::MemorySemanticsMask::Release |
62                        spv::MemorySemanticsMask::AcquireRelease |
63                        spv::MemorySemanticsMask::SequentiallyConsistent));
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() == spv::MemoryModel::VulkanKHR &&
75       value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) {
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 & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) &&
83       !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
84     return _.diag(SPV_ERROR_INVALID_DATA, inst)
85            << spvOpcodeString(opcode)
86            << ": Memory Semantics MakeAvailableKHR requires capability "
87            << "VulkanMemoryModelKHR";
88   }
89 
90   if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) &&
91       !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
92     return _.diag(SPV_ERROR_INVALID_DATA, inst)
93            << spvOpcodeString(opcode)
94            << ": Memory Semantics MakeVisibleKHR requires capability "
95            << "VulkanMemoryModelKHR";
96   }
97 
98   if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) &&
99       !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
100     return _.diag(SPV_ERROR_INVALID_DATA, inst)
101            << spvOpcodeString(opcode)
102            << ": Memory Semantics OutputMemoryKHR requires capability "
103            << "VulkanMemoryModelKHR";
104   }
105 
106   if (value & uint32_t(spv::MemorySemanticsMask::Volatile)) {
107     if (!_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
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 & uint32_t(spv::MemorySemanticsMask::UniformMemory) &&
122       !_.HasCapability(spv::Capability::Shader)) {
123     return _.diag(SPV_ERROR_INVALID_DATA, inst)
124            << spvOpcodeString(opcode)
125            << ": Memory Semantics UniformMemory requires capability Shader";
126   }
127 
128   // Checking for spv::Capability::AtomicStorage is intentionally not done here.
129   // See https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning
130   // why.
131 
132   if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR |
133                        spv::MemorySemanticsMask::MakeVisibleKHR)) {
134     const bool includes_storage_class =
135         value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
136                          spv::MemorySemanticsMask::SubgroupMemory |
137                          spv::MemorySemanticsMask::WorkgroupMemory |
138                          spv::MemorySemanticsMask::CrossWorkgroupMemory |
139                          spv::MemorySemanticsMask::AtomicCounterMemory |
140                          spv::MemorySemanticsMask::ImageMemory |
141                          spv::MemorySemanticsMask::OutputMemoryKHR);
142 
143     if (!includes_storage_class) {
144       return _.diag(SPV_ERROR_INVALID_DATA, inst)
145              << spvOpcodeString(opcode)
146              << ": expected Memory Semantics to include a storage class";
147     }
148   }
149 
150   if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) &&
151       !(value & uint32_t(spv::MemorySemanticsMask::Acquire |
152                          spv::MemorySemanticsMask::AcquireRelease))) {
153     return _.diag(SPV_ERROR_INVALID_DATA, inst)
154            << spvOpcodeString(opcode)
155            << ": MakeVisibleKHR Memory Semantics also requires either Acquire "
156               "or AcquireRelease Memory Semantics";
157   }
158 
159   if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) &&
160       !(value & uint32_t(spv::MemorySemanticsMask::Release |
161                          spv::MemorySemanticsMask::AcquireRelease))) {
162     return _.diag(SPV_ERROR_INVALID_DATA, inst)
163            << spvOpcodeString(opcode)
164            << ": MakeAvailableKHR Memory Semantics also requires either "
165               "Release or AcquireRelease Memory Semantics";
166   }
167 
168   if (spvIsVulkanEnv(_.context()->target_env)) {
169     const bool includes_storage_class =
170         value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
171                          spv::MemorySemanticsMask::WorkgroupMemory |
172                          spv::MemorySemanticsMask::ImageMemory |
173                          spv::MemorySemanticsMask::OutputMemoryKHR);
174 
175     if (opcode == spv::Op::OpMemoryBarrier && !num_memory_order_set_bits) {
176       return _.diag(SPV_ERROR_INVALID_DATA, inst)
177              << _.VkErrorID(4732) << spvOpcodeString(opcode)
178              << ": Vulkan specification requires Memory Semantics to have "
179                 "one "
180                 "of the following bits set: Acquire, Release, "
181                 "AcquireRelease "
182                 "or SequentiallyConsistent";
183     } else if (opcode != spv::Op::OpMemoryBarrier &&
184                num_memory_order_set_bits) {
185       // should leave only atomics and control barriers for Vulkan env
186       bool memory_is_int32 = false, memory_is_const_int32 = false;
187       uint32_t memory_value = 0;
188       std::tie(memory_is_int32, memory_is_const_int32, memory_value) =
189           _.EvalInt32IfConst(memory_scope);
190       if (memory_is_int32 &&
191           spv::Scope(memory_value) == spv::Scope::Invocation) {
192         return _.diag(SPV_ERROR_INVALID_DATA, inst)
193                << _.VkErrorID(4641) << spvOpcodeString(opcode)
194                << ": Vulkan specification requires Memory Semantics to be None "
195                   "if used with Invocation Memory Scope";
196       }
197     }
198 
199     if (opcode == spv::Op::OpMemoryBarrier && !includes_storage_class) {
200       return _.diag(SPV_ERROR_INVALID_DATA, inst)
201              << _.VkErrorID(4733) << spvOpcodeString(opcode)
202              << ": expected Memory Semantics to include a Vulkan-supported "
203                 "storage class";
204     }
205 
206     if (opcode == spv::Op::OpControlBarrier && value && !includes_storage_class) {
207       return _.diag(SPV_ERROR_INVALID_DATA, inst)
208              << _.VkErrorID(4650) << spvOpcodeString(opcode)
209              << ": expected Memory Semantics to include a Vulkan-supported "
210                 "storage class if Memory Semantics is not None";
211     }
212   }
213 
214   if (opcode == spv::Op::OpAtomicFlagClear &&
215       (value & uint32_t(spv::MemorySemanticsMask::Acquire) ||
216        value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
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 == spv::Op::OpAtomicCompareExchange && operand_index == 5 &&
224       (value & uint32_t(spv::MemorySemanticsMask::Release) ||
225        value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
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 == spv::Op::OpAtomicLoad &&
235         (value & uint32_t(spv::MemorySemanticsMask::Release) ||
236          value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) ||
237          value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
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 == spv::Op::OpAtomicStore &&
245         (value & uint32_t(spv::MemorySemanticsMask::Acquire) ||
246          value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) ||
247          value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
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([email protected]) Add checks for OpenCL and OpenGL environments.
256 
257   return SPV_SUCCESS;
258 }
259 
260 }  // namespace val
261 }  // namespace spvtools
262