// TODO(umar): Check linkage capabilities for function declarations // TODO(umar): Better error messages // NOTE: This function does not handle CFG related validation // Performs logical layout validation. See Section 2.4 spv_result_t ModuleLayoutPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { if (_.is_enabled(SPV_VALIDATE_LAYOUT_BIT)) { SpvOp opcode = inst->opcode; switch (_.getLayoutSection()) { case kLayoutCapabilities: case kLayoutExtensions: case kLayoutExtInstImport: case kLayoutMemoryModel: case kLayoutEntryPoint: case kLayoutExecutionMode: case kLayoutDebug1: case kLayoutDebug2: case kLayoutAnnotations: case kLayoutTypes: spvCheckReturn(ModuleScopedInstructions(_, inst, opcode)); break; case kLayoutFunctionDeclarations: case kLayoutFunctionDefinitions: spvCheckReturn(FunctionScopedInstructions(_, inst, opcode)); break; } // switch(getLayoutSection()) } return SPV_SUCCESS; }
spv_result_t ValidateAdjacency(ValidationState_t& _, size_t idx) { const auto& instructions = _.ordered_instructions(); const auto& inst = instructions[idx]; switch (inst.opcode()) { case SpvOpPhi: if (idx > 0) { switch (instructions[idx - 1].opcode()) { case SpvOpLabel: case SpvOpPhi: case SpvOpLine: break; default: return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "OpPhi must appear before all non-OpPhi instructions " << "(except for OpLine, which can be mixed with OpPhi)."; } } break; case SpvOpLoopMerge: if (idx != (instructions.size() - 1)) { switch (instructions[idx + 1].opcode()) { case SpvOpBranch: case SpvOpBranchConditional: break; default: return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "OpLoopMerge must immediately precede either an " << "OpBranch or OpBranchConditional instruction. " << "OpLoopMerge must be the second-to-last instruction in " << "its block."; } } break; case SpvOpSelectionMerge: if (idx != (instructions.size() - 1)) { switch (instructions[idx + 1].opcode()) { case SpvOpBranchConditional: case SpvOpSwitch: break; default: return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "OpSelectionMerge must immediately precede either an " << "OpBranchConditional or OpSwitch instruction. " << "OpSelectionMerge must be the second-to-last " << "instruction in its block."; } } default: break; } return SPV_SUCCESS; }
// TODO(umar): Check linkage capabilities for function declarations // TODO(umar): Better error messages // NOTE: This function does not handle CFG related validation // Performs logical layout validation. See Section 2.4 spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); switch (_.current_layout_section()) { case kLayoutCapabilities: case kLayoutExtensions: case kLayoutExtInstImport: case kLayoutMemoryModel: case kLayoutEntryPoint: case kLayoutExecutionMode: case kLayoutDebug1: case kLayoutDebug2: case kLayoutDebug3: case kLayoutAnnotations: case kLayoutTypes: if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; break; case kLayoutFunctionDeclarations: case kLayoutFunctionDefinitions: if (auto error = FunctionScopedInstructions(_, inst, opcode)) { return error; } break; } return SPV_SUCCESS; }
// Validates correctness of derivative instructions. spv_result_t DerivativesPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast<SpvOp>(inst->opcode); const uint32_t result_type = inst->type_id; switch (opcode) { case SpvOpDPdx: case SpvOpDPdy: case SpvOpFwidth: case SpvOpDPdxFine: case SpvOpDPdyFine: case SpvOpFwidthFine: case SpvOpDPdxCoarse: case SpvOpDPdyCoarse: case SpvOpFwidthCoarse: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA) << "Expected Result Type to be float scalar or vector type: " << spvOpcodeString(opcode); } const uint32_t p_type = _.GetOperandTypeId(inst, 2); if (p_type != result_type) { return _.diag(SPV_ERROR_INVALID_DATA) << "Expected P type and Result Type to be the same: " << spvOpcodeString(opcode); } _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelFragment, std::string( "Derivative instructions require Fragment execution model: ") + spvOpcodeString(opcode)); break; } default: break; } return SPV_SUCCESS; }
spv_result_t spvValidateIDs( const spv_instruction_t* pInsts, const uint64_t count, const spv_opcode_table opcodeTable, const spv_operand_table operandTable, const spv_ext_inst_table extInstTable, const ValidationState_t& state, spv_position position, spv_diagnostic* pDiagnostic) { auto undefd = state.usedefs().FindUsesWithoutDefs(); for (auto id : undefd) { DIAGNOSTIC << "Undefined ID: " << id; } position->index = SPV_INDEX_INSTRUCTION; spvCheckReturn(spvValidateInstructionIDs(pInsts, count, opcodeTable, operandTable, extInstTable, state, position, pDiagnostic)); return undefd.empty() ? SPV_SUCCESS : SPV_ERROR_INVALID_ID; }
// Validates correctness of arithmetic instructions. spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); switch (opcode) { case SpvOpFAdd: case SpvOpFSub: case SpvOpFMul: case SpvOpFDiv: case SpvOpFRem: case SpvOpFMod: case SpvOpFNegate: { bool supportsCoopMat = (opcode != SpvOpFMul && opcode != SpvOpFRem && opcode != SpvOpFMod); if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected floating scalar or vector type as Result Type: " << spvOpcodeString(opcode); for (size_t operand_index = 2; operand_index < inst->operands().size(); ++operand_index) { if (_.GetOperandTypeId(inst, operand_index) != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected arithmetic operands to be of Result Type: " << spvOpcodeString(opcode) << " operand index " << operand_index; } break; } case SpvOpUDiv: case SpvOpUMod: { bool supportsCoopMat = (opcode == SpvOpUDiv); if (!_.IsUnsignedIntScalarType(result_type) && !_.IsUnsignedIntVectorType(result_type) && !(supportsCoopMat && _.IsUnsignedIntCooperativeMatrixType(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected unsigned int scalar or vector type as Result Type: " << spvOpcodeString(opcode); for (size_t operand_index = 2; operand_index < inst->operands().size(); ++operand_index) { if (_.GetOperandTypeId(inst, operand_index) != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected arithmetic operands to be of Result Type: " << spvOpcodeString(opcode) << " operand index " << operand_index; } break; } case SpvOpISub: case SpvOpIAdd: case SpvOpIMul: case SpvOpSDiv: case SpvOpSMod: case SpvOpSRem: case SpvOpSNegate: { bool supportsCoopMat = (opcode != SpvOpIMul && opcode != SpvOpSRem && opcode != SpvOpSMod); if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t dimension = _.GetDimension(result_type); const uint32_t bit_width = _.GetBitWidth(result_type); for (size_t operand_index = 2; operand_index < inst->operands().size(); ++operand_index) { const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); if (!type_id || (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector type as operand: " << spvOpcodeString(opcode) << " operand index " << operand_index; if (_.GetDimension(type_id) != dimension) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected arithmetic operands to have the same dimension " << "as Result Type: " << spvOpcodeString(opcode) << " operand index " << operand_index; if (_.GetBitWidth(type_id) != bit_width) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected arithmetic operands to have the same bit width " << "as Result Type: " << spvOpcodeString(opcode) << " operand index " << operand_index; } break; } case SpvOpDot: { if (!_.IsFloatScalarType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float scalar type as Result Type: " << spvOpcodeString(opcode); uint32_t first_vector_num_components = 0; for (size_t operand_index = 2; operand_index < inst->operands().size(); ++operand_index) { const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); if (!type_id || !_.IsFloatVectorType(type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector as operand: " << spvOpcodeString(opcode) << " operand index " << operand_index; const uint32_t component_type = _.GetComponentType(type_id); if (component_type != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component type to be equal to Result Type: " << spvOpcodeString(opcode) << " operand index " << operand_index; const uint32_t num_components = _.GetDimension(type_id); if (operand_index == 2) { first_vector_num_components = num_components; } else if (num_components != first_vector_num_components) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected operands to have the same number of componenets: " << spvOpcodeString(opcode); } } break; } case SpvOpVectorTimesScalar: { if (!_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); if (result_type != vector_type_id) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected vector operand type to be equal to Result Type: " << spvOpcodeString(opcode); const uint32_t component_type = _.GetComponentType(vector_type_id); const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); if (component_type != scalar_type_id) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected scalar operand type to be equal to the component " << "type of the vector operand: " << spvOpcodeString(opcode); break; } case SpvOpMatrixTimesScalar: { if (!_.IsFloatMatrixType(result_type) && !_.IsCooperativeMatrixType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as Result Type: " << spvOpcodeString(opcode); const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); if (result_type != matrix_type_id) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected matrix operand type to be equal to Result Type: " << spvOpcodeString(opcode); const uint32_t component_type = _.GetComponentType(matrix_type_id); const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); if (component_type != scalar_type_id) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected scalar operand type to be equal to the component " << "type of the matrix operand: " << spvOpcodeString(opcode); break; } case SpvOpVectorTimesMatrix: { const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3); if (!_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t res_component_type = _.GetComponentType(result_type); if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as left operand: " << spvOpcodeString(opcode); if (res_component_type != _.GetComponentType(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component types of Result Type and vector to be " << "equal: " << spvOpcodeString(opcode); uint32_t matrix_num_rows = 0; uint32_t matrix_num_cols = 0; uint32_t matrix_col_type = 0; uint32_t matrix_component_type = 0; if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols, &matrix_col_type, &matrix_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as right operand: " << spvOpcodeString(opcode); if (res_component_type != matrix_component_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component types of Result Type and matrix to be " << "equal: " << spvOpcodeString(opcode); if (matrix_num_cols != _.GetDimension(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of columns of the matrix to be equal to " << "Result Type vector size: " << spvOpcodeString(opcode); if (matrix_num_rows != _.GetDimension(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of rows of the matrix to be equal to the " << "vector operand size: " << spvOpcodeString(opcode); break; } case SpvOpMatrixTimesVector: { const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3); if (!_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as Result Type: " << spvOpcodeString(opcode); uint32_t matrix_num_rows = 0; uint32_t matrix_num_cols = 0; uint32_t matrix_col_type = 0; uint32_t matrix_component_type = 0; if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols, &matrix_col_type, &matrix_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as left operand: " << spvOpcodeString(opcode); if (result_type != matrix_col_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected column type of the matrix to be equal to Result " "Type: " << spvOpcodeString(opcode); if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as right operand: " << spvOpcodeString(opcode); if (matrix_component_type != _.GetComponentType(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component types of the operands to be equal: " << spvOpcodeString(opcode); if (matrix_num_cols != _.GetDimension(vector_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of columns of the matrix to be equal to the " << "vector size: " << spvOpcodeString(opcode); break; } case SpvOpMatrixTimesMatrix: { const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); uint32_t res_num_rows = 0; uint32_t res_num_cols = 0; uint32_t res_col_type = 0; uint32_t res_component_type = 0; if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, &res_col_type, &res_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as Result Type: " << spvOpcodeString(opcode); uint32_t left_num_rows = 0; uint32_t left_num_cols = 0; uint32_t left_col_type = 0; uint32_t left_component_type = 0; if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols, &left_col_type, &left_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as left operand: " << spvOpcodeString(opcode); uint32_t right_num_rows = 0; uint32_t right_num_cols = 0; uint32_t right_col_type = 0; uint32_t right_component_type = 0; if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols, &right_col_type, &right_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as right operand: " << spvOpcodeString(opcode); if (!_.IsFloatScalarType(res_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as Result Type: " << spvOpcodeString(opcode); if (res_col_type != left_col_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected column types of Result Type and left matrix to be " << "equal: " << spvOpcodeString(opcode); if (res_component_type != right_component_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component types of Result Type and right matrix to " "be " << "equal: " << spvOpcodeString(opcode); if (res_num_cols != right_num_cols) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of columns of Result Type and right matrix " "to " << "be equal: " << spvOpcodeString(opcode); if (left_num_cols != right_num_rows) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of columns of left matrix and number of " "rows " << "of right matrix to be equal: " << spvOpcodeString(opcode); assert(left_num_rows == res_num_rows); break; } case SpvOpOuterProduct: { const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); uint32_t res_num_rows = 0; uint32_t res_num_cols = 0; uint32_t res_col_type = 0; uint32_t res_component_type = 0; if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, &res_col_type, &res_component_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as Result Type: " << spvOpcodeString(opcode); if (left_type_id != res_col_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected column type of Result Type to be equal to the type " << "of the left operand: " << spvOpcodeString(opcode); if (!right_type_id || !_.IsFloatVectorType(right_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float vector type as right operand: " << spvOpcodeString(opcode); if (res_component_type != _.GetComponentType(right_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected component types of the operands to be equal: " << spvOpcodeString(opcode); if (res_num_cols != _.GetDimension(right_type_id)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected number of columns of the matrix to be equal to the " << "vector size of the right operand: " << spvOpcodeString(opcode); break; } case SpvOpIAddCarry: case SpvOpISubBorrow: case SpvOpUMulExtended: case SpvOpSMulExtended: { std::vector<uint32_t> result_types; if (!_.GetStructMemberTypes(result_type, &result_types)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected a struct as Result Type: " << spvOpcodeString(opcode); if (result_types.size() != 2) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type struct to have two members: " << spvOpcodeString(opcode); if (opcode == SpvOpSMulExtended) { if (!_.IsIntScalarType(result_types[0]) && !_.IsIntVectorType(result_types[0])) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type struct member types to be integer " "scalar " << "or vector: " << spvOpcodeString(opcode); } else { if (!_.IsUnsignedIntScalarType(result_types[0]) && !_.IsUnsignedIntVectorType(result_types[0])) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type struct member types to be unsigned " << "integer scalar or vector: " << spvOpcodeString(opcode); } if (result_types[0] != result_types[1]) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type struct member types to be identical: " << spvOpcodeString(opcode); const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); if (left_type_id != result_types[0] || right_type_id != result_types[0]) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected both operands to be of Result Type member type: " << spvOpcodeString(opcode); break; } case SpvOpCooperativeMatrixMulAddNV: { const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); if (!_.IsCooperativeMatrixType(A_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as A Type: " << spvOpcodeString(opcode); } if (!_.IsCooperativeMatrixType(B_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as B Type: " << spvOpcodeString(opcode); } if (!_.IsCooperativeMatrixType(C_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as C Type: " << spvOpcodeString(opcode); } if (!_.IsCooperativeMatrixType(D_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as Result Type: " << spvOpcodeString(opcode); } const auto A = _.FindDef(A_type_id); const auto B = _.FindDef(B_type_id); const auto C = _.FindDef(C_type_id); const auto D = _.FindDef(D_type_id); std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2)); B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2)); C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2)); D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2)); A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3)); B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3)); C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3)); D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3)); A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4)); B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4)); C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4)); D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4)); const auto notEqual = [](std::tuple<bool, bool, uint32_t> X, std::tuple<bool, bool, uint32_t> Y) { return (std::get<1>(X) && std::get<1>(Y) && std::get<2>(X) != std::get<2>(Y)); }; if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Cooperative matrix scopes must match: " << spvOpcodeString(opcode); } if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || notEqual(C_rows, D_rows)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Cooperative matrix 'M' mismatch: " << spvOpcodeString(opcode); } if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || notEqual(C_cols, D_cols)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Cooperative matrix 'N' mismatch: " << spvOpcodeString(opcode); } if (notEqual(A_cols, B_rows)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Cooperative matrix 'K' mismatch: " << spvOpcodeString(opcode); } break; } default: break; } return SPV_SUCCESS; }
// Validates correctness of conversion instructions. spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); switch (opcode) { case SpvOpConvertFToU: { if (!_.IsUnsignedIntScalarType(result_type) && !_.IsUnsignedIntVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected unsigned int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be float scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (!_.features().use_int8_type && (8 == _.GetBitWidth(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid cast to 8-bit integer from a floating-point: " << spvOpcodeString(opcode); break; } case SpvOpConvertFToS: { if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be float scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (!_.features().use_int8_type && (8 == _.GetBitWidth(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid cast to 8-bit integer from a floating-point: " << spvOpcodeString(opcode); break; } case SpvOpConvertSToF: case SpvOpConvertUToF: { if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be int scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (!_.features().use_int8_type && (8 == _.GetBitWidth(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid cast to floating-point from an 8-bit integer: " << spvOpcodeString(opcode); break; } case SpvOpUConvert: { if (!_.IsUnsignedIntScalarType(result_type) && !_.IsUnsignedIntVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected unsigned int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be int scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have different bit width from Result " "Type: " << spvOpcodeString(opcode); break; } case SpvOpSConvert: { if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be int scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have different bit width from Result " "Type: " << spvOpcodeString(opcode); break; } case SpvOpFConvert: { if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be float scalar or vector: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have different bit width from Result " "Type: " << spvOpcodeString(opcode); break; } case SpvOpQuantizeToF16: { if ((!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) || _.GetBitWidth(result_type) != 32) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected 32-bit float scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (input_type != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input type to be equal to Result Type: " << spvOpcodeString(opcode); break; } case SpvOpConvertPtrToU: { if (!_.IsUnsignedIntScalarType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected unsigned int scalar type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!_.IsPointerType(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer: " << spvOpcodeString(opcode); if (_.addressing_model() == SpvAddressingModelLogical) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Logical addressing not supported: " << spvOpcodeString(opcode); if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64EXT) { uint32_t input_storage_class = 0; uint32_t input_data_type = 0; _.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class); if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Pointer storage class must be PhysicalStorageBufferEXT: " << spvOpcodeString(opcode); } break; } case SpvOpSatConvertSToU: case SpvOpSatConvertUToS: { if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector type as Result Type: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar or vector as input: " << spvOpcodeString(opcode); if (_.GetDimension(result_type) != _.GetDimension(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same dimension as Result Type: " << spvOpcodeString(opcode); break; } case SpvOpConvertUToPtr: { if (!_.IsPointerType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be a pointer: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type || !_.IsIntScalarType(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected int scalar as input: " << spvOpcodeString(opcode); if (_.addressing_model() == SpvAddressingModelLogical) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Logical addressing not supported: " << spvOpcodeString(opcode); if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64EXT) { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; _.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class); if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Pointer storage class must be PhysicalStorageBufferEXT: " << spvOpcodeString(opcode); } break; } case SpvOpPtrCastToGeneric: { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; if (!_.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be a pointer: " << spvOpcodeString(opcode); if (result_storage_class != SpvStorageClassGeneric) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to have storage class Generic: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); uint32_t input_storage_class = 0; uint32_t input_data_type = 0; if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer: " << spvOpcodeString(opcode); if (input_storage_class != SpvStorageClassWorkgroup && input_storage_class != SpvStorageClassCrossWorkgroup && input_storage_class != SpvStorageClassFunction) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have storage class Workgroup, " << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); if (result_data_type != input_data_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input and Result Type to point to the same type: " << spvOpcodeString(opcode); break; } case SpvOpGenericCastToPtr: { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; if (!_.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be a pointer: " << spvOpcodeString(opcode); if (result_storage_class != SpvStorageClassWorkgroup && result_storage_class != SpvStorageClassCrossWorkgroup && result_storage_class != SpvStorageClassFunction) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to have storage class Workgroup, " << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); uint32_t input_storage_class = 0; uint32_t input_data_type = 0; if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer: " << spvOpcodeString(opcode); if (input_storage_class != SpvStorageClassGeneric) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have storage class Generic: " << spvOpcodeString(opcode); if (result_data_type != input_data_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input and Result Type to point to the same type: " << spvOpcodeString(opcode); break; } case SpvOpGenericCastToPtrExplicit: { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; if (!_.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be a pointer: " << spvOpcodeString(opcode); const uint32_t target_storage_class = inst->word(4); if (result_storage_class != target_storage_class) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be of target storage class: " << spvOpcodeString(opcode); const uint32_t input_type = _.GetOperandTypeId(inst, 2); uint32_t input_storage_class = 0; uint32_t input_data_type = 0; if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer: " << spvOpcodeString(opcode); if (input_storage_class != SpvStorageClassGeneric) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have storage class Generic: " << spvOpcodeString(opcode); if (result_data_type != input_data_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input and Result Type to point to the same type: " << spvOpcodeString(opcode); if (target_storage_class != SpvStorageClassWorkgroup && target_storage_class != SpvStorageClassCrossWorkgroup && target_storage_class != SpvStorageClassFunction) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected target storage class to be Workgroup, " << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); break; } case SpvOpBitcast: { const uint32_t input_type = _.GetOperandTypeId(inst, 2); if (!input_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have a type: " << spvOpcodeString(opcode); const bool result_is_pointer = _.IsPointerType(result_type); const bool result_is_int_scalar = _.IsIntScalarType(result_type); const bool input_is_pointer = _.IsPointerType(input_type); const bool input_is_int_scalar = _.IsIntScalarType(input_type); if (!result_is_pointer && !result_is_int_scalar && !_.IsIntVectorType(result_type) && !_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be a pointer or int or float vector " << "or scalar type: " << spvOpcodeString(opcode); if (!input_is_pointer && !input_is_int_scalar && !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer or int or float vector " << "or scalar: " << spvOpcodeString(opcode); if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to be a pointer or int scalar if Result Type " << "is pointer: " << spvOpcodeString(opcode); if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Pointer can only be converted to another pointer or int " << "scalar: " << spvOpcodeString(opcode); if (!result_is_pointer && !input_is_pointer) { const uint32_t result_size = _.GetBitWidth(result_type) * _.GetDimension(result_type); const uint32_t input_size = _.GetBitWidth(input_type) * _.GetDimension(input_type); if (result_size != input_size) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected input to have the same total bit width as " << "Result Type: " << spvOpcodeString(opcode); } break; } default: break; } return SPV_SUCCESS; }