1 // Copyright (c) 2017 Google Inc.
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 // Validates correctness of atomic SPIR-V instructions.
16
17 #include "source/val/validate.h"
18
19 #include "source/diagnostic.h"
20 #include "source/opcode.h"
21 #include "source/spirv_target_env.h"
22 #include "source/util/bitutils.h"
23 #include "source/val/instruction.h"
24 #include "source/val/validate_memory_semantics.h"
25 #include "source/val/validate_scopes.h"
26 #include "source/val/validation_state.h"
27
28 namespace spvtools {
29 namespace val {
30
31 // Validates correctness of atomic instructions.
AtomicsPass(ValidationState_t & _,const Instruction * inst)32 spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
33 const SpvOp opcode = inst->opcode();
34 const uint32_t result_type = inst->type_id();
35
36 switch (opcode) {
37 case SpvOpAtomicLoad:
38 case SpvOpAtomicStore:
39 case SpvOpAtomicExchange:
40 case SpvOpAtomicCompareExchange:
41 case SpvOpAtomicCompareExchangeWeak:
42 case SpvOpAtomicIIncrement:
43 case SpvOpAtomicIDecrement:
44 case SpvOpAtomicIAdd:
45 case SpvOpAtomicISub:
46 case SpvOpAtomicSMin:
47 case SpvOpAtomicUMin:
48 case SpvOpAtomicSMax:
49 case SpvOpAtomicUMax:
50 case SpvOpAtomicAnd:
51 case SpvOpAtomicOr:
52 case SpvOpAtomicXor:
53 case SpvOpAtomicFlagTestAndSet:
54 case SpvOpAtomicFlagClear: {
55 if (_.HasCapability(SpvCapabilityKernel) &&
56 (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange ||
57 opcode == SpvOpAtomicCompareExchange)) {
58 if (!_.IsFloatScalarType(result_type) &&
59 !_.IsIntScalarType(result_type)) {
60 return _.diag(SPV_ERROR_INVALID_DATA, inst)
61 << spvOpcodeString(opcode)
62 << ": expected Result Type to be int or float scalar type";
63 }
64 } else if (opcode == SpvOpAtomicFlagTestAndSet) {
65 if (!_.IsBoolScalarType(result_type)) {
66 return _.diag(SPV_ERROR_INVALID_DATA, inst)
67 << spvOpcodeString(opcode)
68 << ": expected Result Type to be bool scalar type";
69 }
70 } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
71 assert(result_type == 0);
72 } else {
73 if (!_.IsIntScalarType(result_type)) {
74 return _.diag(SPV_ERROR_INVALID_DATA, inst)
75 << spvOpcodeString(opcode)
76 << ": expected Result Type to be int scalar type";
77 }
78 if (spvIsVulkanEnv(_.context()->target_env) &&
79 _.GetBitWidth(result_type) != 32) {
80 switch (opcode) {
81 case SpvOpAtomicSMin:
82 case SpvOpAtomicUMin:
83 case SpvOpAtomicSMax:
84 case SpvOpAtomicUMax:
85 case SpvOpAtomicAnd:
86 case SpvOpAtomicOr:
87 case SpvOpAtomicXor:
88 case SpvOpAtomicIAdd:
89 case SpvOpAtomicLoad:
90 case SpvOpAtomicStore:
91 case SpvOpAtomicExchange:
92 case SpvOpAtomicCompareExchange: {
93 if (_.GetBitWidth(result_type) == 64 &&
94 !_.HasCapability(SpvCapabilityInt64Atomics))
95 return _.diag(SPV_ERROR_INVALID_DATA, inst)
96 << spvOpcodeString(opcode)
97 << ": 64-bit atomics require the Int64Atomics "
98 "capability";
99 } break;
100 default:
101 return _.diag(SPV_ERROR_INVALID_DATA, inst)
102 << spvOpcodeString(opcode)
103 << ": according to the Vulkan spec atomic Result Type "
104 "needs "
105 "to be a 32-bit int scalar type";
106 }
107 }
108 }
109
110 uint32_t operand_index =
111 opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2;
112 const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
113
114 uint32_t data_type = 0;
115 uint32_t storage_class = 0;
116 if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
117 return _.diag(SPV_ERROR_INVALID_DATA, inst)
118 << spvOpcodeString(opcode)
119 << ": expected Pointer to be of type OpTypePointer";
120 }
121
122 switch (storage_class) {
123 case SpvStorageClassUniform:
124 case SpvStorageClassWorkgroup:
125 case SpvStorageClassCrossWorkgroup:
126 case SpvStorageClassGeneric:
127 case SpvStorageClassAtomicCounter:
128 case SpvStorageClassImage:
129 case SpvStorageClassStorageBuffer:
130 break;
131 default:
132 if (spvIsOpenCLEnv(_.context()->target_env)) {
133 if (storage_class != SpvStorageClassFunction) {
134 return _.diag(SPV_ERROR_INVALID_DATA, inst)
135 << spvOpcodeString(opcode)
136 << ": expected Pointer Storage Class to be Uniform, "
137 "Workgroup, CrossWorkgroup, Generic, AtomicCounter, "
138 "Image, StorageBuffer or Function";
139 }
140 } else {
141 return _.diag(SPV_ERROR_INVALID_DATA, inst)
142 << spvOpcodeString(opcode)
143 << ": expected Pointer Storage Class to be Uniform, "
144 "Workgroup, CrossWorkgroup, Generic, AtomicCounter, "
145 "Image or StorageBuffer";
146 }
147 }
148
149 if (opcode == SpvOpAtomicFlagTestAndSet ||
150 opcode == SpvOpAtomicFlagClear) {
151 if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
152 return _.diag(SPV_ERROR_INVALID_DATA, inst)
153 << spvOpcodeString(opcode)
154 << ": expected Pointer to point to a value of 32-bit int type";
155 }
156 } else if (opcode == SpvOpAtomicStore) {
157 if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
158 return _.diag(SPV_ERROR_INVALID_DATA, inst)
159 << spvOpcodeString(opcode)
160 << ": expected Pointer to be a pointer to int or float "
161 << "scalar type";
162 }
163 } else {
164 if (data_type != result_type) {
165 return _.diag(SPV_ERROR_INVALID_DATA, inst)
166 << spvOpcodeString(opcode)
167 << ": expected Pointer to point to a value of type Result "
168 "Type";
169 }
170 }
171
172 auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);
173 if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
174 return error;
175 }
176
177 if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
178 return error;
179
180 if (opcode == SpvOpAtomicCompareExchange ||
181 opcode == SpvOpAtomicCompareExchangeWeak) {
182 if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
183 return error;
184 }
185
186 if (opcode == SpvOpAtomicStore) {
187 const uint32_t value_type = _.GetOperandTypeId(inst, 3);
188 if (value_type != data_type) {
189 return _.diag(SPV_ERROR_INVALID_DATA, inst)
190 << spvOpcodeString(opcode)
191 << ": expected Value type and the type pointed to by "
192 "Pointer to be the same";
193 }
194 } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
195 opcode != SpvOpAtomicIDecrement &&
196 opcode != SpvOpAtomicFlagTestAndSet &&
197 opcode != SpvOpAtomicFlagClear) {
198 const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
199 if (value_type != result_type) {
200 return _.diag(SPV_ERROR_INVALID_DATA, inst)
201 << spvOpcodeString(opcode)
202 << ": expected Value to be of type Result Type";
203 }
204 }
205
206 if (opcode == SpvOpAtomicCompareExchange ||
207 opcode == SpvOpAtomicCompareExchangeWeak) {
208 const uint32_t comparator_type =
209 _.GetOperandTypeId(inst, operand_index++);
210 if (comparator_type != result_type) {
211 return _.diag(SPV_ERROR_INVALID_DATA, inst)
212 << spvOpcodeString(opcode)
213 << ": expected Comparator to be of type Result Type";
214 }
215 }
216
217 break;
218 }
219
220 default:
221 break;
222 }
223
224 return SPV_SUCCESS;
225 }
226
227 } // namespace val
228 } // namespace spvtools
229