blob: 019d91ad71fdb9a44deb613429f5e41365deca25 [file] [log] [blame]
// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// 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 "test/test_fixture.h"
#include "test/unit_spirv.h"
#include "test/val/val_fixtures.h"
// NOTE: The tests in this file are ONLY testing ID usage, there for the input
// SPIR-V does not follow the logical layout rules from the spec in all cases in
// order to makes the tests smaller. Validation of the whole module is handled
// in stages, ID validation is only one of these stages. All validation stages
// are stand alone.
namespace spvtools {
namespace val {
namespace {
using spvtest::ScopedContext;
using ::testing::HasSubstr;
using ::testing::ValuesIn;
using ValidateIdWithMessage = spvtest::ValidateBase<bool>;
std::string kOpCapabilitySetupWithoutVector16 = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability Int8
OpCapability Int16
OpCapability Int64
OpCapability Float64
OpCapability LiteralSampler
OpCapability Pipes
OpCapability DeviceEnqueue
)";
std::string kOpCapabilitySetup = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability Int8
OpCapability Int16
OpCapability Int64
OpCapability Float64
OpCapability LiteralSampler
OpCapability Pipes
OpCapability DeviceEnqueue
OpCapability Vector16
)";
std::string kOpVariablePtrSetUp = R"(
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
)";
std::string kGLSL450MemoryModel =
kOpCapabilitySetup + kOpVariablePtrSetUp + R"(
OpMemoryModel Logical GLSL450
)";
std::string kGLSL450MemoryModelWithoutVector16 =
kOpCapabilitySetupWithoutVector16 + kOpVariablePtrSetUp + R"(
OpMemoryModel Logical GLSL450
)";
std::string kNoKernelGLSL450MemoryModel = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability Int8
OpCapability Int16
OpCapability Int64
OpCapability Float64
OpMemoryModel Logical GLSL450
)";
std::string kOpenCLMemoryModel32 = R"(
OpCapability Addresses
OpCapability Linkage
OpCapability Kernel
%1 = OpExtInstImport "OpenCL.std"
OpMemoryModel Physical32 OpenCL
)";
std::string kOpenCLMemoryModel64 = R"(
OpCapability Addresses
OpCapability Linkage
OpCapability Kernel
OpCapability Int64
%1 = OpExtInstImport "OpenCL.std"
OpMemoryModel Physical64 OpenCL
)";
std::string sampledImageSetup = R"(
%void = OpTypeVoid
%typeFuncVoid = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%image_type = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_img = OpTypePointer UniformConstant %image_type
%tex = OpVariable %_ptr_UniformConstant_img UniformConstant
%sampler_type = OpTypeSampler
%_ptr_UniformConstant_sam = OpTypePointer UniformConstant %sampler_type
%s = OpVariable %_ptr_UniformConstant_sam UniformConstant
%sampled_image_type = OpTypeSampledImage %image_type
%v2float = OpTypeVector %float 2
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%const_vec_1_1 = OpConstantComposite %v2float %float_1 %float_1
%const_vec_2_2 = OpConstantComposite %v2float %float_2 %float_2
%bool_type = OpTypeBool
%spec_true = OpSpecConstantTrue %bool_type
%main = OpFunction %void None %typeFuncVoid
%label_1 = OpLabel
%image_inst = OpLoad %image_type %tex
%sampler_inst = OpLoad %sampler_type %s
)";
std::string BranchConditionalSetup = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
; type definitions
%bool = OpTypeBool
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
; constants
%true = OpConstantTrue %bool
%i0 = OpConstant %int 0
%i1 = OpConstant %int 1
%f0 = OpConstant %float 0
%f1 = OpConstant %float 1
; main function header
%void = OpTypeVoid
%voidfunc = OpTypeFunction %void
%main = OpFunction %void None %voidfunc
%lmain = OpLabel
)";
std::string BranchConditionalTail = R"(
%target_t = OpLabel
OpNop
OpBranch %end
%target_f = OpLabel
OpNop
OpBranch %end
%end = OpLabel
OpReturn
OpFunctionEnd
)";
// TODO: OpUndef
TEST_F(ValidateIdWithMessage, OpName) {
std::string spirv = kGLSL450MemoryModel + R"(
OpName %2 "name"
%1 = OpTypeInt 32 0
%2 = OpTypePointer UniformConstant %1
%3 = OpVariable %2 UniformConstant)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpMemberNameGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberName %2 0 "foo"
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpMemberNameTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberName %1 0 "foo"
%1 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpMemberName Type <id> '1[%uint]' is not a struct type."));
}
TEST_F(ValidateIdWithMessage, OpMemberNameMemberBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberName %1 1 "foo"
%2 = OpTypeInt 32 0
%1 = OpTypeStruct %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpMemberName Member <id> '1[%_struct_1]' index is larger "
"than Type <id> '1[%_struct_1]'s member count."));
}
TEST_F(ValidateIdWithMessage, OpLineGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpString "/path/to/source.file"
OpLine %1 0 0
%2 = OpTypeInt 32 0
%3 = OpTypePointer Input %2
%4 = OpVariable %3 Input)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpLineFileBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
OpLine %1 0 0
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpLine Target <id> '1[%uint]' is not an OpString."));
}
TEST_F(ValidateIdWithMessage, OpDecorateGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %2 GLSLShared
%1 = OpTypeInt 64 0
%2 = OpTypeStruct %1 %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpDecorateBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 GLSLShared)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("forward referenced IDs have not been defined"));
}
TEST_F(ValidateIdWithMessage, OpMemberDecorateGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %2 0 RelaxedPrecision
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1 %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpMemberDecorateBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 0 RelaxedPrecision
%1 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpMemberDecorate Structure type <id> '1[%uint]' is "
"not a struct type."));
}
TEST_F(ValidateIdWithMessage, OpMemberDecorateMemberBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 3 RelaxedPrecision
%int = OpTypeInt 32 0
%1 = OpTypeStruct %int %int)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Index 3 provided in OpMemberDecorate for struct <id> "
"1[%_struct_1] is out of bounds. The structure has 2 "
"members. Largest valid index is 1."));
}
TEST_F(ValidateIdWithMessage, OpGroupDecorateGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
OpDecorate %1 RelaxedPrecision
OpDecorate %1 GLSLShared
OpGroupDecorate %1 %3 %4
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
%4 = OpConstant %2 23)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpDecorationGroupBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
OpDecorate %1 RelaxedPrecision
OpDecorate %1 GLSLShared
OpMemberDecorate %1 0 Constant
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Result id of OpDecorationGroup can only "
"be targeted by OpName, OpGroupDecorate, "
"OpDecorate, OpDecorateId, and OpGroupMemberDecorate"));
}
TEST_F(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpGroupDecorate %1 %2 %3
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpGroupDecorate Decoration group <id> '1[%1]' is not "
"a decoration group."));
}
TEST_F(ValidateIdWithMessage, OpGroupDecorateTargetBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
OpDecorate %1 RelaxedPrecision
OpDecorate %1 GLSLShared
OpGroupDecorate %1 %3
%2 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("forward referenced IDs have not been defined"));
}
TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpGroupMemberDecorate %1 %2 0
%2 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpGroupMemberDecorate Decoration group <id> '1[%1]' "
"is not a decoration group."));
}
TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
OpGroupMemberDecorate %1 %2 0
%2 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpGroupMemberDecorate Structure type <id> '2[%uint]' "
"is not a struct type."));
}
TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 Offset 0
%1 = OpDecorationGroup
OpGroupMemberDecorate %1 %struct 3
%float = OpTypeFloat 32
%struct = OpTypeStruct %float %float %float
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Index 3 provided in OpGroupMemberDecorate for struct "
"<id> 2[%_struct_2] is out of bounds. The structure "
"has 3 members. Largest valid index is 2."));
}
// TODO: OpExtInst
TEST_F(ValidateIdWithMessage, OpEntryPointGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %3 ""
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpEntryPointFunctionBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %1 ""
%1 = OpTypeVoid)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpEntryPoint Entry Point <id> '1[%void]' is not a "
"function."));
}
TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %3 ""
%1 = OpTypeVoid
%2 = OpTypeFunction %1 %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpEntryPoint Entry Point <id> '1[%1]'s function "
"parameter count is not zero"));
}
TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %3 ""
%1 = OpTypeInt 32 0
%ret = OpConstant %1 0
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturnValue %ret
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpEntryPoint Entry Point <id> '1[%1]'s function "
"return type is not void."));
}
TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability Geometry
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %main "main" %ptr_builtin_1
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
%int = OpTypeInt 32 1
%void = OpTypeVoid
%func = OpTypeFunction %void
%struct_1 = OpTypeStruct %int
%ptr_builtin_1 = OpTypePointer Input %struct_1
%main = OpFunction %void None %func
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Interfaces passed to OpEntryPoint must be of type "
"OpTypeVariable. Found OpTypePointer."));
}
TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability Geometry
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %main "main" %in_1
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
%int = OpTypeInt 32 1
%void = OpTypeVoid
%func = OpTypeFunction %void
%struct_1 = OpTypeStruct %int
%ptr_builtin_1 = OpTypePointer Uniform %struct_1
%in_1 = OpVariable %ptr_builtin_1 Uniform
%main = OpFunction %void None %func
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpEntryPoint interfaces must be OpVariables with "
"Storage Class of Input(1) or Output(3). Found Storage "
"Class 2 for Entry Point id 1."));
}
TEST_F(ValidateIdWithMessage, OpExecutionModeGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %3 ""
OpExecutionMode %3 LocalSize 1 1 1
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) {
std::string spirv = kGLSL450MemoryModel + R"(
OpExecutionMode %3 LocalSize 1 1 1
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpExecutionMode Entry Point <id> '1[%1]' is not the "
"Entry Point operand of an OpEntryPoint."));
}
TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpEntryPoint GLCompute %3 "" %a
OpExecutionMode %a LocalSize 1 1 1
%void = OpTypeVoid
%ptr = OpTypePointer Input %void
%a = OpVariable %ptr Input
%2 = OpTypeFunction %void
%3 = OpFunction %void None %2
%4 = OpLabel
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpExecutionMode Entry Point <id> '2[%2]' is not the "
"Entry Point operand of an OpEntryPoint."));
}
TEST_F(ValidateIdWithMessage, OpTypeVectorFloat) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeVectorInt) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeVector %1 4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeVectorUInt) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 64 0
%2 = OpTypeVector %1 4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeVectorBool) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpTypeVector %1 4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypePointer UniformConstant %1
%3 = OpTypeVector %2 4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeVector Component Type <id> "
"'2[%_ptr_UniformConstant_float]' is not a scalar type."));
}
TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Illegal number of components (1) for TypeVector\n %v1float = "
"OpTypeVector %float 1\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Illegal number of components (5) for TypeVector\n %v5float = "
"OpTypeVector %float 5\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) {
std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 8)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Having 8 components for TypeVector requires the Vector16 "
"capability\n %v8float = OpTypeVector %float 8\n"));
}
TEST_F(ValidateIdWithMessage,
OpTypeVectorColumnCountSixteenWithoutVector16Bad) {
std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Having 16 components for TypeVector requires the Vector16 "
"capability\n %v16float = OpTypeVector %float 16\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 8)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage,
OpTypeVectorColumnCountOfSixteenWithVector16Good) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeMatrixGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeMatrix %1 3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("olumns in a matrix must be of type vector.\n %mat3float = "
"OpTypeMatrix %float 3\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 16 0
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Matrix types can only be parameterized with floating-point "
"types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Matrix types can only be parameterized as having only 2, 3, "
"or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 8)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Matrix types can only be parameterized as having only 2, 3, "
"or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n"));
}
TEST_F(ValidateIdWithMessage, OpTypeSamplerGood) {
// In Rev31, OpTypeSampler takes no arguments.
std::string spirv = kGLSL450MemoryModel + R"(
%s = OpTypeSampler)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeArrayGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1
%3 = OpTypeArray %1 %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeArrayElementTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1
%3 = OpTypeArray %2 %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '2[%uint_1]' is not a "
"type."));
}
// Signed or unsigned.
enum Signed { kSigned, kUnsigned };
// Creates an assembly module declaring OpTypeArray with the given length.
std::string MakeArrayLength(const std::string& len, Signed isSigned, int width,
int max_int_width = 64,
bool use_vulkan_memory_model = false) {
std::ostringstream ss;
ss << R"(
OpCapability Shader
)";
if (use_vulkan_memory_model) {
ss << " OpCapability VulkanMemoryModel\n";
}
if (width == 16) {
ss << " OpCapability Int16\n";
}
if (max_int_width > 32) {
ss << "\n OpCapability Int64\n";
}
if (use_vulkan_memory_model) {
ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
ss << "OpMemoryModel Logical Vulkan\n";
} else {
ss << "OpMemoryModel Logical GLSL450\n";
}
ss << "OpEntryPoint GLCompute %main \"main\"\n";
ss << "OpExecutionMode %main LocalSize 1 1 1\n";
ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0");
ss << " %l = OpConstant %t " << len;
ss << " %a = OpTypeArray %t %l";
ss << " %void = OpTypeVoid \n"
" %voidfn = OpTypeFunction %void \n"
" %main = OpFunction %void None %voidfn \n"
" %entry = OpLabel\n"
" OpReturn\n"
" OpFunctionEnd\n";
return ss.str();
}
// Tests OpTypeArray. Parameter is the width (in bits) of the array-length's
// type.
class OpTypeArrayLengthTest
: public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> {
protected:
OpTypeArrayLengthTest()
: env_(SPV_ENV_UNIVERSAL_1_0),
position_(spv_position_t{0, 0, 0}),
diagnostic_(spvDiagnosticCreate(&position_, "")) {}
~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); }
// Runs spvValidate() on v, printing any errors via spvDiagnosticPrint().
spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") {
spv_const_binary_t cbinary{v.data(), v.size()};
spvDiagnosticDestroy(diagnostic_);
diagnostic_ = nullptr;
const auto status =
spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_);
if (status != SPV_SUCCESS) {
spvDiagnosticPrint(diagnostic_);
EXPECT_THAT(std::string(diagnostic_->error),
testing::ContainsRegex(expected_err));
}
return status;
}
protected:
spv_target_env env_;
private:
spv_position_t position_; // For creating diagnostic_.
spv_diagnostic diagnostic_;
};
TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) {
const int width = GetParam();
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width))));
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("1", kUnsigned, width))));
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("2", kSigned, width))));
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("2", kUnsigned, width))));
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("55", kSigned, width))));
EXPECT_EQ(SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("55", kUnsigned, width))));
const std::string fpad(width / 4 - 1, 'F');
EXPECT_EQ(
SPV_SUCCESS,
Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))))
<< MakeArrayLength("0x7" + fpad, kSigned, width);
}
TEST_P(OpTypeArrayLengthTest, LengthZero) {
const int width = GetParam();
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
}
TEST_P(OpTypeArrayLengthTest, LengthNegative) {
const int width = GetParam();
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0');
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)),
"OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
"least 1."));
}
// Returns the string form of an integer of the form 0x80....0 of the
// given bit width.
std::string big_num_ending_0(int bit_width) {
return "0x8" + std::string(bit_width / 4 - 1, '0');
}
// Returns the string form of an integer of the form 0x80..001 of the
// given bit width.
std::string big_num_ending_1(int bit_width) {
return "0x8" + std::string(bit_width / 4 - 2, '0') + "1";
}
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) {
env_ = SPV_ENV_VULKAN_1_0;
const int width = GetParam();
for (int max_int_width : {32, 64}) {
if (width > max_int_width) {
// Not valid to even make the OpConstant in this case.
continue;
}
const auto module = CompileSuccessfully(MakeArrayLength(
big_num_ending_0(width), kUnsigned, width, max_int_width));
EXPECT_EQ(SPV_SUCCESS, Val(module));
}
}
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) {
env_ = SPV_ENV_VULKAN_1_0;
const int width = GetParam();
for (int max_int_width : {32, 64}) {
if (width > max_int_width) {
// Not valid to even make the OpConstant in this case.
continue;
}
const auto module = CompileSuccessfully(MakeArrayLength(
big_num_ending_1(width), kUnsigned, width, max_int_width));
EXPECT_EQ(SPV_SUCCESS, Val(module));
}
}
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) {
env_ = SPV_ENV_WEBGPU_0;
const int width = GetParam();
// WebGPU only has 32 bit integers.
if (width != 32) return;
const int max_int_width = 32;
const auto module = CompileSuccessfully(MakeArrayLength(
big_num_ending_0(width), kUnsigned, width, max_int_width, true));
EXPECT_EQ(SPV_SUCCESS, Val(module));
}
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) {
env_ = SPV_ENV_WEBGPU_0;
const int width = GetParam();
// WebGPU only has 32 bit integers.
if (width != 32) return;
const int max_int_width = 32;
const auto module = CompileSuccessfully(MakeArrayLength(
big_num_ending_1(width), kUnsigned, width, max_int_width, true));
EXPECT_EQ(SPV_ERROR_INVALID_ID,
Val(module,
"OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value "
"2147483648 permitted by WebGPU: got 2147483649"));
}
// The only valid widths for integers are 8, 16, 32, and 64.
// Since the Int8 capability requires the Kernel capability, and the Kernel
// capability prohibits usage of signed integers, we can skip 8-bit integers
// here since the purpose of these tests is to check the validity of
// OpTypeArray, not OpTypeInt.
INSTANTIATE_TEST_SUITE_P(Widths, OpTypeArrayLengthTest,
ValuesIn(std::vector<int>{16, 32, 64}));
TEST_F(ValidateIdWithMessage, OpTypeArrayLengthNull) {
std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%len = OpConstantNull %i32
%ary = OpTypeArray %i32 %len)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeArray Length <id> '2[%2]' default value must be at least 1."));
}
TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) {
std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%len = OpSpecConstant %i32 2
%ary = OpTypeArray %i32 %len)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) {
std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%c1 = OpConstant %i32 1
%c2 = OpConstant %i32 2
%len = OpSpecConstantOp %i32 IAdd %c1 %c2
%ary = OpTypeArray %i32 %len)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeRuntimeArray %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypeRuntimeArray %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeRuntimeArray Element Type <id> '2[%uint_0]' is not a "
"type."));
}
// TODO: Object of this type can only be created with OpVariable using the
// Unifrom Storage Class
TEST_F(ValidateIdWithMessage, OpTypeStructGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 64
%3 = OpTypePointer Input %1
%4 = OpTypeStruct %1 %2 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeStructMemberTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 64
%3 = OpConstant %2 0.0
%4 = OpTypeStruct %1 %2 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypeStruct Member Type <id> '3[%double_0]' is not "
"a type."));
}
TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
%1 = OpTypeSampler
%2 = OpTypeStruct %1
%void = OpTypeVoid
%3 = OpTypeFunction %void
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypeStruct must not contain an opaque type"));
}
TEST_F(ValidateIdWithMessage, OpTypePointerGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypePointerBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypePointer Input %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypePointer Type <id> '2[%uint_0]' is not a "
"type."));
}
TEST_F(ValidateIdWithMessage, OpTypeFunctionGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypeFunction %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypeFunction Return Type <id> '2[%uint_0]' is not "
"a type."));
}
TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 0
%4 = OpTypeFunction %1 %2 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeFunction Parameter Type <id> '3[%uint_0]' is not a "
"type."));
}
TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%4 = OpTypeFunction %1 %2 %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpTypeFunction Parameter Type <id> '1[%void]' cannot "
"be OpTypeVoid."));
}
TEST_F(ValidateIdWithMessage, OpTypePipeGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16
%3 = OpTypePipe ReadOnly)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantTrueGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpConstantTrue %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantTrueBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantTrue %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantTrue Result Type <id> '1[%void]' is not a boolean "
"type."));
}
TEST_F(ValidateIdWithMessage, OpConstantFalseGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpConstantTrue %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantFalseBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantFalse %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantFalse Result Type <id> '1[%void]' is not a boolean "
"type."));
}
TEST_F(ValidateIdWithMessage, OpConstantGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstant !1 !0)";
// The expected failure code is implementation dependent (currently
// INVALID_BINARY because the binary parser catches these cases) and may
// change over time, but this must always fail.
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%4 = OpConstantComposite %2 %3 %3 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%9 = OpUndef %1
%4 = OpConstantComposite %2 %3 %3 %3 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%4 = OpConstantComposite %1 %3 %3 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantComposite Result Type <id> '1[%float]' is not a "
"composite type."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpConstant %1 3.14
%5 = OpConstant %4 42 ; bad type for constant value
%6 = OpConstantComposite %2 %3 %5 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '5[%uint_42]'s type "
"does not match Result Type <id> '2[%v4float]'s vector "
"element type."));
}
TEST_F(ValidateIdWithMessage,
OpConstantCompositeVectorConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpConstant %1 3.14
%5 = OpUndef %4 ; bad type for undef value
%6 = OpConstantComposite %2 %3 %5 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '5[%5]'s type does not "
"match Result Type <id> '2[%v4float]'s vector element type."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpConstant %1 0.0
%6 = OpConstantComposite %2 %4 %5 %5 %5
%7 = OpConstantComposite %2 %5 %4 %5 %5
%8 = OpConstantComposite %2 %5 %5 %4 %5
%9 = OpConstantComposite %2 %5 %5 %5 %4
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpConstant %1 0.0
%6 = OpConstantComposite %2 %4 %5 %5 %5
%7 = OpConstantComposite %2 %5 %4 %5 %5
%8 = OpConstantComposite %2 %5 %5 %4 %5
%9 = OpUndef %2
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%11 = OpTypeVector %1 3
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpConstant %1 0.0
%6 = OpConstantComposite %2 %4 %5 %5 %5
%7 = OpConstantComposite %2 %5 %4 %5 %5
%8 = OpConstantComposite %2 %5 %5 %4 %5
%9 = OpConstantComposite %11 %5 %5 %5
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '10[%10]' vector "
"component count does not match Result Type <id> "
"'4[%mat4v4float]'s vector component count."));
}
TEST_F(ValidateIdWithMessage,
OpConstantCompositeMatrixConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%11 = OpTypeVector %1 3
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpConstant %1 0.0
%6 = OpConstantComposite %2 %4 %5 %5 %5
%7 = OpConstantComposite %2 %5 %4 %5 %5
%8 = OpConstantComposite %2 %5 %5 %4 %5
%9 = OpUndef %11
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '10[%10]' vector "
"component count does not match Result Type <id> "
"'4[%mat4v4float]'s vector component count."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%9 = OpUndef %1
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %1)"; // Uses a type as operand
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%uint] cannot be a "
"type"));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypePointer Uniform %1
%5 = OpVariable %4 Uniform
%6 = OpConstantComposite %3 %2 %2 %2 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '5[%5]' is not a "
"constant or undef."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpConstant %5 3.14 ; bad type for const value
%4 = OpConstantComposite %3 %2 %2 %2 %6)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> "
"'5[%float_3_1400001]'s type does not match Result "
"Type <id> '3[%_arr_uint_uint_4]'s array element "
"type."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpUndef %5 ; bad type for undef
%4 = OpConstantComposite %3 %2 %2 %2 %6)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> "
"'5[%5]'s type does not match Result "
"Type <id> '3[%_arr_uint_uint_4]'s array element "
"type."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeStructGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpConstant %2 4300000000
%6 = OpConstantComposite %3 %4 %4 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpUndef %2
%6 = OpConstantComposite %3 %4 %4 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpConstant %2 4300000000
%6 = OpConstantComposite %3 %4 %5 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> "
"'5[%ulong_4300000000]' type does not match the "
"Result Type <id> '3[%_struct_3]'s member type."));
}
TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpUndef %2
%6 = OpConstantComposite %3 %4 %5 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantComposite Constituent <id> '5[%5]' type "
"does not match the Result Type <id> '3[%_struct_3]'s "
"member type."));
}
TEST_F(ValidateIdWithMessage, OpConstantSamplerGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%float = OpTypeFloat 32
%samplerType = OpTypeSampler
%3 = OpConstantSampler %samplerType ClampToEdge 0 Nearest)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpConstantSampler %1 Clamp 0 Nearest)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpConstantSampler Result Type <id> '1[%float]' is not a sampler "
"type."));
}
TEST_F(ValidateIdWithMessage, OpConstantNullGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpConstantNull %1
%3 = OpTypeInt 32 0
%4 = OpConstantNull %3
%5 = OpTypeFloat 32
%6 = OpConstantNull %5
%7 = OpTypePointer UniformConstant %3
%8 = OpConstantNull %7
%9 = OpTypeEvent
%10 = OpConstantNull %9
%11 = OpTypeDeviceEvent
%12 = OpConstantNull %11
%13 = OpTypeReserveId
%14 = OpConstantNull %13
%15 = OpTypeQueue
%16 = OpConstantNull %15
%17 = OpTypeVector %5 2
%18 = OpConstantNull %17
%19 = OpTypeMatrix %17 2
%20 = OpConstantNull %19
%25 = OpConstant %3 8
%21 = OpTypeArray %3 %25
%22 = OpConstantNull %21
%23 = OpTypeStruct %3 %5 %1
%24 = OpConstantNull %23
%26 = OpTypeArray %17 %25
%27 = OpConstantNull %26
%28 = OpTypeStruct %7 %26 %26 %1
%29 = OpConstantNull %28
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpConstantNullBasicBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantNull %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpConstantNull Result Type <id> '1[%void]' cannot have a null "
"value."));
}
TEST_F(ValidateIdWithMessage, OpConstantNullArrayBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%2 = OpTypeInt 32 0
%3 = OpTypeSampler
%4 = OpConstant %2 4
%5 = OpTypeArray %3 %4
%6 = OpConstantNull %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpConstantNull Result Type <id> '4[%_arr_2_uint_4]' cannot have a "
"null value."));
}
TEST_F(ValidateIdWithMessage, OpConstantNullStructBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%2 = OpTypeSampler
%3 = OpTypeStruct %2 %2
%4 = OpConstantNull %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpConstantNull Result Type <id> '2[%_struct_2]' "
"cannot have a null value."));
}
TEST_F(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%array = OpTypeRuntimeArray %bool
%null = OpConstantNull %array)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpConstantNull Result Type <id> '2[%_runtimearr_bool]' cannot have "
"a null value."));
}
TEST_F(ValidateIdWithMessage, OpSpecConstantTrueGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpSpecConstantTrue %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpSpecConstantTrueBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstantTrue %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantTrue Result Type <id> '1[%void]' is not "
"a boolean type"));
}
TEST_F(ValidateIdWithMessage, OpSpecConstantFalseGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpSpecConstantFalse %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpSpecConstantFalseBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstantFalse %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpSpecConstantFalse Result Type <id> '1[%void]' is not "
"a boolean type"));
}
TEST_F(ValidateIdWithMessage, OpSpecConstantGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpSpecConstant %1 42)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpSpecConstantBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstant !1 !4)";
// The expected failure code is implementation dependent (currently
// INVALID_BINARY because the binary parser catches these cases) and may
// change over time, but this must always fail.
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Type Id 1 is not a scalar numeric type"));
}
// Valid: SpecConstantComposite specializes to a vector.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%4 = OpConstant %1 3.14
%5 = OpSpecConstantComposite %2 %3 %3 %4 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Valid: Vector of floats and Undefs.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%5 = OpConstant %1 3.14
%9 = OpUndef %1
%4 = OpSpecConstantComposite %2 %3 %5 %3 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: result type is float.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%4 = OpSpecConstantComposite %1 %3 %3 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a composite type"));
}
// Invalid: Vector contains a mix of Int and Float.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpSpecConstant %1 3.14
%5 = OpConstant %4 42 ; bad type for constant value
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> "
"'5[%uint_42]'s type does not match Result Type <id> "
"'2[%v4float]'s vector element type."));
}
// Invalid: Constituent is not a constant
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeVectorConstituentNotConstantBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeInt 32 0
%4 = OpSpecConstant %1 3.14
%5 = OpTypePointer Uniform %1
%6 = OpVariable %5 Uniform
%7 = OpSpecConstantComposite %2 %6 %4 %4 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '6[%6]' is "
"not a constant or undef."));
}
// Invalid: Vector contains a mix of Undef-int and Float.
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeVectorConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpSpecConstant %1 3.14
%5 = OpUndef %4 ; bad type for undef value
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]'s "
"type does not match Result Type <id> '2[%v4float]'s "
"vector element type."));
}
// Invalid: Vector expects 3 components, but 4 specified.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 3
%3 = OpConstant %1 3.14
%5 = OpSpecConstant %1 4.0
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> count does "
"not match Result Type <id> '2[%v3float]'s vector "
"component count."));
}
// Valid: 4x4 matrix of floats
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpSpecConstant %1 0.0
%6 = OpSpecConstantComposite %2 %4 %5 %5 %5
%7 = OpSpecConstantComposite %2 %5 %4 %5 %5
%8 = OpSpecConstantComposite %2 %5 %5 %4 %5
%9 = OpSpecConstantComposite %2 %5 %5 %5 %4
%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Valid: Matrix in which one column is Undef
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeMatrix %2 4
%4 = OpConstant %1 1.0
%5 = OpSpecConstant %1 0.0
%6 = OpSpecConstantComposite %2 %4 %5 %5 %5
%7 = OpSpecConstantComposite %2 %5 %4 %5 %5
%8 = OpSpecConstantComposite %2 %5 %5 %4 %5
%9 = OpUndef %2
%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: Matrix in which the sizes of column vectors are not equal.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeVector %1 3
%4 = OpTypeMatrix %2 4
%5 = OpSpecConstant %1 1.0
%6 = OpConstant %1 0.0
%7 = OpSpecConstantComposite %2 %5 %6 %6 %6
%8 = OpSpecConstantComposite %2 %6 %5 %6 %6
%9 = OpSpecConstantComposite %2 %6 %6 %5 %6
%10 = OpSpecConstantComposite %3 %6 %6 %6
%11 = OpSpecConstantComposite %4 %7 %8 %9 %10)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '10[%10]' "
"vector component count does not match Result Type "
"<id> '4[%mat4v4float]'s vector component count."));
}
// Invalid: Matrix type expects 4 columns but only 3 specified.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeMatrix %2 4
%4 = OpSpecConstant %1 1.0
%5 = OpConstant %1 0.0
%6 = OpSpecConstantComposite %2 %4 %5 %5 %5
%7 = OpSpecConstantComposite %2 %5 %4 %5 %5
%8 = OpSpecConstantComposite %2 %5 %5 %4 %5
%10 = OpSpecConstantComposite %3 %6 %7 %8)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> count does "
"not match Result Type <id> '3[%mat4v4float]'s matrix column "
"count."));
}
// Invalid: Composite contains a non-const/undef component
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeMatrixConstituentNotConstBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpConstant %1 0.0
%3 = OpTypeVector %1 4
%4 = OpTypeMatrix %3 4
%5 = OpSpecConstantComposite %3 %2 %2 %2 %2
%6 = OpTypePointer Uniform %1
%7 = OpVariable %6 Uniform
%8 = OpSpecConstantComposite %4 %5 %5 %5 %7)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '7[%7]' is "
"not a constant or undef."));
}
// Invalid: Composite contains a column that is *not* a vector (it's an array)
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeInt 32 0
%3 = OpSpecConstant %2 4
%4 = OpConstant %1 0.0
%5 = OpTypeVector %1 4
%6 = OpTypeArray %2 %3
%7 = OpTypeMatrix %5 4
%8 = OpSpecConstantComposite %6 %3 %3 %3 %3
%9 = OpSpecConstantComposite %5 %4 %4 %4 %4
%10 = OpSpecConstantComposite %7 %9 %9 %9 %8)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '8[%8]' type "
"does not match Result Type <id> '7[%mat4v4float]'s "
"matrix column type."));
}
// Invalid: Matrix with an Undef column of the wrong size.
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeMatrixConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeVector %1 3
%4 = OpTypeMatrix %2 4
%5 = OpSpecConstant %1 1.0
%6 = OpSpecConstant %1 0.0
%7 = OpSpecConstantComposite %2 %5 %6 %6 %6
%8 = OpSpecConstantComposite %2 %6 %5 %6 %6
%9 = OpSpecConstantComposite %2 %6 %6 %5 %6
%10 = OpUndef %3
%11 = OpSpecConstantComposite %4 %7 %8 %9 %10)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '10[%10]' "
"vector component count does not match Result Type "
"<id> '4[%mat4v4float]'s vector component count."));
}
// Invalid: Matrix in which some columns are Int and some are Float.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 32
%3 = OpTypeVector %1 2
%4 = OpTypeVector %2 2
%5 = OpTypeMatrix %4 2
%6 = OpSpecConstant %1 42
%7 = OpConstant %2 3.14
%8 = OpSpecConstantComposite %3 %6 %6
%9 = OpSpecConstantComposite %4 %7 %7
%10 = OpSpecConstantComposite %5 %8 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '8[%8]' "
"component type does not match Result Type <id> "
"'5[%mat2v2float]'s matrix column component type."));
}
// Valid: Array of integers
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%5 = OpConstant %1 5
%3 = OpTypeArray %1 %2
%6 = OpTypeArray %1 %5
%4 = OpSpecConstantComposite %3 %2 %2 %2 %2
%7 = OpSpecConstantComposite %3 %5 %5 %5 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: Expecting an array of 4 components, but 3 specified.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpSpecConstantComposite %3 %2 %2 %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent count does not "
"match Result Type <id> '3[%_arr_uint_uint_4]'s array "
"length."));
}
// Valid: Array of Integers and Undef-int
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%9 = OpUndef %1
%3 = OpTypeArray %1 %2
%4 = OpSpecConstantComposite %3 %2 %2 %2 %9)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: Array uses a type as operand.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypePointer Uniform %1
%5 = OpVariable %4 Uniform
%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]' is "
"not a constant or undef."));
}
// Invalid: Array has a mix of Int and Float components.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypeFloat 32
%5 = OpSpecConstant %4 3.14 ; bad type for const value
%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]'s "
"type does not match Result Type <id> "
"'3[%_arr_uint_uint_4]'s array element type."));
}
// Invalid: Array has a mix of Int and Undef-float.
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeArrayConstituentUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpUndef %5 ; bad type for undef
%4 = OpSpecConstantComposite %3 %2 %2 %2 %6)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]'s "
"type does not match Result Type <id> "
"'3[%_arr_uint_2]'s array element type."));
}
// Valid: Struct of {Int32,Int32,Int64}.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpSpecConstant %2 4300000000
%6 = OpSpecConstantComposite %3 %4 %4 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: missing one int32 struct member.
TEST_F(ValidateIdWithMessage,
OpSpecConstantCompositeStructMissingComponentBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%3 = OpTypeStruct %1 %1 %1
%4 = OpConstant %1 42
%5 = OpSpecConstant %1 430
%6 = OpSpecConstantComposite %3 %4 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> "
"'2[%_struct_2]' count does not match Result Type "
"<id> '2[%_struct_2]'s struct member count."));
}
// Valid: Struct uses Undef-int64.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpSpecConstantComposite %3 %4 %4 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Invalid: Composite contains non-const/undef component.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpTypePointer Uniform %1
%7 = OpVariable %6 Uniform
%8 = OpSpecConstantComposite %3 %4 %7 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '7[%7]' is "
"not a constant or undef."));
}
// Invalid: Struct component type does not match expected specialization type.
// Second component was expected to be Int32, but got Int64.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpSpecConstant %2 4300000000
%6 = OpSpecConstantComposite %3 %4 %5 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]' type "
"does not match the Result Type <id> '3[%_struct_3]'s "
"member type."));
}
// Invalid: Undef-int64 used when Int32 was expected.
TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpSpecConstantComposite %3 %4 %5 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '5[%5]' type "
"does not match the Result Type <id> '3[%_struct_3]'s "
"member type."));
}
// TODO: OpSpecConstantOp
TEST_F(ValidateIdWithMessage, OpVariableGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1
%3 = OpVariable %2 Input)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableInitializerConstantGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1
%3 = OpConstant %1 42
%4 = OpVariable %2 Input %3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Uniform %1
%3 = OpVariable %2 Uniform
%4 = OpTypePointer Private %2 ; pointer to pointer
%5 = OpVariable %4 Private %3
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// TODO: Positive test OpVariable with OpConstantNull of OpTypePointer
TEST_F(ValidateIdWithMessage, OpVariableResultTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpVariable %1 Input)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpVariable Result Type <id> '1[%uint]' is not a pointer "
"type."));
}
TEST_F(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1
%3 = OpVariable %2 Input %2)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 2[%_ptr_Input_uint] "
"cannot be a type"));
}
TEST_F(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ptrint = OpTypePointer Function %int
%ptrptrint = OpTypePointer Function %ptrint
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%var = OpVariable %ptrint Function
%varinit = OpVariable %ptrptrint Function %var ; Can't initialize function variable.
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpVariable Initializer <id> '8[%8]' is not a constant "
"or module-scope variable"));
}
TEST_F(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ptrint = OpTypePointer Uniform %int
%mvar = OpVariable %ptrint Uniform
%ptrptrint = OpTypePointer Function %ptrint
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%goodvar = OpVariable %ptrptrint Function %mvar ; This is ok
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableContainsBoolBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%int = OpTypeInt 32 0
%block = OpTypeStruct %bool %int
%_ptr_Uniform_block = OpTypePointer Uniform %block
%var = OpVariable %_ptr_Uniform_block Uniform
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %block %var
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("If OpTypeBool is stored in conjunction with OpVariable"
", it can only be used with non-externally visible "
"shader Storage Classes: Workgroup, CrossWorkgroup, "
"Private, and Function"));
}
TEST_F(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%boolptr = OpTypePointer Uniform %bool
%int = OpTypeInt 32 0
%block = OpTypeStruct %boolptr %int
%_ptr_Uniform_block = OpTypePointer Uniform %block
%var = OpVariable %_ptr_Uniform_block Uniform
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %block %var
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %input 0 BuiltIn FrontFacing
%bool = OpTypeBool
%input = OpTypeStruct %bool
%_ptr_input = OpTypePointer Input %input
%var = OpVariable %_ptr_input Input
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %input %var
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) {
std::string spirv = R"(
OpCapability RayTracingNV
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_NV_ray_tracing"
OpMemoryModel Logical GLSL450
%bool = OpTypeBool
%PerRayData = OpTypeStruct %bool
%_ptr_PerRayData = OpTypePointer RayPayloadNV %PerRayData
%var = OpVariable %_ptr_PerRayData RayPayloadNV
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %PerRayData %var
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"In Logical addressing, variables may not allocate a pointer type"));
}
TEST_F(ValidateIdWithMessage,
OpVariablePointerNoVariablePointersRelaxedLogicalGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";
auto options = getValidatorOptions();
options->relax_logical_pointer = true;
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) {
// DXC generates code that looks like when given something like:
// T t;
// t.s.fn_1();
// This needs to be accepted before legalization takes place, so we
// will include it with the relaxed logical pointer.
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
OpSource HLSL 600
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%void = OpTypeVoid
%9 = OpTypeFunction %void
%_struct_5 = OpTypeStruct
%_struct_6 = OpTypeStruct %_struct_5
%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5
%23 = OpTypeFunction %void %_ptr_Function__struct_5
%1 = OpFunction %void None %9
%10 = OpLabel
%11 = OpVariable %_ptr_Function__struct_6 Function
%20 = OpAccessChain %_ptr_Function__struct_5 %11 %int_0
%21 = OpFunctionCall %void %12 %20
OpReturn
OpFunctionEnd
%12 = OpFunction %void None %23
%13 = OpFunctionParameter %_ptr_Function__struct_5
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
auto options = getValidatorOptions();
options->relax_logical_pointer = true;
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage,
OpVariablePointerVariablePointersStorageBufferGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointersStorageBuffer
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) {
const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int
%var = OpVariable %_ptr_uniform_ptr Uniform
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("In Logical addressing with variable pointers, "
"variables that allocate pointers must be in Function "
"or Private storage classes"));
}
TEST_F(ValidateIdWithMessage, OpLoadGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpVariable %3 UniformConstant
%6 = OpFunction %1 None %4
%7 = OpLabel
%8 = OpLoad %2 %5
OpReturn
OpFunctionEnd