// Copyright (c) 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Validates correctness of ExtInst SPIR-V instructions. #include "validate.h" #include #include "latest_version_glsl_std_450_header.h" #include "latest_version_opencl_std_header.h" #include "diagnostic.h" #include "opcode.h" #include "val/instruction.h" #include "val/validation_state.h" namespace libspirv { // Validates correctness of ExtInst instructions. spv_result_t ExtInstPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast(inst->opcode); const uint32_t result_type = inst->type_id; const uint32_t num_operands = inst->num_operands; if (opcode != SpvOpExtInst) return SPV_SUCCESS; const uint32_t ext_inst_set = inst->words[3]; const uint32_t ext_inst_index = inst->words[4]; const spv_ext_inst_type_t ext_inst_type = spv_ext_inst_type_t(inst->ext_inst_type); auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() { spv_ext_inst_desc desc = nullptr; if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) != SPV_SUCCESS || !desc) { return std::string("Unknown ExtInst"); } auto* import_inst = _.FindDef(ext_inst_set); assert(import_inst); std::ostringstream ss; ss << reinterpret_cast(import_inst->words().data() + 2); ss << " "; ss << desc->name; return ss.str(); }; if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index); switch (ext_inst_key) { case GLSLstd450Round: case GLSLstd450RoundEven: case GLSLstd450FAbs: case GLSLstd450Trunc: case GLSLstd450FSign: case GLSLstd450Floor: case GLSLstd450Ceil: case GLSLstd450Fract: case GLSLstd450Sqrt: case GLSLstd450InverseSqrt: case GLSLstd450FMin: case GLSLstd450FMax: case GLSLstd450FClamp: case GLSLstd450FMix: case GLSLstd450Step: case GLSLstd450SmoothStep: case GLSLstd450Fma: case GLSLstd450Normalize: case GLSLstd450FaceForward: case GLSLstd450Reflect: case GLSLstd450NMin: case GLSLstd450NMax: case GLSLstd450NClamp: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a float scalar or vector type"; } for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); if (result_type != operand_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected types of all operands to be equal to Result " "Type"; } } break; } case GLSLstd450SAbs: case GLSLstd450SSign: case GLSLstd450UMin: case GLSLstd450SMin: case GLSLstd450UMax: case GLSLstd450SMax: case GLSLstd450UClamp: case GLSLstd450SClamp: case GLSLstd450FindILsb: case GLSLstd450FindUMsb: case GLSLstd450FindSMsb: { if (!_.IsIntScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be an int scalar or vector type"; } const uint32_t result_type_bit_width = _.GetBitWidth(result_type); const uint32_t result_type_dimension = _.GetDimension(result_type); for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); if (!_.IsIntScalarOrVectorType(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected all operands to be int scalars or vectors"; } if (result_type_dimension != _.GetDimension(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected all operands to have the same dimension as " << "Result Type"; } if (result_type_bit_width != _.GetBitWidth(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected all operands to have the same bit width as " << "Result Type"; } if (ext_inst_key == GLSLstd450FindUMsb || ext_inst_key == GLSLstd450FindSMsb) { if (result_type_bit_width != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "this instruction is currently limited to 32-bit width " << "components"; } } } break; } case GLSLstd450Radians: case GLSLstd450Degrees: case GLSLstd450Sin: case GLSLstd450Cos: case GLSLstd450Tan: case GLSLstd450Asin: case GLSLstd450Acos: case GLSLstd450Atan: case GLSLstd450Sinh: case GLSLstd450Cosh: case GLSLstd450Tanh: case GLSLstd450Asinh: case GLSLstd450Acosh: case GLSLstd450Atanh: case GLSLstd450Exp: case GLSLstd450Exp2: case GLSLstd450Log: case GLSLstd450Log2: case GLSLstd450Atan2: case GLSLstd450Pow: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 16 or 32-bit scalar or " "vector float type"; } const uint32_t result_type_bit_width = _.GetBitWidth(result_type); if (result_type_bit_width != 16 && result_type_bit_width != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 16 or 32-bit scalar or " "vector float type"; } for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); if (result_type != operand_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected types of all operands to be equal to Result " "Type"; } } break; } case GLSLstd450Determinant: { const uint32_t x_type = _.GetOperandTypeId(inst, 4); uint32_t num_rows = 0; uint32_t num_cols = 0; uint32_t col_type = 0; uint32_t component_type = 0; if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type, &component_type) || num_rows != num_cols) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X to be a square matrix"; } if (result_type != component_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X component type to be equal to " << "Result Type"; } break; } case GLSLstd450MatrixInverse: { uint32_t num_rows = 0; uint32_t num_cols = 0; uint32_t col_type = 0; uint32_t component_type = 0; if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, &component_type) || num_rows != num_cols) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a square matrix"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); if (result_type != x_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to Result Type"; } break; } case GLSLstd450Modf: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a scalar or vector float type"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); const uint32_t i_type = _.GetOperandTypeId(inst, 5); if (x_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to Result Type"; } uint32_t i_storage_class = 0; uint32_t i_data_type = 0; if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand I to be a pointer"; } if (i_data_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand I data type to be equal to Result Type"; } break; } case GLSLstd450ModfStruct: { std::vector result_types; if (!_.GetStructMemberTypes(result_type, &result_types) || result_types.size() != 2 || !_.IsFloatScalarOrVectorType(result_types[0]) || result_types[1] != result_types[0]) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a struct with two identical " << "scalar or vector float type members"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); if (x_type != result_types[0]) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to members of " << "Result Type struct"; } break; } case GLSLstd450Frexp: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a scalar or vector float type"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); const uint32_t exp_type = _.GetOperandTypeId(inst, 5); if (x_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to Result Type"; } uint32_t exp_storage_class = 0; uint32_t exp_data_type = 0; if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, &exp_storage_class)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Exp to be a pointer"; } if (!_.IsIntScalarOrVectorType(exp_data_type) || _.GetBitWidth(exp_data_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Exp data type to be a 32-bit int scalar " << "or vector type"; } if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Exp data type to have the same component " << "number as Result Type"; } break; } case GLSLstd450Ldexp: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a scalar or vector float type"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); const uint32_t exp_type = _.GetOperandTypeId(inst, 5); if (x_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to Result Type"; } if (!_.IsIntScalarOrVectorType(exp_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Exp to be a 32-bit int scalar " << "or vector type"; } if (_.GetDimension(result_type) != _.GetDimension(exp_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Exp to have the same component " << "number as Result Type"; } break; } case GLSLstd450FrexpStruct: { std::vector result_types; if (!_.GetStructMemberTypes(result_type, &result_types) || result_types.size() != 2 || !_.IsFloatScalarOrVectorType(result_types[0]) || !_.IsIntScalarOrVectorType(result_types[1]) || _.GetBitWidth(result_types[1]) != 32 || _.GetDimension(result_types[0]) != _.GetDimension(result_types[1])) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a struct with two members, " << "first member a float scalar or vector, second member " << "a 32-bit int scalar or vector with the same number of " << "components as the first member"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); if (x_type != result_types[0]) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to the first member " << "of Result Type struct"; } break; } case GLSLstd450PackSnorm4x8: case GLSLstd450PackUnorm4x8: { if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be 32-bit int scalar type"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 || _.GetBitWidth(v_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand V to be a 32-bit float vector of size 4"; } break; } case GLSLstd450PackSnorm2x16: case GLSLstd450PackUnorm2x16: case GLSLstd450PackHalf2x16: { if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be 32-bit int scalar type"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 || _.GetBitWidth(v_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand V to be a 32-bit float vector of size 2"; } break; } case GLSLstd450PackDouble2x32: { if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(result_type) != 64) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be 64-bit float scalar type"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 || _.GetBitWidth(v_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand V to be a 32-bit int vector of size 2"; } break; } case GLSLstd450UnpackSnorm4x8: case GLSLstd450UnpackUnorm4x8: { if (!_.IsFloatVectorType(result_type) || _.GetDimension(result_type) != 4 || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 32-bit float vector of size " "4"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P to be a 32-bit int scalar"; } break; } case GLSLstd450UnpackSnorm2x16: case GLSLstd450UnpackUnorm2x16: case GLSLstd450UnpackHalf2x16: { if (!_.IsFloatVectorType(result_type) || _.GetDimension(result_type) != 2 || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 32-bit float vector of size " "2"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P to be a 32-bit int scalar"; } break; } case GLSLstd450UnpackDouble2x32: { if (!_.IsIntVectorType(result_type) || _.GetDimension(result_type) != 2 || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 32-bit int vector of size " "2"; } const uint32_t v_type = _.GetOperandTypeId(inst, 4); if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand V to be a 64-bit float scalar"; } break; } case GLSLstd450Length: { if (!_.IsFloatScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a float scalar type"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); if (!_.IsFloatScalarOrVectorType(x_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X to be of float scalar or vector type"; } if (result_type != _.GetComponentType(x_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X component type to be equal to Result " "Type"; } break; } case GLSLstd450Distance: { if (!_.IsFloatScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a float scalar type"; } const uint32_t p0_type = _.GetOperandTypeId(inst, 4); if (!_.IsFloatScalarOrVectorType(p0_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P0 to be of float scalar or vector type"; } if (result_type != _.GetComponentType(p0_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P0 component type to be equal to " << "Result Type"; } const uint32_t p1_type = _.GetOperandTypeId(inst, 5); if (!_.IsFloatScalarOrVectorType(p1_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P1 to be of float scalar or vector type"; } if (result_type != _.GetComponentType(p1_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand P1 component type to be equal to " << "Result Type"; } if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operands P0 and P1 to have the same number of " << "components"; } break; } case GLSLstd450Cross: { if (!_.IsFloatVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a float vector type"; } if (_.GetDimension(result_type) != 3) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to have 3 components"; } const uint32_t x_type = _.GetOperandTypeId(inst, 4); const uint32_t y_type = _.GetOperandTypeId(inst, 5); if (x_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand X type to be equal to Result Type"; } if (y_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Y type to be equal to Result Type"; } break; } case GLSLstd450Refract: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a float scalar or vector type"; } const uint32_t i_type = _.GetOperandTypeId(inst, 4); const uint32_t n_type = _.GetOperandTypeId(inst, 5); const uint32_t eta_type = _.GetOperandTypeId(inst, 6); if (result_type != i_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand I to be of type equal to Result Type"; } if (result_type != n_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand N to be of type equal to Result Type"; } const uint32_t eta_type_bit_width = _.GetBitWidth(eta_type); if (!_.IsFloatScalarType(eta_type) || (eta_type_bit_width != 16 && eta_type_bit_width != 32)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected operand Eta to be a 16 or 32-bit float scalar"; } break; } case GLSLstd450InterpolateAtCentroid: case GLSLstd450InterpolateAtSample: case GLSLstd450InterpolateAtOffset: { if (!_.HasCapability(SpvCapabilityInterpolationFunction)) { return _.diag(SPV_ERROR_INVALID_CAPABILITY) << ext_inst_name() << " requires capability InterpolationFunction"; } if (!_.IsFloatScalarOrVectorType(result_type) || _.GetBitWidth(result_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Result Type to be a 32-bit float scalar " << "or vector type"; } const uint32_t interpolant_type = _.GetOperandTypeId(inst, 4); uint32_t interpolant_storage_class = 0; uint32_t interpolant_data_type = 0; if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, &interpolant_storage_class)) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Interpolant to be a pointer"; } if (result_type != interpolant_data_type) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Interpolant data type to be equal to Result Type"; } if (interpolant_storage_class != SpvStorageClassInput) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Interpolant storage class to be Input"; } if (ext_inst_key == GLSLstd450InterpolateAtSample) { const uint32_t sample_type = _.GetOperandTypeId(inst, 5); if (!_.IsIntScalarType(sample_type) || _.GetBitWidth(sample_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Sample to be 32-bit integer"; } } if (ext_inst_key == GLSLstd450InterpolateAtOffset) { const uint32_t offset_type = _.GetOperandTypeId(inst, 5); if (!_.IsFloatVectorType(offset_type) || _.GetDimension(offset_type) != 2 || _.GetBitWidth(offset_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA) << ext_inst_name() << ": " << "expected Offset to be a vector of 2 32-bit floats"; } } _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelFragment, ext_inst_name() + std::string(" requires Fragment execution model")); break; } case GLSLstd450IMix: { return _.diag(SPV_ERROR_INVALID_DATA) << "Extended instruction GLSLstd450IMix is not supported"; } case GLSLstd450Bad: { return _.diag(SPV_ERROR_INVALID_DATA) << "Encountered extended instruction GLSLstd450Bad"; } case GLSLstd450Count: { assert(0); break; } } } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { // TODO(atgoo@github.com) Add validation rules for OpenCL extended // instructions. } return SPV_SUCCESS; } } // namespace libspirv