Add primitive instruction validation pass

This commit is contained in:
Jeremy Hayes 2017-12-14 15:29:37 -07:00 committed by David Neto
parent af7d5799a5
commit cdfbf26c13
7 changed files with 232 additions and 0 deletions

View File

@ -48,6 +48,7 @@ SPVTOOLS_SRC_FILES := \
source/validate_instruction.cpp \
source/validate_layout.cpp \
source/validate_logicals.cpp \
source/validate_primitives.cpp \
source/validate_type_unique.cpp
SPVTOOLS_OPT_SRC_FILES := \

View File

@ -271,6 +271,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_logicals.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_primitives.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_type_unique.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp

View File

@ -187,6 +187,7 @@ spv_result_t ProcessInstruction(void* user_data,
if (auto error = BitwisePass(_, inst)) return error;
if (auto error = ImagePass(_, inst)) return error;
if (auto error = AtomicsPass(_, inst)) return error;
if (auto error = PrimitivesPass(_, inst)) return error;
return SPV_SUCCESS;
}

View File

@ -148,6 +148,10 @@ spv_result_t AtomicsPass(ValidationState_t& _,
spv_result_t CapabilityPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst);
/// Validates correctness of primitive instructions.
spv_result_t PrimitivesPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst);
} // namespace libspirv
/// @brief Validate the ID usage of the instruction stream

View File

@ -0,0 +1,57 @@
// Copyright (c) 2017 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Validates correctness of primitive SPIR-V instructions.
#include "validate.h"
#include "diagnostic.h"
#include "opcode.h"
#include "val/instruction.h"
#include "val/validation_state.h"
namespace libspirv {
// Validates correctness of composite instructions.
spv_result_t PrimitivesPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
switch (opcode) {
case SpvOpEmitStreamVertex:
case SpvOpEndStreamPrimitive: {
const uint32_t stream_type = _.GetOperandTypeId(inst, 0);
if (!_.IsIntScalarType(stream_type)) {
return _.diag(SPV_ERROR_INVALID_DATA)
<< spvOpcodeString(opcode)
<< ": expected Stream to be int scalar";
}
const uint32_t stream_id = inst->words[1];
const SpvOp stream_opcode = _.GetIdOpcode(stream_id);
if (!spvOpcodeIsConstant(stream_opcode)) {
return _.diag(SPV_ERROR_INVALID_DATA)
<< spvOpcodeString(opcode)
<< ": expected Stream to be constant instruction";
}
}
default:
break;
}
return SPV_SUCCESS;
}
} // namespace libspirv

View File

@ -122,6 +122,12 @@ add_spvtools_unittest(TARGET val_atomics
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_primitives
SRCS val_primitives_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_limits
SRCS val_limits_test.cpp
${VAL_TEST_COMMON_SRCS}

View File

@ -0,0 +1,162 @@
// Copyright (c) 2017 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sstream>
#include <string>
#include "gmock/gmock.h"
#include "unit_spirv.h"
#include "val_fixtures.h"
namespace {
using ::testing::HasSubstr;
using ::testing::Not;
using ValidatePrimitives = spvtest::ValidateBase<bool>;
std::string GenerateShaderCode(
const std::string& body,
const std::string& capabilities_and_extensions = "",
const std::string& execution_model = "Geometry") {
std::ostringstream ss;
ss << R"(
OpCapability Geometry
OpCapability GeometryStreams
)";
ss << capabilities_and_extensions;
ss << "OpMemoryModel Logical GLSL450\n";
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
ss << R"(
%void = OpTypeVoid
%func = OpTypeFunction %void
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%u32vec4 = OpTypeVector %u32 4
%f32_0 = OpConstant %f32 0
%u32_0 = OpConstant %u32 0
%u32_1 = OpConstant %u32 1
%u32_2 = OpConstant %u32 2
%u32_3 = OpConstant %u32 3
%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
%main = OpFunction %void None %func
%main_entry = OpLabel
)";
ss << body;
ss << R"(
OpReturn
OpFunctionEnd)";
return ss.str();
}
TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) {
const std::string body = R"(
OpEmitStreamVertex %u32_0
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) {
const std::string body = R"(
OpEmitStreamVertex %f32_0
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EmitStreamVertex: "
"expected Stream to be int scalar"));
}
TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) {
const std::string body = R"(
OpEmitStreamVertex %u32vec4_0123
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EmitStreamVertex: "
"expected Stream to be int scalar"));
}
TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) {
const std::string body = R"(
%val1 = OpIAdd %u32 %u32_0 %u32_1
OpEmitStreamVertex %val1
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EmitStreamVertex: "
"expected Stream to be constant instruction"));
}
TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) {
const std::string body = R"(
OpEndStreamPrimitive %u32_0
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) {
const std::string body = R"(
OpEndStreamPrimitive %f32_0
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EndStreamPrimitive: "
"expected Stream to be int scalar"));
}
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) {
const std::string body = R"(
OpEndStreamPrimitive %u32vec4_0123
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EndStreamPrimitive: "
"expected Stream to be int scalar"));
}
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) {
const std::string body = R"(
%val1 = OpIAdd %u32 %u32_0 %u32_1
OpEndStreamPrimitive %val1
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("EndStreamPrimitive: "
"expected Stream to be constant instruction"));
}
} // anonymous namespace