Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension allows multiple same OpTypePointer types #783

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions source/val/validation_state.cpp
Expand Up @@ -15,6 +15,7 @@
#include "val/validation_state.h"

#include <cassert>
#include <map>

#include "opcode.h"
#include "val/basic_block.h"
Expand Down Expand Up @@ -425,6 +426,25 @@ bool ValidationState_t::RegisterUniqueTypeDeclaration(
key.insert(key.end(), inst.words + words_begin, inst.words + words_end);
}

// If module has extension SPV_KHR_variable_pointers, then OpTypePointer
// is allowed to create identical types so long as their decorations differ.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking again, this is not the rule.
The SPV_KHR_variable_pointers extension says

Pointer types are also allowed to have multiple id's for the same opcode and operands, to allow for differing ArrayStride Array Stride decoration values.

Also, in that paragraph, "non-aggregate types" will then generally be "non-aggregate non-pointer types."

From 2.8 in the SPIR-V spec:

It is valid to declare multiple aggregate type s having the same opcode and operands. This is to allow multiple instances of aggregate types with the same structure to be decorated differently. (Different decorations are not required; two different aggregate type ids are allowed to have identical declarations and decorations, and will still be two different types.)

That means the validator should allow distinct OpTypePointer with the same operands, and with the same decorations.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think all this new code boils down to:

if (inst.opcode == SpvOpTypePointer && HasExtension....) return true;

Even better: put the check up in the callee, along with the aggregate types. That is, modify TypeUniquePass to do the pointer-and-has-extension check.

if (inst.opcode == SpvOpTypePointer &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to have a comment describing the rule you're enforcing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

HasExtension(Extension::kSPV_KHR_variable_pointers)) {
const std::vector<Decoration>& decorations = id_decorations(inst.result_id);
std::map<SpvDecoration, const std::vector<uint32_t>*> decoration_params;
for (const auto& decoration : decorations) {
decoration_params[decoration.dec_type()] = &decoration.params();
}
for (const auto& kv : decoration_params) {
// Append decoration data to the key, sorted by decoration type.
// Use magic number to separate unrelated chunks of data.
const uint32_t kMagicSeparator = 0xffff9296;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low level comment: This magic number mechanism is fragile. Better to push a kv.second.size() then its contents.

(But really we should not care about duplication of decorations.)

key.push_back(kMagicSeparator);
key.push_back(static_cast<uint32_t>(kv.first));
key.insert(key.end(), kv.second->begin(), kv.second->end());
}
}

return unique_type_declarations_.insert(std::move(key)).second;
}
} /// namespace libspirv
3 changes: 2 additions & 1 deletion source/validate_type_unique.cpp
Expand Up @@ -49,7 +49,8 @@ spv_result_t TypeUniquePass(ValidationState_t& _,
// return _.diag(SPV_ERROR_INVALID_DATA)
return _.diag(SPV_SUCCESS)
<< "Duplicate non-aggregate type declarations are not allowed."
<< " Opcode: " << inst->opcode;
<< " Opcode: " << spvOpcodeString(SpvOp(inst->opcode))
<< " id: " << inst->result_id;
}
}

Expand Down
55 changes: 54 additions & 1 deletion test/val/val_type_unique_test.cpp
Expand Up @@ -95,7 +95,7 @@ OpFunctionEnd
// declaration.
string GetErrorString(SpvOp opcode) {
return "Duplicate non-aggregate type declarations are not allowed. Opcode: "
+ std::to_string(opcode);
+ std::string(spvOpcodeString(opcode));
}

TEST_F(ValidateTypeUnique, success) {
Expand Down Expand Up @@ -238,4 +238,57 @@ OpMemoryModel Physical32 OpenCL
Not(HasSubstr(GetErrorString(SpvOpTypeVoid))));
}

TEST_F(ValidateTypeUnique, PointerTypesDifferentArrayStrideNoExtension) {
string str = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ptr1 ArrayStride 4
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe you just have a typo here?

OpDecorate %ptr2 ArrayStride 8
%u32 = OpTypeInt 32 0
%ptr1 = OpTypePointer Input %u32
%ptr2 = OpTypePointer Input %u32
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr(GetErrorString(SpvOpTypePointer)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. The validation succeeds, but generates an error string?

Does this test even pass?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It logs a warning but returns SPV_SUCCESS.

}

TEST_F(ValidateTypeUnique, PointerTypesDifferentArrayStride) {
string str = R"(
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpDecorate %ptr1 ArrayStride 4
OpDecorate %ptr2 ArrayStride 8
%u32 = OpTypeInt 32 0
%ptr1 = OpTypePointer Input %u32
%ptr2 = OpTypePointer Input %u32
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
Not(HasSubstr(GetErrorString(SpvOpTypePointer))));
}

TEST_F(ValidateTypeUnique, PointerTypesSameArrayStride) {
string str = R"(
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpDecorate %ptr1 ArrayStride 4
OpDecorate %ptr2 ArrayStride 4
%u32 = OpTypeInt 32 0
%ptr1 = OpTypePointer Input %u32
%ptr2 = OpTypePointer Input %u32
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(kDuplicateTypeError, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr(GetErrorString(SpvOpTypePointer)));
}

} // anonymous namespace