mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-29 06:21:06 +00:00
6b2318aec8
open_quote value becomes npos when it fails to find '\'' and then is used to compute the string length for writing to result. This causes crash in win32 builds of the test.
6896 lines
223 KiB
C++
6896 lines
223 KiB
C++
// 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;
|
|
|
|
class ValidateIdWithMessage : public spvtest::ValidateBase<bool> {
|
|
public:
|
|
ValidateIdWithMessage() {
|
|
const bool use_friendly_names = GetParam();
|
|
spvValidatorOptionsSetFriendlyNames(options_, use_friendly_names);
|
|
}
|
|
|
|
std::string make_message(const char* msg);
|
|
};
|
|
|
|
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
|
|
)";
|
|
|
|
// Transform an expected validation message to either use friendly names (as
|
|
// provided in the message) or replace the friendly names by the corresponding
|
|
// id. The same flag used to configure the validator to output friendly names
|
|
// or not is used here.
|
|
std::string ValidateIdWithMessage::make_message(const char* msg) {
|
|
const bool use_friendly_names = GetParam();
|
|
if (use_friendly_names) {
|
|
return msg;
|
|
}
|
|
|
|
std::string message(msg);
|
|
std::ostringstream result;
|
|
|
|
size_t next = 0;
|
|
while (next < message.size()) {
|
|
// Parse 'num[%name]'
|
|
size_t open_quote = message.find('\'', next);
|
|
|
|
if (open_quote == std::string::npos) {
|
|
break;
|
|
}
|
|
|
|
// Copy up to the first quote
|
|
result.write(msg + next, open_quote - next);
|
|
// Handle apostrophes
|
|
if (!isdigit(message[open_quote + 1])) {
|
|
result << '\'';
|
|
next = open_quote + 1;
|
|
continue;
|
|
}
|
|
|
|
size_t open_bracket = message.find('[', open_quote + 1);
|
|
assert(open_bracket != std::string::npos);
|
|
|
|
size_t close_bracket = message.find(']', open_bracket + 1);
|
|
assert(close_bracket != std::string::npos);
|
|
|
|
size_t close_quote = close_bracket + 1;
|
|
assert(close_quote < message.size() && message[close_quote] == '\'');
|
|
|
|
// Change to 'num[%num]' because friendly names are not being used.
|
|
result.write(msg + open_quote, open_bracket - open_quote + 1);
|
|
result << '%';
|
|
result.write(msg + open_quote + 1, open_bracket - open_quote - 1);
|
|
result << "]'";
|
|
|
|
// Continue to the next id, or end of string.
|
|
next = close_quote + 1;
|
|
}
|
|
|
|
return result.str();
|
|
}
|
|
|
|
// TODO: OpUndef
|
|
|
|
TEST_P(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_P(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_P(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(make_message(
|
|
"OpMemberName Type <id> '1[%uint]' is not a struct type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpMemberName Member <id> '1[%_struct_1]' index is larger "
|
|
"than Type <id> '1[%_struct_1]'s member count.")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpLine Target <id> '1[%uint]' is not an OpString.")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message("forward referenced IDs have not been defined")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpMemberDecorate Structure type <id> '1[%uint]' is "
|
|
"not a struct type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"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_P(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_P(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(make_message(
|
|
"Result id of OpDecorationGroup can only "
|
|
"be targeted by OpName, OpGroupDecorate, "
|
|
"OpDecorate, OpDecorateId, and OpGroupMemberDecorate")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpGroupDecorate Decoration group <id> '1[%1]' is not "
|
|
"a decoration group.")));
|
|
}
|
|
TEST_P(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(make_message("forward referenced IDs have not been defined")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpGroupMemberDecorate Decoration group <id> '1[%1]' "
|
|
"is not a decoration group.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpGroupMemberDecorate Structure type <id> '2[%uint]' "
|
|
"is not a struct type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"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_P(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_P(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(make_message(
|
|
"OpEntryPoint Entry Point <id> '1[%void]' is not a "
|
|
"function.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpEntryPoint GLCompute %1 ""
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2 %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
|
|
"parameter count is not zero")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpEntryPoint GLCompute %1 ""
|
|
%2 = OpTypeInt 32 0
|
|
%ret = OpConstant %2 0
|
|
%3 = OpTypeFunction %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = OpLabel
|
|
OpReturnValue %ret
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
|
|
"return type is not void.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %1 ""
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2 %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = 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(),
|
|
AnyVUID("VUID-StandaloneSpirv-None-04633"));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
|
|
"parameter count is not zero")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %1 ""
|
|
%2 = OpTypeInt 32 0
|
|
%ret = OpConstant %2 0
|
|
%3 = OpTypeFunction %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = OpLabel
|
|
OpReturnValue %ret
|
|
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(),
|
|
AnyVUID("VUID-StandaloneSpirv-None-04633"));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
|
|
"return type is not void.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Interfaces passed to OpEntryPoint must be of type "
|
|
"OpTypeVariable. Found OpTypePointer.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpEntryPoint interfaces must be OpVariables with "
|
|
"Storage Class of Input(1) or Output(3). Found Storage "
|
|
"Class 2 for Entry Point id 1.")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpExecutionMode Entry Point <id> '1[%1]' is not the "
|
|
"Entry Point operand of an OpEntryPoint.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpExecutionMode Entry Point <id> '2[%2]' is not the "
|
|
"Entry Point operand of an OpEntryPoint.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpTypeVectorFloat) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeFloat 32
|
|
%2 = OpTypeVector %1 4)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(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_P(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_P(ValidateIdWithMessage, OpTypeVectorBool) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeBool
|
|
%2 = OpTypeVector %1 4)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpTypeVector Component Type <id> "
|
|
"'2[%_ptr_UniformConstant_float]' is not a scalar type.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Illegal number of components (1) for TypeVector\n %v1float = "
|
|
"OpTypeVector %float 1\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Illegal number of components (5) for TypeVector\n %v5float = "
|
|
"OpTypeVector %float 5\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Having 8 components for TypeVector requires the Vector16 "
|
|
"capability\n %v8float = OpTypeVector %float 8\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Having 16 components for TypeVector requires the Vector16 "
|
|
"capability\n %v16float = OpTypeVector %float 16\n")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeFloat 32
|
|
%2 = OpTypeVector %1 8)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
OpTypeVectorColumnCountOfSixteenWithVector16Good) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeFloat 32
|
|
%2 = OpTypeVector %1 16)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"olumns in a matrix must be of type vector.\n %mat3float = "
|
|
"OpTypeMatrix %float 3\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Matrix types can only be parameterized with floating-point "
|
|
"types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Matrix types can only be parameterized as having only 2, 3, "
|
|
"or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"Matrix types can only be parameterized as having only 2, 3, "
|
|
"or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n")));
|
|
}
|
|
|
|
TEST_P(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_P(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_P(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(make_message(
|
|
"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() override { 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));
|
|
}
|
|
}
|
|
|
|
// 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_P(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(make_message("OpTypeArray Length <id> '2[%2]' default "
|
|
"value must be at least 1.")));
|
|
}
|
|
|
|
TEST_P(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_P(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_P(ValidateIdWithMessage, OpTypeRuntimeArrayGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeInt 32 0
|
|
%2 = OpTypeRuntimeArray %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpTypeRuntimeArray Element Type <id> '2[%uint_0]' is not a "
|
|
"type.")));
|
|
}
|
|
// TODO: Object of this type can only be created with OpVariable using the
|
|
// Uniform Storage Class
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpTypeStruct Member Type <id> '3[%double_0]' is not "
|
|
"a type.")));
|
|
}
|
|
|
|
TEST_P(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(),
|
|
AnyVUID("VUID-StandaloneSpirv-None-04667"));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpTypeStruct must not contain an opaque type")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message("OpTypePointer Type <id> '2[%uint_0]' is not a "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpTypeFunctionGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpTypeFunction Return Type <id> '2[%uint_0]' is not "
|
|
"a type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpTypeFunction Parameter Type <id> '3[%uint_0]' is not a "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpTypeFunction Parameter Type <id> '1[%void]' cannot "
|
|
"be OpTypeVoid.")));
|
|
}
|
|
|
|
TEST_P(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_P(ValidateIdWithMessage, OpConstantTrueGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeBool
|
|
%2 = OpConstantTrue %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantTrue Result Type <id> '1[%void]' is not a boolean "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpConstantFalseGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeBool
|
|
%2 = OpConstantTrue %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantFalse Result Type <id> '1[%void]' is not a boolean "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(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_P(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_P(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_P(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_P(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(make_message(
|
|
"OpConstantComposite Result Type <id> '1[%float]' is not a "
|
|
"composite type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '5[%uint_42]'s type "
|
|
"does not match Result Type <id> '2[%v4float]'s vector "
|
|
"element type.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '5[%5]'s type does not "
|
|
"match Result Type <id> '2[%v4float]'s vector element type.")));
|
|
}
|
|
TEST_P(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_P(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_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '10[%10]' vector "
|
|
"component count does not match Result Type <id> "
|
|
"'4[%mat4v4float]'s vector component count.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '10[%10]' vector "
|
|
"component count does not match Result Type <id> "
|
|
"'4[%mat4v4float]'s vector component count.")));
|
|
}
|
|
TEST_P(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_P(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_P(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(make_message("Operand '1[%uint]' cannot be a "
|
|
"type")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '5[%5]' is not a "
|
|
"constant or undef.")));
|
|
}
|
|
TEST_P(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(make_message(
|
|
"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_P(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(make_message("OpConstantComposite Constituent <id> "
|
|
"'5[%5]'s type does not match Result "
|
|
"Type <id> '3[%_arr_uint_uint_4]'s array element "
|
|
"type.")));
|
|
}
|
|
TEST_P(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_P(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_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> "
|
|
"'5[%ulong_4300000000]' type does not match the "
|
|
"Result Type <id> '3[%_struct_3]'s member type.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpConstantComposite Constituent <id> '5[%5]' type "
|
|
"does not match the Result Type <id> '3[%_struct_3]'s "
|
|
"member type.")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpConstantSampler Result Type <id> '1[%float]' is not a sampler "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"OpConstantNull Result Type <id> '1[%void]' cannot have a null "
|
|
"value.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpConstantNull Result Type <id> '4[%_arr_2_uint_4]' cannot have a "
|
|
"null value.")));
|
|
}
|
|
|
|
TEST_P(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(make_message("OpConstantNull Result Type <id> '2[%_struct_2]' "
|
|
"cannot have a null value.")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpConstantNull Result Type <id> '2[%_runtimearr_bool]' cannot have "
|
|
"a null value.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpSpecConstantTrueGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeBool
|
|
%2 = OpSpecConstantTrue %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpSpecConstantTrue Result Type <id> '1[%void]' is not "
|
|
"a boolean type")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpSpecConstantFalseGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeBool
|
|
%2 = OpSpecConstantFalse %1)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message(
|
|
"OpSpecConstantFalse Result Type <id> '1[%void]' is not "
|
|
"a boolean type")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpSpecConstantGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeFloat 32
|
|
%2 = OpSpecConstant %1 42)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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(make_message("Type Id 1 is not a scalar numeric type")));
|
|
}
|
|
|
|
// Valid: SpecConstantComposite specializes to a vector.
|
|
TEST_P(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_P(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_P(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(make_message("is not a composite type")));
|
|
}
|
|
|
|
// Invalid: Vector contains a mix of Int and Float.
|
|
TEST_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"OpSpecConstantComposite Constituent <id> '6[%6]' is "
|
|
"not a constant or undef.")));
|
|
}
|
|
|
|
// Invalid: Vector contains a mix of Undef-int and Float.
|
|
TEST_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"OpSpecConstantComposite Constituent <id> count does "
|
|
"not match Result Type <id> '2[%v3float]'s vector "
|
|
"component count.")));
|
|
}
|
|
|
|
// Valid: 4x4 matrix of floats
|
|
TEST_P(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_P(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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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_P(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(make_message(
|
|
"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_P(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_P(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(make_message(
|
|
"OpSpecConstantComposite Constituent <id> '5[%5]' is "
|
|
"not a constant or undef.")));
|
|
}
|
|
|
|
// Invalid: Array has a mix of Int and Float components.
|
|
TEST_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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_P(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(make_message("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_P(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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"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_P(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(make_message(
|
|
"OpSpecConstantComposite Constituent <id> '5[%5]' type "
|
|
"does not match the Result Type <id> '3[%_struct_3]'s "
|
|
"member type.")));
|
|
}
|
|
|
|
// TODO: OpSpecConstantOp
|
|
|
|
TEST_P(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_P(ValidateIdWithMessage, OpVariableInitializerConstantGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeInt 32 0
|
|
%2 = OpTypePointer Output %1
|
|
%3 = OpConstant %1 42
|
|
%4 = OpVariable %2 Output %3)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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_P(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(make_message(
|
|
"OpVariable Result Type <id> '1[%uint]' is not a pointer "
|
|
"type.")));
|
|
}
|
|
TEST_P(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(make_message("Operand '2[%_ptr_Input_uint]' "
|
|
"cannot be a type")));
|
|
}
|
|
|
|
TEST_P(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(make_message(
|
|
"OpVariable Initializer <id> '8[%8]' is not a constant "
|
|
"or module-scope variable")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"If OpTypeBool is stored in conjunction with OpVariable, it can only "
|
|
"be used with non-externally visible shader Storage Classes: "
|
|
"Workgroup, CrossWorkgroup, Private, Function, Input, Output, "
|
|
"RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, "
|
|
"CallableDataKHR, or IncomingCallableDataKHR")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%bool = OpTypeBool
|
|
%int = OpTypeInt 32 0
|
|
%block = OpTypeStruct %bool %int
|
|
%_ptr_Private_block = OpTypePointer Private %block
|
|
%var = OpVariable %_ptr_Private_block Private
|
|
%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_P(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_P(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_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%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_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"If OpTypeBool is stored in conjunction with OpVariable using Input "
|
|
"or Output Storage Classes it requires a BuiltIn decoration")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBadVulkan) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %var
|
|
OpExecutionMode %main OriginUpperLeft
|
|
%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(), SPV_ENV_VULKAN_1_0);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
AnyVUID("VUID-StandaloneSpirv-Input-07290"));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"If OpTypeBool is stored in conjunction with OpVariable using Input "
|
|
"or Output Storage Classes it requires a BuiltIn decoration")));
|
|
}
|
|
|
|
TEST_P(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_P(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(make_message(
|
|
"In Logical addressing, variables may not allocate a pointer type")));
|
|
}
|
|
|
|
TEST_P(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_P(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_P(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_P(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_P(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(make_message(
|
|
"In Logical addressing with variable pointers, "
|
|
"variables that allocate pointers must be in Function "
|
|
"or Private storage classes")));
|
|
}
|
|
|
|
TEST_P(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
|
|
)";
|
|
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 << "OpExtension \"SPV_KHR_storage_buffer_storage_class\" ";
|
|
*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 StorageBuffer %f32
|
|
%i = OpConstant %i32 1
|
|
%zero = OpConstant %i32 0
|
|
%float_1 = OpConstant %f32 1.0
|
|
%ptr1 = OpVariable %f32ptr StorageBuffer
|
|
%ptr2 = OpVariable %f32ptr StorageBuffer
|
|
)";
|
|
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_P(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_P(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(make_message("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_P(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_P(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(make_message(
|
|
"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_P(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_P(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(make_message("OpLoad Result Type <id> "
|
|
"'3[%_ptr_UniformConstant_uint]' does not match "
|
|
"Pointer <id> '5[%5]'s type.")));
|
|
}
|
|
|
|
TEST_P(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(make_message("ID '8[%8]' has not been "
|
|
"defined")));
|
|
}
|
|
|
|
// Disabled as bitcasting type to object is now not valid.
|
|
TEST_P(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(make_message(
|
|
"OpLoad Pointer <id> '9' is not a logical pointer.")));
|
|
}
|
|
|
|
TEST_P(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 Uniform
|
|
%7 = OpFunction %1 None %4
|
|
%8 = OpLabel
|
|
OpStore %6 %5
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(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 = OpConstant %2 0
|
|
%8 = OpFunction %1 None %4
|
|
%9 = OpLabel
|
|
OpStore %7 %5
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpStore Pointer <id> '7[%uint_0]' is not a logical "
|
|
"pointer.")));
|
|
}
|
|
|
|
// Disabled as bitcasting type to object is now not valid.
|
|
TEST_P(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(make_message(
|
|
"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_P(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(make_message("is not a logical pointer.")));
|
|
}
|
|
|
|
// With the VariablePointer Capability, OpStore should allow storing through a
|
|
// variable pointer.
|
|
TEST_P(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_P(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 Uniform
|
|
%7 = OpFunction %1 None %4
|
|
%8 = OpLabel
|
|
%9 = OpFunctionCall %1 %10
|
|
OpStore %6 %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpStore Object <id> '9[%9]'s type is void.")));
|
|
}
|
|
TEST_P(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 Uniform
|
|
%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(make_message(
|
|
"OpStore Pointer <id> '7[%7]'s type does not match "
|
|
"Object <id> '6[%float_3_1400001]'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_P(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(make_message(
|
|
"OpStore Pointer <id> '8[%8]'s type does not match "
|
|
"Object <id> '11[%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_P(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 except for an extra decoration on one of the
|
|
// members. With the relaxed rules, the code is still valid.
|
|
TEST_P(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());
|
|
}
|
|
|
|
// This test check that we recursively traverse the struct to check if they are
|
|
// interchangable.
|
|
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpMemberDecorate %1 0 Offset 0
|
|
OpMemberDecorate %1 1 Offset 4
|
|
OpMemberDecorate %2 0 Offset 0
|
|
OpMemberDecorate %2 1 Offset 8
|
|
OpMemberDecorate %3 0 Offset 0
|
|
OpMemberDecorate %3 1 Offset 4
|
|
OpMemberDecorate %4 0 Offset 0
|
|
OpMemberDecorate %4 1 Offset 8
|
|
%5 = OpTypeVoid
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypeFloat 32
|
|
%1 = OpTypeStruct %7 %6
|
|
%2 = OpTypeStruct %1 %1
|
|
%8 = OpTypePointer Uniform %2
|
|
%3 = OpTypeStruct %7 %6
|
|
%4 = OpTypeStruct %3 %3
|
|
%9 = OpTypeFunction %5
|
|
%10 = OpConstant %6 7
|
|
%11 = OpConstant %7 3.14
|
|
%12 = OpConstantComposite %3 %11 %10
|
|
%13 = OpVariable %8 Uniform
|
|
%14 = OpFunction %5 None %9
|
|
%15 = OpLabel
|
|
%16 = OpCompositeConstruct %4 %12 %12
|
|
OpStore %13 %16
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
spvValidatorOptionsSetRelaxStoreStruct(options_, true);
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// This test check that the even with the relaxed rules an error is identified
|
|
// if the members of the struct are in a different order.
|
|
TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpMemberDecorate %1 0 Offset 0
|
|
OpMemberDecorate %1 1 Offset 4
|
|
OpMemberDecorate %2 0 Offset 0
|
|
OpMemberDecorate %2 1 Offset 8
|
|
OpMemberDecorate %3 0 Offset 0
|
|
OpMemberDecorate %3 1 Offset 4
|
|
OpMemberDecorate %4 0 Offset 0
|
|
OpMemberDecorate %4 1 Offset 8
|
|
%5 = OpTypeVoid
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypeFloat 32
|
|
%1 = OpTypeStruct %6 %7
|
|
%2 = OpTypeStruct %1 %1
|
|
%8 = OpTypePointer Uniform %2
|
|
%3 = OpTypeStruct %7 %6
|
|
%4 = OpTypeStruct %3 %3
|
|
%9 = OpTypeFunction %5
|
|
%10 = OpConstant %6 7
|
|
%11 = OpConstant %7 3.14
|
|
%12 = OpConstantComposite %3 %11 %10
|
|
%13 = OpVariable %8 Uniform
|
|
%14 = OpFunction %5 None %9
|
|
%15 = OpLabel
|
|
%16 = OpCompositeConstruct %4 %12 %12
|
|
OpStore %13 %16
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
spvValidatorOptionsSetRelaxStoreStruct(options_, true);
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpStore Pointer <id> '13[%13]'s layout does not match Object "
|
|
"<id> '16[%16]'s layout.")));
|
|
}
|
|
|
|
// This test check that the even with the relaxed rules an error is identified
|
|
// if the members of the struct are at different offsets.
|
|
TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpMemberDecorate %1 0 Offset 4
|
|
OpMemberDecorate %1 1 Offset 0
|
|
OpMemberDecorate %2 0 Offset 0
|
|
OpMemberDecorate %2 1 Offset 8
|
|
OpMemberDecorate %3 0 Offset 0
|
|
OpMemberDecorate %3 1 Offset 4
|
|
OpMemberDecorate %4 0 Offset 0
|
|
OpMemberDecorate %4 1 Offset 8
|
|
%5 = OpTypeVoid
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypeFloat 32
|
|
%1 = OpTypeStruct %7 %6
|
|
%2 = OpTypeStruct %1 %1
|
|
%8 = OpTypePointer Uniform %2
|
|
%3 = OpTypeStruct %7 %6
|
|
%4 = OpTypeStruct %3 %3
|
|
%9 = OpTypeFunction %5
|
|
%10 = OpConstant %6 7
|
|
%11 = OpConstant %7 3.14
|
|
%12 = OpConstantComposite %3 %11 %10
|
|
%13 = OpVariable %8 Uniform
|
|
%14 = OpFunction %5 None %9
|
|
%15 = OpLabel
|
|
%16 = OpCompositeConstruct %4 %12 %12
|
|
OpStore %13 %16
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
spvValidatorOptionsSetRelaxStoreStruct(options_, true);
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpStore Pointer <id> '13[%13]'s layout does not match Object "
|
|
"<id> '16[%16]'s layout.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) {
|
|
const std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical GLSL450
|
|
%1 = OpTypeInt 32 1
|
|
%2 = OpTypePointer Function %1
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpFunctionParameter %2
|
|
%6 = OpLabel
|
|
OpReturnValue %5
|
|
OpFunctionEnd)";
|
|
|
|
spvValidatorOptionsSetRelaxLogicalPointer(options_, true);
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) {
|
|
const std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical GLSL450
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 1
|
|
%3 = OpTypeFunction %1 ; void(void)
|
|
%4 = OpTypePointer Uniform %2 ; int*
|
|
%5 = OpTypePointer Private %4 ; int** (Private)
|
|
%6 = OpTypePointer Function %4 ; int** (Function)
|
|
%7 = OpVariable %5 Private
|
|
%8 = OpFunction %1 None %3
|
|
%9 = OpLabel
|
|
%10 = OpVariable %6 Function
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
|
|
spvValidatorOptionsSetRelaxLogicalPointer(options_, true);
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpStoreVoid) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpTypeFunction %1
|
|
%6 = OpVariable %3 Uniform
|
|
%7 = OpFunction %1 None %4
|
|
%8 = OpLabel
|
|
%9 = OpFunctionCall %1 %7
|
|
OpStore %6 %9
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpStore Object <id> '8[%8]'s type is void.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpStoreLabel) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpTypeFunction %1
|
|
%6 = OpVariable %3 Uniform
|
|
%7 = OpFunction %1 None %4
|
|
%8 = OpLabel
|
|
OpStore %6 %8
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Operand '7[%7]' requires a type")));
|
|
}
|
|
|
|
// TODO: enable when this bug is fixed:
|
|
// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404
|
|
TEST_P(ValidateIdWithMessage, DISABLED_OpStoreFunction) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypeFunction %2
|
|
%5 = OpConstant %2 123
|
|
%6 = OpVariable %3 UniformConstant
|
|
%7 = OpFunction %2 None %4
|
|
%8 = OpLabel
|
|
OpStore %6 %7
|
|
OpReturnValue %5
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpStoreBuiltin) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
|
OpExecutionMode %main LocalSize 1 1 1
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
|
|
OpName %gl_GlobalInvocationID "gl_GlobalInvocationID"
|
|
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
|
|
|
%int = OpTypeInt 32 1
|
|
%uint = OpTypeInt 32 0
|
|
%v3uint = OpTypeVector %uint 3
|
|
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
|
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
|
|
|
%zero = OpConstant %uint 0
|
|
%v3uint_000 = OpConstantComposite %v3uint %zero %zero %zero
|
|
|
|
%void = OpTypeVoid
|
|
%voidfunc = OpTypeFunction %void
|
|
%main = OpFunction %void None %voidfunc
|
|
%lmain = OpLabel
|
|
|
|
OpStore %gl_GlobalInvocationID %v3uint_000
|
|
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("storage class is read-only")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpConstant %2 42
|
|
%5 = OpVariable %3 UniformConstant %4
|
|
%6 = OpTypePointer Function %2
|
|
%7 = OpTypeFunction %1
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
%10 = OpVariable %6 Function
|
|
OpCopyMemory %10 %5 None
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpTypeFunction %1 %2 %3
|
|
%5 = OpFunction %1 None %4
|
|
%6 = OpFunctionParameter %2
|
|
%7 = OpFunctionParameter %3
|
|
%8 = OpLabel
|
|
OpCopyMemory %6 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Target operand <id> '6[%6]' is not a pointer.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpTypeFunction %1 %2 %3
|
|
%5 = OpFunction %1 None %4
|
|
%6 = OpFunctionParameter %2
|
|
%7 = OpFunctionParameter %3
|
|
%8 = OpLabel
|
|
OpCopyMemory %7 %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Source operand <id> '6[%6]' is not a pointer.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpConstant %2 42
|
|
%5 = OpVariable %3 UniformConstant %4
|
|
%11 = OpTypeFloat 32
|
|
%6 = OpTypePointer Function %11
|
|
%7 = OpTypeFunction %1
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
%10 = OpVariable %6 Function
|
|
OpCopyMemory %10 %5 None
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Target <id> '5[%5]'s type does not match "
|
|
"Source <id> '2[%uint]'s type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidTarget) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %1
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFunction %1 %3 %4
|
|
%6 = OpFunction %1 None %5
|
|
%7 = OpFunctionParameter %3
|
|
%8 = OpFunctionParameter %4
|
|
%9 = OpLabel
|
|
OpCopyMemory %7 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Target operand <id> '7[%7]' cannot be a void "
|
|
"pointer.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidSource) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %1
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFunction %1 %3 %4
|
|
%6 = OpFunction %1 None %5
|
|
%7 = OpFunctionParameter %3
|
|
%8 = OpFunctionParameter %4
|
|
%9 = OpLabel
|
|
OpCopyMemory %8 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Source operand <id> '7[%7]' cannot be a void "
|
|
"pointer.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypePointer Function %2
|
|
%5 = OpConstant %2 4
|
|
%6 = OpVariable %3 UniformConstant %5
|
|
%7 = OpTypeFunction %1
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
%10 = OpVariable %4 Function
|
|
OpCopyMemorySized %10 %6 %5 None
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypePointer Function %2
|
|
%5 = OpConstant %2 4
|
|
%6 = OpVariable %3 UniformConstant %5
|
|
%7 = OpTypeFunction %1
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
OpCopyMemorySized %5 %5 %5 None
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Target operand <id> '5[%uint_4]' is not a pointer.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypePointer Function %2
|
|
%5 = OpConstant %2 4
|
|
%6 = OpTypeFunction %1
|
|
%7 = OpFunction %1 None %6
|
|
%8 = OpLabel
|
|
%9 = OpVariable %4 Function
|
|
OpCopyMemorySized %9 %5 %5 None
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Source operand <id> '5[%uint_4]' is not a pointer.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypePointer Function %2
|
|
%5 = OpConstant %2 4
|
|
%6 = OpVariable %3 UniformConstant %5
|
|
%7 = OpTypeFunction %1
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
%10 = OpVariable %4 Function
|
|
OpCopyMemorySized %10 %6 %6 None
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '6[%6]' must be a scalar integer type.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer UniformConstant %2
|
|
%4 = OpTypePointer Function %2
|
|
%5 = OpConstant %2 4
|
|
%6 = OpVariable %3 UniformConstant %5
|
|
%7 = OpTypeFunction %1
|
|
%11 = OpTypeFloat 32
|
|
%12 = OpConstant %11 1.0
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpLabel
|
|
%10 = OpVariable %4 Function
|
|
OpCopyMemorySized %10 %6 %12 None
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '9[%float_1]' must be a scalar integer "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpConstantNull %2
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Size operand <id> '3[%3]' cannot be a constant "
|
|
"zero.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpConstant %2 0
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '3[%uint_0]' cannot be a constant "
|
|
"zero.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 64 0
|
|
%3 = OpConstant %2 0
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '3[%ulong_0]' cannot be a constant "
|
|
"zero.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 1
|
|
%3 = OpConstant %2 -1
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '3[%int_n1]' cannot have the sign bit set "
|
|
"to 1.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 64 1
|
|
%3 = OpConstant %2 -1
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Size operand <id> '3[%long_n1]' cannot have the sign bit set "
|
|
"to 1.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpConstant %2 2147483648
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 64 0
|
|
%3 = OpConstant %2 9223372036854775808
|
|
%4 = OpTypePointer Uniform %2
|
|
%5 = OpTypeFloat 32
|
|
%6 = OpTypePointer UniformConstant %5
|
|
%7 = OpTypeFunction %1 %4 %6
|
|
%8 = OpFunction %1 None %7
|
|
%9 = OpFunctionParameter %4
|
|
%10 = OpFunctionParameter %6
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %9 %10 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
const char kDeeplyNestedStructureSetup[] = R"(
|
|
%void = OpTypeVoid
|
|
%void_f = OpTypeFunction %void
|
|
%int = OpTypeInt 32 0
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%mat4x3 = OpTypeMatrix %v3float 4
|
|
%_ptr_Private_mat4x3 = OpTypePointer Private %mat4x3
|
|
%_ptr_Private_float = OpTypePointer Private %float
|
|
%my_matrix = OpVariable %_ptr_Private_mat4x3 Private
|
|
%my_float_var = OpVariable %_ptr_Private_float Private
|
|
%_ptr_Function_float = OpTypePointer Function %float
|
|
%int_0 = OpConstant %int 0
|
|
%int_1 = OpConstant %int 1
|
|
%int_2 = OpConstant %int 2
|
|
%int_3 = OpConstant %int 3
|
|
%int_5 = OpConstant %int 5
|
|
|
|
; Making the following nested structures.
|
|
;
|
|
; struct S {
|
|
; bool b;
|
|
; vec4 v[5];
|
|
; int i;
|
|
; mat4x3 m[5];
|
|
; }
|
|
; uniform blockName {
|
|
; S s;
|
|
; bool cond;
|
|
; RunTimeArray arr;
|
|
; }
|
|
|
|
%f32arr = OpTypeRuntimeArray %float
|
|
%v4float = OpTypeVector %float 4
|
|
%array5_mat4x3 = OpTypeArray %mat4x3 %int_5
|
|
%array5_vec4 = OpTypeArray %v4float %int_5
|
|
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
|
%_ptr_Function_vec4 = OpTypePointer Function %v4float
|
|
%_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float
|
|
%struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3
|
|
%struct_blockName = OpTypeStruct %struct_s %int %f32arr
|
|
%_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName
|
|
%_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s
|
|
%_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3
|
|
%_ptr_Uniform_mat4x3 = OpTypePointer Uniform %mat4x3
|
|
%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float
|
|
%blockName_var = OpVariable %_ptr_Uniform_blockName Uniform
|
|
%spec_int = OpSpecConstant %int 2
|
|
%float_0 = OpConstant %float 0
|
|
%func = OpFunction %void None %void_f
|
|
%my_label = OpLabel
|
|
)";
|
|
|
|
// In what follows, Access Chain Instruction refers to one of the following:
|
|
// OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, and
|
|
// OpInBoundsPtrAccessChain
|
|
using AccessChainInstructionTest = spvtest::ValidateBase<std::string>;
|
|
|
|
// Determines whether the access chain instruction requires the 'element id'
|
|
// argument.
|
|
bool AccessChainRequiresElemId(const std::string& instr) {
|
|
return (instr == "OpPtrAccessChain" || instr == "OpInBoundsPtrAccessChain");
|
|
}
|
|
|
|
// Valid: Access a float in a matrix using an access chain instruction.
|
|
TEST_P(AccessChainInstructionTest, AccessChainGood) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup +
|
|
"%float_entry = " + instr +
|
|
R"( %_ptr_Private_float %my_matrix )" + elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid. The result type of an access chain instruction must be a pointer.
|
|
TEST_P(AccessChainInstructionTest, AccessChainResultTypeBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%float_entry = )" +
|
|
instr +
|
|
R"( %float %my_matrix )" + elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string expected_err = "The Result Type of " + instr +
|
|
" <id> '36[%36]' must be "
|
|
"OpTypePointer. Found OpTypeFloat.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid. The base type of an access chain instruction must be a pointer.
|
|
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeVoidBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%float_entry = )" +
|
|
instr + " %_ptr_Private_float %void " + elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a "
|
|
"type"));
|
|
}
|
|
|
|
// Invalid. The base type of an access chain instruction must be a pointer.
|
|
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeNonPtrVariableBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_float %_ptr_Private_float )" +
|
|
elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr("Operand '8[%_ptr_Private_float]' cannot be a type"));
|
|
}
|
|
|
|
// Invalid: The storage class of Base and Result do not match.
|
|
TEST_P(AccessChainInstructionTest,
|
|
AccessChainResultAndBaseStorageClassDoesntMatchBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Function_float %my_matrix )" + elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err =
|
|
"The result pointer storage class and base pointer storage class in " +
|
|
instr + " do not match.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid. The base type of an access chain instruction must point to a
|
|
// composite object.
|
|
TEST_P(AccessChainInstructionTest,
|
|
AccessChainBasePtrNotPointingToCompositeBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_float %my_float_var )" + elem +
|
|
R"(%int_0
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = instr +
|
|
" reached non-composite type while "
|
|
"indexes still remain to be traversed.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Valid. No Indexes were passed to the access chain instruction. The Result
|
|
// Type is the same as the Base type.
|
|
TEST_P(AccessChainInstructionTest, AccessChainNoIndexesGood) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_float %my_float_var )" + elem +
|
|
R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid. No Indexes were passed to the access chain instruction, but the
|
|
// Result Type is different from the Base type.
|
|
TEST_P(AccessChainInstructionTest, AccessChainNoIndexesBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem +
|
|
R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr("result type (OpTypeMatrix) does not match the type that "
|
|
"results from indexing into the base <id> (OpTypeFloat)."));
|
|
}
|
|
|
|
// Valid: 255 indexes passed to the access chain instruction. Limit is 255.
|
|
TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
|
|
const std::string arrayStride =
|
|
" OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
|
|
int depth = 255;
|
|
std::string header =
|
|
kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
|
|
header.erase(header.find("%func"));
|
|
std::ostringstream spirv;
|
|
spirv << header << "\n";
|
|
|
|
// Build nested structures. Struct 'i' contains struct 'i-1'
|
|
spirv << "%s_depth_1 = OpTypeStruct %float\n";
|
|
for (int i = 2; i <= depth; ++i) {
|
|
spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n";
|
|
}
|
|
|
|
// Define Pointer and Variable to use for the AccessChain instruction.
|
|
spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_"
|
|
<< depth << "\n";
|
|
spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n";
|
|
|
|
// Function Start
|
|
spirv << R"(
|
|
%func = OpFunction %void None %void_f
|
|
%my_label = OpLabel
|
|
)";
|
|
|
|
// AccessChain with 'n' indexes (n = depth)
|
|
spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem;
|
|
for (int i = 0; i < depth; ++i) {
|
|
spirv << " %int_0";
|
|
}
|
|
|
|
// Function end
|
|
spirv << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid: 256 indexes passed to the access chain instruction. Limit is 255.
|
|
TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
|
|
std::ostringstream spirv;
|
|
spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
|
|
spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem;
|
|
for (int i = 0; i < 256; ++i) {
|
|
spirv << " %int_0";
|
|
}
|
|
spirv << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = "The number of indexes in " + instr +
|
|
" may not exceed 255. Found 256 indexes.";
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Valid: 10 indexes passed to the access chain instruction. (Custom limit: 10)
|
|
TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesGood) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
|
|
const std::string arrayStride =
|
|
" OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
|
|
int depth = 10;
|
|
std::string header =
|
|
kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
|
|
header.erase(header.find("%func"));
|
|
std::ostringstream spirv;
|
|
spirv << header << "\n";
|
|
|
|
// Build nested structures. Struct 'i' contains struct 'i-1'
|
|
spirv << "%s_depth_1 = OpTypeStruct %float\n";
|
|
for (int i = 2; i <= depth; ++i) {
|
|
spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n";
|
|
}
|
|
|
|
// Define Pointer and Variable to use for the AccessChain instruction.
|
|
spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_"
|
|
<< depth << "\n";
|
|
spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n";
|
|
|
|
// Function Start
|
|
spirv << R"(
|
|
%func = OpFunction %void None %void_f
|
|
%my_label = OpLabel
|
|
)";
|
|
|
|
// AccessChain with 'n' indexes (n = depth)
|
|
spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem;
|
|
for (int i = 0; i < depth; ++i) {
|
|
spirv << " %int_0";
|
|
}
|
|
|
|
// Function end
|
|
spirv << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
spvValidatorOptionsSetUniversalLimit(
|
|
options_, spv_validator_limit_max_access_chain_indexes, 10u);
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid: 11 indexes passed to the access chain instruction. Custom Limit:10
|
|
TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
|
|
std::ostringstream spirv;
|
|
spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
|
|
spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem;
|
|
for (int i = 0; i < 11; ++i) {
|
|
spirv << " %int_0";
|
|
}
|
|
spirv << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = "The number of indexes in " + instr +
|
|
" may not exceed 10. Found 11 indexes.";
|
|
spvValidatorOptionsSetUniversalLimit(
|
|
options_, spv_validator_limit_max_access_chain_indexes, 10u);
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: Index passed to the access chain instruction is float (must be
|
|
// integer).
|
|
TEST_P(AccessChainInstructionTest, AccessChainUndefinedIndexBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_float %my_matrix )" + elem +
|
|
R"(%float_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err =
|
|
"Indexes passed to " + instr + " must be of type integer.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: The index argument that indexes into a struct must be of type
|
|
// OpConstant.
|
|
TEST_P(AccessChainInstructionTest, AccessChainStructIndexNotConstantBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%f = )" +
|
|
instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_0 %spec_int %int_2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err =
|
|
"The <id> passed to " + instr +
|
|
" to index into a structure must be an OpConstant.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: Indexing up to a vec4 granularity, but result type expected float.
|
|
TEST_P(AccessChainInstructionTest,
|
|
AccessChainStructResultTypeDoesntMatchIndexedTypeBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_0 %int_1 %int_2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = instr +
|
|
" result type (OpTypeFloat) does not match "
|
|
"the type that results from indexing into "
|
|
"the base <id> (OpTypeVector).";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: Reach non-composite type (bool) when unused indexes remain.
|
|
TEST_P(AccessChainInstructionTest, AccessChainStructTooManyIndexesBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_0 %int_2 %int_2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = instr +
|
|
" reached non-composite type while "
|
|
"indexes still remain to be traversed.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: Trying to find index 3 of the struct that has only 3 members.
|
|
TEST_P(AccessChainInstructionTest, AccessChainStructIndexOutOfBoundBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_3 %int_2 %int_2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = "Index is out of bounds: " + instr +
|
|
" can not find index 3 into the structure "
|
|
"<id> '25[%_struct_25]'. This structure "
|
|
"has 3 members. Largest valid index is 2.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Valid: Tests that we can index into Struct, Array, Matrix, and Vector!
|
|
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoAllTypesGood) {
|
|
// indexes that we are passing are: 0, 3, 1, 2, 0
|
|
// 0 will select the struct_s within the base struct (blockName)
|
|
// 3 will select the Array that contains 5 matrices
|
|
// 1 will select the Matrix that is at index 1 of the array
|
|
// 2 will select the column (which is a vector) within the matrix at index 2
|
|
// 0 will select the element at the index 0 of the vector. (which is a float).
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
const std::string arrayStride =
|
|
" OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
|
|
std::ostringstream spirv;
|
|
spirv << kGLSL450MemoryModel << arrayStride << kDeeplyNestedStructureSetup
|
|
<< std::endl;
|
|
spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var "
|
|
<< elem << "%int_0" << std::endl;
|
|
spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var "
|
|
<< elem << "%int_0 %int_3" << std::endl;
|
|
spirv << "%sm = " << instr << " %_ptr_Uniform_mat4x3 %blockName_var " << elem
|
|
<< "%int_0 %int_3 %int_1" << std::endl;
|
|
spirv << "%sc = " << instr << " %_ptr_Uniform_v3float %blockName_var " << elem
|
|
<< "%int_0 %int_3 %int_1 %int_2" << std::endl;
|
|
spirv << "%entry = " << instr << " %_ptr_Uniform_float %blockName_var "
|
|
<< elem << "%int_0 %int_3 %int_1 %int_2 %int_0" << std::endl;
|
|
spirv << R"(
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Valid: Access an element of OpTypeRuntimeArray.
|
|
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
const std::string arrayStride =
|
|
" OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
|
|
std::string spirv = kGLSL450MemoryModel + arrayStride +
|
|
kDeeplyNestedStructureSetup + R"(
|
|
%runtime_arr_entry = )" + instr +
|
|
R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_2 %int_0
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid: Unused index when accessing OpTypeRuntimeArray.
|
|
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%runtime_arr_entry = )" +
|
|
instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
|
|
R"(%int_2 %int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err =
|
|
instr +
|
|
" reached non-composite type while indexes still remain to be traversed.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: Reached scalar type before arguments to the access chain instruction
|
|
// finished.
|
|
TEST_P(AccessChainInstructionTest, AccessChainMatrixMoreArgsThanNeededBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_float %my_matrix )" + elem +
|
|
R"(%int_0 %int_1 %int_0
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = instr +
|
|
" reached non-composite type while "
|
|
"indexes still remain to be traversed.";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Invalid: The result type and the type indexed into do not match.
|
|
TEST_P(AccessChainInstructionTest,
|
|
AccessChainResultTypeDoesntMatchIndexedTypeBad) {
|
|
const std::string instr = GetParam();
|
|
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
|
|
std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
|
|
%entry = )" +
|
|
instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem +
|
|
R"(%int_0 %int_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const std::string expected_err = instr +
|
|
" result type (OpTypeMatrix) does not match "
|
|
"the type that results from indexing into "
|
|
"the base <id> (OpTypeFloat).";
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
|
|
}
|
|
|
|
// Run tests for Access Chain Instructions.
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
CheckAccessChainInstructions, AccessChainInstructionTest,
|
|
::testing::Values("OpAccessChain", "OpInBoundsAccessChain",
|
|
"OpPtrAccessChain", "OpInBoundsPtrAccessChain"));
|
|
|
|
// TODO: OpArrayLength
|
|
// TODO: OpImagePointer
|
|
// TODO: OpGenericPtrMemSemantics
|
|
|
|
TEST_P(ValidateIdWithMessage, OpFunctionGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %1 %2 %2
|
|
%4 = OpFunction %1 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionResultTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpConstant %2 42
|
|
%4 = OpTypeFunction %1 %2 %2
|
|
%5 = OpFunction %2 None %4
|
|
%6 = OpLabel
|
|
OpReturnValue %3
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpFunction Result Type <id> '2[%uint]' does not "
|
|
"match the Function Type's return type <id> "
|
|
"'1[%void]'.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeInt 32 0
|
|
%2 = OpTypeFloat 32
|
|
%3 = OpConstant %2 0
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpFunction %1 None %4
|
|
%6 = OpLabel
|
|
OpReturnValue %3
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpReturnValue Value <id> '3[%float_0]'s type does "
|
|
"not match OpFunction's return type.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionFunctionTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%4 = OpFunction %1 None %2
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpFunction Function Type <id> '2[%uint]' is not a function "
|
|
"type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpFunctionUseBad) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeFloat 32
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpFunction %1 None %2
|
|
%4 = OpLabel
|
|
OpReturnValue %3
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Invalid use of function result id '3[%3]'.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpFunctionParameterGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %1 %2
|
|
%4 = OpFunction %1 None %3
|
|
%5 = OpFunctionParameter %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionParameterMultipleGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %1 %2 %2
|
|
%4 = OpFunction %1 None %3
|
|
%5 = OpFunctionParameter %2
|
|
%6 = OpFunctionParameter %2
|
|
%7 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %1 %2
|
|
%4 = OpFunction %1 None %3
|
|
%5 = OpFunctionParameter %1
|
|
%6 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpFunctionParameter Result Type <id> '1[%void]' does not "
|
|
"match the OpTypeFunction parameter type of the same index.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpFunctionCallGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpConstant %2 42 ;21
|
|
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpFunctionParameter %2
|
|
%8 = OpLabel
|
|
OpReturnValue %7
|
|
OpFunctionEnd
|
|
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
%12 = OpFunctionCall %2 %6 %5
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionCallResultTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpConstant %2 42 ;21
|
|
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpFunctionParameter %2
|
|
%8 = OpLabel
|
|
%9 = OpIAdd %2 %7 %7
|
|
OpReturnValue %9
|
|
OpFunctionEnd
|
|
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
%12 = OpFunctionCall %1 %6 %5
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpFunctionCall Result Type <id> '1[%void]'s type "
|
|
"does not match Function <id> '2[%uint]'s return "
|
|
"type.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionCallFunctionBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpConstant %2 42 ;21
|
|
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
%12 = OpFunctionCall %2 %5 %5
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpFunctionCall Function <id> '5[%uint_42]' is not a "
|
|
"function.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpConstant %2 42
|
|
|
|
%13 = OpTypeFloat 32
|
|
%14 = OpConstant %13 3.14
|
|
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpFunctionParameter %2
|
|
%8 = OpLabel
|
|
%9 = OpIAdd %2 %7 %7
|
|
OpReturnValue %9
|
|
OpFunctionEnd
|
|
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
%12 = OpFunctionCall %2 %6 %14
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpFunctionCall Argument <id> '7[%float_3_1400001]'s "
|
|
"type does not match Function <id> '2[%uint]'s "
|
|
"parameter type.")));
|
|
}
|
|
|
|
// Valid: OpSampledImage result <id> is used in the same block by
|
|
// OpImageSampleImplictLod
|
|
TEST_P(ValidateIdWithMessage, OpSampledImageGood) {
|
|
std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
|
|
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
|
|
%si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Invalid: OpSampledImage result <id> is defined in one block and used in a
|
|
// different block.
|
|
TEST_P(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) {
|
|
std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
|
|
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
|
|
OpBranch %label_2
|
|
%label_2 = OpLabel
|
|
%si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"All OpSampledImage instructions must be in the same block in "
|
|
"which their Result <id> are consumed. OpSampledImage Result "
|
|
"Type <id> '23[%23]' has a consumer in a different basic "
|
|
"block. The consumer instruction <id> is '25[%25]'.")));
|
|
}
|
|
|
|
// Invalid: OpSampledImage result <id> is used by OpSelect
|
|
// Note: According to the Spec, OpSelect parameters must be either a scalar or a
|
|
// vector. Therefore, OpTypeSampledImage is an illegal parameter for OpSelect.
|
|
// However, the OpSelect validation does not catch this today. Therefore, it is
|
|
// caught by the OpSampledImage validation. If the OpSelect validation code is
|
|
// updated, the error message for this test may change.
|
|
//
|
|
// Disabled since OpSelect catches this now.
|
|
TEST_P(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) {
|
|
std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
|
|
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
|
|
%select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Result <id> from OpSampledImage instruction must not "
|
|
"appear as operands of OpSelect. Found result <id> "
|
|
"'23' as an operand of <id> '24'.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpCopyObjectSampledImageGood) {
|
|
std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
|
|
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
|
|
%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img
|
|
%image_inst2 = OpCopyObject %image_type %image_inst
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Valid: Get a float in a matrix using CompositeExtract.
|
|
// Valid: Insert float into a matrix using CompositeInsert.
|
|
TEST_P(ValidateIdWithMessage, CompositeExtractInsertGood) {
|
|
std::ostringstream spirv;
|
|
spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
|
|
spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
|
|
spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl;
|
|
|
|
// To test CompositeInsert, insert the object back in after extraction.
|
|
spirv << "%new_composite = OpCompositeInsert %mat4x3 %float_entry %matrix 0 1"
|
|
<< std::endl;
|
|
spirv << R"(OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
#if 0
|
|
TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) {
|
|
const char *spirv = R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2 %2
|
|
%4 = OpTypeFunction %1
|
|
%5 = OpConstant %2 42 ;21
|
|
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpFunctionParameter %2
|
|
%8 = OpLabel
|
|
%9 = OpLoad %2 %7
|
|
OpReturnValue %9
|
|
OpFunctionEnd
|
|
|
|
%10 = OpFunction %1 None %4
|
|
%11 = OpLabel
|
|
OpReturn
|
|
%12 = OpFunctionCall %2 %6 %5
|
|
OpFunctionEnd)";
|
|
CHECK(spirv, SPV_ERROR_INVALID_ID);
|
|
}
|
|
#endif
|
|
|
|
// TODO: The many things that changed with how images are used.
|
|
// TODO: OpTextureSample
|
|
// TODO: OpTextureSampleDref
|
|
// TODO: OpTextureSampleLod
|
|
// TODO: OpTextureSampleProj
|
|
// TODO: OpTextureSampleGrad
|
|
// TODO: OpTextureSampleOffset
|
|
// TODO: OpTextureSampleProjLod
|
|
// TODO: OpTextureSampleProjGrad
|
|
// TODO: OpTextureSampleLodOffset
|
|
// TODO: OpTextureSampleProjOffset
|
|
// TODO: OpTextureSampleGradOffset
|
|
// TODO: OpTextureSampleProjLodOffset
|
|
// TODO: OpTextureSampleProjGradOffset
|
|
// TODO: OpTextureFetchTexelLod
|
|
// TODO: OpTextureFetchTexelOffset
|
|
// TODO: OpTextureFetchSample
|
|
// TODO: OpTextureFetchTexel
|
|
// TODO: OpTextureGather
|
|
// TODO: OpTextureGatherOffset
|
|
// TODO: OpTextureGatherOffsets
|
|
// TODO: OpTextureQuerySizeLod
|
|
// TODO: OpTextureQuerySize
|
|
// TODO: OpTextureQueryLevels
|
|
// TODO: OpTextureQuerySamples
|
|
// TODO: OpConvertUToF
|
|
// TODO: OpConvertFToS
|
|
// TODO: OpConvertSToF
|
|
// TODO: OpConvertUToF
|
|
// TODO: OpUConvert
|
|
// TODO: OpSConvert
|
|
// TODO: OpFConvert
|
|
// TODO: OpConvertPtrToU
|
|
// TODO: OpConvertUToPtr
|
|
// TODO: OpPtrCastToGeneric
|
|
// TODO: OpGenericCastToPtr
|
|
// TODO: OpBitcast
|
|
// TODO: OpGenericCastToPtrExplicit
|
|
// TODO: OpSatConvertSToU
|
|
// TODO: OpSatConvertUToS
|
|
// TODO: OpVectorExtractDynamic
|
|
// TODO: OpVectorInsertDynamic
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleIntGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec3 = OpTypeVector %int 3
|
|
%ivec4 = OpTypeVector %int 4
|
|
%ptr_ivec3 = OpTypePointer Function %ivec3
|
|
%undef = OpUndef %ivec4
|
|
%int_42 = OpConstant %int 42
|
|
%int_0 = OpConstant %int 0
|
|
%int_2 = OpConstant %int 2
|
|
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
|
|
%2 = OpTypeFunction %ivec3
|
|
%3 = OpFunction %ivec3 None %2
|
|
%4 = OpLabel
|
|
%var = OpVariable %ptr_ivec3 Function %1
|
|
%5 = OpLoad %ivec3 %var
|
|
%6 = OpVectorShuffle %ivec3 %5 %undef 2 1 0
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleFloatGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%float = OpTypeFloat 32
|
|
%vec2 = OpTypeVector %float 2
|
|
%vec3 = OpTypeVector %float 3
|
|
%vec4 = OpTypeVector %float 4
|
|
%ptr_vec2 = OpTypePointer Function %vec2
|
|
%ptr_vec3 = OpTypePointer Function %vec3
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%1 = OpConstantComposite %vec2 %float_2 %float_1
|
|
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
|
|
%3 = OpTypeFunction %vec4
|
|
%4 = OpFunction %vec4 None %3
|
|
%5 = OpLabel
|
|
%var = OpVariable %ptr_vec2 Function %1
|
|
%var2 = OpVariable %ptr_vec3 Function %2
|
|
%6 = OpLoad %vec2 %var
|
|
%7 = OpLoad %vec3 %var2
|
|
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff
|
|
OpReturnValue %8
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%float = OpTypeFloat 32
|
|
%vec2 = OpTypeVector %float 2
|
|
%ptr_vec2 = OpTypePointer Function %vec2
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%1 = OpConstantComposite %vec2 %float_2 %float_1
|
|
%2 = OpTypeFunction %float
|
|
%3 = OpFunction %float None %2
|
|
%4 = OpLabel
|
|
%var = OpVariable %ptr_vec2 Function %1
|
|
%5 = OpLoad %vec2 %var
|
|
%6 = OpVectorShuffle %float %5 %5 0
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Result Type of OpVectorShuffle must be OpTypeVector.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec3 = OpTypeVector %int 3
|
|
%ptr_ivec3 = OpTypePointer Function %ivec3
|
|
%int_42 = OpConstant %int 42
|
|
%int_0 = OpConstant %int 0
|
|
%int_2 = OpConstant %int 2
|
|
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
|
|
%2 = OpTypeFunction %ivec3
|
|
%3 = OpFunction %ivec3 None %2
|
|
%4 = OpLabel
|
|
%var = OpVariable %ptr_ivec3 Function %1
|
|
%5 = OpLoad %ivec3 %var
|
|
%6 = OpVectorShuffle %ivec3 %5 %5 0 1
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpVectorShuffle component literals count does not match "
|
|
"Result Type <id> '2[%v3uint]'s vector component count.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1Type) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec2 = OpTypeVector %int 2
|
|
%ptr_int = OpTypePointer Function %int
|
|
%undef = OpUndef %ivec2
|
|
%int_42 = OpConstant %int 42
|
|
%2 = OpTypeFunction %ivec2
|
|
%3 = OpFunction %ivec2 None %2
|
|
%4 = OpLabel
|
|
%var = OpVariable %ptr_int Function %int_42
|
|
%5 = OpLoad %int %var
|
|
%6 = OpVectorShuffle %ivec2 %5 %undef 0 0
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("The type of Vector 1 must be OpTypeVector.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec2 = OpTypeVector %int 2
|
|
%ptr_ivec2 = OpTypePointer Function %ivec2
|
|
%undef = OpUndef %int
|
|
%int_42 = OpConstant %int 42
|
|
%1 = OpConstantComposite %ivec2 %int_42 %int_42
|
|
%2 = OpTypeFunction %ivec2
|
|
%3 = OpFunction %ivec2 None %2
|
|
%4 = OpLabel
|
|
%var = OpVariable %ptr_ivec2 Function %1
|
|
%5 = OpLoad %ivec2 %var
|
|
%6 = OpVectorShuffle %ivec2 %5 %undef 0 1
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("The type of Vector 2 must be OpTypeVector.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec3 = OpTypeVector %int 3
|
|
%ptr_ivec3 = OpTypePointer Function %ivec3
|
|
%int_42 = OpConstant %int 42
|
|
%int_0 = OpConstant %int 0
|
|
%int_2 = OpConstant %int 2
|
|
%float = OpTypeFloat 32
|
|
%vec3 = OpTypeVector %float 3
|
|
%vec4 = OpTypeVector %float 4
|
|
%ptr_vec3 = OpTypePointer Function %vec3
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
|
|
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
|
|
%3 = OpTypeFunction %vec4
|
|
%4 = OpFunction %vec4 None %3
|
|
%5 = OpLabel
|
|
%var = OpVariable %ptr_ivec3 Function %1
|
|
%var2 = OpVariable %ptr_vec3 Function %2
|
|
%6 = OpLoad %ivec3 %var
|
|
%7 = OpLoad %vec3 %var2
|
|
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0
|
|
OpReturnValue %8
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"The Component Type of Vector 1 must be the same as "
|
|
"ResultType.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%int = OpTypeInt 32 0
|
|
%ivec3 = OpTypeVector %int 3
|
|
%ptr_ivec3 = OpTypePointer Function %ivec3
|
|
%int_42 = OpConstant %int 42
|
|
%int_0 = OpConstant %int 0
|
|
%int_2 = OpConstant %int 2
|
|
%float = OpTypeFloat 32
|
|
%vec3 = OpTypeVector %float 3
|
|
%vec4 = OpTypeVector %float 4
|
|
%ptr_vec3 = OpTypePointer Function %vec3
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
|
|
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
|
|
%3 = OpTypeFunction %vec4
|
|
%4 = OpFunction %vec4 None %3
|
|
%5 = OpLabel
|
|
%var = OpVariable %ptr_ivec3 Function %1
|
|
%var2 = OpVariable %ptr_vec3 Function %2
|
|
%6 = OpLoad %vec3 %var2
|
|
%7 = OpLoad %ivec3 %var
|
|
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0
|
|
OpReturnValue %8
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"The Component Type of Vector 2 must be the same as "
|
|
"ResultType.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpVectorShuffleLiterals) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%float = OpTypeFloat 32
|
|
%vec2 = OpTypeVector %float 2
|
|
%vec3 = OpTypeVector %float 3
|
|
%vec4 = OpTypeVector %float 4
|
|
%ptr_vec2 = OpTypePointer Function %vec2
|
|
%ptr_vec3 = OpTypePointer Function %vec3
|
|
%float_1 = OpConstant %float 1
|
|
%float_2 = OpConstant %float 2
|
|
%1 = OpConstantComposite %vec2 %float_2 %float_1
|
|
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
|
|
%3 = OpTypeFunction %vec4
|
|
%4 = OpFunction %vec4 None %3
|
|
%5 = OpLabel
|
|
%var = OpVariable %ptr_vec2 Function %1
|
|
%var2 = OpVariable %ptr_vec3 Function %2
|
|
%6 = OpLoad %vec2 %var
|
|
%7 = OpLoad %vec3 %var2
|
|
%8 = OpVectorShuffle %vec4 %6 %7 0 8 2 6
|
|
OpReturnValue %8
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Component index 8 is out of bounds for combined (Vector1 + Vector2) "
|
|
"size of 5.")));
|
|
}
|
|
|
|
// TODO: OpCompositeConstruct
|
|
// TODO: OpCompositeExtract
|
|
// TODO: OpCompositeInsert
|
|
// TODO: OpCopyObject
|
|
// TODO: OpTranspose
|
|
// TODO: OpSNegate
|
|
// TODO: OpFNegate
|
|
// TODO: OpNot
|
|
// TODO: OpIAdd
|
|
// TODO: OpFAdd
|
|
// TODO: OpISub
|
|
// TODO: OpFSub
|
|
// TODO: OpIMul
|
|
// TODO: OpFMul
|
|
// TODO: OpUDiv
|
|
// TODO: OpSDiv
|
|
// TODO: OpFDiv
|
|
// TODO: OpUMod
|
|
// TODO: OpSRem
|
|
// TODO: OpSMod
|
|
// TODO: OpFRem
|
|
// TODO: OpFMod
|
|
// TODO: OpVectorTimesScalar
|
|
// TODO: OpMatrixTimesScalar
|
|
// TODO: OpVectorTimesMatrix
|
|
// TODO: OpMatrixTimesVector
|
|
// TODO: OpMatrixTimesMatrix
|
|
// TODO: OpOuterProduct
|
|
// TODO: OpDot
|
|
// TODO: OpShiftRightLogical
|
|
// TODO: OpShiftRightArithmetic
|
|
// TODO: OpShiftLeftLogical
|
|
// TODO: OpBitwiseOr
|
|
// TODO: OpBitwiseXor
|
|
// TODO: OpBitwiseAnd
|
|
// TODO: OpAny
|
|
// TODO: OpAll
|
|
// TODO: OpIsNan
|
|
// TODO: OpIsInf
|
|
// TODO: OpIsFinite
|
|
// TODO: OpIsNormal
|
|
// TODO: OpSignBitSet
|
|
// TODO: OpLessOrGreater
|
|
// TODO: OpOrdered
|
|
// TODO: OpUnordered
|
|
// TODO: OpLogicalOr
|
|
// TODO: OpLogicalXor
|
|
// TODO: OpLogicalAnd
|
|
// TODO: OpSelect
|
|
// TODO: OpIEqual
|
|
// TODO: OpFOrdEqual
|
|
// TODO: OpFUnordEqual
|
|
// TODO: OpINotEqual
|
|
// TODO: OpFOrdNotEqual
|
|
// TODO: OpFUnordNotEqual
|
|
// TODO: OpULessThan
|
|
// TODO: OpSLessThan
|
|
// TODO: OpFOrdLessThan
|
|
// TODO: OpFUnordLessThan
|
|
// TODO: OpUGreaterThan
|
|
// TODO: OpSGreaterThan
|
|
// TODO: OpFOrdGreaterThan
|
|
// TODO: OpFUnordGreaterThan
|
|
// TODO: OpULessThanEqual
|
|
// TODO: OpSLessThanEqual
|
|
// TODO: OpFOrdLessThanEqual
|
|
// TODO: OpFUnordLessThanEqual
|
|
// TODO: OpUGreaterThanEqual
|
|
// TODO: OpSGreaterThanEqual
|
|
// TODO: OpFOrdGreaterThanEqual
|
|
// TODO: OpFUnordGreaterThanEqual
|
|
// TODO: OpDPdx
|
|
// TODO: OpDPdy
|
|
// TODO: OpFWidth
|
|
// TODO: OpDPdxFine
|
|
// TODO: OpDPdyFine
|
|
// TODO: OpFwidthFine
|
|
// TODO: OpDPdxCoarse
|
|
// TODO: OpDPdyCoarse
|
|
// TODO: OpFwidthCoarse
|
|
// TODO: OpLoopMerge
|
|
// TODO: OpSelectionMerge
|
|
// TODO: OpBranch
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiNotAType) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
%9 = OpPhi %3 %3 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("ID '3[%true]' is not a type "
|
|
"id")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiSamePredecessor) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranchConditional %3 %8 %8
|
|
%8 = OpLabel
|
|
%9 = OpPhi %2 %3 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiOddArgumentNumber) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
%9 = OpPhi %2 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpPhi does not have an equal number of incoming "
|
|
"values and basic blocks.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiTooFewPredecessors) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
%9 = OpPhi %2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpPhi's number of incoming blocks (0) does not match "
|
|
"block's predecessor count (1).")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiTooManyPredecessors) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%9 = OpLabel
|
|
OpReturn
|
|
%8 = OpLabel
|
|
%10 = OpPhi %2 %3 %7 %3 %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpPhi's number of incoming blocks (2) does not match "
|
|
"block's predecessor count (1).")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiMismatchedTypes) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeInt 32 0
|
|
%6 = OpConstant %5 0
|
|
%7 = OpTypeFunction %4
|
|
%8 = OpFunction %4 None %7
|
|
%9 = OpLabel
|
|
OpBranchConditional %3 %10 %11
|
|
%11 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
%12 = OpPhi %2 %3 %9 %6 %11
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpPhi's result type <id> '2[%bool]' does not match "
|
|
"incoming value <id> '6[%uint_0]' type <id> "
|
|
"'5[%uint]'.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiPredecessorNotABlock) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranchConditional %3 %8 %9
|
|
%9 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
%10 = OpPhi %2 %3 %7 %3 %3
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpPhi's incoming basic block <id> '3[%true]' is not an "
|
|
"OpLabel.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPhiNotAPredecessor) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
%2 = OpTypeBool
|
|
%3 = OpConstantTrue %2
|
|
%4 = OpTypeVoid
|
|
%5 = OpTypeFunction %4
|
|
%6 = OpFunction %4 None %5
|
|
%7 = OpLabel
|
|
OpBranchConditional %3 %8 %9
|
|
%9 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
%10 = OpPhi %2 %3 %7 %3 %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpPhi's incoming basic block <id> '9[%9]' is not a "
|
|
"predecessor of <id> '8[%8]'.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditionalGood) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
%branch_cond = OpINotEqual %bool %i0 %i1
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %branch_cond %target_t %target_f
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
%branch_cond = OpINotEqual %bool %i0 %i1
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %branch_cond %target_t %target_f 1 1
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %i0 %target_t %target_f
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Condition operand for OpBranchConditional must "
|
|
"be of boolean type")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %true %i0 %target_f
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"The 'True Label' operand for OpBranchConditional must "
|
|
"be the ID of an OpLabel instruction")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %true %target_t %i0
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"The 'False Label' operand for OpBranchConditional "
|
|
"must be the ID of an OpLabel instruction")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
%branch_cond = OpINotEqual %bool %i0 %i1
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %branch_cond %target_t %target_f 1
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpBranchConditional requires either 3 or 5 parameters")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
%branch_cond = OpINotEqual %bool %i0 %i1
|
|
OpSelectionMerge %end None
|
|
OpBranchConditional %branch_cond %target_t %target_f 1 2 3
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpBranchConditional requires either 3 or 5 parameters")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) {
|
|
std::string spirv = BranchConditionalSetup + R"(
|
|
OpBranchConditional %bool %target_t %target_f
|
|
)" + BranchConditionalTail;
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Operand '3[%bool]' cannot be a "
|
|
"type")));
|
|
}
|
|
|
|
// TODO: OpSwitch
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueConstantGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpConstant %2 42
|
|
%5 = OpFunction %2 None %3
|
|
%6 = OpLabel
|
|
OpReturnValue %4
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueVariableGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0 ;10
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypePointer Function %2 ;18
|
|
%4 = OpConstant %2 42 ;22
|
|
%5 = OpFunction %2 None %3 ;27
|
|
%6 = OpLabel ;29
|
|
%7 = OpVariable %8 Function %4 ;34
|
|
%9 = OpLoad %2 %7
|
|
OpReturnValue %9 ;36
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueExpressionGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpConstant %2 42
|
|
%5 = OpFunction %2 None %3
|
|
%6 = OpLabel
|
|
%7 = OpIAdd %2 %4 %4
|
|
OpReturnValue %7
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueIsType) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2
|
|
%5 = OpFunction %2 None %3
|
|
%6 = OpLabel
|
|
OpReturnValue %1
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Operand '1[%void]' cannot be a "
|
|
"type")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueIsLabel) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2
|
|
%5 = OpFunction %2 None %3
|
|
%6 = OpLabel
|
|
OpReturnValue %6
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Operand '5[%5]' requires a type")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueIsVoid) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %1
|
|
%5 = OpFunction %1 None %3
|
|
%6 = OpLabel
|
|
%7 = OpFunctionCall %1 %5
|
|
OpReturnValue %7
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpReturnValue value's type <id> '1[%void]' is missing or "
|
|
"void.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) {
|
|
// It's valid to return a pointer in a physical addressing model.
|
|
std::string spirv = kOpCapabilitySetup + R"(
|
|
OpMemoryModel Physical32 OpenCL
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Function %2
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %3 None %4
|
|
%6 = OpLabel
|
|
%7 = OpVariable %3 Function
|
|
OpReturnValue %7
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) {
|
|
// It's invalid to return a pointer in a physical addressing model.
|
|
std::string spirv = kOpCapabilitySetup + R"(
|
|
OpMemoryModel Logical GLSL450
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Function %2
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %3 None %4
|
|
%6 = OpLabel
|
|
%7 = OpVariable %3 Function
|
|
OpReturnValue %7
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("OpReturnValue value's type <id> "
|
|
"'3[%_ptr_Function_uint]' is a pointer, which is "
|
|
"invalid in the Logical addressing model.")));
|
|
}
|
|
|
|
// With the VariablePointer Capability, the return value of a function is
|
|
// allowed to be a pointer.
|
|
TEST_P(ValidateIdWithMessage, OpReturnValueVarPtrGood) {
|
|
std::ostringstream spirv;
|
|
createVariablePointerSpirvProgram(&spirv,
|
|
"" /* Instructions to add to "main" */,
|
|
true /* Add VariablePointers Capability?*/,
|
|
true /* Use Helper Function? */);
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
// Without the VariablePointer Capability, the return value of a function is
|
|
// *not* allowed to be a pointer.
|
|
// Disabled since using OpSelect with pointers without VariablePointers will
|
|
// fail LogicalsPass.
|
|
TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) {
|
|
std::ostringstream spirv;
|
|
createVariablePointerSpirvProgram(&spirv,
|
|
"" /* Instructions to add to "main" */,
|
|
false /* Add VariablePointers Capability?*/,
|
|
true /* Use Helper Function? */);
|
|
CompileSuccessfully(spirv.str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpReturnValue value's type <id> '7' is a pointer, "
|
|
"which is invalid in the Logical addressing model.")));
|
|
}
|
|
|
|
// TODO: enable when this bug is fixed:
|
|
// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404
|
|
TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeFunction %2
|
|
%5 = OpFunction %2 None %3
|
|
%6 = OpLabel
|
|
OpReturnValue %5
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, UndefinedTypeId) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%s = OpTypeStruct %i32
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Operand '2[%2]' requires a previous definition")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, UndefinedIdScope) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%u32 = OpTypeInt 32 0
|
|
%memsem = OpConstant %u32 0
|
|
%void = OpTypeVoid
|
|
%void_f = OpTypeFunction %void
|
|
%f = OpFunction %void None %void_f
|
|
%l = OpLabel
|
|
OpMemoryBarrier %undef %memsem
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("ID '7[%7]' has not been "
|
|
"defined")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, UndefinedIdMemSem) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%u32 = OpTypeInt 32 0
|
|
%scope = OpConstant %u32 0
|
|
%void = OpTypeVoid
|
|
%void_f = OpTypeFunction %void
|
|
%f = OpFunction %void None %void_f
|
|
%l = OpLabel
|
|
OpMemoryBarrier %scope %undef
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("ID '7[%7]' has not been "
|
|
"defined")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
KernelOpEntryPointAndOpInBoundsPtrAccessChainGood) {
|
|
std::string spirv = kOpenCLMemoryModel32 + R"(
|
|
OpEntryPoint Kernel %2 "simple_kernel"
|
|
OpSource OpenCL_C 200000
|
|
OpDecorate %3 BuiltIn GlobalInvocationId
|
|
OpDecorate %3 Constant
|
|
OpDecorate %4 FuncParamAttr NoCapture
|
|
OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import
|
|
%5 = OpTypeInt 32 0
|
|
%6 = OpTypeVector %5 3
|
|
%7 = OpTypePointer UniformConstant %6
|
|
%3 = OpVariable %7 UniformConstant
|
|
%8 = OpTypeVoid
|
|
%9 = OpTypeStruct %5
|
|
%10 = OpTypePointer CrossWorkgroup %9
|
|
%11 = OpTypeFunction %8 %10
|
|
%12 = OpConstant %5 0
|
|
%13 = OpTypePointer CrossWorkgroup %5
|
|
%14 = OpConstant %5 42
|
|
%2 = OpFunction %8 None %11
|
|
%4 = OpFunctionParameter %10
|
|
%15 = OpLabel
|
|
%16 = OpLoad %6 %3 Aligned 0
|
|
%17 = OpCompositeExtract %5 %16 0
|
|
%18 = OpInBoundsPtrAccessChain %13 %4 %17 %12
|
|
OpStore %18 %14 Aligned 4
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpPtrAccessChainGood) {
|
|
std::string spirv = kOpenCLMemoryModel64 + R"(
|
|
OpEntryPoint Kernel %2 "another_kernel"
|
|
OpSource OpenCL_C 200000
|
|
OpDecorate %3 BuiltIn GlobalInvocationId
|
|
OpDecorate %3 Constant
|
|
OpDecorate %4 FuncParamAttr NoCapture
|
|
OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import
|
|
%5 = OpTypeInt 64 0
|
|
%6 = OpTypeVector %5 3
|
|
%7 = OpTypePointer UniformConstant %6
|
|
%3 = OpVariable %7 UniformConstant
|
|
%8 = OpTypeVoid
|
|
%9 = OpTypeInt 32 0
|
|
%10 = OpTypeStruct %9
|
|
%11 = OpTypePointer CrossWorkgroup %10
|
|
%12 = OpTypeFunction %8 %11
|
|
%13 = OpConstant %5 4294967295
|
|
%14 = OpConstant %9 0
|
|
%15 = OpTypePointer CrossWorkgroup %9
|
|
%16 = OpConstant %9 42
|
|
%2 = OpFunction %8 None %12
|
|
%4 = OpFunctionParameter %11
|
|
%17 = OpLabel
|
|
%18 = OpLoad %6 %3 Aligned 0
|
|
%19 = OpCompositeExtract %5 %18 0
|
|
%20 = OpBitwiseAnd %5 %19 %13
|
|
%21 = OpPtrAccessChain %15 %4 %20 %14
|
|
OpStore %21 %16 Aligned 4
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VariablePointersStorageBuffer
|
|
OpExtension "SPV_KHR_variable_pointers"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %3 ""
|
|
OpDecorate %ptr ArrayStride 8
|
|
%int = OpTypeInt 32 0
|
|
%int_2 = OpConstant %int 2
|
|
%int_4 = OpConstant %int 4
|
|
%struct = OpTypeStruct %int
|
|
%array = OpTypeArray %struct %int_4
|
|
%ptr = OpTypePointer StorageBuffer %array
|
|
%var = OpVariable %ptr StorageBuffer
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpFunction %1 None %2
|
|
%4 = OpLabel
|
|
%5 = OpPtrAccessChain %ptr %var %int_2
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpLoadBitcastPointerGood) {
|
|
std::string spirv = kOpenCLMemoryModel64 + R"(
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeInt 32 0
|
|
%4 = OpTypeFloat 32
|
|
%5 = OpTypePointer UniformConstant %3
|
|
%6 = OpTypePointer UniformConstant %4
|
|
%7 = OpVariable %5 UniformConstant
|
|
%8 = OpTypeFunction %2
|
|
%9 = OpFunction %2 None %8
|
|
%10 = OpLabel
|
|
%11 = OpBitcast %6 %7
|
|
%12 = OpLoad %4 %11
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) {
|
|
std::string spirv = kOpenCLMemoryModel64 + R"(
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeInt 32 0
|
|
%4 = OpTypeFloat 32
|
|
%5 = OpTypePointer UniformConstant %3
|
|
%6 = OpTypeFunction %2
|
|
%7 = OpVariable %5 UniformConstant
|
|
%8 = OpFunction %2 None %6
|
|
%9 = OpLabel
|
|
%10 = OpLoad %3 %7
|
|
%11 = OpBitcast %4 %10
|
|
%12 = OpLoad %3 %11
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpLoad type for pointer <id> '11[%11]' is not a pointer "
|
|
"type.")));
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpStoreBitcastPointerGood) {
|
|
std::string spirv = kOpenCLMemoryModel64 + R"(
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeInt 32 0
|
|
%4 = OpTypeFloat 32
|
|
%5 = OpTypePointer Function %3
|
|
%6 = OpTypePointer Function %4
|
|
%7 = OpTypeFunction %2
|
|
%8 = OpConstant %3 42
|
|
%9 = OpFunction %2 None %7
|
|
%10 = OpLabel
|
|
%11 = OpVariable %6 Function
|
|
%12 = OpBitcast %5 %11
|
|
OpStore %12 %8
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
|
}
|
|
TEST_P(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) {
|
|
std::string spirv = kOpenCLMemoryModel64 + R"(
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeInt 32 0
|
|
%4 = OpTypeFloat 32
|
|
%5 = OpTypePointer Function %4
|
|
%6 = OpTypeFunction %2
|
|
%7 = OpConstant %4 42
|
|
%8 = OpFunction %2 None %6
|
|
%9 = OpLabel
|
|
%10 = OpVariable %5 Function
|
|
%11 = OpBitcast %3 %7
|
|
OpStore %11 %7
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"OpStore type for pointer <id> '11[%11]' is not a pointer "
|
|
"type.")));
|
|
}
|
|
|
|
// Result <id> resulting from an instruction within a function may not be used
|
|
// outside that function.
|
|
TEST_P(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeInt 32 0
|
|
%4 = OpTypePointer Function %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
%7 = OpVariable %4 Function
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %1 None %2
|
|
%9 = OpLabel
|
|
%10 = OpLoad %3 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("ID '7[%7]' defined in block '6[%6]' does "
|
|
"not dominate its use in block "
|
|
"'9[%9]'")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpDecorate %1 SpecId 200
|
|
%void = OpTypeVoid
|
|
%2 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 0
|
|
%1 = OpConstant %int 3
|
|
%main = OpFunction %void None %2
|
|
%4 = OpLabel
|
|
OpReturnValue %1
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("SpecId decoration on target <id> "
|
|
"'1[%uint_3]' must be a scalar specialization "
|
|
"constant")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpDecorate %1 SpecId 200
|
|
%void = OpTypeVoid
|
|
%2 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 0
|
|
%3 = OpConstant %int 1
|
|
%4 = OpConstant %int 2
|
|
%1 = OpSpecConstantOp %int IAdd %3 %4
|
|
%main = OpFunction %void None %2
|
|
%6 = OpLabel
|
|
OpReturnValue %3
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
|
|
"must be a scalar specialization constant")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpDecorate %1 SpecId 200
|
|
%void = OpTypeVoid
|
|
%2 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 0
|
|
%3 = OpConstant %int 1
|
|
%1 = OpSpecConstantComposite %int
|
|
%main = OpFunction %void None %2
|
|
%4 = OpLabel
|
|
OpReturnValue %3
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
|
|
"must be a scalar specialization constant")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, SpecIdTargetGood) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
OpDecorate %3 SpecId 200
|
|
OpDecorate %4 SpecId 201
|
|
OpDecorate %5 SpecId 202
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%int = OpTypeInt 32 0
|
|
%bool = OpTypeBool
|
|
%3 = OpSpecConstant %int 3
|
|
%4 = OpSpecConstantTrue %bool
|
|
%5 = OpSpecConstantFalse %bool
|
|
%main = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, CorrectErrorForShuffle) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%uint = OpTypeInt 32 0
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%v2float = OpTypeVector %float 2
|
|
%void = OpTypeVoid
|
|
%548 = OpTypeFunction %void
|
|
%CS = OpFunction %void None %548
|
|
%550 = OpLabel
|
|
%6275 = OpUndef %v2float
|
|
%6280 = OpUndef %v2float
|
|
%6282 = OpVectorShuffle %v4float %6275 %6280 0 1 4 5
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv.c_str());
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Component index 4 is out of bounds for combined (Vector1 + Vector2) "
|
|
"size of 4.")));
|
|
EXPECT_EQ(25, getErrorPosition().index);
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VoidStructMember) {
|
|
const std::string spirv = kGLSL450MemoryModel + R"(
|
|
%void = OpTypeVoid
|
|
%struct = OpTypeStruct %void
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("Structures cannot contain a void type.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, TypeFunctionBadUse) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypePointer Function %2
|
|
%4 = OpFunction %1 None %2
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Invalid use of function type result id '2[%2]'.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, BadTypeId) {
|
|
std::string spirv = kGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpConstant %3 0
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
%7 = OpUndef %4
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("ID '4[%float_0]' is not a type "
|
|
"id")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 2
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerVisibleKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelLoadMakePointerVisibleMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 2
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
%9 = OpLoad %2 %4 MakePointerVisibleKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerVisibleKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelLoadNonPrivatePointerBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Private %2
|
|
%4 = OpVariable %3 Private
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 2
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
%9 = OpLoad %2 %4 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelLoadMakePointerAvailableCannotBeUsed) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 2
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerAvailableKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"MakePointerAvailableKHR cannot be used with OpLoad")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpVariable %3 Uniform
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 5
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
OpStore %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelStoreMakePointerAvailableMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpVariable %3 Uniform
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 5
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
OpStore %4 %6 MakePointerAvailableKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerAvailableKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelStoreNonPrivatePointerBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Output %2
|
|
%4 = OpVariable %3 Output
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 5
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
OpStore %4 %6 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelStoreMakePointerVisibleCannotBeUsed) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpCapability Linkage
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Uniform %2
|
|
%4 = OpVariable %3 Uniform
|
|
%5 = OpTypeFunction %1
|
|
%6 = OpConstant %2 5
|
|
%7 = OpFunction %1 None %5
|
|
%8 = OpLabel
|
|
OpStore %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"MakePointerVisibleKHR cannot be used with OpStore.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemoryAvailableMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 MakePointerAvailableKHR %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerAvailableKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemoryVisibleMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 MakePointerVisibleKHR %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerVisibleKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemoryAvailableBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Output %2
|
|
%4 = OpVariable %3 Output
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemoryVisibleBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Input %2
|
|
%6 = OpVariable %5 Input
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemory %4 %6 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerVisibleKHR %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemorySizedAvailableAndVisible) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemorySizedAvailableMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 MakePointerAvailableKHR %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerAvailableKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemorySizedVisibleMissingNonPrivatePointer) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 MakePointerVisibleKHR %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
|
|
"MakePointerVisibleKHR is specified.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemorySizedAvailableBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Output %2
|
|
%4 = OpVariable %3 Output
|
|
%5 = OpTypePointer Uniform %2
|
|
%6 = OpVariable %5 Uniform
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage,
|
|
VulkanMemoryModelCopyMemorySizedVisibleBadStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability Linkage
|
|
OpCapability Addresses
|
|
OpCapability VulkanMemoryModelKHR
|
|
OpExtension "SPV_KHR_vulkan_memory_model"
|
|
OpMemoryModel Logical VulkanKHR
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypePointer Workgroup %2
|
|
%4 = OpVariable %3 Workgroup
|
|
%5 = OpTypePointer Input %2
|
|
%6 = OpVariable %5 Input
|
|
%7 = OpConstant %2 2
|
|
%8 = OpConstant %2 5
|
|
%9 = OpTypeFunction %1
|
|
%10 = OpFunction %1 None %9
|
|
%11 = OpLabel
|
|
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"NonPrivatePointerKHR requires a pointer in Uniform, "
|
|
"Workgroup, CrossWorkgroup, Generic, Image or "
|
|
"StorageBuffer storage classes.")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock1) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%7 = OpLabel
|
|
%8 = OpFunctionCall %3 %9
|
|
OpUnreachable
|
|
OpFunctionEnd
|
|
%9 = OpFunction %3 None %4
|
|
%10 = OpLabel
|
|
OpReturnValue %8
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"ID '8[%8]' defined in block '7[%7]' does not dominate its "
|
|
"use in block '10[%10]'\n %10 = OpLabel")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock2) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%7 = OpLabel
|
|
%8 = OpFunctionCall %3 %9
|
|
OpUnreachable
|
|
OpFunctionEnd
|
|
%9 = OpFunction %3 None %4
|
|
%10 = OpLabel
|
|
OpReturnValue %8
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"ID '8[%8]' defined in block '7[%7]' does not dominate its "
|
|
"use in block '10[%10]'\n %10 = OpLabel")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock3) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%7 = OpLabel
|
|
%8 = OpFunctionCall %3 %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%9 = OpFunction %3 None %4
|
|
%10 = OpLabel
|
|
OpReturnValue %8
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"ID '8[%8]' defined in block '7[%7]' does not dominate its "
|
|
"use in block '10[%10]'\n %10 = OpLabel")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock4) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%7 = OpLabel
|
|
%8 = OpUndef %3
|
|
%9 = OpCopyObject %3 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock5) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%7 = OpLabel
|
|
%8 = OpUndef %3
|
|
OpBranch %9
|
|
%9 = OpLabel
|
|
%10 = OpCopyObject %3 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock6) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
OpBranch %7
|
|
%8 = OpLabel
|
|
%9 = OpUndef %3
|
|
OpBranch %7
|
|
%7 = OpLabel
|
|
%10 = OpCopyObject %3 %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"ID '9[%9]' defined in block '8[%8]' does not dominate its "
|
|
"use in block '7[%7]'\n %7 = OpLabel")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, ReachableDefUnreachableUse) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%1 = OpTypeVoid
|
|
%2 = OpTypeFunction %1
|
|
%3 = OpTypeFloat 32
|
|
%4 = OpTypeFunction %3
|
|
%5 = OpFunction %1 None %2
|
|
%6 = OpLabel
|
|
%7 = OpUndef %3
|
|
OpReturn
|
|
%8 = OpLabel
|
|
%9 = OpCopyObject %3 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, UnreachableDefUsedInPhi) {
|
|
const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%bool = OpTypeBool
|
|
%6 = OpTypeFunction %float
|
|
%1 = OpFunction %void None %3
|
|
%7 = OpLabel
|
|
%8 = OpUndef %bool
|
|
OpSelectionMerge %9 None
|
|
OpBranchConditional %8 %10 %9
|
|
%10 = OpLabel
|
|
%11 = OpUndef %float
|
|
OpBranch %9
|
|
%12 = OpLabel
|
|
%13 = OpUndef %float
|
|
OpUnreachable
|
|
%9 = OpLabel
|
|
%14 = OpPhi %float %11 %10 %13 %7
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(
|
|
getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"In OpPhi instruction '14[%14]', ID '13[%13]' definition does not "
|
|
"dominate its parent '7[%7]'\n %14 = OpPhi %float %11 %10 %13 "
|
|
"%7")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) {
|
|
std::string spirv = R"(
|
|
OpCapability GenericPointer
|
|
OpCapability VariablePointersStorageBuffer
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %1 "main"
|
|
OpExecutionMode %1 OriginLowerLeft
|
|
OpTypeForwardPointer %2 CrossWorkgroup
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%1 = OpFunction %2 DontInline %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Pointer type in OpTypeForwardPointer is not a pointer "
|
|
"type.\n OpTypeForwardPointer %void CrossWorkgroup")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) {
|
|
std::string spirv = R"(
|
|
OpCapability GenericPointer
|
|
OpCapability VariablePointersStorageBuffer
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %1 "main"
|
|
OpExecutionMode %1 OriginLowerLeft
|
|
OpTypeForwardPointer %2 CrossWorkgroup
|
|
%int = OpTypeInt 32 1
|
|
%2 = OpTypePointer Function %int
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message(
|
|
"Storage class in OpTypeForwardPointer does not match the "
|
|
"pointer definition.\n OpTypeForwardPointer "
|
|
"%_ptr_Function_int CrossWorkgroup")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, MissingForwardPointer) {
|
|
const std::string spirv = R"(
|
|
OpCapability Linkage
|
|
OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
%float = OpTypeFloat 32
|
|
%_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9
|
|
%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
|
|
%1278 = OpVariable %_ptr_Uniform__struct_9 Uniform
|
|
)";
|
|
|
|
CompileSuccessfully(spirv);
|
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
|
EXPECT_THAT(getDiagnosticString(),
|
|
HasSubstr(make_message("Operand '3[%_ptr_Uniform__struct_2]' "
|
|
"requires a previous definition")));
|
|
}
|
|
|
|
TEST_P(ValidateIdWithMessage, NVBindlessSamplerInStruct) {
|
|
std::string spirv = R"(
|
|
OpCapability Shader
|
|
OpCapability BindlessTextureNV
|
|
OpExtension "SPV_NV_bindless_texture"
|
|
OpMemoryModel Logical GLSL450
|
|
OpSamplerImageAddressingModeNV 64
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%7 = OpTypeImage %float 2D 0 0 0 1 Unknown
|
|
%8 = OpTypeSampledImage %7
|
|
%9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
|
|
%10 = OpTypeSampler
|
|
%UBO = OpTypeStruct %8 %9 %10
|
|
%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
|
|
%_ = OpVariable %_ptr_Uniform_UBO Uniform
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
|
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool());
|
|
|
|
} // namespace
|
|
} // namespace val
|
|
} // namespace spvtools
|