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 conversion instructions.
16
17#include "source/val/validate.h"
18
19#include "source/diagnostic.h"
20#include "source/opcode.h"
21#include "source/spirv_constant.h"
22#include "source/val/instruction.h"
23#include "source/val/validation_state.h"
24
25namespace spvtools {
26namespace val {
27
28// Validates correctness of conversion instructions.
29spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
30 const SpvOp opcode = inst->opcode();
31 const uint32_t result_type = inst->type_id();
32
33 switch (opcode) {
34 case SpvOpConvertFToU: {
35 if (!_.IsUnsignedIntScalarType(result_type) &&
36 !_.IsUnsignedIntVectorType(result_type) &&
37 !_.IsUnsignedIntCooperativeMatrixType(result_type))
38 return _.diag(SPV_ERROR_INVALID_DATA, inst)
39 << "Expected unsigned int scalar or vector type as Result Type: "
40 << spvOpcodeString(opcode);
41
42 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
43 if (!input_type || (!_.IsFloatScalarType(input_type) &&
44 !_.IsFloatVectorType(input_type) &&
45 !_.IsFloatCooperativeMatrixType(input_type)))
46 return _.diag(SPV_ERROR_INVALID_DATA, inst)
47 << "Expected input to be float scalar or vector: "
48 << spvOpcodeString(opcode);
49
50 if (_.IsCooperativeMatrixType(result_type) ||
51 _.IsCooperativeMatrixType(input_type)) {
52 spv_result_t ret =
53 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
54 if (ret != SPV_SUCCESS) return ret;
55 } else {
56 if (_.GetDimension(result_type) != _.GetDimension(input_type))
57 return _.diag(SPV_ERROR_INVALID_DATA, inst)
58 << "Expected input to have the same dimension as Result Type: "
59 << spvOpcodeString(opcode);
60 }
61
62 break;
63 }
64
65 case SpvOpConvertFToS: {
66 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
67 !_.IsIntCooperativeMatrixType(result_type))
68 return _.diag(SPV_ERROR_INVALID_DATA, inst)
69 << "Expected int scalar or vector type as Result Type: "
70 << spvOpcodeString(opcode);
71
72 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
73 if (!input_type || (!_.IsFloatScalarType(input_type) &&
74 !_.IsFloatVectorType(input_type) &&
75 !_.IsFloatCooperativeMatrixType(input_type)))
76 return _.diag(SPV_ERROR_INVALID_DATA, inst)
77 << "Expected input to be float scalar or vector: "
78 << spvOpcodeString(opcode);
79
80 if (_.IsCooperativeMatrixType(result_type) ||
81 _.IsCooperativeMatrixType(input_type)) {
82 spv_result_t ret =
83 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
84 if (ret != SPV_SUCCESS) return ret;
85 } else {
86 if (_.GetDimension(result_type) != _.GetDimension(input_type))
87 return _.diag(SPV_ERROR_INVALID_DATA, inst)
88 << "Expected input to have the same dimension as Result Type: "
89 << spvOpcodeString(opcode);
90 }
91
92 break;
93 }
94
95 case SpvOpConvertSToF:
96 case SpvOpConvertUToF: {
97 if (!_.IsFloatScalarType(result_type) &&
98 !_.IsFloatVectorType(result_type) &&
99 !_.IsFloatCooperativeMatrixType(result_type))
100 return _.diag(SPV_ERROR_INVALID_DATA, inst)
101 << "Expected float scalar or vector type as Result Type: "
102 << spvOpcodeString(opcode);
103
104 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
105 if (!input_type ||
106 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
107 !_.IsIntCooperativeMatrixType(input_type)))
108 return _.diag(SPV_ERROR_INVALID_DATA, inst)
109 << "Expected input to be int scalar or vector: "
110 << spvOpcodeString(opcode);
111
112 if (_.IsCooperativeMatrixType(result_type) ||
113 _.IsCooperativeMatrixType(input_type)) {
114 spv_result_t ret =
115 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
116 if (ret != SPV_SUCCESS) return ret;
117 } else {
118 if (_.GetDimension(result_type) != _.GetDimension(input_type))
119 return _.diag(SPV_ERROR_INVALID_DATA, inst)
120 << "Expected input to have the same dimension as Result Type: "
121 << spvOpcodeString(opcode);
122 }
123
124 break;
125 }
126
127 case SpvOpUConvert: {
128 if (!_.IsUnsignedIntScalarType(result_type) &&
129 !_.IsUnsignedIntVectorType(result_type) &&
130 !_.IsUnsignedIntCooperativeMatrixType(result_type))
131 return _.diag(SPV_ERROR_INVALID_DATA, inst)
132 << "Expected unsigned int scalar or vector type as Result Type: "
133 << spvOpcodeString(opcode);
134
135 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
136 if (!input_type ||
137 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
138 !_.IsIntCooperativeMatrixType(input_type)))
139 return _.diag(SPV_ERROR_INVALID_DATA, inst)
140 << "Expected input to be int scalar or vector: "
141 << spvOpcodeString(opcode);
142
143 if (_.IsCooperativeMatrixType(result_type) ||
144 _.IsCooperativeMatrixType(input_type)) {
145 spv_result_t ret =
146 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
147 if (ret != SPV_SUCCESS) return ret;
148 } else {
149 if (_.GetDimension(result_type) != _.GetDimension(input_type))
150 return _.diag(SPV_ERROR_INVALID_DATA, inst)
151 << "Expected input to have the same dimension as Result Type: "
152 << spvOpcodeString(opcode);
153 }
154
155 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
156 return _.diag(SPV_ERROR_INVALID_DATA, inst)
157 << "Expected input to have different bit width from Result "
158 "Type: "
159 << spvOpcodeString(opcode);
160 break;
161 }
162
163 case SpvOpSConvert: {
164 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
165 !_.IsIntCooperativeMatrixType(result_type))
166 return _.diag(SPV_ERROR_INVALID_DATA, inst)
167 << "Expected int scalar or vector type as Result Type: "
168 << spvOpcodeString(opcode);
169
170 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
171 if (!input_type ||
172 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
173 !_.IsIntCooperativeMatrixType(input_type)))
174 return _.diag(SPV_ERROR_INVALID_DATA, inst)
175 << "Expected input to be int scalar or vector: "
176 << spvOpcodeString(opcode);
177
178 if (_.IsCooperativeMatrixType(result_type) ||
179 _.IsCooperativeMatrixType(input_type)) {
180 spv_result_t ret =
181 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
182 if (ret != SPV_SUCCESS) return ret;
183 } else {
184 if (_.GetDimension(result_type) != _.GetDimension(input_type))
185 return _.diag(SPV_ERROR_INVALID_DATA, inst)
186 << "Expected input to have the same dimension as Result Type: "
187 << spvOpcodeString(opcode);
188 }
189
190 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
191 return _.diag(SPV_ERROR_INVALID_DATA, inst)
192 << "Expected input to have different bit width from Result "
193 "Type: "
194 << spvOpcodeString(opcode);
195 break;
196 }
197
198 case SpvOpFConvert: {
199 if (!_.IsFloatScalarType(result_type) &&
200 !_.IsFloatVectorType(result_type) &&
201 !_.IsFloatCooperativeMatrixType(result_type))
202 return _.diag(SPV_ERROR_INVALID_DATA, inst)
203 << "Expected float scalar or vector type as Result Type: "
204 << spvOpcodeString(opcode);
205
206 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
207 if (!input_type || (!_.IsFloatScalarType(input_type) &&
208 !_.IsFloatVectorType(input_type) &&
209 !_.IsFloatCooperativeMatrixType(input_type)))
210 return _.diag(SPV_ERROR_INVALID_DATA, inst)
211 << "Expected input to be float scalar or vector: "
212 << spvOpcodeString(opcode);
213
214 if (_.IsCooperativeMatrixType(result_type) ||
215 _.IsCooperativeMatrixType(input_type)) {
216 spv_result_t ret =
217 _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
218 if (ret != SPV_SUCCESS) return ret;
219 } else {
220 if (_.GetDimension(result_type) != _.GetDimension(input_type))
221 return _.diag(SPV_ERROR_INVALID_DATA, inst)
222 << "Expected input to have the same dimension as Result Type: "
223 << spvOpcodeString(opcode);
224 }
225
226 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
227 return _.diag(SPV_ERROR_INVALID_DATA, inst)
228 << "Expected input to have different bit width from Result "
229 "Type: "
230 << spvOpcodeString(opcode);
231 break;
232 }
233
234 case SpvOpQuantizeToF16: {
235 if ((!_.IsFloatScalarType(result_type) &&
236 !_.IsFloatVectorType(result_type)) ||
237 _.GetBitWidth(result_type) != 32)
238 return _.diag(SPV_ERROR_INVALID_DATA, inst)
239 << "Expected 32-bit float scalar or vector type as Result Type: "
240 << spvOpcodeString(opcode);
241
242 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
243 if (input_type != result_type)
244 return _.diag(SPV_ERROR_INVALID_DATA, inst)
245 << "Expected input type to be equal to Result Type: "
246 << spvOpcodeString(opcode);
247 break;
248 }
249
250 case SpvOpConvertPtrToU: {
251 if (!_.IsUnsignedIntScalarType(result_type))
252 return _.diag(SPV_ERROR_INVALID_DATA, inst)
253 << "Expected unsigned int scalar type as Result Type: "
254 << spvOpcodeString(opcode);
255
256 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
257 if (!_.IsPointerType(input_type))
258 return _.diag(SPV_ERROR_INVALID_DATA, inst)
259 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
260
261 if (_.addressing_model() == SpvAddressingModelLogical)
262 return _.diag(SPV_ERROR_INVALID_DATA, inst)
263 << "Logical addressing not supported: "
264 << spvOpcodeString(opcode);
265
266 if (_.addressing_model() ==
267 SpvAddressingModelPhysicalStorageBuffer64EXT) {
268 uint32_t input_storage_class = 0;
269 uint32_t input_data_type = 0;
270 _.GetPointerTypeInfo(input_type, &input_data_type,
271 &input_storage_class);
272 if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
273 return _.diag(SPV_ERROR_INVALID_DATA, inst)
274 << "Pointer storage class must be PhysicalStorageBufferEXT: "
275 << spvOpcodeString(opcode);
276 }
277 break;
278 }
279
280 case SpvOpSatConvertSToU:
281 case SpvOpSatConvertUToS: {
282 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
283 return _.diag(SPV_ERROR_INVALID_DATA, inst)
284 << "Expected int scalar or vector type as Result Type: "
285 << spvOpcodeString(opcode);
286
287 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
288 if (!input_type ||
289 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type)))
290 return _.diag(SPV_ERROR_INVALID_DATA, inst)
291 << "Expected int scalar or vector as input: "
292 << spvOpcodeString(opcode);
293
294 if (_.GetDimension(result_type) != _.GetDimension(input_type))
295 return _.diag(SPV_ERROR_INVALID_DATA, inst)
296 << "Expected input to have the same dimension as Result Type: "
297 << spvOpcodeString(opcode);
298 break;
299 }
300
301 case SpvOpConvertUToPtr: {
302 if (!_.IsPointerType(result_type))
303 return _.diag(SPV_ERROR_INVALID_DATA, inst)
304 << "Expected Result Type to be a pointer: "
305 << spvOpcodeString(opcode);
306
307 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
308 if (!input_type || !_.IsIntScalarType(input_type))
309 return _.diag(SPV_ERROR_INVALID_DATA, inst)
310 << "Expected int scalar as input: " << spvOpcodeString(opcode);
311
312 if (_.addressing_model() == SpvAddressingModelLogical)
313 return _.diag(SPV_ERROR_INVALID_DATA, inst)
314 << "Logical addressing not supported: "
315 << spvOpcodeString(opcode);
316
317 if (_.addressing_model() ==
318 SpvAddressingModelPhysicalStorageBuffer64EXT) {
319 uint32_t result_storage_class = 0;
320 uint32_t result_data_type = 0;
321 _.GetPointerTypeInfo(result_type, &result_data_type,
322 &result_storage_class);
323 if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
324 return _.diag(SPV_ERROR_INVALID_DATA, inst)
325 << "Pointer storage class must be PhysicalStorageBufferEXT: "
326 << spvOpcodeString(opcode);
327 }
328 break;
329 }
330
331 case SpvOpPtrCastToGeneric: {
332 uint32_t result_storage_class = 0;
333 uint32_t result_data_type = 0;
334 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
335 &result_storage_class))
336 return _.diag(SPV_ERROR_INVALID_DATA, inst)
337 << "Expected Result Type to be a pointer: "
338 << spvOpcodeString(opcode);
339
340 if (result_storage_class != SpvStorageClassGeneric)
341 return _.diag(SPV_ERROR_INVALID_DATA, inst)
342 << "Expected Result Type to have storage class Generic: "
343 << spvOpcodeString(opcode);
344
345 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
346 uint32_t input_storage_class = 0;
347 uint32_t input_data_type = 0;
348 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
349 &input_storage_class))
350 return _.diag(SPV_ERROR_INVALID_DATA, inst)
351 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
352
353 if (input_storage_class != SpvStorageClassWorkgroup &&
354 input_storage_class != SpvStorageClassCrossWorkgroup &&
355 input_storage_class != SpvStorageClassFunction)
356 return _.diag(SPV_ERROR_INVALID_DATA, inst)
357 << "Expected input to have storage class Workgroup, "
358 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
359
360 if (result_data_type != input_data_type)
361 return _.diag(SPV_ERROR_INVALID_DATA, inst)
362 << "Expected input and Result Type to point to the same type: "
363 << spvOpcodeString(opcode);
364 break;
365 }
366
367 case SpvOpGenericCastToPtr: {
368 uint32_t result_storage_class = 0;
369 uint32_t result_data_type = 0;
370 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
371 &result_storage_class))
372 return _.diag(SPV_ERROR_INVALID_DATA, inst)
373 << "Expected Result Type to be a pointer: "
374 << spvOpcodeString(opcode);
375
376 if (result_storage_class != SpvStorageClassWorkgroup &&
377 result_storage_class != SpvStorageClassCrossWorkgroup &&
378 result_storage_class != SpvStorageClassFunction)
379 return _.diag(SPV_ERROR_INVALID_DATA, inst)
380 << "Expected Result Type to have storage class Workgroup, "
381 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
382
383 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
384 uint32_t input_storage_class = 0;
385 uint32_t input_data_type = 0;
386 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
387 &input_storage_class))
388 return _.diag(SPV_ERROR_INVALID_DATA, inst)
389 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
390
391 if (input_storage_class != SpvStorageClassGeneric)
392 return _.diag(SPV_ERROR_INVALID_DATA, inst)
393 << "Expected input to have storage class Generic: "
394 << spvOpcodeString(opcode);
395
396 if (result_data_type != input_data_type)
397 return _.diag(SPV_ERROR_INVALID_DATA, inst)
398 << "Expected input and Result Type to point to the same type: "
399 << spvOpcodeString(opcode);
400 break;
401 }
402
403 case SpvOpGenericCastToPtrExplicit: {
404 uint32_t result_storage_class = 0;
405 uint32_t result_data_type = 0;
406 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
407 &result_storage_class))
408 return _.diag(SPV_ERROR_INVALID_DATA, inst)
409 << "Expected Result Type to be a pointer: "
410 << spvOpcodeString(opcode);
411
412 const uint32_t target_storage_class = inst->word(4);
413 if (result_storage_class != target_storage_class)
414 return _.diag(SPV_ERROR_INVALID_DATA, inst)
415 << "Expected Result Type to be of target storage class: "
416 << spvOpcodeString(opcode);
417
418 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
419 uint32_t input_storage_class = 0;
420 uint32_t input_data_type = 0;
421 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
422 &input_storage_class))
423 return _.diag(SPV_ERROR_INVALID_DATA, inst)
424 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
425
426 if (input_storage_class != SpvStorageClassGeneric)
427 return _.diag(SPV_ERROR_INVALID_DATA, inst)
428 << "Expected input to have storage class Generic: "
429 << spvOpcodeString(opcode);
430
431 if (result_data_type != input_data_type)
432 return _.diag(SPV_ERROR_INVALID_DATA, inst)
433 << "Expected input and Result Type to point to the same type: "
434 << spvOpcodeString(opcode);
435
436 if (target_storage_class != SpvStorageClassWorkgroup &&
437 target_storage_class != SpvStorageClassCrossWorkgroup &&
438 target_storage_class != SpvStorageClassFunction)
439 return _.diag(SPV_ERROR_INVALID_DATA, inst)
440 << "Expected target storage class to be Workgroup, "
441 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
442 break;
443 }
444
445 case SpvOpBitcast: {
446 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
447 if (!input_type)
448 return _.diag(SPV_ERROR_INVALID_DATA, inst)
449 << "Expected input to have a type: " << spvOpcodeString(opcode);
450
451 const bool result_is_pointer = _.IsPointerType(result_type);
452 const bool result_is_int_scalar = _.IsIntScalarType(result_type);
453 const bool input_is_pointer = _.IsPointerType(input_type);
454 const bool input_is_int_scalar = _.IsIntScalarType(input_type);
455
456 if (!result_is_pointer && !result_is_int_scalar &&
457 !_.IsIntVectorType(result_type) &&
458 !_.IsFloatScalarType(result_type) &&
459 !_.IsFloatVectorType(result_type))
460 return _.diag(SPV_ERROR_INVALID_DATA, inst)
461 << "Expected Result Type to be a pointer or int or float vector "
462 << "or scalar type: " << spvOpcodeString(opcode);
463
464 if (!input_is_pointer && !input_is_int_scalar &&
465 !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) &&
466 !_.IsFloatVectorType(input_type))
467 return _.diag(SPV_ERROR_INVALID_DATA, inst)
468 << "Expected input to be a pointer or int or float vector "
469 << "or scalar: " << spvOpcodeString(opcode);
470
471 if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) ||
472 _.HasExtension(kSPV_KHR_physical_storage_buffer)) {
473 const bool result_is_int_vector = _.IsIntVectorType(result_type);
474 const bool result_has_int32 =
475 _.ContainsSizedIntOrFloatType(result_type, SpvOpTypeInt, 32);
476 const bool input_is_int_vector = _.IsIntVectorType(input_type);
477 const bool input_has_int32 =
478 _.ContainsSizedIntOrFloatType(input_type, SpvOpTypeInt, 32);
479 if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
480 !(input_is_int_vector && input_has_int32))
481 return _.diag(SPV_ERROR_INVALID_DATA, inst)
482 << "Expected input to be a pointer, int scalar or 32-bit int "
483 "vector if Result Type is pointer: "
484 << spvOpcodeString(opcode);
485
486 if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
487 !(result_is_int_vector && result_has_int32))
488 return _.diag(SPV_ERROR_INVALID_DATA, inst)
489 << "Pointer can only be converted to another pointer, int "
490 "scalar or 32-bit int vector: "
491 << spvOpcodeString(opcode);
492 } else {
493 if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
494 return _.diag(SPV_ERROR_INVALID_DATA, inst)
495 << "Expected input to be a pointer or int scalar if Result "
496 "Type is pointer: "
497 << spvOpcodeString(opcode);
498
499 if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
500 return _.diag(SPV_ERROR_INVALID_DATA, inst)
501 << "Pointer can only be converted to another pointer or int "
502 "scalar: "
503 << spvOpcodeString(opcode);
504 }
505
506 if (!result_is_pointer && !input_is_pointer) {
507 const uint32_t result_size =
508 _.GetBitWidth(result_type) * _.GetDimension(result_type);
509 const uint32_t input_size =
510 _.GetBitWidth(input_type) * _.GetDimension(input_type);
511 if (result_size != input_size)
512 return _.diag(SPV_ERROR_INVALID_DATA, inst)
513 << "Expected input to have the same total bit width as "
514 << "Result Type: " << spvOpcodeString(opcode);
515 }
516 break;
517 }
518
519 default:
520 break;
521 }
522
523 if (_.HasCapability(SpvCapabilityShader)) {
524 switch (inst->opcode()) {
525 case SpvOpConvertFToU:
526 case SpvOpConvertFToS:
527 case SpvOpConvertSToF:
528 case SpvOpConvertUToF:
529 case SpvOpBitcast:
530 if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) ||
531 _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) {
532 return _.diag(SPV_ERROR_INVALID_DATA, inst)
533 << "8- or 16-bit types can only be used with width-only "
534 "conversions";
535 }
536 break;
537 default:
538 break;
539 }
540 }
541
542 return SPV_SUCCESS;
543}
544
545} // namespace val
546} // namespace spvtools
547