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 composite 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/val/instruction.h" |
23 | #include "source/val/validation_state.h" |
24 | |
25 | namespace spvtools { |
26 | namespace val { |
27 | namespace { |
28 | |
29 | // Returns the type of the value accessed by OpCompositeExtract or |
30 | // OpCompositeInsert instruction. The function traverses the hierarchy of |
31 | // nested data structures (structs, arrays, vectors, matrices) as directed by |
32 | // the sequence of indices in the instruction. May return error if traversal |
33 | // fails (encountered non-composite, out of bounds, no indices, nesting too |
34 | // deep). |
35 | spv_result_t (ValidationState_t& _, |
36 | const Instruction* inst, |
37 | uint32_t* member_type) { |
38 | const SpvOp opcode = inst->opcode(); |
39 | assert(opcode == SpvOpCompositeExtract || opcode == SpvOpCompositeInsert); |
40 | uint32_t word_index = opcode == SpvOpCompositeExtract ? 4 : 5; |
41 | const uint32_t num_words = static_cast<uint32_t>(inst->words().size()); |
42 | const uint32_t composite_id_index = word_index - 1; |
43 | const uint32_t num_indices = num_words - word_index; |
44 | const uint32_t = 255; |
45 | |
46 | if (num_indices == 0) { |
47 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
48 | << "Expected at least one index to Op" |
49 | << spvOpcodeString(inst->opcode()) << ", zero found" ; |
50 | |
51 | } else if (num_indices > kCompositeExtractInsertMaxNumIndices) { |
52 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
53 | << "The number of indexes in Op" << spvOpcodeString(opcode) |
54 | << " may not exceed " << kCompositeExtractInsertMaxNumIndices |
55 | << ". Found " << num_indices << " indexes." ; |
56 | } |
57 | |
58 | *member_type = _.GetTypeId(inst->word(composite_id_index)); |
59 | if (*member_type == 0) { |
60 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
61 | << "Expected Composite to be an object of composite type" ; |
62 | } |
63 | |
64 | for (; word_index < num_words; ++word_index) { |
65 | const uint32_t component_index = inst->word(word_index); |
66 | const Instruction* const type_inst = _.FindDef(*member_type); |
67 | assert(type_inst); |
68 | switch (type_inst->opcode()) { |
69 | case SpvOpTypeVector: { |
70 | *member_type = type_inst->word(2); |
71 | const uint32_t vector_size = type_inst->word(3); |
72 | if (component_index >= vector_size) { |
73 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
74 | << "Vector access is out of bounds, vector size is " |
75 | << vector_size << ", but access index is " << component_index; |
76 | } |
77 | break; |
78 | } |
79 | case SpvOpTypeMatrix: { |
80 | *member_type = type_inst->word(2); |
81 | const uint32_t num_cols = type_inst->word(3); |
82 | if (component_index >= num_cols) { |
83 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
84 | << "Matrix access is out of bounds, matrix has " << num_cols |
85 | << " columns, but access index is " << component_index; |
86 | } |
87 | break; |
88 | } |
89 | case SpvOpTypeArray: { |
90 | uint64_t array_size = 0; |
91 | auto size = _.FindDef(type_inst->word(3)); |
92 | *member_type = type_inst->word(2); |
93 | if (spvOpcodeIsSpecConstant(size->opcode())) { |
94 | // Cannot verify against the size of this array. |
95 | break; |
96 | } |
97 | |
98 | if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { |
99 | assert(0 && "Array type definition is corrupt" ); |
100 | } |
101 | if (component_index >= array_size) { |
102 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
103 | << "Array access is out of bounds, array size is " |
104 | << array_size << ", but access index is " << component_index; |
105 | } |
106 | break; |
107 | } |
108 | case SpvOpTypeRuntimeArray: { |
109 | *member_type = type_inst->word(2); |
110 | // Array size is unknown. |
111 | break; |
112 | } |
113 | case SpvOpTypeStruct: { |
114 | const size_t num_struct_members = type_inst->words().size() - 2; |
115 | if (component_index >= num_struct_members) { |
116 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
117 | << "Index is out of bounds, can not find index " |
118 | << component_index << " in the structure <id> '" |
119 | << type_inst->id() << "'. This structure has " |
120 | << num_struct_members << " members. Largest valid index is " |
121 | << num_struct_members - 1 << "." ; |
122 | } |
123 | *member_type = type_inst->word(component_index + 2); |
124 | break; |
125 | } |
126 | case SpvOpTypeCooperativeMatrixNV: { |
127 | *member_type = type_inst->word(2); |
128 | break; |
129 | } |
130 | default: |
131 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
132 | << "Reached non-composite type while indexes still remain to " |
133 | "be traversed." ; |
134 | } |
135 | } |
136 | |
137 | return SPV_SUCCESS; |
138 | } |
139 | |
140 | spv_result_t (ValidationState_t& _, |
141 | const Instruction* inst) { |
142 | const uint32_t result_type = inst->type_id(); |
143 | const SpvOp result_opcode = _.GetIdOpcode(result_type); |
144 | if (!spvOpcodeIsScalarType(result_opcode)) { |
145 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
146 | << "Expected Result Type to be a scalar type" ; |
147 | } |
148 | |
149 | const uint32_t vector_type = _.GetOperandTypeId(inst, 2); |
150 | const SpvOp vector_opcode = _.GetIdOpcode(vector_type); |
151 | if (vector_opcode != SpvOpTypeVector) { |
152 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
153 | << "Expected Vector type to be OpTypeVector" ; |
154 | } |
155 | |
156 | if (_.GetComponentType(vector_type) != result_type) { |
157 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
158 | << "Expected Vector component type to be equal to Result Type" ; |
159 | } |
160 | |
161 | const auto index = _.FindDef(inst->GetOperandAs<uint32_t>(3)); |
162 | if (!index || index->type_id() == 0 || !_.IsIntScalarType(index->type_id())) { |
163 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
164 | << "Expected Index to be int scalar" ; |
165 | } |
166 | |
167 | if (_.HasCapability(SpvCapabilityShader) && |
168 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
169 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
170 | << "Cannot extract from a vector of 8- or 16-bit types" ; |
171 | } |
172 | return SPV_SUCCESS; |
173 | } |
174 | |
175 | spv_result_t ValidateVectorInsertDyanmic(ValidationState_t& _, |
176 | const Instruction* inst) { |
177 | const uint32_t result_type = inst->type_id(); |
178 | const SpvOp result_opcode = _.GetIdOpcode(result_type); |
179 | if (result_opcode != SpvOpTypeVector) { |
180 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
181 | << "Expected Result Type to be OpTypeVector" ; |
182 | } |
183 | |
184 | const uint32_t vector_type = _.GetOperandTypeId(inst, 2); |
185 | if (vector_type != result_type) { |
186 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
187 | << "Expected Vector type to be equal to Result Type" ; |
188 | } |
189 | |
190 | const uint32_t component_type = _.GetOperandTypeId(inst, 3); |
191 | if (_.GetComponentType(result_type) != component_type) { |
192 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
193 | << "Expected Component type to be equal to Result Type " |
194 | << "component type" ; |
195 | } |
196 | |
197 | const uint32_t index_type = _.GetOperandTypeId(inst, 4); |
198 | if (!_.IsIntScalarType(index_type)) { |
199 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
200 | << "Expected Index to be int scalar" ; |
201 | } |
202 | |
203 | if (_.HasCapability(SpvCapabilityShader) && |
204 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
205 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
206 | << "Cannot insert into a vector of 8- or 16-bit types" ; |
207 | } |
208 | return SPV_SUCCESS; |
209 | } |
210 | |
211 | spv_result_t ValidateCompositeConstruct(ValidationState_t& _, |
212 | const Instruction* inst) { |
213 | const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size()); |
214 | const uint32_t result_type = inst->type_id(); |
215 | const SpvOp result_opcode = _.GetIdOpcode(result_type); |
216 | switch (result_opcode) { |
217 | case SpvOpTypeVector: { |
218 | const uint32_t num_result_components = _.GetDimension(result_type); |
219 | const uint32_t result_component_type = _.GetComponentType(result_type); |
220 | uint32_t given_component_count = 0; |
221 | |
222 | if (num_operands <= 3) { |
223 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
224 | << "Expected number of constituents to be at least 2" ; |
225 | } |
226 | |
227 | for (uint32_t operand_index = 2; operand_index < num_operands; |
228 | ++operand_index) { |
229 | const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); |
230 | if (operand_type == result_component_type) { |
231 | ++given_component_count; |
232 | } else { |
233 | if (_.GetIdOpcode(operand_type) != SpvOpTypeVector || |
234 | _.GetComponentType(operand_type) != result_component_type) { |
235 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
236 | << "Expected Constituents to be scalars or vectors of" |
237 | << " the same type as Result Type components" ; |
238 | } |
239 | |
240 | given_component_count += _.GetDimension(operand_type); |
241 | } |
242 | } |
243 | |
244 | if (num_result_components != given_component_count) { |
245 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
246 | << "Expected total number of given components to be equal " |
247 | << "to the size of Result Type vector" ; |
248 | } |
249 | |
250 | break; |
251 | } |
252 | case SpvOpTypeMatrix: { |
253 | uint32_t result_num_rows = 0; |
254 | uint32_t result_num_cols = 0; |
255 | uint32_t result_col_type = 0; |
256 | uint32_t result_component_type = 0; |
257 | if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, |
258 | &result_col_type, &result_component_type)) { |
259 | assert(0); |
260 | } |
261 | |
262 | if (result_num_cols + 2 != num_operands) { |
263 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
264 | << "Expected total number of Constituents to be equal " |
265 | << "to the number of columns of Result Type matrix" ; |
266 | } |
267 | |
268 | for (uint32_t operand_index = 2; operand_index < num_operands; |
269 | ++operand_index) { |
270 | const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); |
271 | if (operand_type != result_col_type) { |
272 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
273 | << "Expected Constituent type to be equal to the column " |
274 | << "type Result Type matrix" ; |
275 | } |
276 | } |
277 | |
278 | break; |
279 | } |
280 | case SpvOpTypeArray: { |
281 | const Instruction* const array_inst = _.FindDef(result_type); |
282 | assert(array_inst); |
283 | assert(array_inst->opcode() == SpvOpTypeArray); |
284 | |
285 | auto size = _.FindDef(array_inst->word(3)); |
286 | if (spvOpcodeIsSpecConstant(size->opcode())) { |
287 | // Cannot verify against the size of this array. |
288 | break; |
289 | } |
290 | |
291 | uint64_t array_size = 0; |
292 | if (!_.GetConstantValUint64(array_inst->word(3), &array_size)) { |
293 | assert(0 && "Array type definition is corrupt" ); |
294 | } |
295 | |
296 | if (array_size + 2 != num_operands) { |
297 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
298 | << "Expected total number of Constituents to be equal " |
299 | << "to the number of elements of Result Type array" ; |
300 | } |
301 | |
302 | const uint32_t result_component_type = array_inst->word(2); |
303 | for (uint32_t operand_index = 2; operand_index < num_operands; |
304 | ++operand_index) { |
305 | const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); |
306 | if (operand_type != result_component_type) { |
307 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
308 | << "Expected Constituent type to be equal to the column " |
309 | << "type Result Type array" ; |
310 | } |
311 | } |
312 | |
313 | break; |
314 | } |
315 | case SpvOpTypeStruct: { |
316 | const Instruction* const struct_inst = _.FindDef(result_type); |
317 | assert(struct_inst); |
318 | assert(struct_inst->opcode() == SpvOpTypeStruct); |
319 | |
320 | if (struct_inst->operands().size() + 1 != num_operands) { |
321 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
322 | << "Expected total number of Constituents to be equal " |
323 | << "to the number of members of Result Type struct" ; |
324 | } |
325 | |
326 | for (uint32_t operand_index = 2; operand_index < num_operands; |
327 | ++operand_index) { |
328 | const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); |
329 | const uint32_t member_type = struct_inst->word(operand_index); |
330 | if (operand_type != member_type) { |
331 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
332 | << "Expected Constituent type to be equal to the " |
333 | << "corresponding member type of Result Type struct" ; |
334 | } |
335 | } |
336 | |
337 | break; |
338 | } |
339 | case SpvOpTypeCooperativeMatrixNV: { |
340 | const auto result_type_inst = _.FindDef(result_type); |
341 | assert(result_type_inst); |
342 | const auto component_type_id = |
343 | result_type_inst->GetOperandAs<uint32_t>(1); |
344 | |
345 | if (3 != num_operands) { |
346 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
347 | << "Expected single constituent" ; |
348 | } |
349 | |
350 | const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2); |
351 | |
352 | if (operand_type_id != component_type_id) { |
353 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
354 | << "Expected Constituent type to be equal to the component type" ; |
355 | } |
356 | |
357 | break; |
358 | } |
359 | default: { |
360 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
361 | << "Expected Result Type to be a composite type" ; |
362 | } |
363 | } |
364 | |
365 | if (_.HasCapability(SpvCapabilityShader) && |
366 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
367 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
368 | << "Cannot create a composite containing 8- or 16-bit types" ; |
369 | } |
370 | return SPV_SUCCESS; |
371 | } |
372 | |
373 | spv_result_t (ValidationState_t& _, |
374 | const Instruction* inst) { |
375 | uint32_t member_type = 0; |
376 | if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { |
377 | return error; |
378 | } |
379 | |
380 | const uint32_t result_type = inst->type_id(); |
381 | if (result_type != member_type) { |
382 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
383 | << "Result type (Op" << spvOpcodeString(_.GetIdOpcode(result_type)) |
384 | << ") does not match the type that results from indexing into " |
385 | "the composite (Op" |
386 | << spvOpcodeString(_.GetIdOpcode(member_type)) << ")." ; |
387 | } |
388 | |
389 | if (_.HasCapability(SpvCapabilityShader) && |
390 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
391 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
392 | << "Cannot extract from a composite of 8- or 16-bit types" ; |
393 | } |
394 | |
395 | return SPV_SUCCESS; |
396 | } |
397 | |
398 | spv_result_t ValidateCompositeInsert(ValidationState_t& _, |
399 | const Instruction* inst) { |
400 | const uint32_t object_type = _.GetOperandTypeId(inst, 2); |
401 | const uint32_t composite_type = _.GetOperandTypeId(inst, 3); |
402 | const uint32_t result_type = inst->type_id(); |
403 | if (result_type != composite_type) { |
404 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
405 | << "The Result Type must be the same as Composite type in Op" |
406 | << spvOpcodeString(inst->opcode()) << " yielding Result Id " |
407 | << result_type << "." ; |
408 | } |
409 | |
410 | uint32_t member_type = 0; |
411 | if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { |
412 | return error; |
413 | } |
414 | |
415 | if (object_type != member_type) { |
416 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
417 | << "The Object type (Op" |
418 | << spvOpcodeString(_.GetIdOpcode(object_type)) |
419 | << ") does not match the type that results from indexing into the " |
420 | "Composite (Op" |
421 | << spvOpcodeString(_.GetIdOpcode(member_type)) << ")." ; |
422 | } |
423 | |
424 | if (_.HasCapability(SpvCapabilityShader) && |
425 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
426 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
427 | << "Cannot insert into a composite of 8- or 16-bit types" ; |
428 | } |
429 | |
430 | return SPV_SUCCESS; |
431 | } |
432 | |
433 | spv_result_t ValidateCopyObject(ValidationState_t& _, const Instruction* inst) { |
434 | const uint32_t result_type = inst->type_id(); |
435 | const uint32_t operand_type = _.GetOperandTypeId(inst, 2); |
436 | if (operand_type != result_type) { |
437 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
438 | << "Expected Result Type and Operand type to be the same" ; |
439 | } |
440 | return SPV_SUCCESS; |
441 | } |
442 | |
443 | spv_result_t ValidateTranspose(ValidationState_t& _, const Instruction* inst) { |
444 | uint32_t result_num_rows = 0; |
445 | uint32_t result_num_cols = 0; |
446 | uint32_t result_col_type = 0; |
447 | uint32_t result_component_type = 0; |
448 | const uint32_t result_type = inst->type_id(); |
449 | if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, |
450 | &result_col_type, &result_component_type)) { |
451 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
452 | << "Expected Result Type to be a matrix type" ; |
453 | } |
454 | |
455 | const uint32_t matrix_type = _.GetOperandTypeId(inst, 2); |
456 | uint32_t matrix_num_rows = 0; |
457 | uint32_t matrix_num_cols = 0; |
458 | uint32_t matrix_col_type = 0; |
459 | uint32_t matrix_component_type = 0; |
460 | if (!_.GetMatrixTypeInfo(matrix_type, &matrix_num_rows, &matrix_num_cols, |
461 | &matrix_col_type, &matrix_component_type)) { |
462 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
463 | << "Expected Matrix to be of type OpTypeMatrix" ; |
464 | } |
465 | |
466 | if (result_component_type != matrix_component_type) { |
467 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
468 | << "Expected component types of Matrix and Result Type to be " |
469 | << "identical" ; |
470 | } |
471 | |
472 | if (result_num_rows != matrix_num_cols || |
473 | result_num_cols != matrix_num_rows) { |
474 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
475 | << "Expected number of columns and the column size of Matrix " |
476 | << "to be the reverse of those of Result Type" ; |
477 | } |
478 | |
479 | if (_.HasCapability(SpvCapabilityShader) && |
480 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
481 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
482 | << "Cannot transpose matrices of 16-bit floats" ; |
483 | } |
484 | return SPV_SUCCESS; |
485 | } |
486 | |
487 | spv_result_t ValidateVectorShuffle(ValidationState_t& _, |
488 | const Instruction* inst) { |
489 | auto resultType = _.FindDef(inst->type_id()); |
490 | if (!resultType || resultType->opcode() != SpvOpTypeVector) { |
491 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
492 | << "The Result Type of OpVectorShuffle must be" |
493 | << " OpTypeVector. Found Op" |
494 | << spvOpcodeString(static_cast<SpvOp>(resultType->opcode())) << "." ; |
495 | } |
496 | |
497 | // The number of components in Result Type must be the same as the number of |
498 | // Component operands. |
499 | auto componentCount = inst->operands().size() - 4; |
500 | auto resultVectorDimension = resultType->GetOperandAs<uint32_t>(2); |
501 | if (componentCount != resultVectorDimension) { |
502 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
503 | << "OpVectorShuffle component literals count does not match " |
504 | "Result Type <id> '" |
505 | << _.getIdName(resultType->id()) << "'s vector component count." ; |
506 | } |
507 | |
508 | // Vector 1 and Vector 2 must both have vector types, with the same Component |
509 | // Type as Result Type. |
510 | auto vector1Object = _.FindDef(inst->GetOperandAs<uint32_t>(2)); |
511 | auto vector1Type = _.FindDef(vector1Object->type_id()); |
512 | auto vector2Object = _.FindDef(inst->GetOperandAs<uint32_t>(3)); |
513 | auto vector2Type = _.FindDef(vector2Object->type_id()); |
514 | if (!vector1Type || vector1Type->opcode() != SpvOpTypeVector) { |
515 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
516 | << "The type of Vector 1 must be OpTypeVector." ; |
517 | } |
518 | if (!vector2Type || vector2Type->opcode() != SpvOpTypeVector) { |
519 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
520 | << "The type of Vector 2 must be OpTypeVector." ; |
521 | } |
522 | |
523 | auto resultComponentType = resultType->GetOperandAs<uint32_t>(1); |
524 | if (vector1Type->GetOperandAs<uint32_t>(1) != resultComponentType) { |
525 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
526 | << "The Component Type of Vector 1 must be the same as ResultType." ; |
527 | } |
528 | if (vector2Type->GetOperandAs<uint32_t>(1) != resultComponentType) { |
529 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
530 | << "The Component Type of Vector 2 must be the same as ResultType." ; |
531 | } |
532 | |
533 | // All Component literals must either be FFFFFFFF or in [0, N - 1]. |
534 | // For WebGPU specifically, Component literals cannot be FFFFFFFF. |
535 | auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2); |
536 | auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2); |
537 | auto N = vector1ComponentCount + vector2ComponentCount; |
538 | auto firstLiteralIndex = 4; |
539 | const auto is_webgpu_env = spvIsWebGPUEnv(_.context()->target_env); |
540 | for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) { |
541 | auto literal = inst->GetOperandAs<uint32_t>(i); |
542 | if (literal != 0xFFFFFFFF && literal >= N) { |
543 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
544 | << "Component index " << literal << " is out of bounds for " |
545 | << "combined (Vector1 + Vector2) size of " << N << "." ; |
546 | } |
547 | |
548 | if (is_webgpu_env && literal == 0xFFFFFFFF) { |
549 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
550 | << "Component literal at operand " << i - firstLiteralIndex |
551 | << " cannot be 0xFFFFFFFF in WebGPU execution environment." ; |
552 | } |
553 | } |
554 | |
555 | if (_.HasCapability(SpvCapabilityShader) && |
556 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
557 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
558 | << "Cannot shuffle a vector of 8- or 16-bit types" ; |
559 | } |
560 | |
561 | return SPV_SUCCESS; |
562 | } |
563 | |
564 | spv_result_t ValidateCopyLogical(ValidationState_t& _, |
565 | const Instruction* inst) { |
566 | const auto result_type = _.FindDef(inst->type_id()); |
567 | const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); |
568 | const auto source_type = _.FindDef(source->type_id()); |
569 | if (!source_type || !result_type || source_type == result_type) { |
570 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
571 | << "Result Type must not equal the Operand type" ; |
572 | } |
573 | |
574 | if (!_.LogicallyMatch(source_type, result_type, false)) { |
575 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
576 | << "Result Type does not logically match the Operand type" ; |
577 | } |
578 | |
579 | if (_.HasCapability(SpvCapabilityShader) && |
580 | _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { |
581 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
582 | << "Cannot copy composites of 8- or 16-bit types" ; |
583 | } |
584 | |
585 | return SPV_SUCCESS; |
586 | } |
587 | |
588 | } // anonymous namespace |
589 | |
590 | // Validates correctness of composite instructions. |
591 | spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) { |
592 | switch (inst->opcode()) { |
593 | case SpvOpVectorExtractDynamic: |
594 | return ValidateVectorExtractDynamic(_, inst); |
595 | case SpvOpVectorInsertDynamic: |
596 | return ValidateVectorInsertDyanmic(_, inst); |
597 | case SpvOpVectorShuffle: |
598 | return ValidateVectorShuffle(_, inst); |
599 | case SpvOpCompositeConstruct: |
600 | return ValidateCompositeConstruct(_, inst); |
601 | case SpvOpCompositeExtract: |
602 | return ValidateCompositeExtract(_, inst); |
603 | case SpvOpCompositeInsert: |
604 | return ValidateCompositeInsert(_, inst); |
605 | case SpvOpCopyObject: |
606 | return ValidateCopyObject(_, inst); |
607 | case SpvOpTranspose: |
608 | return ValidateTranspose(_, inst); |
609 | case SpvOpCopyLogical: |
610 | return ValidateCopyLogical(_, inst); |
611 | default: |
612 | break; |
613 | } |
614 | |
615 | return SPV_SUCCESS; |
616 | } |
617 | |
618 | } // namespace val |
619 | } // namespace spvtools |
620 | |