using libspirv::Decoration;
using libspirv::DiagnosticStream;
+using libspirv::Instruction;
using libspirv::ValidationState_t;
namespace {
});
}
-} // anonymous namespace
+// Returns true if the given ID has the Import LinkageAttributes decoration.
+bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
+ const auto& decorations = vstate.id_decorations(id);
+ return std::any_of(decorations.begin(), decorations.end(),
+ [](const Decoration& d) {
+ return SpvDecorationLinkageAttributes == d.dec_type() &&
+ d.params().size() >= 2u &&
+ d.params().back() == SpvLinkageTypeImport;
+ });
+}
-namespace libspirv {
+spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
+ for (const auto& function : vstate.functions()) {
+ if (function.block_count() == 0u) {
+ // A function declaration (an OpFunction with no basic blocks), must have
+ // a Linkage Attributes Decoration with the Import Linkage Type.
+ if (!hasImportLinkageAttribute(function.id(), vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_BINARY)
+ << "Function declaration (id " << function.id()
+ << ") must have a LinkageAttributes decoration with the Import "
+ "Linkage type.";
+ }
+ } else {
+ if (hasImportLinkageAttribute(function.id(), vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_BINARY)
+ << "Function definition (id " << function.id()
+ << ") may not be decorated with Import Linkage type.";
+ }
+ }
+ }
+ return SPV_SUCCESS;
+}
-// Validates that decorations have been applied properly.
-spv_result_t ValidateDecorations(ValidationState_t& vstate) {
+// Checks whether an imported variable is initialized by this module.
+spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) {
// According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported
// variable. This means that a module-scope OpVariable with initialization
// value cannot be marked with the Import Linkage Type (import type id = 1).
// Initializer <id> is an optional argument for OpVariable. If initializer
// <id> is present, the instruction will have 5 words.
auto variable_instr = vstate.FindDef(global_var_id);
- if (variable_instr->words().size() == 5u) {
- for (const auto& decoration : vstate.id_decorations(global_var_id)) {
- // the Linkage Type is the last parameter of the decoration.
- if (SpvDecorationLinkageAttributes == decoration.dec_type() &&
- decoration.params().size() >= 2u &&
- decoration.params().back() == 1) {
- return vstate.diag(SPV_ERROR_INVALID_ID)
- << "A module-scope OpVariable with initialization value "
- "cannot be marked with the Import Linkage Type.";
- }
- }
+ if (variable_instr->words().size() == 5u &&
+ hasImportLinkageAttribute(global_var_id, vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID)
+ << "A module-scope OpVariable with initialization value "
+ "cannot be marked with the Import Linkage Type.";
}
}
+ return SPV_SUCCESS;
+}
+// Checks whether proper decorations have been appied to the entry points.
+spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
for (uint32_t entry_point : vstate.entry_points()) {
const auto& interfaces = vstate.entry_point_interfaces(entry_point);
int num_builtin_inputs = 0;
}
}
}
+ return SPV_SUCCESS;
+}
- // TODO: Refactor this function into smaller pieces.
+} // anonymous namespace
+
+namespace libspirv {
+// Validates that decorations have been applied properly.
+spv_result_t ValidateDecorations(ValidationState_t& vstate) {
+ if (auto error = CheckImportedVariableInitialization(vstate)) return error;
+ if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
+ if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
return SPV_SUCCESS;
}
TEST_P(ValidateCFG, EmptyFunction) {
string str = header(GetParam()) + string(types_consts()) +
- "%func = OpFunction %voidt None %funct\n" + "OpFunctionEnd\n";
+ R"(%func = OpFunction %voidt None %funct
+ %l = OpLabel
+ OpReturn
+ OpFunctionEnd)";
CompileSuccessfully(str);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
"an OpEntryPoint instruction."));
}
+TEST_F(ValidateDecorations, FunctionDeclarationWithoutImportLinkageBad) {
+ string spirv = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Function declaration (id 3) must have a LinkageAttributes "
+ "decoration with the Import Linkage type."));
+}
+
+TEST_F(ValidateDecorations, FunctionDeclarationWithImportLinkageGood) {
+ string spirv = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ OpDecorate %main LinkageAttributes "link_fn" Import
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, FunctionDeclarationWithExportLinkageBad) {
+ string spirv = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ OpDecorate %main LinkageAttributes "link_fn" Export
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Function declaration (id 1) must have a LinkageAttributes "
+ "decoration with the Import Linkage type."));
+}
+
+TEST_F(ValidateDecorations, FunctionDefinitionWithImportLinkageBad) {
+ string spirv = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ OpDecorate %main LinkageAttributes "link_fn" Import
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Function definition (id 1) may not be decorated with "
+ "Import Linkage type."));
+}
+
+TEST_F(ValidateDecorations, FunctionDefinitionWithoutImportLinkageGood) {
+ string spirv = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
} // anonymous namespace
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1 %2 %2
%4 = OpFunction %1 None %3
+%5 = OpLabel
+ OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
-%5 = OpConstant %2 42
-%3 = OpTypeFunction %1 %2 %2
-%4 = OpFunction %2 None %3
+%3 = OpConstant %2 42
+%4 = OpTypeFunction %1 %2 %2
+%5 = OpFunction %2 None %4
+%6 = OpLabel
+ OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%4 = OpFunction %1 None %2
+%5 = OpLabel
+ OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
"OpLine %str 3 4",
"OpNoLine",
"%func = OpFunction %voidt None %vfunct",
- "OpFunctionEnd",
+ "%l = OpLabel",
+ "OpReturn ; %func return",
+ "OpFunctionEnd ; %func end",
"%func2 = OpFunction %voidt None %viifunct",
"%funcp1 = OpFunctionParameter %intt",
"%funcp2 = OpFunctionParameter %intt",
"%fLabel = OpLabel",
- " OpNop",
- " OpReturn",
+ "OpNop",
+ "OpReturn ; %func2 return",
"OpFunctionEnd"
};
return instructions;
, make_tuple(string("OpTypeVoid") , Range<17, 30>() , Range<0, 25>())
, make_tuple(string("OpTypeFloat") , Range<17, 30>() , Range<0,20>())
, make_tuple(string("OpTypeInt") , Range<17, 30>() , Range<0, 20>())
- , make_tuple(string("OpTypeVector %floatt 4") , Range<17, 30>() , Range<19, 23>())
+ , make_tuple(string("OpTypeVector %floatt 4") , Range<17, 30>() , Range<19, 23>())
, make_tuple(string("OpTypeMatrix %vec4 4") , Range<17, 30>() , Range<22, kRangeEnd>())
, make_tuple(string("OpTypeStruct") , Range<17, 30>() , Range<24, kRangeEnd>())
, make_tuple(string("%vfunct = OpTypeFunction"), Range<17, 30>() , Range<20, 30>())
, make_tuple(string("OpConstant") , Range<17, 30>() , Range<20, kRangeEnd>())
, make_tuple(string("OpLine ") , Range<17, kRangeEnd>() , Range<7, kRangeEnd>())
, make_tuple(string("OpNoLine") , Range<17, kRangeEnd>() , All)
- , make_tuple(string("OpLabel") , Equals<36> , All)
- , make_tuple(string("OpNop") , Equals<37> , All)
- , make_tuple(string("OpReturn") , Equals<38> , All)
+ , make_tuple(string("%fLabel = OpLabel") , Equals<38> , All)
+ , make_tuple(string("OpNop") , Equals<39> , Range<39,kRangeEnd>())
+ , make_tuple(string("OpReturn ; %func2 return") , Equals<40> , All)
)),);
// clang-format on