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