// Copyright (c) 2018 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. #include #include #include #include "gmock/gmock.h" #include "source/spirv_target_env.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" namespace spvtools { namespace val { namespace { using ::testing::Combine; using ::testing::HasSubstr; using ::testing::Values; using ::testing::ValuesIn; using ValidateMode = spvtest::ValidateBase; const std::string kVoidFunction = R"(%void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; TEST_F(ValidateMode, GLComputeNoMode) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateMode, GLComputeNoModeVulkan) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "In the Vulkan environment, GLCompute execution model entry " "points require either the LocalSize or LocalSizeId execution mode " "or an object decorated with WorkgroupSize must be specified.")); } TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpDecorate %int3_1 BuiltIn WorkgroupSize %int = OpTypeInt 32 0 %int3 = OpTypeVector %int 3 %int_1 = OpConstant %int 1 %int3_1 = OpConstantComposite %int3 %int_1 %int_1 %int_1 )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateMode, GLComputeVulkanLocalSize) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT( getDiagnosticString(), HasSubstr("LocalSizeId mode is not allowed by the current environment.")); } TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 CompileSuccessfully(spirv, env); spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginLowerLeft )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-OriginLowerLeft-04653")); EXPECT_THAT(getDiagnosticString(), HasSubstr("In the Vulkan environment, the OriginLowerLeft " "execution mode must not be used.")); } TEST_F(ValidateMode, FragmentPixelCenterIntegerVulkan) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main PixelCenterInteger )" + kVoidFunction; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-PixelCenterInteger-04654")); EXPECT_THAT(getDiagnosticString(), HasSubstr("In the Vulkan environment, the PixelCenterInteger " "execution mode must not be used.")); } TEST_F(ValidateMode, GeometryNoOutputMode) { const std::string spirv = R"( OpCapability Geometry OpMemoryModel Logical GLSL450 OpEntryPoint Geometry %main "main" OpExecutionMode %main InputPoints )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Geometry execution model entry points must specify " "exactly one of OutputPoints, OutputLineStrip or " "OutputTriangleStrip execution modes.")); } TEST_F(ValidateMode, GeometryNoInputMode) { const std::string spirv = R"( OpCapability Geometry OpMemoryModel Logical GLSL450 OpEntryPoint Geometry %main "main" OpExecutionMode %main OutputPoints )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Geometry execution model entry points must specify exactly " "one of InputPoints, InputLines, InputLinesAdjacency, " "Triangles or InputTrianglesAdjacency execution modes.")); } TEST_F(ValidateMode, FragmentNoOrigin) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Fragment execution model entry points require either an " "OriginUpperLeft or OriginLowerLeft execution mode.")); } TEST_F(ValidateMode, FragmentBothOrigins) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main OriginLowerLeft )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Fragment execution model entry points can only specify one of " "OriginUpperLeft or OriginLowerLeft execution modes.")); } TEST_F(ValidateMode, FragmentDepthGreaterAndLess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main DepthGreater OpExecutionMode %main DepthLess )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at " "most one of DepthGreater, DepthLess or DepthUnchanged " "execution modes.")); } TEST_F(ValidateMode, FragmentDepthGreaterAndUnchanged) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main DepthGreater OpExecutionMode %main DepthUnchanged )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at " "most one of DepthGreater, DepthLess or DepthUnchanged " "execution modes.")); } TEST_F(ValidateMode, FragmentDepthLessAndUnchanged) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main DepthLess OpExecutionMode %main DepthUnchanged )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at " "most one of DepthGreater, DepthLess or DepthUnchanged " "execution modes.")); } TEST_F(ValidateMode, FragmentAllDepths) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main DepthGreater OpExecutionMode %main DepthLess OpExecutionMode %main DepthUnchanged )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at " "most one of DepthGreater, DepthLess or DepthUnchanged " "execution modes.")); } TEST_F(ValidateMode, TessellationControlSpacingEqualAndFractionalOdd) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalOdd )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationControlSpacingEqualAndSpacingFractionalEven) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationControlSpacingFractionalOddAndSpacingFractionalEven) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %main "main" OpExecutionMode %main SpacingFractionalOdd OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationControlAllSpacing) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalOdd OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationEvaluationSpacingEqualAndSpacingFractionalOdd) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalOdd )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationEvaluationSpacingEqualAndSpacingFractionalEven) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationEvaluationSpacingFractionalOddAndSpacingFractionalEven) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" OpExecutionMode %main SpacingFractionalOdd OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationEvaluationAllSpacing) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" OpExecutionMode %main SpacingEqual OpExecutionMode %main SpacingFractionalOdd OpExecutionMode %main SpacingFractionalEven )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify " "at most one of SpacingEqual, SpacingFractionalOdd or " "SpacingFractionalEven execution modes.")); } TEST_F(ValidateMode, TessellationControlBothVertex) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %main "main" OpExecutionMode %main VertexOrderCw OpExecutionMode %main VertexOrderCcw )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify at most " "one of VertexOrderCw or VertexOrderCcw execution modes.")); } TEST_F(ValidateMode, TessellationEvaluationBothVertex) { const std::string spirv = R"( OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" OpExecutionMode %main VertexOrderCw OpExecutionMode %main VertexOrderCcw )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Tessellation execution model entry points can specify at most " "one of VertexOrderCw or VertexOrderCcw execution modes.")); } using ValidateModeGeometry = spvtest::ValidateBase, std::tuple>>; TEST_P(ValidateModeGeometry, ExecutionMode) { std::vector input_modes; std::vector output_modes; input_modes.push_back(std::get<0>(std::get<0>(GetParam()))); input_modes.push_back(std::get<1>(std::get<0>(GetParam()))); input_modes.push_back(std::get<2>(std::get<0>(GetParam()))); input_modes.push_back(std::get<3>(std::get<0>(GetParam()))); input_modes.push_back(std::get<4>(std::get<0>(GetParam()))); output_modes.push_back(std::get<0>(std::get<1>(GetParam()))); output_modes.push_back(std::get<1>(std::get<1>(GetParam()))); output_modes.push_back(std::get<2>(std::get<1>(GetParam()))); std::ostringstream sstr; sstr << "OpCapability Geometry\n"; sstr << "OpMemoryModel Logical GLSL450\n"; sstr << "OpEntryPoint Geometry %main \"main\"\n"; size_t num_input_modes = 0; for (auto input : input_modes) { if (!input.empty()) { num_input_modes++; sstr << "OpExecutionMode %main " << input << "\n"; } } size_t num_output_modes = 0; for (auto output : output_modes) { if (!output.empty()) { num_output_modes++; sstr << "OpExecutionMode %main " << output << "\n"; } } sstr << "%void = OpTypeVoid\n"; sstr << "%void_fn = OpTypeFunction %void\n"; sstr << "%int = OpTypeInt 32 0\n"; sstr << "%int1 = OpConstant %int 1\n"; sstr << "%main = OpFunction %void None %void_fn\n"; sstr << "%entry = OpLabel\n"; sstr << "OpReturn\n"; sstr << "OpFunctionEnd\n"; CompileSuccessfully(sstr.str()); if (num_input_modes == 1 && num_output_modes == 1) { EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } else { EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); if (num_input_modes != 1) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Geometry execution model entry points must " "specify exactly one of InputPoints, InputLines, " "InputLinesAdjacency, Triangles or " "InputTrianglesAdjacency execution modes.")); } else { EXPECT_THAT( getDiagnosticString(), HasSubstr("Geometry execution model entry points must specify " "exactly one of OutputPoints, OutputLineStrip or " "OutputTriangleStrip execution modes.")); } } } INSTANTIATE_TEST_SUITE_P( GeometryRequiredModes, ValidateModeGeometry, Combine(Combine(Values("InputPoints", ""), Values("InputLines", ""), Values("InputLinesAdjacency", ""), Values("Triangles", ""), Values("InputTrianglesAdjacency", "")), Combine(Values("OutputPoints", ""), Values("OutputLineStrip", ""), Values("OutputTriangleStrip", "")))); using ValidateModeExecution = spvtest::ValidateBase>; TEST_P(ValidateModeExecution, ExecutionMode) { const spv_result_t expectation = std::get<0>(GetParam()); const std::string error = std::get<1>(GetParam()); const std::string model = std::get<2>(GetParam()); const std::string mode = std::get<3>(GetParam()); const spv_target_env env = std::get<4>(GetParam()); std::ostringstream sstr; sstr << "OpCapability Shader\n"; sstr << "OpCapability Geometry\n"; sstr << "OpCapability Tessellation\n"; sstr << "OpCapability TransformFeedback\n"; if (!spvIsVulkanEnv(env)) { sstr << "OpCapability Kernel\n"; if (env == SPV_ENV_UNIVERSAL_1_3) { sstr << "OpCapability SubgroupDispatch\n"; } else if (env == SPV_ENV_UNIVERSAL_1_5) { sstr << "OpCapability TileImageColorReadAccessEXT\n"; sstr << "OpCapability TileImageDepthReadAccessEXT\n"; sstr << "OpCapability TileImageStencilReadAccessEXT\n"; sstr << "OpExtension \"SPV_EXT_shader_tile_image\"\n"; } } sstr << "OpMemoryModel Logical GLSL450\n"; sstr << "OpEntryPoint " << model << " %main \"main\"\n"; if (mode.find("LocalSizeId") == 0 || mode.find("LocalSizeHintId") == 0 || mode.find("SubgroupsPerWorkgroupId") == 0) { sstr << "OpExecutionModeId %main " << mode << "\n"; } else { sstr << "OpExecutionMode %main " << mode << "\n"; } if (model == "Geometry") { if (!(mode.find("InputPoints") == 0 || mode.find("InputLines") == 0 || mode.find("InputLinesAdjacency") == 0 || mode.find("Triangles") == 0 || mode.find("InputTrianglesAdjacency") == 0)) { // Exactly one of the above modes is required for Geometry shaders. sstr << "OpExecutionMode %main InputPoints\n"; } if (!(mode.find("OutputPoints") == 0 || mode.find("OutputLineStrip") == 0 || mode.find("OutputTriangleStrip") == 0)) { // Exactly one of the above modes is required for Geometry shaders. sstr << "OpExecutionMode %main OutputPoints\n"; } } else if (model == "Fragment") { if (!(mode.find("OriginUpperLeft") == 0 || mode.find("OriginLowerLeft") == 0)) { // Exactly one of the above modes is required for Fragment shaders. sstr << "OpExecutionMode %main OriginUpperLeft\n"; } } sstr << "%void = OpTypeVoid\n"; sstr << "%void_fn = OpTypeFunction %void\n"; sstr << "%int = OpTypeInt 32 0\n"; sstr << "%int1 = OpConstant %int 1\n"; sstr << "%main = OpFunction %void None %void_fn\n"; sstr << "%entry = OpLabel\n"; sstr << "OpReturn\n"; sstr << "OpFunctionEnd\n"; CompileSuccessfully(sstr.str(), env); EXPECT_THAT(expectation, ValidateInstructions(env)); if (expectation != SPV_SUCCESS) { EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); } } INSTANTIATE_TEST_SUITE_P( ValidateModeGeometryOnlyGoodSpv10, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Geometry"), Values("Invocations 3", "InputPoints", "InputLines", "InputLinesAdjacency", "InputTrianglesAdjacency", "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeGeometryOnlyBadSpv10, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with the Geometry " "execution model."), Values("Fragment", "TessellationEvaluation", "TessellationControl", "GLCompute", "Vertex", "Kernel"), Values("Invocations 3", "InputPoints", "InputLines", "InputLinesAdjacency", "InputTrianglesAdjacency", "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeTessellationOnlyGoodSpv10, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("TessellationControl", "TessellationEvaluation"), Values("SpacingEqual", "SpacingFractionalEven", "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", "PointMode", "Quads", "Isolines"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeTessellationOnlyBadSpv10, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with a tessellation " "execution model."), Values("Fragment", "Geometry", "GLCompute", "Vertex", "Kernel"), Values("SpacingEqual", "SpacingFractionalEven", "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", "PointMode", "Quads", "Isolines"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P(ValidateModeGeometryAndTessellationGoodSpv10, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("TessellationControl", "TessellationEvaluation", "Geometry"), Values("Triangles", "OutputVertices 3"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeGeometryAndTessellationBadSpv10, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with a Geometry or " "tessellation execution model."), Values("Fragment", "GLCompute", "Vertex", "Kernel"), Values("Triangles", "OutputVertices 3"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeFragmentOnlyGoodSpv10, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", "EarlyFragmentTests", "DepthReplacing", "DepthLess", "DepthUnchanged"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P( ValidateModeFragmentOnlyBadSpv10, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with the Fragment " "execution model."), Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Kernel"), Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", "EarlyFragmentTests", "DepthReplacing", "DepthGreater", "DepthLess", "DepthUnchanged"), Values(SPV_ENV_UNIVERSAL_1_0))); INSTANTIATE_TEST_SUITE_P(ValidateModeFragmentOnlyGoodSpv15, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), Values("NonCoherentColorAttachmentReadEXT", "NonCoherentDepthAttachmentReadEXT", "NonCoherentStencilAttachmentReadEXT"), Values(SPV_ENV_UNIVERSAL_1_5))); INSTANTIATE_TEST_SUITE_P( ValidateModeFragmentOnlyBadSpv15, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with the Fragment " "execution model."), Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Kernel"), Values("NonCoherentColorAttachmentReadEXT", "NonCoherentDepthAttachmentReadEXT", "NonCoherentStencilAttachmentReadEXT"), Values(SPV_ENV_UNIVERSAL_1_5))); INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Kernel"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( ValidateModeKernelOnlyBadSpv13, ValidateModeExecution, Combine( Values(SPV_ERROR_INVALID_DATA), Values( "Execution mode can only be used with the Kernel execution model."), Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Fragment"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( ValidateModeGLComputeAndKernelGoodSpv13, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Kernel", "GLCompute"), Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( ValidateModeGLComputeAndKernelBadSpv13, ValidateModeExecution, Combine(Values(SPV_ERROR_INVALID_DATA), Values("Execution mode can only be used with a Kernel or GLCompute " "execution model."), Values("Geometry", "TessellationControl", "TessellationEvaluation", "Fragment", "Vertex"), Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( ValidateModeAllGoodSpv13, ValidateModeExecution, Combine(Values(SPV_SUCCESS), Values(""), Values("Kernel", "GLCompute", "Geometry", "TessellationControl", "TessellationEvaluation", "Fragment", "Vertex"), Values("Xfb", "Initializer", "Finalizer", "SubgroupSize 1", "SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); TEST_F(ValidateModeExecution, MeshNVLocalSize) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint MeshNV %main "main" OpExecutionMode %main LocalSize 1 1 1 )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateModeExecution, TaskNVLocalSize) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint TaskNV %main "main" OpExecutionMode %main LocalSize 1 1 1 )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateModeExecution, MeshNVOutputPoints) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint MeshNV %main "main" OpExecutionMode %main OutputPoints )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateModeExecution, MeshNVOutputVertices) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint MeshNV %main "main" OpExecutionMode %main OutputVertices 42 )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateModeExecution, MeshNVLocalSizeId) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint MeshNV %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateModeExecution, TaskNVLocalSizeId) { const std::string spirv = R"( OpCapability Shader OpCapability MeshShadingNV OpExtension "SPV_NV_mesh_shader" OpMemoryModel Logical GLSL450 OpEntryPoint TaskNV %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateModeExecution, ExecModeSubgroupsPerWorkgroupIdBad) { const std::string spirv = R"( OpCapability Shader OpCapability SubgroupDispatch OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionMode %main SubgroupsPerWorkgroupId %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpExecutionMode is only valid when the Mode operand " "is an execution mode that takes no Extra Operands")); } TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdGood) { const std::string spirv = R"( OpCapability Shader OpCapability SubgroupDispatch OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdNonConstantBad) { const std::string spirv = R"( OpCapability Shader OpCapability SubgroupDispatch OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For OpExecutionModeId all Extra Operand ids must be " "constant instructions.")); } TEST_F(ValidateModeExecution, ExecModeLocalSizeHintIdBad) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpExecutionMode is only valid when the Mode operand " "is an execution mode that takes no Extra Operands")); } TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdGood) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdNonConstantBad) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For OpExecutionModeId all Extra Operand ids must be " "constant instructions.")); } TEST_F(ValidateModeExecution, ExecModeLocalSizeIdBad) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" OpExecutionMode %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpExecutionMode is only valid when the Mode operand " "is an execution mode that takes no Extra Operands")); } TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdGood) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdNonConstantBad) { const std::string spirv = R"( OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private )" + kVoidFunction; spv_target_env env = SPV_ENV_UNIVERSAL_1_3; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For OpExecutionModeId all Extra Operand ids must be " "constant instructions.")); } using AllowMultipleExecutionModes = spvtest::ValidateBase; TEST_P(AllowMultipleExecutionModes, DifferentOperand) { const std::string mode = GetParam(); const std::string spirv = R"( OpCapability Shader OpCapability DenormPreserve OpCapability DenormFlushToZero OpCapability SignedZeroInfNanPreserve OpCapability RoundingModeRTE OpCapability RoundingModeRTZ OpExtension "SPV_KHR_float_controls" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionMode %main )" + mode + R"( 16 OpExecutionMode %main )" + mode + R"( 32 %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_P(AllowMultipleExecutionModes, SameOperand) { const std::string mode = GetParam(); const std::string spirv = R"( OpCapability Shader OpCapability DenormPreserve OpCapability DenormFlushToZero OpCapability SignedZeroInfNanPreserve OpCapability RoundingModeRTE OpCapability RoundingModeRTZ OpExtension "SPV_KHR_float_controls" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionMode %main )" + mode + R"( 32 OpExecutionMode %main )" + mode + R"( 32 %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("execution mode must not be specified multiple times " "for the same entry point and operands")); } INSTANTIATE_TEST_SUITE_P(MultipleFloatControlsExecModes, AllowMultipleExecutionModes, Values("DenormPreserve", "DenormFlushToZero", "SignedZeroInfNanPreserve", "RoundingModeRTE", "RoundingModeRTZ")); using MultipleExecModes = spvtest::ValidateBase; TEST_P(MultipleExecModes, DuplicateMode) { const std::string mode = GetParam(); const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main )" + mode + R"( OpExecutionMode %main )" + mode + R"( %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("execution mode must not be specified multiple times " "per entry point")); } INSTANTIATE_TEST_SUITE_P(MultipleFragmentExecMode, MultipleExecModes, Values("DepthReplacing", "DepthGreater", "DepthLess", "DepthUnchanged")); TEST_F(ValidateMode, FloatControls2FPFastMathDefaultSameOperand) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %none OpExecutionModeId %main FPFastMathDefault %float %none %void = OpTypeVoid %float = OpTypeFloat 32 %int = OpTypeInt 32 0 %none = OpConstant %int 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); EXPECT_THAT(getDiagnosticString(), HasSubstr("execution mode must not be specified multiple times " "for the same entry point and operands")); } TEST_F(ValidateMode, FloatControls2FPFastMathDefaultDifferentOperand) { const std::string spirv = R"( OpCapability Shader OpCapability Float16 OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %none OpExecutionModeId %main FPFastMathDefault %half %none %void = OpTypeVoid %float = OpTypeFloat 32 %int = OpTypeInt 32 0 %none = OpConstant %int 0 %half = OpTypeFloat 16 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); } TEST_F(ValidateMode, FragmentShaderInterlockVertexBad) { const std::string spirv = R"( OpCapability Shader OpCapability FragmentShaderPixelInterlockEXT OpExtension "SPV_EXT_fragment_shader_interlock" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpExecutionMode %main PixelInterlockOrderedEXT )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Execution mode can only be used with the Fragment execution model")); } TEST_F(ValidateMode, FragmentShaderInterlockTooManyModesBad) { const std::string spirv = R"( OpCapability Shader OpCapability FragmentShaderPixelInterlockEXT OpCapability FragmentShaderSampleInterlockEXT OpExtension "SPV_EXT_fragment_shader_interlock" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main PixelInterlockOrderedEXT OpExecutionMode %main SampleInterlockOrderedEXT )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at most " "one fragment shader interlock execution mode")); } TEST_F(ValidateMode, FragmentShaderInterlockNoModeBad) { const std::string spirv = R"( OpCapability Shader OpCapability FragmentShaderPixelInterlockEXT OpExtension "SPV_EXT_fragment_shader_interlock" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %void = OpTypeVoid %void_fn = OpTypeFunction %void %func = OpFunction %void None %void_fn %entryf = OpLabel OpBeginInvocationInterlockEXT OpEndInvocationInterlockEXT OpReturn OpFunctionEnd %main = OpFunction %void None %void_fn %entry = OpLabel %1 = OpFunctionCall %void %func OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT require a " "fragment shader interlock execution mode")); } TEST_F(ValidateMode, FragmentShaderInterlockGood) { const std::string spirv = R"( OpCapability Shader OpCapability FragmentShaderPixelInterlockEXT OpExtension "SPV_EXT_fragment_shader_interlock" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main PixelInterlockOrderedEXT %void = OpTypeVoid %void_fn = OpTypeFunction %void %func = OpFunction %void None %void_fn %entryf = OpLabel OpBeginInvocationInterlockEXT OpEndInvocationInterlockEXT OpReturn OpFunctionEnd %main = OpFunction %void None %void_fn %entry = OpLabel %1 = OpFunctionCall %void %func OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) { const std::string spirv = R"( OpCapability Shader OpCapability StencilExportEXT OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" OpExtension "SPV_EXT_shader_stencil_export" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main EarlyAndLateFragmentTestsAMD OpExecutionMode %main StencilRefLessFrontAMD OpExecutionMode %main StencilRefGreaterFrontAMD )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at most " "one of StencilRefUnchangedFrontAMD, " "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD " "execution modes.")); } TEST_F(ValidateMode, FragmentShaderStencilRefBackTooManyModesBad) { const std::string spirv = R"( OpCapability Shader OpCapability StencilExportEXT OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" OpExtension "SPV_EXT_shader_stencil_export" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main EarlyAndLateFragmentTestsAMD OpExecutionMode %main StencilRefLessBackAMD OpExecutionMode %main StencilRefGreaterBackAMD )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Fragment execution model entry points can specify at most " "one of StencilRefUnchangedBackAMD, " "StencilRefLessBackAMD or StencilRefGreaterBackAMD " "execution modes.")); } TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) { const std::string spirv = R"( OpCapability Shader OpCapability StencilExportEXT OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" OpExtension "SPV_EXT_shader_stencil_export" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main EarlyAndLateFragmentTestsAMD OpExecutionMode %main StencilRefLessFrontAMD )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateMode, FragmentShaderStencilRefBackGood) { const std::string spirv = R"( OpCapability Shader OpCapability StencilExportEXT OpExtension "SPV_AMD_shader_early_and_late_fragment_tests" OpExtension "SPV_EXT_shader_stencil_export" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpExecutionMode %main EarlyAndLateFragmentTestsAMD OpExecutionMode %main StencilRefLessBackAMD )" + kVoidFunction; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) { const std::string spirv = R"( OpCapability Shader OpCapability DemoteToHelperInvocationEXT OpExtension "SPV_EXT_demote_to_helper_invocation" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %bool = OpTypeBool %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpDemoteToHelperInvocationEXT %1 = OpIsHelperInvocationEXT %bool OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpDemoteToHelperInvocationEXT requires Fragment execution model")); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpIsHelperInvocationEXT requires Fragment execution model")); } TEST_F(ValidateMode, FragmentShaderDemoteGood) { const std::string spirv = R"( OpCapability Shader OpCapability DemoteToHelperInvocationEXT OpExtension "SPV_EXT_demote_to_helper_invocation" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %bool = OpTypeBool %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpDemoteToHelperInvocationEXT %1 = OpIsHelperInvocationEXT %bool OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateMode, FragmentShaderDemoteBadType) { const std::string spirv = R"( OpCapability Shader OpCapability DemoteToHelperInvocationEXT OpExtension "SPV_EXT_demote_to_helper_invocation" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %u32 = OpTypeInt 32 0 %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpDemoteToHelperInvocationEXT %1 = OpIsHelperInvocationEXT %u32 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected bool scalar type as Result Type")); } TEST_F(ValidateMode, LocalSizeIdVulkan1p3DoesNotRequireOption) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } TEST_F(ValidateMode, MaximalReconvergenceRequiresExtension) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionMode %main MaximallyReconvergesKHR %void = OpTypeVoid %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("(6023) requires one of these extensions: " "SPV_KHR_maximal_reconvergence ")); } TEST_F(ValidateMode, FPFastMathDefaultNotExecutionModeId) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionMode %main FPFastMathDefault %int_0 %int_0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpExecutionMode is only valid when the Mode operand " "is an execution mode that takes no Extra Operands, or " "takes Extra Operands that are not id operands")); } TEST_F(ValidateMode, FPFastMathDefaultNotAType) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %int_0 %int_0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "The Target Type operand must be a floating-point scalar type")); } TEST_F(ValidateMode, FPFastMathDefaultNotAFloatType) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %int %int_0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "The Target Type operand must be a floating-point scalar type")); } TEST_F(ValidateMode, FPFastMathDefaultNotAFloatScalarType) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float2 %int_0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %float = OpTypeFloat 32 %float2 = OpTypeVector %float 2 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "The Target Type operand must be a floating-point scalar type")); } TEST_F(ValidateMode, FPFastMathDefaultSpecConstant) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %int_0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpSpecConstant %int 0 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("The Fast Math Default operand must be a " "non-specialization constant")); } TEST_F(ValidateMode, FPFastMathDefaultInvalidMask) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 524288 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The Fast Math Default operand is an invalid bitmask value")); } TEST_F(ValidateMode, FPFastMathDefaultContainsFast) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 16 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("The Fast Math Default operand must not include Fast")); } TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowReassoc) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 327680 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The Fast Math Default operand must include AllowContract and " "AllowReassoc when AllowTransform is specified")); } TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowContract) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 393216 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The Fast Math Default operand must include AllowContract and " "AllowReassoc when AllowTransform is specified")); } TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingContractAndReassoc) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 262144 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The Fast Math Default operand must include AllowContract and " "AllowReassoc when AllowTransform is specified")); } TEST_F(ValidateMode, FPFastMathDefaultSignedZeroInfNanPreserve) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpCapability SignedZeroInfNanPreserve OpExtension "SPV_KHR_float_controls2" OpExtension "SPV_KHR_float_controls" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main SignedZeroInfNanPreserve 32 %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("FPFastMathDefault and SignedZeroInfNanPreserve execution " "modes cannot be applied to the same entry point")); } TEST_F(ValidateMode, FPFastMathDefaultConractionOff) { const std::string spirv = R"( OpCapability Kernel OpCapability Addresses OpCapability FloatControls2 OpCapability SignedZeroInfNanPreserve OpExtension "SPV_KHR_float_controls2" OpExtension "SPV_KHR_float_controls" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main ContractionOff %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("FPFastMathDefault and ContractionOff execution modes " "cannot be applied to the same entry point")); } TEST_F(ValidateMode, FPFastMathDefaultNoContractionNotInCallTree) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main LocalSize 1 1 1 OpDecorate %add NoContraction %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %zero %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main LocalSize 1 1 1 OpDecorate %add NoContraction %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %void %func OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %zero %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("NoContraction cannot be used by an entry point with " "the FPFastMathDefault execution mode")); } TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree2) { const std::string spirv = R"( OpCapability Shader OpCapability Kernel OpCapability Addresses OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpDecorate %const NoContraction %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %const = OpSpecConstantOp %float FAdd %zero %zero %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %void %func OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %const %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("NoContraction cannot be used by an entry point with " "the FPFastMathDefault execution mode")); } TEST_F(ValidateMode, FPFastMathDefaultFastMathFastNotInCallTree) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main LocalSize 1 1 1 OpDecorate %add FPFastMathMode Fast %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %zero %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree) { const std::string spirv = R"( OpCapability Shader OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpExecutionMode %main LocalSize 1 1 1 OpDecorate %add FPFastMathMode Fast %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %void %func OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %zero %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("FPFastMathMode Fast cannot be used by an entry point " "with the FPFastMathDefault execution mode")); } TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree2) { const std::string spirv = R"( OpCapability Kernel OpCapability Addresses OpCapability FloatControls2 OpExtension "SPV_KHR_float_controls2" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %main "main" OpExecutionModeId %main FPFastMathDefault %float %constant OpDecorate %const FPFastMathMode Fast %void = OpTypeVoid %int = OpTypeInt 32 0 %constant = OpConstant %int 0 %float = OpTypeFloat 32 %zero = OpConstant %float 0 %const = OpSpecConstantOp %float FAdd %zero %zero %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %void %func OpReturn OpFunctionEnd %func = OpFunction %void None %void_fn %func_entry = OpLabel %add = OpFAdd %float %const %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("FPFastMathMode Fast cannot be used by an entry point " "with the FPFastMathDefault execution mode")); } TEST_F(ValidateMode, FragmentShaderRequireFullQuadsKHR) { const std::string spirv = R"( OpCapability Shader OpCapability GroupNonUniform OpCapability GroupNonUniformVote OpCapability GroupNonUniformBallot OpCapability QuadControlKHR OpExtension "SPV_KHR_quad_control" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %4 "main" OpExecutionMode %4 OriginUpperLeft OpExecutionMode %4 RequireFullQuadsKHR OpDecorate %17 Location 0 OpDecorate %31 BuiltIn HelperInvocation OpDecorate %40 Location 0 OpDecorate %44 DescriptorSet 0 OpDecorate %44 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 0 %7 = OpTypeVector %6 4 %8 = OpTypePointer Function %7 %10 = OpTypeBool %11 = OpConstantTrue %10 %12 = OpConstant %6 7 %14 = OpTypeFloat 32 %15 = OpTypeVector %14 4 %16 = OpTypePointer Output %15 %17 = OpVariable %16 Output %18 = OpConstant %14 1 %19 = OpConstant %14 0 %20 = OpConstantComposite %15 %18 %19 %19 %18 %23 = OpConstant %6 4 %27 = OpConstant %6 1 %28 = OpTypePointer Output %14 %30 = OpTypePointer Input %10 %31 = OpVariable %30 Input %36 = OpConstant %6 2 %38 = OpTypeVector %14 2 %39 = OpTypePointer Input %38 %40 = OpVariable %39 Input %41 = OpTypeImage %14 2D 0 0 0 1 Unknown %42 = OpTypeSampledImage %41 %43 = OpTypePointer UniformConstant %42 %44 = OpVariable %43 UniformConstant %4 = OpFunction %2 None %3 %5 = OpLabel %9 = OpVariable %8 Function %13 = OpGroupNonUniformBallot %7 %12 %11 OpStore %9 %13 OpStore %17 %20 %21 = OpLoad %7 %9 %22 = OpGroupNonUniformBallotBitCount %6 %12 Reduce %21 %24 = OpIEqual %10 %22 %23 OpSelectionMerge %26 None OpBranchConditional %24 %25 %26 %25 = OpLabel %29 = OpAccessChain %28 %17 %27 OpStore %29 %18 OpBranch %26 %26 = OpLabel %32 = OpLoad %10 %31 %33 = OpGroupNonUniformAny %10 %12 %32 OpSelectionMerge %35 None OpBranchConditional %33 %34 %35 %34 = OpLabel %37 = OpAccessChain %28 %17 %36 OpStore %37 %18 OpBranch %35 %35 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Execution mode can only be used with the Fragment execution model")); } TEST_F(ValidateMode, FragmentShaderQuadDerivativesKHR) { const std::string spirv = R"( OpCapability Shader OpCapability GroupNonUniform OpCapability GroupNonUniformVote OpCapability QuadControlKHR OpExtension "SPV_KHR_quad_control" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %4 "main" OpExecutionMode %4 OriginUpperLeft OpExecutionMode %4 QuadDerivativesKHR OpDecorate %12 BuiltIn FragCoord OpDecorate %41 Location 0 OpDecorate %45 DescriptorSet 0 OpDecorate %45 Binding 0 OpDecorate %49 Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeBool %7 = OpTypePointer Function %6 %9 = OpTypeFloat 32 %10 = OpTypeVector %9 4 %11 = OpTypePointer Input %10 %12 = OpVariable %11 Input %13 = OpTypeInt 32 0 %14 = OpConstant %13 1 %15 = OpTypePointer Input %9 %18 = OpConstant %9 8.5 %21 = OpConstant %9 0.100000001 %25 = OpConstant %13 0 %28 = OpConstant %9 3.5 %30 = OpConstant %9 6 %36 = OpConstant %13 7 %40 = OpTypePointer Output %10 %41 = OpVariable %40 Output %42 = OpTypeImage %9 2D 0 0 0 1 Unknown %43 = OpTypeSampledImage %42 %44 = OpTypePointer UniformConstant %43 %45 = OpVariable %44 UniformConstant %47 = OpTypeVector %9 2 %48 = OpTypePointer Input %47 %49 = OpVariable %48 Input %53 = OpConstant %9 0.899999976 %54 = OpConstant %9 0.200000003 %55 = OpConstant %9 1 %56 = OpConstantComposite %10 %53 %54 %54 %55 %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %16 = OpAccessChain %15 %12 %14 %17 = OpLoad %9 %16 %19 = OpFSub %9 %17 %18 %20 = OpExtInst %9 %1 FAbs %19 %22 = OpFOrdLessThan %6 %20 %21 OpSelectionMerge %24 None OpBranchConditional %22 %23 %24 %23 = OpLabel %26 = OpAccessChain %15 %12 %25 %27 = OpLoad %9 %26 %29 = OpFSub %9 %27 %28 %31 = OpFMod %9 %29 %30 %33 = OpFOrdLessThan %6 %31 %21 OpBranch %24 %24 = OpLabel %34 = OpPhi %6 %22 %5 %33 %23 OpStore %8 %34 %35 = OpLoad %6 %8 %37 = OpGroupNonUniformAny %6 %36 %35 OpSelectionMerge %39 None OpBranchConditional %37 %38 %52 %38 = OpLabel %46 = OpLoad %43 %45 %50 = OpLoad %47 %49 %51 = OpImageSampleImplicitLod %10 %46 %50 OpStore %41 %51 OpBranch %39 %52 = OpLabel OpStore %41 %56 OpBranch %39 %39 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Execution mode can only be used with the Fragment execution model")); } } // namespace } // namespace val } // namespace spvtools