Adding validation code for OpTypeStruct.

According to the Data Rules section of 2.16.1. Universal Validation
Rules of the SPIR-V Spec:

Forward reference operands in an OpTypeStruct
* must be later declared with OpTypePointer
* the type pointed to must be an OpTypeStruct
* had an earlier OpTypeForwardPointer forward reference to the same <id>
This commit is contained in:
Ehsan Nasiri 2016-11-10 15:12:26 -05:00 committed by David Neto
parent 5c19de2510
commit 8c414eb579
6 changed files with 153 additions and 5 deletions

View File

@ -207,6 +207,15 @@ spv_result_t ValidationState_t::RemoveIfForwardDeclared(uint32_t id) {
return SPV_SUCCESS;
}
spv_result_t ValidationState_t::RegisterForwardPointer(uint32_t id) {
forward_pointer_ids_.insert(id);
return SPV_SUCCESS;
}
bool ValidationState_t::IsForwardPointer(uint32_t id) const {
return (forward_pointer_ids_.find(id) != forward_pointer_ids_.end());
}
void ValidationState_t::AssignNameToId(uint32_t id, string name) {
operand_names_[id] = name;
}

View File

@ -64,6 +64,12 @@ class ValidationState_t {
/// Removes a forward declared ID if it has been defined
spv_result_t RemoveIfForwardDeclared(uint32_t id);
/// Registers an ID as a forward pointer
spv_result_t RegisterForwardPointer(uint32_t id);
/// Returns whether or not an ID is a forward pointer
bool IsForwardPointer(uint32_t id) const;
/// Assigns a name to an ID
void AssignNameToId(uint32_t id, std::string name);
@ -184,6 +190,9 @@ class ValidationState_t {
/// IDs which have been forward declared but have not been defined
std::unordered_set<uint32_t> unresolved_forward_ids_;
/// IDs that have been declared as forward pointers.
std::unordered_set<uint32_t> forward_pointer_ids_;
/// A map of operand IDs and their names defined by the OpName instruction
std::unordered_map<uint32_t, std::string> operand_names_;

View File

@ -230,7 +230,7 @@ spv_result_t spvValidateBinary(const spv_const_context context,
auto id_str = ss.str();
return vstate.diag(SPV_ERROR_INVALID_ID)
<< "The following forward referenced IDs have not be defined:\n"
<< "The following forward referenced IDs have not been defined:\n"
<< id_str.substr(0, id_str.size() - 1);
}

View File

@ -190,6 +190,33 @@ spv_result_t ValidateSpecConstBoolean(ValidationState_t& _,
return SPV_SUCCESS;
}
// Records the <id> of the forward pointer to be used for validation.
spv_result_t ValidateForwardPointer(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
// Record the <id> (which is operand 0) to ensure it's used properly.
// OpTypeStruct can only include undefined pointers that are
// previously declared as a ForwardPointer
return (_.RegisterForwardPointer(inst->words[inst->operands[0].offset]));
}
// Validates that any undefined component of the struct is a forward pointer.
// It is valid to declare a forward pointer, and use its <id> as one of the
// components of a struct.
spv_result_t ValidateStruct(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
// Struct components are operands 1, 2, etc.
for (unsigned i = 1; i < inst->num_operands; i++) {
auto type_id = inst->words[inst->operands[i].offset];
auto type_instruction = _.FindDef(type_id);
if (type_instruction == nullptr && !_.IsForwardPointer(type_id)) {
return _.diag(SPV_ERROR_INVALID_ID)
<< "Forward reference operands in an OpTypeStruct must first be "
"declared using OpTypeForwardPointer.";
}
}
return SPV_SUCCESS;
}
} // anonymous namespace
namespace libspirv {
@ -227,6 +254,14 @@ spv_result_t DataRulesPass(ValidationState_t& _,
if (auto error = ValidateSpecConstBoolean(_, inst)) return error;
break;
}
case SpvOpTypeForwardPointer: {
if (auto error = ValidateForwardPointer(_, inst)) return error;
break;
}
case SpvOpTypeStruct: {
if (auto error = ValidateStruct(_, inst)) return error;
break;
}
// TODO(ehsan): add more data rules validation here.
default: { break; }
}

View File

@ -355,13 +355,35 @@ bool idUsage::isValid<SpvOpTypeStruct>(const spv_instruction_t* inst,
const spv_opcode_desc) {
for (size_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
++memberTypeIndex) {
auto memberType = module_.FindDef(inst->words[memberTypeIndex]);
auto memberTypeId = inst->words[memberTypeIndex];
auto memberType = module_.FindDef(memberTypeId);
if (!memberType || !spvOpcodeGeneratesType(memberType->opcode())) {
DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '"
<< inst->words[memberTypeIndex]
<< "' is not a type.";
return false;
}
if (memberType && module_.IsForwardPointer(memberTypeId)) {
if (memberType->opcode() != SpvOpTypePointer) {
DIAG(memberTypeIndex) << "Found a forward reference to a non-pointer "
"type in OpTypeStruct instruction.";
return false;
}
// If we're dealing with a forward pointer:
// Find out the type that the pointer is pointing to (must be struct)
// word 3 is the <id> of the type being pointed to.
auto typePointingTo = module_.FindDef(memberType->words()[3]);
if (typePointingTo && typePointingTo->opcode() != SpvOpTypeStruct) {
// Forward declared operands of a struct may only point to a struct.
DIAG(memberTypeIndex)
<< "A forward reference operand in an OpTypeStruct must be an "
"OpTypePointer that points to an OpTypeStruct. "
"Found OpTypePointer that points to Op"
<< spvOpcodeString(static_cast<SpvOp>(typePointingTo->opcode()))
<< ".";
return false;
}
}
}
return true;
}
@ -591,7 +613,8 @@ bool idUsage::isValid<SpvOpConstantComposite>(const spv_instruction_t* inst,
constituentIndex < inst->words.size();
constituentIndex++, memberIndex++) {
auto constituent = module_.FindDef(inst->words[constituentIndex]);
if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
if (!constituent ||
!spvOpcodeIsConstantOrUndef(constituent->opcode())) {
DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
<< inst->words[constituentIndex]
<< "' is not a constant or undef.";
@ -2384,8 +2407,8 @@ function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
// The Invoke parameter.
out = [](unsigned index) { return index == 2; };
break;
case SpvOpTypeForwardPointer:
out = [](unsigned index) { return index == 0; };
case SpvOpTypeForwardPointer:
out = [](unsigned index) { return index == 0; };
break;
default:
out = [](unsigned) { return false; };

View File

@ -36,6 +36,12 @@ string header = R"(
OpMemoryModel Logical GLSL450
%1 = OpTypeFloat 32
)";
string header_with_addresses = R"(
OpCapability Addresses
OpCapability Kernel
OpCapability GenericPointer
OpMemoryModel Physical32 OpenCL
)";
string header_with_vec16_cap = R"(
OpCapability Shader
OpCapability Vector16
@ -390,3 +396,69 @@ TEST_F(ValidateData, specialize_boolean_to_int) {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Specialization constant must be a boolean"));
}
TEST_F(ValidateData, missing_forward_pointer_decl) {
string str = header_with_addresses + R"(
%uintt = OpTypeInt 32 0
%3 = OpTypeStruct %fwd_ptrt %uintt
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("must first be declared using OpTypeForwardPointer"));
}
TEST_F(ValidateData, forward_pointer_missing_definition) {
string str = header_with_addresses + R"(
OpTypeForwardPointer %_ptr_Generic_struct_A Generic
%uintt = OpTypeInt 32 0
%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("forward referenced IDs have not been defined"));
}
TEST_F(ValidateData, forward_ref_bad_type) {
string str = header_with_addresses + R"(
OpTypeForwardPointer %_ptr_Generic_struct_A Generic
%uintt = OpTypeInt 32 0
%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A
%_ptr_Generic_struct_A = OpTypeFloat 32
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Found a forward reference to a non-pointer type in "
"OpTypeStruct instruction."));
}
TEST_F(ValidateData, forward_ref_points_to_non_struct) {
string str = header_with_addresses + R"(
OpTypeForwardPointer %_ptr_Generic_struct_A Generic
%uintt = OpTypeInt 32 0
%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A
%_ptr_Generic_struct_A = OpTypePointer Generic %uintt
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("A forward reference operand in an OpTypeStruct must "
"be an OpTypePointer that points to an OpTypeStruct. "
"Found OpTypePointer that points to OpTypeInt."));
}
TEST_F(ValidateData, struct_forward_pointer_good) {
string str = header_with_addresses + R"(
OpTypeForwardPointer %_ptr_Generic_struct_A Generic
%uintt = OpTypeInt 32 0
%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A
%struct_C = OpTypeStruct %uintt %struct_B
%struct_A = OpTypeStruct %uintt %struct_C
%_ptr_Generic_struct_A = OpTypePointer Generic %struct_C
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}