Skip to content

Commit

Permalink
Validation of OpEntryPoint usage.
Browse files Browse the repository at this point in the history
According to the SPIRV Spec (2.16.1):
* There is at least one OpEntryPoint instruction, unless the Linkage
capability is being used.

* No function can be targeted by both an OpEntryPoint instruction and an
OpFunctionCall instruction.

Also updated unit tests to includ OpEntryPoint.
  • Loading branch information
ehsannas authored and dneto0 committed Jan 13, 2017
1 parent 68e36ec commit 1c11c86
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 138 deletions.
13 changes: 13 additions & 0 deletions source/val/validation_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ class ValidationState_t {
std::vector<uint32_t>& entry_points() { return entry_points_; }
const std::vector<uint32_t>& entry_points() const { return entry_points_; }

/// Inserts an <id> to the set of functions that are target of OpFunctionCall.
void AddFunctionCallTarget(const uint32_t id) {
function_call_targets_.insert(id);
}

/// Returns whether or not a function<id> is the target of OpFunctionCall.
bool IsFunctionCallTarget(const uint32_t id) {
return (function_call_targets_.find(id) != function_call_targets_.end());
}

/// Registers the capability and its dependent capabilities
void RegisterCapability(SpvCapability cap);

Expand Down Expand Up @@ -255,6 +265,9 @@ class ValidationState_t {
/// IDs that are entry points, ie, arguments to OpEntryPoint.
std::vector<uint32_t> entry_points_;

/// Functions IDs that are target of OpFunctionCall.
std::unordered_set<uint32_t> function_call_targets_;

/// ID Bound from the Header
uint32_t id_bound_;

Expand Down
27 changes: 26 additions & 1 deletion source/validate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ spv_result_t ProcessInstruction(void* user_data,
const spv_parsed_instruction_t* inst) {
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
_.increment_instruction_count();
if (static_cast<SpvOp>(inst->opcode) == SpvOpEntryPoint)
if (static_cast<SpvOp>(inst->opcode) == SpvOpEntryPoint) {
_.entry_points().push_back(inst->words[2]);
}
if (static_cast<SpvOp>(inst->opcode) == SpvOpFunctionCall) {
_.AddFunctionCallTarget(inst->words[3]);
}

DebugInstructionPass(_, inst);
if (auto error = DataRulesPass(_, inst)) return error;
Expand Down Expand Up @@ -235,6 +239,27 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
if (auto error = UpdateIdUse(*vstate)) return error;
if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error;

// Entry point validation. Based on 2.16.1 (Universal Validation Rules) of the
// SPIRV spec:
// * There is at least one OpEntryPoint instruction, unless the Linkage
// capability is being used.
// * No function can be targeted by both an OpEntryPoint instruction and an
// OpFunctionCall instruction.
if (vstate->entry_points().empty() &&
!vstate->HasCapability(SpvCapabilityLinkage)) {
return vstate->diag(SPV_ERROR_INVALID_BINARY)
<< "No OpEntryPoint instruction was found. This is only allowed if "
"the Linkage capability is being used.";
}
for (const auto& entry_point : vstate->entry_points()) {
if (vstate->IsFunctionCallTarget(entry_point)) {
return vstate->diag(SPV_ERROR_INVALID_BINARY)
<< "A function (" << entry_point
<< ") may not be targeted by both an OpEntryPoint instruction and "
"an OpFunctionCall instruction.";
}
}

// NOTE: Copy each instruction for easier processing
std::vector<spv_instruction_t> instructions;
uint64_t index = SPV_INDEX_INSTRUCTION;
Expand Down
4 changes: 3 additions & 1 deletion test/c_interface_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ using namespace spvtools;
TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] =
"OpCapability Shader\nOpMemoryModel Logical GLSL450";
"OpCapability Shader\n"
"OpCapability Linkage\n"
"OpMemoryModel Logical GLSL450";

spv_binary binary = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
Expand Down
12 changes: 8 additions & 4 deletions test/cpp_interface_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,10 @@ TEST(CppInterface, DisassembleWithWrongTargetEnv) {
}

TEST(CppInterface, SuccessfulValidation) {
const std::string input_text =
"OpCapability Shader\nOpMemoryModel Logical GLSL450";
const std::string input_text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450)";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
int invocation_count = 0;
t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*,
Expand All @@ -189,8 +191,10 @@ TEST(CppInterface, SuccessfulValidation) {
}

TEST(CppInterface, ValidateOverloads) {
const std::string input_text =
"OpCapability Shader\nOpMemoryModel Logical GLSL450";
const std::string input_text = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450)";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text, &binary));
Expand Down
Loading

0 comments on commit 1c11c86

Please sign in to comment.