Skip to content

Commit cdfbf26

Browse files
jeremy-lunargdneto0
authored andcommitted
Add primitive instruction validation pass
1 parent af7d579 commit cdfbf26

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ SPVTOOLS_SRC_FILES := \
4848
source/validate_instruction.cpp \
4949
source/validate_layout.cpp \
5050
source/validate_logicals.cpp \
51+
source/validate_primitives.cpp \
5152
source/validate_type_unique.cpp
5253

5354
SPVTOOLS_OPT_SRC_FILES := \

source/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ set(SPIRV_SOURCES
271271
${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
272272
${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
273273
${CMAKE_CURRENT_SOURCE_DIR}/validate_logicals.cpp
274+
${CMAKE_CURRENT_SOURCE_DIR}/validate_primitives.cpp
274275
${CMAKE_CURRENT_SOURCE_DIR}/validate_type_unique.cpp
275276
${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
276277
${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp

source/validate.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ spv_result_t ProcessInstruction(void* user_data,
187187
if (auto error = BitwisePass(_, inst)) return error;
188188
if (auto error = ImagePass(_, inst)) return error;
189189
if (auto error = AtomicsPass(_, inst)) return error;
190+
if (auto error = PrimitivesPass(_, inst)) return error;
190191

191192
return SPV_SUCCESS;
192193
}

source/validate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ spv_result_t AtomicsPass(ValidationState_t& _,
148148
spv_result_t CapabilityPass(ValidationState_t& _,
149149
const spv_parsed_instruction_t* inst);
150150

151+
/// Validates correctness of primitive instructions.
152+
spv_result_t PrimitivesPass(ValidationState_t& _,
153+
const spv_parsed_instruction_t* inst);
154+
151155
} // namespace libspirv
152156

153157
/// @brief Validate the ID usage of the instruction stream

source/validate_primitives.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2017 LunarG Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Validates correctness of primitive SPIR-V instructions.
16+
17+
#include "validate.h"
18+
19+
#include "diagnostic.h"
20+
#include "opcode.h"
21+
#include "val/instruction.h"
22+
#include "val/validation_state.h"
23+
24+
namespace libspirv {
25+
26+
// Validates correctness of composite instructions.
27+
spv_result_t PrimitivesPass(ValidationState_t& _,
28+
const spv_parsed_instruction_t* inst) {
29+
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
30+
31+
switch (opcode) {
32+
case SpvOpEmitStreamVertex:
33+
case SpvOpEndStreamPrimitive: {
34+
const uint32_t stream_type = _.GetOperandTypeId(inst, 0);
35+
if (!_.IsIntScalarType(stream_type)) {
36+
return _.diag(SPV_ERROR_INVALID_DATA)
37+
<< spvOpcodeString(opcode)
38+
<< ": expected Stream to be int scalar";
39+
}
40+
41+
const uint32_t stream_id = inst->words[1];
42+
const SpvOp stream_opcode = _.GetIdOpcode(stream_id);
43+
if (!spvOpcodeIsConstant(stream_opcode)) {
44+
return _.diag(SPV_ERROR_INVALID_DATA)
45+
<< spvOpcodeString(opcode)
46+
<< ": expected Stream to be constant instruction";
47+
}
48+
}
49+
50+
default:
51+
break;
52+
}
53+
54+
return SPV_SUCCESS;
55+
}
56+
57+
} // namespace libspirv

test/val/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ add_spvtools_unittest(TARGET val_atomics
122122
LIBS ${SPIRV_TOOLS}
123123
)
124124

125+
add_spvtools_unittest(TARGET val_primitives
126+
SRCS val_primitives_test.cpp
127+
${VAL_TEST_COMMON_SRCS}
128+
LIBS ${SPIRV_TOOLS}
129+
)
130+
125131
add_spvtools_unittest(TARGET val_limits
126132
SRCS val_limits_test.cpp
127133
${VAL_TEST_COMMON_SRCS}

test/val/val_primitives_test.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright (c) 2017 LunarG Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <sstream>
16+
#include <string>
17+
18+
#include "gmock/gmock.h"
19+
#include "unit_spirv.h"
20+
#include "val_fixtures.h"
21+
22+
namespace {
23+
24+
using ::testing::HasSubstr;
25+
using ::testing::Not;
26+
27+
using ValidatePrimitives = spvtest::ValidateBase<bool>;
28+
29+
std::string GenerateShaderCode(
30+
const std::string& body,
31+
const std::string& capabilities_and_extensions = "",
32+
const std::string& execution_model = "Geometry") {
33+
std::ostringstream ss;
34+
ss << R"(
35+
OpCapability Geometry
36+
OpCapability GeometryStreams
37+
)";
38+
39+
ss << capabilities_and_extensions;
40+
ss << "OpMemoryModel Logical GLSL450\n";
41+
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
42+
43+
ss << R"(
44+
%void = OpTypeVoid
45+
%func = OpTypeFunction %void
46+
%f32 = OpTypeFloat 32
47+
%u32 = OpTypeInt 32 0
48+
%u32vec4 = OpTypeVector %u32 4
49+
50+
%f32_0 = OpConstant %f32 0
51+
%u32_0 = OpConstant %u32 0
52+
%u32_1 = OpConstant %u32 1
53+
%u32_2 = OpConstant %u32 2
54+
%u32_3 = OpConstant %u32 3
55+
%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
56+
57+
%main = OpFunction %void None %func
58+
%main_entry = OpLabel
59+
)";
60+
61+
ss << body;
62+
63+
ss << R"(
64+
OpReturn
65+
OpFunctionEnd)";
66+
67+
return ss.str();
68+
}
69+
70+
TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) {
71+
const std::string body = R"(
72+
OpEmitStreamVertex %u32_0
73+
)";
74+
75+
CompileSuccessfully(GenerateShaderCode(body).c_str());
76+
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
77+
}
78+
79+
TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) {
80+
const std::string body = R"(
81+
OpEmitStreamVertex %f32_0
82+
)";
83+
84+
CompileSuccessfully(GenerateShaderCode(body).c_str());
85+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
86+
EXPECT_THAT(getDiagnosticString(),
87+
HasSubstr("EmitStreamVertex: "
88+
"expected Stream to be int scalar"));
89+
}
90+
91+
TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) {
92+
const std::string body = R"(
93+
OpEmitStreamVertex %u32vec4_0123
94+
)";
95+
96+
CompileSuccessfully(GenerateShaderCode(body).c_str());
97+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
98+
EXPECT_THAT(getDiagnosticString(),
99+
HasSubstr("EmitStreamVertex: "
100+
"expected Stream to be int scalar"));
101+
}
102+
103+
TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) {
104+
const std::string body = R"(
105+
%val1 = OpIAdd %u32 %u32_0 %u32_1
106+
OpEmitStreamVertex %val1
107+
)";
108+
109+
CompileSuccessfully(GenerateShaderCode(body).c_str());
110+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
111+
EXPECT_THAT(getDiagnosticString(),
112+
HasSubstr("EmitStreamVertex: "
113+
"expected Stream to be constant instruction"));
114+
}
115+
116+
TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) {
117+
const std::string body = R"(
118+
OpEndStreamPrimitive %u32_0
119+
)";
120+
121+
CompileSuccessfully(GenerateShaderCode(body).c_str());
122+
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
123+
}
124+
125+
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) {
126+
const std::string body = R"(
127+
OpEndStreamPrimitive %f32_0
128+
)";
129+
130+
CompileSuccessfully(GenerateShaderCode(body).c_str());
131+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
132+
EXPECT_THAT(getDiagnosticString(),
133+
HasSubstr("EndStreamPrimitive: "
134+
"expected Stream to be int scalar"));
135+
}
136+
137+
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) {
138+
const std::string body = R"(
139+
OpEndStreamPrimitive %u32vec4_0123
140+
)";
141+
142+
CompileSuccessfully(GenerateShaderCode(body).c_str());
143+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
144+
EXPECT_THAT(getDiagnosticString(),
145+
HasSubstr("EndStreamPrimitive: "
146+
"expected Stream to be int scalar"));
147+
}
148+
149+
TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) {
150+
const std::string body = R"(
151+
%val1 = OpIAdd %u32 %u32_0 %u32_1
152+
OpEndStreamPrimitive %val1
153+
)";
154+
155+
CompileSuccessfully(GenerateShaderCode(body).c_str());
156+
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
157+
EXPECT_THAT(getDiagnosticString(),
158+
HasSubstr("EndStreamPrimitive: "
159+
"expected Stream to be constant instruction"));
160+
}
161+
162+
} // anonymous namespace

0 commit comments

Comments
 (0)