| // 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); |
| |
| // Copy up to the first quote |
| result.write(msg + next, open_quote - next); |
| if (open_quote == std::string::npos) { |
| break; |
| } |
| // 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"( |
| |