SPIRV-Tools/test/val/val_modes_test.cpp
alan-baker 02470f606f
Validate duplicate decorations and execution modes (#5641)
* Disallow duplicate decorations generally

* Only FuncParamAttr and UserSemantic can be applied to the same target
  multiple times
* Unchecked: completely duplicate UserSemantic and FuncParamAttr

* Disallow duplicate execution modes generally
  * Exceptions for float controls, float controls2 and some intel
    execution modes

* Fix invalid fuzzer transforms
2024-04-12 08:51:41 -04:00

2214 lines
72 KiB
C++

// 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 <sstream>
#include <string>
#include <vector>
#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<bool>;
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<
std::tuple<std::string, std::string, std::string, std::string, std::string>,
std::tuple<std::string, std::string, std::string>>>;
TEST_P(ValidateModeGeometry, ExecutionMode) {
std::vector<std::string> input_modes;
std::vector<std::string> 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<std::tuple<spv_result_t, std::string, std::string,
std::string, spv_target_env>>;
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<std::string>;
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<std::string>;
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