blob: 4cb2384b11e276aa65b9b499a305ea1a98c9b39e [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 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 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[foo]' 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[foo]' index is larger than "
"Type <id> '1[foo]'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' 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 Uniform
%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 Uniform
%1 = OpTypeInt 32 0)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpMemberDecorate Structure type <id> '1' is not a struct type."));
}
TEST_F(ValidateIdWithMessage, OpMemberDecorateMemberBad) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 3 Uniform
%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 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 Uniform
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 Uniform
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, 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' is not a "
"decoration group."));
}
TEST_F(ValidateIdWithMessage, OpGroupDecorateTargetBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
OpDecorate %1 Uniform
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' 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' 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 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' 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'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'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' 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' 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' is not a scalar type."));
}
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, OpTypeMatrixColumnTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeMatrix %1 3)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Columns in a matrix must be of type vector."));
}
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' is not a type."));
}
// Signed or unsigned.
enum Signed { kSigned, kUnsigned };
// Creates an assembly snippet declaring OpTypeArray with the given length.
std::string MakeArrayLength(const std::string& len, Signed isSigned,
int width) {
std::ostringstream ss;
ss << R"(
OpCapability Shader
OpCapability Linkage
OpCapability Int16
OpCapability Int64
)";
ss << "OpMemoryModel Logical GLSL450\n";
ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0");
ss << " %l = OpConstant %t " << len;
ss << " %a = OpTypeArray %t %l";
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()
: 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()};
const auto status =
spvValidate(ScopedContext().context, &cbinary, &diagnostic_);
if (status != SPV_SUCCESS) {
spvDiagnosticPrint(diagnostic_);
EXPECT_THAT(std::string(diagnostic_->error), HasSubstr(expected_err));
}
return status;
}
private:
spv_position_t position_; // For creating diagnostic_.
spv_diagnostic diagnostic_;
};
TEST_P(OpTypeArrayLengthTest, LengthPositive) {
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))));
EXPECT_EQ(SPV_SUCCESS, Val(CompileSuccessfully(
MakeArrayLength("0xF" + fpad, kUnsigned, width))));
}
TEST_P(OpTypeArrayLengthTest, LengthZero) {
const int width = GetParam();
EXPECT_EQ(
SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)),
"OpTypeArray Length <id> '2' default value must be at least 1."));
EXPECT_EQ(
SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)),
"OpTypeArray Length <id> '2' 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> '2' default value must be at least 1."));
EXPECT_EQ(
SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)),
"OpTypeArray Length <id> '2' default value must be at least 1."));
EXPECT_EQ(
SPV_ERROR_INVALID_ID,
Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)),
"OpTypeArray Length <id> '2' 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> '2' default value must be at least 1."));
}
// 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_CASE_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' 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' 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' is not a 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' 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' 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' is not a type."));
}
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' 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' 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' 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's type does not match "
"Result Type <id> '2'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's type does not match "
"Result Type <id> '2'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' vector "
"component count does not match Result Type <id> '4'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' vector "
"component count does not match Result Type <id> '4'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, OpConstantCompositeArrayConstConstituentBad) {
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("OpConstantComposite Constituent <id> '1' 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's type does "
"not match Result Type <id> '3'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's type does "
"not match Result Type <id> '3'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' type does "
"not match the Result Type <id> '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' type does "
"not match the Result Type <id> '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' 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' 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' 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' 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' 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("Specialization constant must be 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("Specialization constant must be 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's type "
"does not match Result Type <id> '2'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
%6 = OpSpecConstantComposite %2 %3 %4 %4 %4)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '3' 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's type "
"does not match Result Type <id> '2'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'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' vector "
"component count does not match Result Type <id> '4'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'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 = OpSpecConstantComposite %4 %5 %5 %5 %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '1' is not a "
"constant composite 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' type "
"does not match Result Type <id> '7'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' vector "
"component count does not match Result Type <id> '4'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' "
"component type does not match Result Type <id> '5'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'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 = OpSpecConstantComposite %3 %2 %2 %2 %1)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '1' 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's type "
"does not match Result Type <id> '3'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's type "
"does not match Result Type <id> '3'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' count "
"does not match Result Type <id> '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 = OpSpecConstantComposite %3 %4 %1 %5)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpSpecConstantComposite Constituent <id> '1' 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' type "
"does not match the Result Type <id> '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' type "
"does not match the Result Type <id> '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 Uniform %2 ; pointer to pointer
%5 = OpVariable %4 Uniform %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' 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("OpVariable Initializer <id> '2' is not a constant or "
"module-scope variable"));
}
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' 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, 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
%9 = OpReturn
%10 = OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// TODO: Add tests that exercise VariablePointersStorageBuffer instead of
// VariablePointers.
void createVariablePointerSpirvProgram(std::ostringstream* spirv,
std::string result_strategy,
bool use_varptr_cap,
bool add_helper_function) {
*spirv << "OpCapability Shader ";
if (use_varptr_cap) {
*spirv << "OpCapability VariablePointers ";
*spirv << "OpExtension \"SPV_KHR_variable_pointers\" ";
}
*spirv << R"(
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
%void = OpTypeVoid
%voidf = OpTypeFunction %void
%bool = OpTypeBool
%i32 = OpTypeInt 32 1
%f32 = OpTypeFloat 32
%f32ptr = OpTypePointer Uniform %f32
%i = OpConstant %i32 1
%zero = OpConstant %i32 0
%float_1 = OpConstant %f32 1.0
%ptr1 = OpVariable %f32ptr Uniform
%ptr2 = OpVariable %f32ptr Uniform
)";
if (add_helper_function) {
*spirv << R"(
; ////////////////////////////////////////////////////////////
;;;; Function that returns a pointer
; ////////////////////////////////////////////////////////////
%selector_func_type = OpTypeFunction %f32ptr %bool %f32ptr %f32ptr
%choose_input_func = OpFunction %f32ptr None %selector_func_type
%is_neg_param = OpFunctionParameter %bool
%first_ptr_param = OpFunctionParameter %f32ptr
%second_ptr_param = OpFunctionParameter %f32ptr
%selector_func_begin = OpLabel
%result_ptr = OpSelect %f32ptr %is_neg_param %first_ptr_param %second_ptr_param
OpReturnValue %result_ptr
OpFunctionEnd
)";
}
*spirv << R"(
%main = OpFunction %void None %voidf
%label = OpLabel
)";
*spirv << result_strategy;
*spirv << R"(
OpReturn
OpFunctionEnd
)";
}
// With the VariablePointer Capability, OpLoad should allow loading a
// VaiablePointer. In this test the variable pointer is obtained by an OpSelect
TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) {
std::string result_strategy = R"(
%isneg = OpSLessThan %bool %i %zero
%varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2
%result = OpLoad %f32 %varptr
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(&spirv, result_strategy,
true /* Add VariablePointers Capability? */,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Without the VariablePointers Capability, OpLoad will not allow loading
// through a variable pointer.
// Disabled since using OpSelect with pointers without VariablePointers will
// fail LogicalsPass.
TEST_F(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) {
std::string result_strategy = R"(
%isneg = OpSLessThan %bool %i %zero
%varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2
%result = OpLoad %f32 %varptr
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(&spirv, result_strategy,
false /* Add VariablePointers Capability?*/,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer."));
}
// With the VariablePointer Capability, OpLoad should allow loading a
// VaiablePointer. In this test the variable pointer is obtained by an OpPhi
TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) {
std::string result_strategy = R"(
%is_neg = OpSLessThan %bool %i %zero
OpSelectionMerge %end_label None
OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2
%take_ptr_1 = OpLabel
OpBranch %end_label
%take_ptr_2 = OpLabel
OpBranch %end_label
%end_label = OpLabel
%varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2
%result = OpLoad %f32 %varptr
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(&spirv, result_strategy,
true /* Add VariablePointers Capability?*/,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Without the VariablePointers Capability, OpPhi can have a pointer result
// type.
TEST_F(ValidateIdWithMessage, OpPhiBad) {
std::string result_strategy = R"(
%is_neg = OpSLessThan %bool %i %zero
OpSelectionMerge %end_label None
OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2
%take_ptr_1 = OpLabel
OpBranch %end_label
%take_ptr_2 = OpLabel
OpBranch %end_label
%end_label = OpLabel
%varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2
%result = OpLoad %f32 %varptr
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(&spirv, result_strategy,
false /* Add VariablePointers Capability?*/,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Using pointers with OpPhi requires capability "
"VariablePointers or VariablePointersStorageBuffer"));
}
// With the VariablePointer Capability, OpLoad should allow loading through a
// VaiablePointer. In this test the variable pointer is obtained from an
// OpFunctionCall (return value from a function)
TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) {
std::ostringstream spirv;
std::string result_strategy = R"(
%isneg = OpSLessThan %bool %i %zero
%varptr = OpFunctionCall %f32ptr %choose_input_func %isneg %ptr1 %ptr2
%result = OpLoad %f32 %varptr
)";
createVariablePointerSpirvProgram(&spirv, result_strategy,
true /* Add VariablePointers Capability?*/,
true /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpLoadResultTypeBad) {
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 %3 %5
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpLoad Result Type <id> '3' does not match Pointer "
"<id> '5's type."));
}
TEST_F(ValidateIdWithMessage, OpLoadPointerBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpFunction %1 None %4
%6 = OpLabel
%7 = OpLoad %2 %8
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
// Prove that SSA checks trigger for a bad Id value.
// The next test case show the not-a-logical-pointer case.
EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 8 has not been defined"));
}
// Disabled as bitcasting type to object is now not valid.
TEST_F(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFloat 32
%4 = OpTypePointer UniformConstant %2
%5 = OpTypePointer UniformConstant %3
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
%9 = OpBitcast %5 %4 ; Not valid in logical addressing
%10 = OpLoad %3 %9 ; Should trigger message
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
// Once we start checking bitcasts, we might catch that
// as the error first, instead of catching it here.
// I don't know if it's possible to generate a bad case
// if/when the validator is complete.
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpLoad Pointer <id> '9' is not a logical pointer."));
}
TEST_F(ValidateIdWithMessage, OpStoreGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 UniformConstant
%7 = OpFunction %1 None %4
%8 = OpLabel
OpStore %6 %5
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpStorePointerBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 UniformConstant
%7 = OpFunction %1 None %4
%8 = OpLabel
OpStore %3 %5
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpStore Pointer <id> '3' is not a logical pointer."));
}
// Disabled as bitcasting type to object is now not valid.
TEST_F(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFloat 32
%4 = OpTypePointer UniformConstant %2
%5 = OpTypePointer UniformConstant %3
%6 = OpTypeFunction %1
%7 = OpConstantNull %5
%8 = OpFunction %1 None %6
%9 = OpLabel
%10 = OpBitcast %5 %4 ; Not valid in logical addressing
%11 = OpStore %10 %7 ; Should trigger message
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpStore Pointer <id> '10' is not a logical pointer."));
}
// Without the VariablePointer Capability, OpStore should may not store
// through a variable pointer.
// Disabled since using OpSelect with pointers without VariablePointers will
// fail LogicalsPass.
TEST_F(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) {
std::string result_strategy = R"(
%isneg = OpSLessThan %bool %i %zero
%varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2
OpStore %varptr %float_1
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(
&spirv, result_strategy, false /* Add VariablePointers Capability? */,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer."));
}
// With the VariablePointer Capability, OpStore should allow storing through a
// variable pointer.
TEST_F(ValidateIdWithMessage, OpStoreVarPtrGood) {
std::string result_strategy = R"(
%isneg = OpSLessThan %bool %i %zero
%varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2
OpStore %varptr %float_1
)";
std::ostringstream spirv;
createVariablePointerSpirvProgram(&spirv, result_strategy,
true /* Add VariablePointers Capability? */,
false /* Use Helper Function? */);
CompileSuccessfully(spirv.str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpStoreObjectGood) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 UniformConstant
%7 = OpFunction %1 None %4
%8 = OpLabel
%9 = OpUndef %1
OpStore %6 %9
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpStore Object <id> '9's type is void."));
}
TEST_F(ValidateIdWithMessage, OpStoreTypeBad) {
std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%9 = OpTypeFloat 32
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %9 3.14
%6 = OpVariable %3 UniformConstant
%7 = OpFunction %1 None %4
%8 = OpLabel
OpStore %6 %5
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpStore Pointer <id> '7's type does not match Object "
"<id> '6's type."));
}
// The next series of test check test a relaxation of the rules for stores to
// structs. The first test checks that we get a failure when the option is not
// set to relax the rule.
// TODO: Add tests for layout compatible arrays and matricies when the validator
// relaxes the rules for them as well. Also need test to check for layout
// decorations specific to those types.
TEST_F(ValidateIdWithMessage, OpStoreTypeBadStruct) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 0 Offset 0
OpMemberDecorate %1 1 Offset 4
OpMemberDecorate %2 0 Offset 0
OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
OpStore %8 %11
OpReturn
OpFunctionEnd)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("OpStore Pointer <id> '8's type does not match Object "
"<id> '11's type."));
}
// Same code as the last test. The difference is that we relax the rule.
// Because the structs %3 and %5 are defined the same way.
TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 0 Offset 0
OpMemberDecorate %1 1 Offset 4
OpMemberDecorate %2 0 Offset 0
OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
OpStore %8 %11
OpReturn
OpFunctionEnd)";
spvValidatorOptionsSetRelaxStoreStruct(options_, true);
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// Same code as the last test excect for an extra decoration on one of the
// members. With the relaxed rules, the code is still valid.
TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) {
std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %1 0 Offset 0
OpMemberDecorate %1 1 Offset 4
OpMemberDecorate %1 0 RelaxedPrecision
OpMemberDecorate %2 0 Offset 0
OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
OpStore %8 %11
OpReturn
OpFunctionEnd)";
spvValidatorOptionsSetRelaxStoreStruct(options_, true);
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// T