diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp index a404134b6e..cb18d0ec42 100644 --- a/source/val/validate_misc.cpp +++ b/source/val/validate_misc.cpp @@ -82,6 +82,27 @@ spv_result_t ValidateShaderClock(ValidationState_t& _, return SPV_SUCCESS; } +spv_result_t ValidateSizeOf(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected OpSizeOf Result Type to be a 32-bit int scalar."; + } + + uint32_t pointer_id = inst->GetOperandAs(2); + if (!_.IsConcreteType(pointer_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpSizeOf Pointer operand is not concrete."; + } else if (_.FindDef(pointer_id)->opcode() == + spv::Op::OpTypeUntypedPointerKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpSizeOf Pointer operand is to an untyped pointer, which size " + "is not well defined."; + } + + return SPV_SUCCESS; +} + spv_result_t ValidateAssumeTrue(ValidationState_t& _, const Instruction* inst) { const auto operand_type_id = _.GetOperandTypeId(inst, 0); if (!operand_type_id || !_.IsBoolScalarType(operand_type_id)) { @@ -193,6 +214,11 @@ spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { return error; } break; + case spv::Op::OpSizeOf: + if (auto error = ValidateSizeOf(_, inst)) { + return error; + } + break; case spv::Op::OpAssumeTrueKHR: if (auto error = ValidateAssumeTrue(_, inst)) { return error; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 8e9e617bb6..bfee48d359 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -30,6 +30,7 @@ #include "source/val/construct.h" #include "source/val/function.h" #include "spirv-tools/libspirv.h" +#include "spirv/unified1/spirv.hpp11" namespace spvtools { namespace val { @@ -927,22 +928,16 @@ uint32_t ValidationState_t::GetComponentType(uint32_t id) const { case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: - return inst->word(2); - case spv::Op::OpTypeVector: - return inst->word(2); - - case spv::Op::OpTypeMatrix: - return GetComponentType(inst->word(2)); - + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: - case spv::Op::OpTypeVectorIdEXT: - return inst->word(2); - case spv::Op::OpTypeTensorARM: return inst->word(2); + case spv::Op::OpTypeMatrix: + return GetComponentType(inst->word(2)); + default: break; } @@ -1632,6 +1627,57 @@ bool ValidationState_t::IsDescriptorHeapBaseVariable(const Instruction* inst) { is_heap_base); } +// From the spec (SPIRV.html#PhysicalPointerType) +bool ValidationState_t::IsPhysicalPointerType(uint32_t id) const { + const Instruction* inst = FindDef(id); + const spv::Op opcode = inst->opcode(); + if (opcode != spv::Op::OpTypePointer && + opcode != spv::Op::OpTypeUntypedPointerKHR) { + return false; + } + + const spv::AddressingModel am = addressing_model(); + if (am == spv::AddressingModel::Logical) { + return false; + } else if (am == spv::AddressingModel::Physical32 || + am == spv::AddressingModel::Physical64) { + return true; + } else if (am == spv::AddressingModel::PhysicalStorageBuffer64) { + const spv::StorageClass storage_class = spv::StorageClass(inst->word(2)); + return storage_class == spv::StorageClass::PhysicalStorageBuffer; + } + + assert(0); + return false; +} + +// From the spec (SPIRV.html#Numerical) +bool ValidationState_t::IsNumericalType(uint32_t id) const { + const Instruction* inst = FindDef(id); + const spv::Op opcode = inst->opcode(); + return opcode == spv::Op::OpTypeInt || opcode == spv::Op::OpTypeFloat; +} + +// From the spec (SPIRV.html#Concrete) +bool ValidationState_t::IsConcreteType(uint32_t id) const { + const Instruction* inst = FindDef(id); + const spv::Op opcode = inst->opcode(); + + if (opcode == spv::Op::OpTypeStruct) { + // all elements must be concrete + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (!IsConcreteType(inst->GetOperandAs(i))) { + return false; + } + } + return true; + } + + const uint32_t component_type = GetComponentType(id); + return IsNumericalType(component_type) || + IsPhysicalPointerType(component_type); +} + spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( const Instruction* inst, uint32_t result_type_id, uint32_t m2, bool is_conversion, bool swap_row_col) { diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 560122433b..07d6e96f73 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -733,6 +733,9 @@ class ValidationState_t { bool IsTensorType(uint32_t id) const; bool IsDescriptorType(spv::Op opcode) const; bool IsDescriptorType(uint32_t id) const; + bool IsPhysicalPointerType(uint32_t id) const; + bool IsNumericalType(uint32_t id) const; + bool IsConcreteType(uint32_t id) const; // When |length| is not 0, return true only if the array length is equal to // |length| and the array length is not defined by a specialization constant. bool IsArrayType(uint32_t id, uint64_t length = 0) const; diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp index f54b20cdcf..4d52ba0c7e 100644 --- a/test/val/val_misc_test.cpp +++ b/test/val/val_misc_test.cpp @@ -105,6 +105,134 @@ TEST_F(ValidateMisc, SizeOfValid) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); } +TEST_F(ValidateMisc, SizeOfStructValid) { + const std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %f "f" + %void = OpTypeVoid + %i32 = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %i32 + %struct_a = OpTypeStruct %ptr %i32 + %struct_b = OpTypeStruct %struct_a %ptr + %fnTy = OpTypeFunction %void + %f = OpFunction %void None %fnTy + %entry = OpLabel + %s = OpSizeOf %i32 %struct_b + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateMisc, SizeOfStructWithAbstract) { + const std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %f "f" + %void = OpTypeVoid + %i32 = OpTypeInt 32 0 + %bool = OpTypeBool + %ptr = OpTypePointer CrossWorkgroup %i32 + %struct_a = OpTypeStruct %ptr %bool + %struct_b = OpTypeStruct %struct_a %ptr + %fnTy = OpTypeFunction %void + %f = OpFunction %void None %fnTy + %entry = OpLabel + %s = OpSizeOf %i32 %struct_b + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSizeOf Pointer operand is not concrete")); +} + +TEST_F(ValidateMisc, SizeOfUntyped) { + const std::string spirv = R"( + OpCapability Addresses + OpCapability UntypedPointersKHR + OpCapability Kernel + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %f "f" + %void = OpTypeVoid + %i32 = OpTypeInt 32 0 + %fnTy = OpTypeFunction %void +%untyped_ptr = OpTypeUntypedPointerKHR CrossWorkgroup + %f = OpFunction %void None %fnTy + %entry = OpLabel + %s = OpSizeOf %i32 %untyped_ptr + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSizeOf Pointer operand is to an untyped pointer, " + "which size is not well defined")); +} + +TEST_F(ValidateMisc, SizeOfFloat) { + const std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %f "f" + %void = OpTypeVoid + %f32 = OpTypeFloat 32 + %ptr = OpTypePointer CrossWorkgroup %f32 + %fnTy = OpTypeFunction %void + %f = OpFunction %void None %fnTy + %entry = OpLabel + %s = OpSizeOf %f32 %ptr + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected OpSizeOf Result Type to be a 32-bit int scalar")); +} + +TEST_F(ValidateMisc, SizeOfVector) { + const std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %f "f" + %void = OpTypeVoid + %i32 = OpTypeInt 32 0 + %v2i32 = OpTypeVector %i32 2 + %ptr = OpTypePointer CrossWorkgroup %v2i32 + %fnTy = OpTypeFunction %void + %f = OpFunction %void None %fnTy + %entry = OpLabel + %s = OpSizeOf %v2i32 %ptr + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected OpSizeOf Result Type to be a 32-bit int scalar")); +} + const std::string ShaderClockSpirv = R"( OpCapability Shader OpCapability Int64