| // Copyright (c) 2019 Google LLC. | |
| // | |
| // 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. | |
| // Validation tests for non-semantic instructions | |
| #include <string> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "test/unit_spirv.h" | |
| #include "test/val/val_code_generator.h" | |
| #include "test/val/val_fixtures.h" | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| struct TestResult { | |
| TestResult(spv_result_t in_validation_result = SPV_SUCCESS, | |
| const char* in_error_str = nullptr, | |
| const char* in_error_str2 = nullptr) | |
| : validation_result(in_validation_result), | |
| error_str(in_error_str), | |
| error_str2(in_error_str2) {} | |
| spv_result_t validation_result; | |
| const char* error_str; | |
| const char* error_str2; | |
| }; | |
| using ::testing::Combine; | |
| using ::testing::HasSubstr; | |
| using ::testing::Values; | |
| using ::testing::ValuesIn; | |
| using ValidateNonSemanticGenerated = spvtest::ValidateBase< | |
| std::tuple<bool, bool, const char*, const char*, TestResult>>; | |
| using ValidateNonSemanticString = spvtest::ValidateBase<bool>; | |
| CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext, | |
| const bool declare_extinst, | |
| const char* const global_extinsts, | |
| const char* const function_extinsts) { | |
| CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); | |
| if (declare_ext) { | |
| generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n"; | |
| } | |
| if (declare_extinst) { | |
| generator.extensions_ += | |
| "%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n"; | |
| } | |
| generator.after_types_ = global_extinsts; | |
| generator.before_types_ = "%decorate_group = OpDecorationGroup"; | |
| EntryPoint entry_point; | |
| entry_point.name = "main"; | |
| entry_point.execution_model = "Vertex"; | |
| entry_point.body = R"( | |
| )"; | |
| entry_point.body += function_extinsts; | |
| generator.entry_points_.push_back(std::move(entry_point)); | |
| return generator; | |
| } | |
| TEST_P(ValidateNonSemanticGenerated, InTest) { | |
| const bool declare_ext = std::get<0>(GetParam()); | |
| const bool declare_extinst = std::get<1>(GetParam()); | |
| const char* const global_extinsts = std::get<2>(GetParam()); | |
| const char* const function_extinsts = std::get<3>(GetParam()); | |
| const TestResult& test_result = std::get<4>(GetParam()); | |
| CodeGenerator generator = GetNonSemanticCodeGenerator( | |
| declare_ext, declare_extinst, global_extinsts, function_extinsts); | |
| CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); | |
| ASSERT_EQ(test_result.validation_result, | |
| ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| if (test_result.error_str) { | |
| EXPECT_THAT(getDiagnosticString(), | |
| testing::ContainsRegex(test_result.error_str)); | |
| } | |
| if (test_result.error_str2) { | |
| EXPECT_THAT(getDiagnosticString(), | |
| testing::ContainsRegex(test_result.error_str2)); | |
| } | |
| } | |
| INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(false), Values(""), | |
| Values(""), Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| MissingOpExtensionPre1p6, ValidateNonSemanticGenerated, | |
| Combine(Values(false), Values(true), Values(""), Values(""), | |
| Values(TestResult( | |
| SPV_ERROR_INVALID_DATA, | |
| "NonSemantic extended instruction sets cannot be declared " | |
| "without SPV_KHR_non_semantic_info.")))); | |
| INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), Values(""), | |
| Values(""), Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| SimpleGlobalExtInst, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), | |
| Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""), | |
| Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| ComplexGlobalExtInst, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), | |
| Values("%result = OpExtInst %void %extinst 123 %i32 %u32_2 " | |
| "%f32vec4_1234 %u32_0"), | |
| Values(""), Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), Values(""), | |
| Values("%result = OpExtInst %void %extinst 123 %i32"), | |
| Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| FunctionTypeReference, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), | |
| Values("%result = OpExtInst %void %extinst 123 %func"), Values(""), | |
| Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| EntryPointReference, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), Values(""), | |
| Values("%result = OpExtInst %void %extinst 123 %main"), | |
| Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| DecorationGroupReference, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), Values(""), | |
| Values("%result = OpExtInst %void %extinst 123 %decorate_group"), | |
| Values(TestResult()))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| UnknownIDReference, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), | |
| Values("%result = OpExtInst %void %extinst 123 %undefined_id"), | |
| Values(""), | |
| Values(TestResult(SPV_ERROR_INVALID_ID, | |
| "ID .* has not been defined")))); | |
| INSTANTIATE_TEST_SUITE_P( | |
| NonSemanticUseInSemantic, ValidateNonSemanticGenerated, | |
| Combine(Values(true), Values(true), | |
| Values("%result = OpExtInst %f32 %extinst 123 %i32\n" | |
| "%invalid = OpConstantComposite %f32vec2 %f32_0 %result"), | |
| Values(""), | |
| Values(TestResult(SPV_ERROR_INVALID_ID, | |
| "in semantic instruction cannot be a " | |
| "non-semantic instruction")))); | |
| TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpExtension "SPV_KHR_non_semantic_info" | |
| %extinst = OpExtInstImport "NonSemantic.Testing.Set" | |
| %test = OpExtInst %void %extinst 4 %void | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Vertex %main "main" | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| // there's no specific error for using an OpExtInst too early, it requires a | |
| // type so by definition any use of a type in it will be an undefined ID | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("ID '2[%2]' has not been defined")); | |
| } | |
| TEST_F(ValidateNonSemanticString, MissingOpExtensionPost1p6) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| %extinst = OpExtInstImport "NonSemantic.Testing.Set" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %main "main" | |
| OpExecutionMode %main OriginUpperLeft | |
| %void = OpTypeVoid | |
| %test = OpExtInst %void %extinst 3 | |
| %void_fn = OpTypeFunction %void | |
| %main = OpFunction %void None %void_fn | |
| %entry = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); | |
| } | |
| } // namespace | |
| } // namespace val | |
| } // namespace spvtools |