/src/spirv-tools/source/val/validate_constants.cpp
Line | Count | Source |
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/opcode.h" |
16 | | #include "source/val/instruction.h" |
17 | | #include "source/val/validate.h" |
18 | | #include "source/val/validation_state.h" |
19 | | |
20 | | namespace spvtools { |
21 | | namespace val { |
22 | | namespace { |
23 | | |
24 | | spv_result_t ValidateConstantBool(ValidationState_t& _, |
25 | 3.16k | const Instruction* inst) { |
26 | 3.16k | auto type = _.FindDef(inst->type_id()); |
27 | 3.16k | if (!type || type->opcode() != spv::Op::OpTypeBool) { |
28 | 5 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
29 | 5 | << "Op" << spvOpcodeString(inst->opcode()) << " Result Type <id> " |
30 | 5 | << _.getIdName(inst->type_id()) << " is not a boolean type."; |
31 | 5 | } |
32 | | |
33 | 3.15k | return SPV_SUCCESS; |
34 | 3.16k | } |
35 | | |
36 | 20.3k | bool isCompositeType(const Instruction* inst) { |
37 | 20.3k | bool is_tensor = inst->opcode() == spv::Op::OpTypeTensorARM; |
38 | 20.3k | bool tensor_is_shaped = inst->words().size() == 5; |
39 | 20.3k | return spvOpcodeIsComposite(inst->opcode()) || |
40 | 7 | (is_tensor && tensor_is_shaped); |
41 | 20.3k | } |
42 | | |
43 | | spv_result_t ValidateConstantOperand(ValidationState_t& _, |
44 | 76.5k | const Instruction* inst, size_t operand) { |
45 | 76.5k | std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); |
46 | | |
47 | 76.5k | const auto operand_id = inst->GetOperandAs<uint32_t>(operand); |
48 | 76.5k | const bool inst_is_spec_constant = spvOpcodeIsSpecConstant(inst->opcode()); |
49 | 76.5k | const auto operand_opcode = _.GetIdOpcode(operand_id); |
50 | 76.5k | const bool is_constant = spvOpcodeIsConstantOrUndef(operand_opcode); |
51 | 76.5k | const bool is_spec_constant = spvOpcodeIsSpecConstant(operand_opcode); |
52 | 76.5k | if (!is_constant) { |
53 | | // All operands must be constant, undef, or poison. |
54 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
55 | 0 | << opcode_name |
56 | 0 | << " must only have constant, undef, or poison operands: <id> " |
57 | 0 | << _.getIdName(operand_id); |
58 | 76.5k | } else if (!inst_is_spec_constant && is_spec_constant) { |
59 | | // Spec constants are only allowed for spec constant opcodes. |
60 | 9 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
61 | 9 | << opcode_name << " must not have spec constant operands: <id> " |
62 | 9 | << _.getIdName(operand_id); |
63 | 9 | } |
64 | | |
65 | 76.5k | return SPV_SUCCESS; |
66 | 76.5k | } |
67 | | |
68 | | spv_result_t ValidateConstantComposite(ValidationState_t& _, |
69 | 20.3k | const Instruction* inst) { |
70 | 20.3k | std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); |
71 | | |
72 | 20.3k | const auto result_type = _.FindDef(inst->type_id()); |
73 | 20.3k | if (!result_type || !isCompositeType(result_type)) { |
74 | 7 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
75 | 7 | << opcode_name << " Result Type <id> " |
76 | 7 | << _.getIdName(inst->type_id()) << " is not a composite type."; |
77 | 7 | } |
78 | | |
79 | 20.3k | const auto constituent_count = inst->operands().size() - 2; |
80 | 20.3k | switch (result_type->opcode()) { |
81 | 17.2k | case spv::Op::OpTypeVector: |
82 | 17.2k | case spv::Op::OpTypeVectorIdEXT: { |
83 | 17.2k | uint32_t num_result_components = _.GetDimension(result_type->id()); |
84 | 17.2k | bool comp_is_int32 = true, comp_is_const_int32 = true; |
85 | | |
86 | 17.2k | if (result_type->opcode() == spv::Op::OpTypeVectorIdEXT) { |
87 | 0 | uint32_t comp_count_id = result_type->GetOperandAs<uint32_t>(2); |
88 | 0 | std::tie(comp_is_int32, comp_is_const_int32, num_result_components) = |
89 | 0 | _.EvalInt32IfConst(comp_count_id); |
90 | 0 | } |
91 | | |
92 | 17.2k | if (comp_is_const_int32 && num_result_components != constituent_count) { |
93 | | // TODO: Output ID's on diagnostic |
94 | 6 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
95 | 6 | << opcode_name |
96 | 6 | << " Constituent <id> count does not match " |
97 | 6 | "Result Type <id> " |
98 | 6 | << _.getIdName(result_type->id()) << "s vector component count."; |
99 | 6 | } |
100 | 17.2k | const auto component_type = |
101 | 17.2k | _.FindDef(result_type->GetOperandAs<uint32_t>(1)); |
102 | 17.2k | if (!component_type) { |
103 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
104 | 0 | << "Component type is not defined."; |
105 | 0 | } |
106 | 17.2k | for (size_t constituent_index = 2; |
107 | 76.1k | constituent_index < inst->operands().size(); constituent_index++) { |
108 | 58.9k | const auto constituent_id = |
109 | 58.9k | inst->GetOperandAs<uint32_t>(constituent_index); |
110 | 58.9k | const auto constituent = _.FindDef(constituent_id); |
111 | 58.9k | const auto constituent_result_type = _.FindDef(constituent->type_id()); |
112 | 58.9k | if (!constituent_result_type || |
113 | 58.9k | component_type->id() != constituent_result_type->id()) { |
114 | 10 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
115 | 10 | << opcode_name << " Constituent <id> " |
116 | 10 | << _.getIdName(constituent_id) |
117 | 10 | << "s type does not match Result Type <id> " |
118 | 10 | << _.getIdName(result_type->id()) << "s vector element type."; |
119 | 10 | } |
120 | 58.9k | } |
121 | 17.2k | } break; |
122 | 17.1k | case spv::Op::OpTypeMatrix: { |
123 | 41 | const auto column_count = result_type->GetOperandAs<uint32_t>(2); |
124 | 41 | if (column_count != constituent_count) { |
125 | | // TODO: Output ID's on diagnostic |
126 | 4 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
127 | 4 | << opcode_name |
128 | 4 | << " Constituent <id> count does not match " |
129 | 4 | "Result Type <id> " |
130 | 4 | << _.getIdName(result_type->id()) << "s matrix column count."; |
131 | 4 | } |
132 | | |
133 | 37 | const auto column_type = |
134 | 37 | _.FindDef(result_type->GetOperandAs<uint32_t>(1)); |
135 | 37 | if (!column_type) { |
136 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
137 | 0 | << "Column type is not defined."; |
138 | 0 | } |
139 | 37 | const auto component_count = column_type->GetOperandAs<uint32_t>(2); |
140 | 37 | const auto component_type = |
141 | 37 | _.FindDef(column_type->GetOperandAs<uint32_t>(1)); |
142 | 37 | if (!component_type) { |
143 | 0 | return _.diag(SPV_ERROR_INVALID_ID, column_type) |
144 | 0 | << "Component type is not defined."; |
145 | 0 | } |
146 | | |
147 | 37 | for (size_t constituent_index = 2; |
148 | 111 | constituent_index < inst->operands().size(); constituent_index++) { |
149 | 84 | const auto constituent_id = |
150 | 84 | inst->GetOperandAs<uint32_t>(constituent_index); |
151 | 84 | const auto constituent = _.FindDef(constituent_id); |
152 | 84 | const auto vector = _.FindDef(constituent->type_id()); |
153 | 84 | if (!vector) { |
154 | 2 | return _.diag(SPV_ERROR_INVALID_ID, constituent) |
155 | 2 | << "Result type is not defined."; |
156 | 2 | } |
157 | 82 | if (column_type->opcode() != vector->opcode()) { |
158 | 6 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
159 | 6 | << opcode_name << " Constituent <id> " |
160 | 6 | << _.getIdName(constituent_id) |
161 | 6 | << " type does not match Result Type <id> " |
162 | 6 | << _.getIdName(result_type->id()) << "s matrix column type."; |
163 | 6 | } |
164 | 76 | const auto vector_component_type = |
165 | 76 | _.FindDef(vector->GetOperandAs<uint32_t>(1)); |
166 | 76 | if (component_type->id() != vector_component_type->id()) { |
167 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
168 | 0 | << opcode_name << " Constituent <id> " |
169 | 0 | << _.getIdName(constituent_id) |
170 | 0 | << " component type does not match Result Type <id> " |
171 | 0 | << _.getIdName(result_type->id()) |
172 | 0 | << "s matrix column component type."; |
173 | 0 | } |
174 | 76 | if (component_count != vector->GetOperandAs<uint32_t>(2)) { |
175 | 2 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
176 | 2 | << opcode_name << " Constituent <id> " |
177 | 2 | << _.getIdName(constituent_id) |
178 | 2 | << " vector component count does not match Result Type <id> " |
179 | 2 | << _.getIdName(result_type->id()) |
180 | 2 | << "s vector component count."; |
181 | 2 | } |
182 | 76 | } |
183 | 37 | } break; |
184 | 555 | case spv::Op::OpTypeArray: { |
185 | 555 | auto element_type = _.FindDef(result_type->GetOperandAs<uint32_t>(1)); |
186 | 555 | if (!element_type) { |
187 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
188 | 0 | << "Element type is not defined."; |
189 | 0 | } |
190 | 555 | const auto length = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); |
191 | 555 | if (!length) { |
192 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
193 | 0 | << "Length is not defined."; |
194 | 0 | } |
195 | 555 | bool is_int32; |
196 | 555 | bool is_const; |
197 | 555 | uint32_t value; |
198 | 555 | std::tie(is_int32, is_const, value) = _.EvalInt32IfConst(length->id()); |
199 | 555 | if (is_int32 && is_const && value != constituent_count) { |
200 | 25 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
201 | 25 | << opcode_name |
202 | 25 | << " Constituent count does not match " |
203 | 25 | "Result Type <id> " |
204 | 25 | << _.getIdName(result_type->id()) << "s array length."; |
205 | 25 | } |
206 | 530 | for (size_t constituent_index = 2; |
207 | 12.0k | constituent_index < inst->operands().size(); constituent_index++) { |
208 | 11.5k | const auto constituent_id = |
209 | 11.5k | inst->GetOperandAs<uint32_t>(constituent_index); |
210 | 11.5k | const auto constituent = _.FindDef(constituent_id); |
211 | 11.5k | const auto constituent_type = _.FindDef(constituent->type_id()); |
212 | 11.5k | if (!constituent_type) { |
213 | 3 | return _.diag(SPV_ERROR_INVALID_ID, constituent) |
214 | 3 | << "Result type is not defined."; |
215 | 3 | } |
216 | 11.5k | if (element_type->id() != constituent_type->id()) { |
217 | 15 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
218 | 15 | << opcode_name << " Constituent <id> " |
219 | 15 | << _.getIdName(constituent_id) |
220 | 15 | << "s type does not match Result Type <id> " |
221 | 15 | << _.getIdName(result_type->id()) << "s array element type."; |
222 | 15 | } |
223 | 11.5k | } |
224 | 530 | } break; |
225 | 2.51k | case spv::Op::OpTypeStruct: { |
226 | 2.51k | const auto member_count = result_type->operands().size() - 1; |
227 | 2.51k | if (member_count != constituent_count) { |
228 | 2 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
229 | 2 | << opcode_name << " Constituent <id> " |
230 | 2 | << _.getIdName(inst->type_id()) |
231 | 2 | << " count does not match Result Type <id> " |
232 | 2 | << _.getIdName(result_type->id()) << "s struct member count."; |
233 | 2 | } |
234 | 2.51k | for (uint32_t constituent_index = 2, member_index = 1; |
235 | 9.80k | constituent_index < inst->operands().size(); |
236 | 7.29k | constituent_index++, member_index++) { |
237 | 7.29k | const auto constituent_id = |
238 | 7.29k | inst->GetOperandAs<uint32_t>(constituent_index); |
239 | 7.29k | const auto constituent = _.FindDef(constituent_id); |
240 | 7.29k | const auto constituent_type = _.FindDef(constituent->type_id()); |
241 | 7.29k | if (!constituent_type) { |
242 | 3 | return _.diag(SPV_ERROR_INVALID_ID, constituent) |
243 | 3 | << "Result type is not defined."; |
244 | 3 | } |
245 | | |
246 | 7.29k | const auto member_type_id = |
247 | 7.29k | result_type->GetOperandAs<uint32_t>(member_index); |
248 | 7.29k | const auto member_type = _.FindDef(member_type_id); |
249 | 7.29k | if (!member_type || member_type->id() != constituent_type->id()) { |
250 | 7 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
251 | 7 | << opcode_name << " Constituent <id> " |
252 | 7 | << _.getIdName(constituent_id) |
253 | 7 | << " type does not match the Result Type <id> " |
254 | 7 | << _.getIdName(result_type->id()) << "s member type."; |
255 | 7 | } |
256 | 7.29k | } |
257 | 2.51k | } break; |
258 | 2.50k | case spv::Op::OpTypeCooperativeMatrixKHR: |
259 | 0 | case spv::Op::OpTypeCooperativeMatrixNV: { |
260 | 0 | if (1 != constituent_count) { |
261 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
262 | 0 | << opcode_name << " Constituent <id> " |
263 | 0 | << _.getIdName(inst->type_id()) << " count must be one."; |
264 | 0 | } |
265 | 0 | const auto constituent_id = inst->GetOperandAs<uint32_t>(2); |
266 | 0 | const auto constituent = _.FindDef(constituent_id); |
267 | 0 | const auto constituent_type = _.FindDef(constituent->type_id()); |
268 | 0 | if (!constituent_type) { |
269 | 0 | return _.diag(SPV_ERROR_INVALID_ID, constituent) |
270 | 0 | << "Result type is not defined."; |
271 | 0 | } |
272 | | |
273 | 0 | const auto component_type_id = result_type->GetOperandAs<uint32_t>(1); |
274 | 0 | const auto component_type = _.FindDef(component_type_id); |
275 | 0 | if (!component_type || component_type->id() != constituent_type->id()) { |
276 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
277 | 0 | << opcode_name << " Constituent <id> " |
278 | 0 | << _.getIdName(constituent_id) |
279 | 0 | << " type does not match the Result Type <id> " |
280 | 0 | << _.getIdName(result_type->id()) << "s component type."; |
281 | 0 | } |
282 | 0 | } break; |
283 | 0 | case spv::Op::OpTypeTensorARM: { |
284 | 0 | auto inst_element_type = |
285 | 0 | _.FindDef(result_type->GetOperandAs<uint32_t>(1)); |
286 | 0 | if (!inst_element_type) { |
287 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
288 | 0 | << "Element type is not defined."; |
289 | 0 | } |
290 | 0 | const auto inst_rank = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); |
291 | 0 | if (!inst_rank) { |
292 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
293 | 0 | << "Rank is not defined."; |
294 | 0 | } |
295 | 0 | const auto inst_shape = _.FindDef(result_type->GetOperandAs<uint32_t>(3)); |
296 | 0 | if (!inst_shape) { |
297 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
298 | 0 | << "Shape is not defined."; |
299 | 0 | } |
300 | | |
301 | 0 | uint64_t rank = 0; |
302 | 0 | _.EvalConstantValUint64(inst_rank->id(), &rank); |
303 | |
|
304 | 0 | uint64_t outermost_shape = 0; |
305 | 0 | if (_.EvalConstantValUint64(inst_shape->GetOperandAs<uint32_t>(2), |
306 | 0 | &outermost_shape) && |
307 | 0 | (outermost_shape != constituent_count)) { |
308 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
309 | 0 | << opcode_name |
310 | 0 | << " Constituent count does not match " |
311 | 0 | "the shape of Result Type <id> " |
312 | 0 | << _.getIdName(result_type->id()) |
313 | 0 | << " along its outermost dimension, " << "expected " |
314 | 0 | << outermost_shape << " but got " << constituent_count << "."; |
315 | 0 | } |
316 | | |
317 | 0 | for (size_t constituent_index = 2; |
318 | 0 | constituent_index < inst->operands().size(); constituent_index++) { |
319 | 0 | const auto constituent_id = |
320 | 0 | inst->GetOperandAs<uint32_t>(constituent_index); |
321 | 0 | const auto constituent = _.FindDef(constituent_id); |
322 | 0 | const auto constituent_type = _.FindDef(constituent->type_id()); |
323 | 0 | if (!constituent_type) { |
324 | 0 | return _.diag(SPV_ERROR_INVALID_ID, constituent) |
325 | 0 | << "Type of Constituent " << constituent_index - 2 |
326 | 0 | << " is not defined."; |
327 | 0 | } |
328 | | |
329 | 0 | if (rank == 0) { |
330 | | // The rank of the returned tensor constant is not known. |
331 | | // Skip rank-dependent validation. |
332 | 0 | continue; |
333 | 0 | } |
334 | | |
335 | 0 | if (rank == 1) { |
336 | 0 | if (inst_element_type->id() != constituent_type->id()) { |
337 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
338 | 0 | << opcode_name << " Constituent <id> " |
339 | 0 | << _.getIdName(constituent_id) |
340 | 0 | << " type does not match the element type of the tensor (" |
341 | 0 | << _.getIdName(result_type->id()) << ")."; |
342 | 0 | } |
343 | 0 | } else { |
344 | 0 | if (constituent_type->opcode() != spv::Op::OpTypeTensorARM) { |
345 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
346 | 0 | << opcode_name << " Constituent <id> " |
347 | 0 | << _.getIdName(constituent_id) |
348 | 0 | << " must be an OpTypeTensorARM."; |
349 | 0 | } |
350 | 0 | auto inst_constituent_element_type = |
351 | 0 | _.FindDef(constituent_type->GetOperandAs<uint32_t>(1)); |
352 | 0 | if (!inst_constituent_element_type || |
353 | 0 | inst_constituent_element_type->id() != inst_element_type->id()) { |
354 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
355 | 0 | << opcode_name << " Constituent <id> " |
356 | 0 | << _.getIdName(constituent_id) |
357 | 0 | << " must have the same Element Type as Result Type <id> " |
358 | 0 | << _.getIdName(result_type->id()) << "."; |
359 | 0 | } |
360 | 0 | auto inst_constituent_rank = |
361 | 0 | _.FindDef(constituent_type->GetOperandAs<uint32_t>(2)); |
362 | 0 | uint64_t constituent_rank; |
363 | 0 | if (inst_constituent_rank && |
364 | 0 | _.EvalConstantValUint64(inst_constituent_rank->id(), |
365 | 0 | &constituent_rank) && |
366 | 0 | (constituent_rank != rank - 1)) { |
367 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
368 | 0 | << opcode_name << " Constituent <id> " |
369 | 0 | << _.getIdName(constituent_id) |
370 | 0 | << " must have a Rank that is 1 less than the Rank of " |
371 | 0 | "Result Type <id> " |
372 | 0 | << _.getIdName(result_type->id()) << ", expected " |
373 | 0 | << rank - 1 << " but got " << constituent_rank << "."; |
374 | 0 | } |
375 | | |
376 | 0 | auto inst_constituent_shape = |
377 | 0 | _.FindDef(constituent_type->GetOperandAs<uint32_t>(3)); |
378 | 0 | if (!inst_constituent_shape) { |
379 | 0 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
380 | 0 | << "Shape of Constituent " << constituent_index - 2 |
381 | 0 | << " is not defined."; |
382 | 0 | } |
383 | 0 | for (size_t constituent_shape_index = 2; |
384 | 0 | constituent_shape_index < |
385 | 0 | inst_constituent_shape->operands().size(); |
386 | 0 | constituent_shape_index++) { |
387 | 0 | size_t shape_index = constituent_shape_index + 1; |
388 | 0 | uint64_t constituent_shape = 0, shape = 1; |
389 | 0 | if (_.EvalConstantValUint64( |
390 | 0 | inst_constituent_shape->GetOperandAs<uint32_t>( |
391 | 0 | constituent_shape_index), |
392 | 0 | &constituent_shape) && |
393 | 0 | _.EvalConstantValUint64( |
394 | 0 | inst_shape->GetOperandAs<uint32_t>(shape_index), &shape) && |
395 | 0 | (constituent_shape != shape)) { |
396 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
397 | 0 | << opcode_name << " Constituent <id> " |
398 | 0 | << _.getIdName(constituent_id) |
399 | 0 | << " must have a Shape that matches that of Result Type " |
400 | 0 | "<id> " |
401 | 0 | << _.getIdName(result_type->id()) |
402 | 0 | << " along all inner dimensions of Result Type, expected " |
403 | 0 | << shape << " for dimension " |
404 | 0 | << constituent_shape_index - 2 |
405 | 0 | << " of Constituent but got " << constituent_shape << "."; |
406 | 0 | } |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } break; |
411 | 9 | default: |
412 | 9 | break; |
413 | 20.3k | } |
414 | | |
415 | 96.7k | for (size_t i = 2; i < inst->operands().size(); i++) { |
416 | 76.5k | if (auto error = ValidateConstantOperand(_, inst, i)) { |
417 | 9 | return error; |
418 | 9 | } |
419 | 76.5k | } |
420 | | |
421 | 20.2k | return SPV_SUCCESS; |
422 | 20.2k | } |
423 | | |
424 | | spv_result_t ValidateConstantCompositeReplicate(ValidationState_t& _, |
425 | 0 | const Instruction* inst) { |
426 | 0 | std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); |
427 | |
|
428 | 0 | const auto result_type = _.FindDef(inst->type_id()); |
429 | 0 | if (!result_type || !isCompositeType(result_type)) { |
430 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
431 | 0 | << opcode_name << " Result Type <id> " |
432 | 0 | << _.getIdName(inst->type_id()) << " is not a composite type."; |
433 | 0 | } |
434 | | |
435 | 0 | const auto constituent_id = inst->GetOperandAs<uint32_t>(2); |
436 | 0 | const auto constituent = _.FindDef(constituent_id); |
437 | 0 | switch (result_type->opcode()) { |
438 | 0 | case spv::Op::OpTypeVector: |
439 | 0 | case spv::Op::OpTypeVectorIdEXT: |
440 | 0 | case spv::Op::OpTypeMatrix: |
441 | 0 | case spv::Op::OpTypeArray: |
442 | 0 | case spv::Op::OpTypeCooperativeMatrixKHR: |
443 | 0 | case spv::Op::OpTypeCooperativeMatrixNV: |
444 | 0 | case spv::Op::OpTypeTensorARM: { |
445 | 0 | const auto component_type = result_type->GetOperandAs<uint32_t>(1); |
446 | 0 | if (component_type != constituent->type_id()) { |
447 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
448 | 0 | << opcode_name << " Constituent <id> " |
449 | 0 | << _.getIdName(constituent_id) |
450 | 0 | << "s type does not match Result Type <id> " |
451 | 0 | << _.getIdName(result_type->id()) << "s element type."; |
452 | 0 | } |
453 | 0 | break; |
454 | 0 | } |
455 | 0 | case spv::Op::OpTypeStruct: { |
456 | 0 | const auto member_count = result_type->operands().size() - 1; |
457 | 0 | for (uint32_t member_index = 1; member_index <= member_count; |
458 | 0 | member_index++) { |
459 | 0 | const auto member_type_id = |
460 | 0 | result_type->GetOperandAs<uint32_t>(member_index); |
461 | 0 | if (member_type_id != constituent->type_id()) { |
462 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
463 | 0 | << opcode_name << " Constituent <id> " |
464 | 0 | << _.getIdName(constituent_id) |
465 | 0 | << " type does not match the Result Type <id> " |
466 | 0 | << _.getIdName(result_type->id()) << "s member type."; |
467 | 0 | } |
468 | 0 | } |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | default: |
472 | 0 | break; |
473 | 0 | } |
474 | | |
475 | 0 | return ValidateConstantOperand(_, inst, 2); |
476 | 0 | } |
477 | | |
478 | | spv_result_t ValidateConstantSampler(ValidationState_t& _, |
479 | 3 | const Instruction* inst) { |
480 | 3 | const auto result_type = _.FindDef(inst->type_id()); |
481 | 3 | if (!result_type || result_type->opcode() != spv::Op::OpTypeSampler) { |
482 | 2 | return _.diag(SPV_ERROR_INVALID_ID, result_type) |
483 | 2 | << "OpConstantSampler Result Type <id> " |
484 | 2 | << _.getIdName(inst->type_id()) << " is not a sampler type."; |
485 | 2 | } |
486 | | |
487 | 1 | return SPV_SUCCESS; |
488 | 3 | } |
489 | | |
490 | | // True if instruction defines a type that can have a null value, as defined by |
491 | | // the SPIR-V spec. Tracks composite-type components through module to check |
492 | | // nullability transitively. |
493 | | bool IsTypeNullable(const std::vector<uint32_t>& instruction, |
494 | 2.25k | const ValidationState_t& _) { |
495 | 2.25k | uint16_t opcode; |
496 | 2.25k | uint16_t word_count; |
497 | 2.25k | spvOpcodeSplit(instruction[0], &word_count, &opcode); |
498 | 2.25k | switch (static_cast<spv::Op>(opcode)) { |
499 | 480 | case spv::Op::OpTypeBool: |
500 | 1.35k | case spv::Op::OpTypeInt: |
501 | 1.60k | case spv::Op::OpTypeFloat: |
502 | 1.61k | case spv::Op::OpTypeEvent: |
503 | 1.62k | case spv::Op::OpTypeDeviceEvent: |
504 | 1.63k | case spv::Op::OpTypeReserveId: |
505 | 1.64k | case spv::Op::OpTypeQueue: |
506 | 1.64k | return true; |
507 | 14 | case spv::Op::OpTypeArray: |
508 | 17 | case spv::Op::OpTypeMatrix: |
509 | 17 | case spv::Op::OpTypeCooperativeMatrixNV: |
510 | 17 | case spv::Op::OpTypeCooperativeMatrixKHR: |
511 | 17 | case spv::Op::OpTypeVectorIdEXT: |
512 | 95 | case spv::Op::OpTypeVector: { |
513 | 95 | auto base_type = _.FindDef(instruction[2]); |
514 | 95 | return base_type && IsTypeNullable(base_type->words(), _); |
515 | 17 | } |
516 | 327 | case spv::Op::OpTypeStruct: { |
517 | 1.11k | for (size_t elementIndex = 2; elementIndex < instruction.size(); |
518 | 795 | ++elementIndex) { |
519 | 795 | auto element = _.FindDef(instruction[elementIndex]); |
520 | 795 | if (!element || !IsTypeNullable(element->words(), _)) return false; |
521 | 795 | } |
522 | 320 | return true; |
523 | 327 | } |
524 | 0 | case spv::Op::OpTypeUntypedPointerKHR: |
525 | 182 | case spv::Op::OpTypePointer: |
526 | 182 | if (spv::StorageClass(instruction[2]) == |
527 | 182 | spv::StorageClass::PhysicalStorageBuffer) { |
528 | 0 | return false; |
529 | 0 | } |
530 | 182 | return true; |
531 | 0 | case spv::Op::OpTypeTensorARM: { |
532 | 0 | auto elem_type = _.FindDef(instruction[2]); |
533 | 0 | return (instruction.size() > 4) && elem_type && |
534 | 0 | IsTypeNullable(elem_type->words(), _); |
535 | 182 | } |
536 | 8 | default: |
537 | 8 | return false; |
538 | 2.25k | } |
539 | 2.25k | } |
540 | | |
541 | | spv_result_t ValidateConstantNull(ValidationState_t& _, |
542 | 1.36k | const Instruction* inst) { |
543 | 1.36k | const auto result_type = _.FindDef(inst->type_id()); |
544 | 1.36k | if (!result_type || !IsTypeNullable(result_type->words(), _)) { |
545 | 8 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
546 | 8 | << "OpConstantNull Result Type <id> " << _.getIdName(inst->type_id()) |
547 | 8 | << " cannot have a null value."; |
548 | 8 | } |
549 | | |
550 | 1.35k | return SPV_SUCCESS; |
551 | 1.36k | } |
552 | | |
553 | | spv_result_t ValidateConstantSizeOfEXT(ValidationState_t& _, |
554 | 0 | const Instruction* inst) { |
555 | 0 | const Instruction* result_type = _.FindDef(inst->type_id()); |
556 | 0 | const uint32_t bit_width = result_type->opcode() == spv::Op::OpTypeInt |
557 | 0 | ? result_type->GetOperandAs<uint32_t>(1) |
558 | 0 | : 0; |
559 | | |
560 | | // VVL will validate the SPV_EXT_shader_64bit_indexing interaction |
561 | 0 | if (bit_width != 64 && bit_width != 32) { |
562 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
563 | 0 | << "For OpConstantSizeOfEXT instruction, its result type " |
564 | 0 | << "must be a 32-bit or 64-bit integer type scalar." |
565 | 0 | << " (OpCapability Int64 is required for 64-bit)"; |
566 | 0 | } |
567 | | |
568 | 0 | const uint32_t type_operand = inst->GetOperandAs<uint32_t>(2); |
569 | 0 | if (!_.IsDescriptorType(type_operand)) { |
570 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
571 | 0 | << "For OpConstantSizeOfEXT instruction, its Type operand <Id> " |
572 | 0 | << _.getIdName(type_operand) << " must be a Descriptor type."; |
573 | 0 | } |
574 | 0 | return SPV_SUCCESS; |
575 | 0 | } |
576 | | |
577 | | // Validates that OpSpecConstant specializes to either int or float type. |
578 | | spv_result_t ValidateSpecConstant(ValidationState_t& _, |
579 | 828 | const Instruction* inst) { |
580 | | // Operand 0 is the <id> of the type that we're specializing to. |
581 | 828 | auto type_id = inst->GetOperandAs<const uint32_t>(0); |
582 | 828 | auto type_instruction = _.FindDef(type_id); |
583 | 828 | auto type_opcode = type_instruction->opcode(); |
584 | 828 | if (type_opcode != spv::Op::OpTypeInt && |
585 | 145 | type_opcode != spv::Op::OpTypeFloat) { |
586 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant " |
587 | 0 | "must be an integer or " |
588 | 0 | "floating-point number."; |
589 | 0 | } |
590 | 828 | return SPV_SUCCESS; |
591 | 828 | } |
592 | | |
593 | | spv_result_t ValidateSpecConstantOp(ValidationState_t& _, |
594 | 333 | const Instruction* inst) { |
595 | 333 | const auto op = inst->GetOperandAs<spv::Op>(2); |
596 | | |
597 | | // The binary parser already ensures that the op is valid for *some* |
598 | | // environment. Here we check restrictions. |
599 | 333 | switch (op) { |
600 | 9 | case spv::Op::OpQuantizeToF16: |
601 | 9 | if (!_.HasCapability(spv::Capability::Shader)) { |
602 | 1 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
603 | 1 | << "Specialization constant operation " << spvOpcodeString(op) |
604 | 1 | << " requires Shader capability"; |
605 | 1 | } |
606 | 8 | break; |
607 | | |
608 | 8 | case spv::Op::OpUConvert: |
609 | 6 | if (!_.features().uconvert_spec_constant_op && |
610 | 6 | !_.HasCapability(spv::Capability::Kernel)) { |
611 | 1 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
612 | 1 | << "Prior to SPIR-V 1.4, specialization constant operation " |
613 | 1 | "UConvert requires Kernel capability or extension " |
614 | 1 | "SPV_AMD_gpu_shader_int16"; |
615 | 1 | } |
616 | 5 | break; |
617 | | |
618 | 5 | case spv::Op::OpConvertFToS: |
619 | 3 | case spv::Op::OpConvertSToF: |
620 | 21 | case spv::Op::OpConvertFToU: |
621 | 24 | case spv::Op::OpConvertUToF: |
622 | 26 | case spv::Op::OpConvertPtrToU: |
623 | 27 | case spv::Op::OpConvertUToPtr: |
624 | 30 | case spv::Op::OpGenericCastToPtr: |
625 | 31 | case spv::Op::OpPtrCastToGeneric: |
626 | 81 | case spv::Op::OpBitcast: |
627 | 105 | case spv::Op::OpFNegate: |
628 | 108 | case spv::Op::OpFAdd: |
629 | 110 | case spv::Op::OpFSub: |
630 | 113 | case spv::Op::OpFMul: |
631 | 114 | case spv::Op::OpFDiv: |
632 | 118 | case spv::Op::OpFRem: |
633 | 120 | case spv::Op::OpFMod: |
634 | 126 | case spv::Op::OpAccessChain: |
635 | 129 | case spv::Op::OpInBoundsAccessChain: |
636 | 130 | case spv::Op::OpPtrAccessChain: |
637 | 131 | case spv::Op::OpInBoundsPtrAccessChain: |
638 | 134 | case spv::Op::OpUntypedAccessChainKHR: |
639 | 137 | case spv::Op::OpUntypedInBoundsAccessChainKHR: |
640 | 138 | case spv::Op::OpUntypedPtrAccessChainKHR: |
641 | 139 | case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: |
642 | 139 | if (!_.HasCapability(spv::Capability::Kernel)) { |
643 | 11 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
644 | 11 | << "Specialization constant operation " << spvOpcodeString(op) |
645 | 11 | << " requires Kernel capability"; |
646 | 11 | } |
647 | 128 | break; |
648 | | |
649 | 179 | default: |
650 | 179 | break; |
651 | 333 | } |
652 | | |
653 | | // TODO(dneto): Validate result type and arguments to the various operations. |
654 | 320 | return SPV_SUCCESS; |
655 | 333 | } |
656 | | |
657 | | spv_result_t ValidateConstantFunctionPointerINTEL(ValidationState_t& _, |
658 | 0 | const Instruction* inst) { |
659 | 0 | const auto result_type = _.FindDef(inst->type_id()); |
660 | | // Result Type must be a pointer type |
661 | 0 | if (result_type->opcode() != spv::Op::OpTypePointer && |
662 | 0 | result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { |
663 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
664 | 0 | << "OpConstantFunctionPointerINTEL Result Type <id> " |
665 | 0 | << _.getIdName(inst->type_id()) << " is not a pointer type"; |
666 | 0 | } |
667 | | |
668 | | // For typed pointers, check that pointee is a function type |
669 | 0 | const Instruction* pointee_type = nullptr; |
670 | 0 | if (result_type->opcode() == spv::Op::OpTypePointer) { |
671 | 0 | pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); |
672 | 0 | if (pointee_type->opcode() != spv::Op::OpTypeFunction) { |
673 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
674 | 0 | << "OpConstantFunctionPointerINTEL Result Type <id> " |
675 | 0 | << _.getIdName(inst->type_id()) |
676 | 0 | << " must be a pointer to function type"; |
677 | 0 | } |
678 | 0 | } |
679 | | |
680 | | // Validate that the function operand refers to an OpFunction |
681 | 0 | const uint32_t function_id = inst->GetOperandAs<uint32_t>(2); |
682 | 0 | const auto function_inst = _.FindDef(function_id); |
683 | 0 | if (function_inst->opcode() != spv::Op::OpFunction) { |
684 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
685 | 0 | << "OpConstantFunctionPointerINTEL Function operand <id> " |
686 | 0 | << _.getIdName(function_id) << " is not an OpFunction"; |
687 | 0 | } |
688 | | |
689 | | // For typed pointers, validate that function type matches pointee type |
690 | 0 | if (pointee_type) { |
691 | 0 | const uint32_t function_type_id = function_inst->GetOperandAs<uint32_t>(3); |
692 | 0 | if (function_type_id != pointee_type->id()) { |
693 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
694 | 0 | << "OpConstantFunctionPointerINTEL Function operand <id> " |
695 | 0 | << _.getIdName(function_id) |
696 | 0 | << " type does not match the pointer's function type"; |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | 0 | return SPV_SUCCESS; |
701 | 0 | } |
702 | | |
703 | | spv_result_t ValidateConstantData(ValidationState_t& _, |
704 | 0 | const Instruction* inst) { |
705 | 0 | const auto array_inst = _.FindDef(inst->type_id()); |
706 | 0 | if (array_inst->opcode() != spv::Op::OpTypeArray) { |
707 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
708 | 0 | << "Result type must be an array."; |
709 | 0 | } |
710 | | |
711 | 0 | const auto element_type_inst = |
712 | 0 | _.FindDef(array_inst->GetOperandAs<uint32_t>(1)); |
713 | 0 | if (!_.IsIntScalarType(element_type_inst->id())) { |
714 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
715 | 0 | << "Result type must be an array of integer scalar type."; |
716 | 0 | } |
717 | | |
718 | | // The Data literals are tightly packed, so an explicit layout stride on the |
719 | | // result type would contradict how the constant is encoded. |
720 | 0 | if (_.HasDecoration(array_inst->id(), spv::Decoration::ArrayStride)) { |
721 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
722 | 0 | << "Result type must not be decorated with ArrayStride."; |
723 | 0 | } |
724 | | |
725 | 0 | const uint32_t int_width = element_type_inst->word(2); |
726 | 0 | const uint32_t data_words = static_cast<uint32_t>(inst->words().size() - 3); |
727 | |
|
728 | 0 | if (data_words == 0) { |
729 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
730 | 0 | << "There must be at least 1 literal integer (because an array of " |
731 | 0 | "zero is not allowed)."; |
732 | 0 | } |
733 | | |
734 | 0 | uint64_t array_length = 0; |
735 | 0 | if (!_.EvalConstantValUint64(array_inst->GetOperandAs<uint32_t>(2), |
736 | 0 | &array_length)) { |
737 | | // The length could be a SpecConstant, will need to be frozen to validate |
738 | 0 | return SPV_SUCCESS; |
739 | 0 | } |
740 | | |
741 | 0 | const uint32_t words_needed = |
742 | 0 | (((int_width / 8) * static_cast<uint32_t>(array_length) + 3) & ~3) / 4; |
743 | 0 | if (data_words != words_needed) { |
744 | 0 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
745 | 0 | << "contains " << data_words << " words of data, but needs to have " |
746 | 0 | << words_needed << " words to match the array of " << array_length |
747 | 0 | << " of " << int_width << "-bit ints."; |
748 | 0 | } |
749 | | |
750 | 0 | return SPV_SUCCESS; |
751 | 0 | } |
752 | | |
753 | | } // namespace |
754 | | |
755 | 14.1M | spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) { |
756 | 14.1M | switch (inst->opcode()) { |
757 | 1.41k | case spv::Op::OpConstantTrue: |
758 | 2.85k | case spv::Op::OpConstantFalse: |
759 | 3.01k | case spv::Op::OpSpecConstantTrue: |
760 | 3.16k | case spv::Op::OpSpecConstantFalse: |
761 | 3.16k | if (auto error = ValidateConstantBool(_, inst)) return error; |
762 | 3.15k | break; |
763 | 20.1k | case spv::Op::OpConstantComposite: |
764 | 20.3k | case spv::Op::OpSpecConstantComposite: |
765 | 20.3k | if (auto error = ValidateConstantComposite(_, inst)) return error; |
766 | 20.2k | break; |
767 | 20.2k | case spv::Op::OpConstantCompositeReplicateEXT: |
768 | 0 | case spv::Op::OpSpecConstantCompositeReplicateEXT: |
769 | 0 | if (auto error = ValidateConstantCompositeReplicate(_, inst)) |
770 | 0 | return error; |
771 | 0 | break; |
772 | 3 | case spv::Op::OpConstantSampler: |
773 | 3 | if (auto error = ValidateConstantSampler(_, inst)) return error; |
774 | 1 | break; |
775 | 1.36k | case spv::Op::OpConstantNull: |
776 | 1.36k | if (auto error = ValidateConstantNull(_, inst)) return error; |
777 | 1.35k | break; |
778 | 1.35k | case spv::Op::OpSpecConstant: |
779 | 828 | if (auto error = ValidateSpecConstant(_, inst)) return error; |
780 | 828 | break; |
781 | 828 | case spv::Op::OpSpecConstantOp: |
782 | 333 | if (auto error = ValidateSpecConstantOp(_, inst)) return error; |
783 | 320 | break; |
784 | 320 | case spv::Op::OpConstantSizeOfEXT: |
785 | 0 | if (auto error = ValidateConstantSizeOfEXT(_, inst)) return error; |
786 | 0 | break; |
787 | 0 | case spv::Op::OpConstantFunctionPointerINTEL: |
788 | 0 | if (auto error = ValidateConstantFunctionPointerINTEL(_, inst)) |
789 | 0 | return error; |
790 | 0 | break; |
791 | 0 | case spv::Op::OpConstantDataKHR: |
792 | 0 | case spv::Op::OpSpecConstantDataKHR: |
793 | 0 | if (auto error = ValidateConstantData(_, inst)) return error; |
794 | 0 | break; |
795 | 14.1M | default: |
796 | 14.1M | break; |
797 | 14.1M | } |
798 | | |
799 | | // Generally disallow creating 8- or 16-bit constants unless the full |
800 | | // capabilities are present. |
801 | 14.1M | if (spvOpcodeIsConstant(inst->opcode()) && |
802 | 158k | _.HasCapability(spv::Capability::Shader) && |
803 | 158k | !_.IsPointerType(inst->type_id()) && |
804 | 158k | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
805 | 1 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
806 | 1 | << "Cannot form constants of 8- or 16-bit types"; |
807 | 1 | } |
808 | | |
809 | 14.1M | return SPV_SUCCESS; |
810 | 14.1M | } |
811 | | |
812 | | } // namespace val |
813 | | } // namespace spvtools |