• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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