| // Copyright (c) 2015-2016 The Khronos Group Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <sstream> | |
| #include <string> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "test/test_fixture.h" | |
| #include "test/unit_spirv.h" | |
| #include "test/val/val_fixtures.h" | |
| // NOTE: The tests in this file are ONLY testing ID usage, there for the input | |
| // SPIR-V does not follow the logical layout rules from the spec in all cases in | |
| // order to makes the tests smaller. Validation of the whole module is handled | |
| // in stages, ID validation is only one of these stages. All validation stages | |
| // are stand alone. | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| using spvtest::ScopedContext; | |
| using ::testing::HasSubstr; | |
| using ::testing::ValuesIn; | |
| class ValidateIdWithMessage : public spvtest::ValidateBase<bool> { | |
| public: | |
| ValidateIdWithMessage() { | |
| const bool use_friendly_names = GetParam(); | |
| spvValidatorOptionsSetFriendlyNames(options_, use_friendly_names); | |
| } | |
| std::string make_message(const char* msg); | |
| }; | |
| std::string kOpCapabilitySetupWithoutVector16 = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability Int8 | |
| OpCapability Int16 | |
| OpCapability Int64 | |
| OpCapability Float64 | |
| OpCapability LiteralSampler | |
| OpCapability Pipes | |
| OpCapability DeviceEnqueue | |
| )"; | |
| std::string kOpCapabilitySetup = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability Int8 | |
| OpCapability Int16 | |
| OpCapability Int64 | |
| OpCapability Float64 | |
| OpCapability LiteralSampler | |
| OpCapability Pipes | |
| OpCapability DeviceEnqueue | |
| OpCapability Vector16 | |
| )"; | |
| std::string kOpVariablePtrSetUp = R"( | |
| OpCapability VariablePointers | |
| OpExtension "SPV_KHR_variable_pointers" | |
| )"; | |
| std::string kGLSL450MemoryModel = | |
| kOpCapabilitySetup + kOpVariablePtrSetUp + R"( | |
| OpMemoryModel Logical GLSL450 | |
| )"; | |
| std::string kGLSL450MemoryModelWithoutVector16 = | |
| kOpCapabilitySetupWithoutVector16 + kOpVariablePtrSetUp + R"( | |
| OpMemoryModel Logical GLSL450 | |
| )"; | |
| std::string kNoKernelGLSL450MemoryModel = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability Int8 | |
| OpCapability Int16 | |
| OpCapability Int64 | |
| OpCapability Float64 | |
| OpMemoryModel Logical GLSL450 | |
| )"; | |
| std::string kOpenCLMemoryModel32 = R"( | |
| OpCapability Addresses | |
| OpCapability Linkage | |
| OpCapability Kernel | |
| %1 = OpExtInstImport "OpenCL.std" | |
| OpMemoryModel Physical32 OpenCL | |
| )"; | |
| std::string kOpenCLMemoryModel64 = R"( | |
| OpCapability Addresses | |
| OpCapability Linkage | |
| OpCapability Kernel | |
| OpCapability Int64 | |
| %1 = OpExtInstImport "OpenCL.std" | |
| OpMemoryModel Physical64 OpenCL | |
| )"; | |
| std::string sampledImageSetup = R"( | |
| %void = OpTypeVoid | |
| %typeFuncVoid = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %v4float = OpTypeVector %float 4 | |
| %image_type = OpTypeImage %float 2D 0 0 0 1 Unknown | |
| %_ptr_UniformConstant_img = OpTypePointer UniformConstant %image_type | |
| %tex = OpVariable %_ptr_UniformConstant_img UniformConstant | |
| %sampler_type = OpTypeSampler | |
| %_ptr_UniformConstant_sam = OpTypePointer UniformConstant %sampler_type | |
| %s = OpVariable %_ptr_UniformConstant_sam UniformConstant | |
| %sampled_image_type = OpTypeSampledImage %image_type | |
| %v2float = OpTypeVector %float 2 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %const_vec_1_1 = OpConstantComposite %v2float %float_1 %float_1 | |
| %const_vec_2_2 = OpConstantComposite %v2float %float_2 %float_2 | |
| %bool_type = OpTypeBool | |
| %spec_true = OpSpecConstantTrue %bool_type | |
| %main = OpFunction %void None %typeFuncVoid | |
| %label_1 = OpLabel | |
| %image_inst = OpLoad %image_type %tex | |
| %sampler_inst = OpLoad %sampler_type %s | |
| )"; | |
| std::string BranchConditionalSetup = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %main "main" | |
| OpExecutionMode %main OriginUpperLeft | |
| OpSource GLSL 140 | |
| OpName %main "main" | |
| ; type definitions | |
| %bool = OpTypeBool | |
| %uint = OpTypeInt 32 0 | |
| %int = OpTypeInt 32 1 | |
| %float = OpTypeFloat 32 | |
| %v4float = OpTypeVector %float 4 | |
| ; constants | |
| %true = OpConstantTrue %bool | |
| %i0 = OpConstant %int 0 | |
| %i1 = OpConstant %int 1 | |
| %f0 = OpConstant %float 0 | |
| %f1 = OpConstant %float 1 | |
| ; main function header | |
| %void = OpTypeVoid | |
| %voidfunc = OpTypeFunction %void | |
| %main = OpFunction %void None %voidfunc | |
| %lmain = OpLabel | |
| )"; | |
| std::string BranchConditionalTail = R"( | |
| %target_t = OpLabel | |
| OpNop | |
| OpBranch %end | |
| %target_f = OpLabel | |
| OpNop | |
| OpBranch %end | |
| %end = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // Transform an expected validation message to either use friendly names (as | |
| // provided in the message) or replace the friendly names by the corresponding | |
| // id. The same flag used to configure the validator to output friendly names | |
| // or not is used here. | |
| std::string ValidateIdWithMessage::make_message(const char* msg) { | |
| const bool use_friendly_names = GetParam(); | |
| if (use_friendly_names) { | |
| return msg; | |
| } | |
| std::string message(msg); | |
| std::ostringstream result; | |
| size_t next = 0; | |
| while (next < message.size()) { | |
| // Parse 'num[%name]' | |
| size_t open_quote = message.find('\'', next); | |
| if (open_quote == std::string::npos) { | |
| break; | |
| } | |
| // Copy up to the first quote | |
| result.write(msg + next, open_quote - next); | |
| // Handle apostrophes | |
| if (!isdigit(message[open_quote + 1])) { | |
| result << '\''; | |
| next = open_quote + 1; | |
| continue; | |
| } | |
| size_t open_bracket = message.find('[', open_quote + 1); | |
| assert(open_bracket != std::string::npos); | |
| size_t close_bracket = message.find(']', open_bracket + 1); | |
| assert(close_bracket != std::string::npos); | |
| size_t close_quote = close_bracket + 1; | |
| assert(close_quote < message.size() && message[close_quote] == '\''); | |
| // Change to 'num[%num]' because friendly names are not being used. | |
| result.write(msg + open_quote, open_bracket - open_quote + 1); | |
| result << '%'; | |
| result.write(msg + open_quote + 1, open_bracket - open_quote - 1); | |
| result << "]'"; | |
| // Continue to the next id, or end of string. | |
| next = close_quote + 1; | |
| } | |
| return result.str(); | |
| } | |
| // TODO: OpUndef | |
| TEST_P(ValidateIdWithMessage, OpName) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpName %2 "name" | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer UniformConstant %1 | |
| %3 = OpVariable %2 UniformConstant)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberNameGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberName %2 0 "foo" | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeStruct %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberNameTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberName %1 0 "foo" | |
| %1 = OpTypeInt 32 0)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpMemberName Type <id> '1[%uint]' is not a struct type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberNameMemberBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberName %1 1 "foo" | |
| %2 = OpTypeInt 32 0 | |
| %1 = OpTypeStruct %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpMemberName Member <id> '1[%_struct_1]' index is larger " | |
| "than Type <id> '1[%_struct_1]'s member count."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLineGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpString "/path/to/source.file" | |
| OpLine %1 0 0 | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Input %2 | |
| %4 = OpVariable %3 Input)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLineFileBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| OpLine %1 0 0 | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpLine Target <id> '1[%uint]' is not an OpString."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpDecorateGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %2 GLSLShared | |
| %1 = OpTypeInt 64 0 | |
| %2 = OpTypeStruct %1 %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpDecorateBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %1 GLSLShared)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("forward referenced IDs have not been defined"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberDecorateGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %2 0 RelaxedPrecision | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeStruct %1 %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberDecorateBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 RelaxedPrecision | |
| %1 = OpTypeInt 32 0)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpMemberDecorate Structure type <id> '1[%uint]' is " | |
| "not a struct type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpMemberDecorateMemberBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 3 RelaxedPrecision | |
| %int = OpTypeInt 32 0 | |
| %1 = OpTypeStruct %int %int)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Index 3 provided in OpMemberDecorate for struct <id> " | |
| "'1[%_struct_1]' is out of bounds. The structure has 2 " | |
| "members. Largest valid index is 1."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupDecorateGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpDecorationGroup | |
| OpDecorate %1 RelaxedPrecision | |
| OpDecorate %1 GLSLShared | |
| OpGroupDecorate %1 %3 %4 | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 42 | |
| %4 = OpConstant %2 23)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpDecorationGroupBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpDecorationGroup | |
| OpDecorate %1 RelaxedPrecision | |
| OpDecorate %1 GLSLShared | |
| OpMemberDecorate %1 0 Constant | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Result id of OpDecorationGroup can only " | |
| "be targeted by OpName, OpGroupDecorate, " | |
| "OpDecorate, OpDecorateId, and OpGroupMemberDecorate"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpGroupDecorate %1 %2 %3 | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 42)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpGroupDecorate Decoration group <id> '1[%1]' is not " | |
| "a decoration group."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupDecorateTargetBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpDecorationGroup | |
| OpDecorate %1 RelaxedPrecision | |
| OpDecorate %1 GLSLShared | |
| OpGroupDecorate %1 %3 | |
| %2 = OpTypeInt 32 0)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("forward referenced IDs have not been defined"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpGroupMemberDecorate %1 %2 0 | |
| %2 = OpTypeInt 32 0)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpGroupMemberDecorate Decoration group <id> '1[%1]' " | |
| "is not a decoration group."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpDecorationGroup | |
| OpGroupMemberDecorate %1 %2 0 | |
| %2 = OpTypeInt 32 0)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpGroupMemberDecorate Structure type <id> '2[%uint]' " | |
| "is not a struct type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %1 Offset 0 | |
| %1 = OpDecorationGroup | |
| OpGroupMemberDecorate %1 %struct 3 | |
| %float = OpTypeFloat 32 | |
| %struct = OpTypeStruct %float %float %float | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Index 3 provided in OpGroupMemberDecorate for struct " | |
| "<id> '2[%_struct_2]' is out of bounds. The structure " | |
| "has 3 members. Largest valid index is 2."))); | |
| } | |
| // TODO: OpExtInst | |
| TEST_P(ValidateIdWithMessage, OpEntryPointGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %3 "" | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointFunctionBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %1 "" | |
| %1 = OpTypeVoid)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpEntryPoint Entry Point <id> '1[%void]' is not a " | |
| "function."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %1 "" | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 %2 | |
| %1 = OpFunction %2 None %3 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function " | |
| "parameter count is not zero"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %1 "" | |
| %2 = OpTypeInt 32 0 | |
| %ret = OpConstant %2 0 | |
| %3 = OpTypeFunction %2 | |
| %1 = OpFunction %2 None %3 | |
| %4 = OpLabel | |
| OpReturnValue %ret | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function " | |
| "return type is not void."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %1 "" | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 %2 | |
| %1 = OpFunction %2 None %3 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04633")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function " | |
| "parameter count is not zero"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %1 "" | |
| %2 = OpTypeInt 32 0 | |
| %ret = OpConstant %2 0 | |
| %3 = OpTypeFunction %2 | |
| %1 = OpFunction %2 None %3 | |
| %4 = OpLabel | |
| OpReturnValue %ret | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04633")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function " | |
| "return type is not void."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Geometry | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Geometry %main "main" %ptr_builtin_1 | |
| OpExecutionMode %main InputPoints | |
| OpExecutionMode %main OutputPoints | |
| OpMemberDecorate %struct_1 0 BuiltIn InvocationId | |
| %int = OpTypeInt 32 1 | |
| %void = OpTypeVoid | |
| %func = OpTypeFunction %void | |
| %struct_1 = OpTypeStruct %int | |
| %ptr_builtin_1 = OpTypePointer Input %struct_1 | |
| %main = OpFunction %void None %func | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Interfaces passed to OpEntryPoint must be of type " | |
| "OpTypeVariable. Found OpTypePointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Geometry | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Geometry %main "main" %in_1 | |
| OpExecutionMode %main InputPoints | |
| OpExecutionMode %main OutputPoints | |
| OpMemberDecorate %struct_1 0 BuiltIn InvocationId | |
| %int = OpTypeInt 32 1 | |
| %void = OpTypeVoid | |
| %func = OpTypeFunction %void | |
| %struct_1 = OpTypeStruct %int | |
| %ptr_builtin_1 = OpTypePointer Uniform %struct_1 | |
| %in_1 = OpVariable %ptr_builtin_1 Uniform | |
| %main = OpFunction %void None %func | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpEntryPoint interfaces must be OpVariables with " | |
| "Storage Class of Input(1) or Output(3). Found Storage " | |
| "Class 2 for Entry Point id 1."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpExecutionModeGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %3 "" | |
| OpExecutionMode %3 LocalSize 1 1 1 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpExecutionMode %3 LocalSize 1 1 1 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpExecutionMode Entry Point <id> '1[%1]' is not the " | |
| "Entry Point operand of an OpEntryPoint."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpEntryPoint GLCompute %3 "" %a | |
| OpExecutionMode %a LocalSize 1 1 1 | |
| %void = OpTypeVoid | |
| %ptr = OpTypePointer Input %void | |
| %a = OpVariable %ptr Input | |
| %2 = OpTypeFunction %void | |
| %3 = OpFunction %void None %2 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpExecutionMode Entry Point <id> '2[%2]' is not the " | |
| "Entry Point operand of an OpEntryPoint."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorFloat) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorInt) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeVector %1 4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorUInt) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 64 0 | |
| %2 = OpTypeVector %1 4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorBool) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpTypeVector %1 4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypePointer UniformConstant %1 | |
| %3 = OpTypeVector %2 4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeVector Component Type <id> " | |
| "'2[%_ptr_UniformConstant_float]' is not a scalar type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Illegal number of components (1) for TypeVector\n %v1float = " | |
| "OpTypeVector %float 1\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Illegal number of components (5) for TypeVector\n %v5float = " | |
| "OpTypeVector %float 5\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) { | |
| std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 8)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Having 8 components for TypeVector requires the Vector16 " | |
| "capability\n %v8float = OpTypeVector %float 8\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpTypeVectorColumnCountSixteenWithoutVector16Bad) { | |
| std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 16)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Having 16 components for TypeVector requires the Vector16 " | |
| "capability\n %v16float = OpTypeVector %float 16\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 8)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpTypeVectorColumnCountOfSixteenWithVector16Good) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 16)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeMatrixGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 2 | |
| %3 = OpTypeMatrix %2 3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeMatrix %1 3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "olumns in a matrix must be of type vector.\n %mat3float = " | |
| "OpTypeMatrix %float 3\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 16 0 | |
| %2 = OpTypeVector %1 2 | |
| %3 = OpTypeMatrix %2 2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Matrix types can only be parameterized with floating-point " | |
| "types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 2 | |
| %3 = OpTypeMatrix %2 1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Matrix types can only be parameterized as having only 2, 3, " | |
| "or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 2 | |
| %3 = OpTypeMatrix %2 8)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Matrix types can only be parameterized as having only 2, 3, " | |
| "or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeSamplerGood) { | |
| // In Rev31, OpTypeSampler takes no arguments. | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %s = OpTypeSampler)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeArrayGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 1 | |
| %3 = OpTypeArray %1 %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeArrayElementTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 1 | |
| %3 = OpTypeArray %2 %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeArray Element Type <id> '2[%uint_1]' is not a " | |
| "type."))); | |
| } | |
| // Signed or unsigned. | |
| enum Signed { kSigned, kUnsigned }; | |
| // Creates an assembly module declaring OpTypeArray with the given length. | |
| std::string MakeArrayLength(const std::string& len, Signed isSigned, int width, | |
| int max_int_width = 64, | |
| bool use_vulkan_memory_model = false) { | |
| std::ostringstream ss; | |
| ss << R"( | |
| OpCapability Shader | |
| )"; | |
| if (use_vulkan_memory_model) { | |
| ss << " OpCapability VulkanMemoryModel\n"; | |
| } | |
| if (width == 16) { | |
| ss << " OpCapability Int16\n"; | |
| } | |
| if (max_int_width > 32) { | |
| ss << "\n OpCapability Int64\n"; | |
| } | |
| if (use_vulkan_memory_model) { | |
| ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; | |
| ss << "OpMemoryModel Logical Vulkan\n"; | |
| } else { | |
| ss << "OpMemoryModel Logical GLSL450\n"; | |
| } | |
| ss << "OpEntryPoint GLCompute %main \"main\"\n"; | |
| ss << "OpExecutionMode %main LocalSize 1 1 1\n"; | |
| ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0"); | |
| ss << " %l = OpConstant %t " << len; | |
| ss << " %a = OpTypeArray %t %l"; | |
| ss << " %void = OpTypeVoid \n" | |
| " %voidfn = OpTypeFunction %void \n" | |
| " %main = OpFunction %void None %voidfn \n" | |
| " %entry = OpLabel\n" | |
| " OpReturn\n" | |
| " OpFunctionEnd\n"; | |
| return ss.str(); | |
| } | |
| // Tests OpTypeArray. Parameter is the width (in bits) of the array-length's | |
| // type. | |
| class OpTypeArrayLengthTest | |
| : public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> { | |
| protected: | |
| OpTypeArrayLengthTest() | |
| : env_(SPV_ENV_UNIVERSAL_1_0), | |
| position_(spv_position_t{0, 0, 0}), | |
| diagnostic_(spvDiagnosticCreate(&position_, "")) {} | |
| ~OpTypeArrayLengthTest() override { spvDiagnosticDestroy(diagnostic_); } | |
| // Runs spvValidate() on v, printing any errors via spvDiagnosticPrint(). | |
| spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") { | |
| spv_const_binary_t cbinary{v.data(), v.size()}; | |
| spvDiagnosticDestroy(diagnostic_); | |
| diagnostic_ = nullptr; | |
| const auto status = | |
| spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_); | |
| if (status != SPV_SUCCESS) { | |
| spvDiagnosticPrint(diagnostic_); | |
| EXPECT_THAT(std::string(diagnostic_->error), | |
| testing::ContainsRegex(expected_err)); | |
| } | |
| return status; | |
| } | |
| protected: | |
| spv_target_env env_; | |
| private: | |
| spv_position_t position_; // For creating diagnostic_. | |
| spv_diagnostic diagnostic_; | |
| }; | |
| TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) { | |
| const int width = GetParam(); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width)))); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("1", kUnsigned, width)))); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("2", kSigned, width)))); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("2", kUnsigned, width)))); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("55", kSigned, width)))); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("55", kUnsigned, width)))); | |
| const std::string fpad(width / 4 - 1, 'F'); | |
| EXPECT_EQ( | |
| SPV_SUCCESS, | |
| Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width)))) | |
| << MakeArrayLength("0x7" + fpad, kSigned, width); | |
| } | |
| TEST_P(OpTypeArrayLengthTest, LengthZero) { | |
| const int width = GetParam(); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| } | |
| TEST_P(OpTypeArrayLengthTest, LengthNegative) { | |
| const int width = GetParam(); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0'); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, | |
| Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)), | |
| "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " | |
| "least 1.")); | |
| } | |
| // Returns the string form of an integer of the form 0x80....0 of the | |
| // given bit width. | |
| std::string big_num_ending_0(int bit_width) { | |
| return "0x8" + std::string(bit_width / 4 - 1, '0'); | |
| } | |
| // Returns the string form of an integer of the form 0x80..001 of the | |
| // given bit width. | |
| std::string big_num_ending_1(int bit_width) { | |
| return "0x8" + std::string(bit_width / 4 - 2, '0') + "1"; | |
| } | |
| TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) { | |
| env_ = SPV_ENV_VULKAN_1_0; | |
| const int width = GetParam(); | |
| for (int max_int_width : {32, 64}) { | |
| if (width > max_int_width) { | |
| // Not valid to even make the OpConstant in this case. | |
| continue; | |
| } | |
| const auto module = CompileSuccessfully(MakeArrayLength( | |
| big_num_ending_0(width), kUnsigned, width, max_int_width)); | |
| EXPECT_EQ(SPV_SUCCESS, Val(module)); | |
| } | |
| } | |
| TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) { | |
| env_ = SPV_ENV_VULKAN_1_0; | |
| const int width = GetParam(); | |
| for (int max_int_width : {32, 64}) { | |
| if (width > max_int_width) { | |
| // Not valid to even make the OpConstant in this case. | |
| continue; | |
| } | |
| const auto module = CompileSuccessfully(MakeArrayLength( | |
| big_num_ending_1(width), kUnsigned, width, max_int_width)); | |
| EXPECT_EQ(SPV_SUCCESS, Val(module)); | |
| } | |
| } | |
| // The only valid widths for integers are 8, 16, 32, and 64. | |
| // Since the Int8 capability requires the Kernel capability, and the Kernel | |
| // capability prohibits usage of signed integers, we can skip 8-bit integers | |
| // here since the purpose of these tests is to check the validity of | |
| // OpTypeArray, not OpTypeInt. | |
| INSTANTIATE_TEST_SUITE_P(Widths, OpTypeArrayLengthTest, | |
| ValuesIn(std::vector<int>{16, 32, 64})); | |
| TEST_P(ValidateIdWithMessage, OpTypeArrayLengthNull) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %i32 = OpTypeInt 32 0 | |
| %len = OpConstantNull %i32 | |
| %ary = OpTypeArray %i32 %len)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("OpTypeArray Length <id> '2[%2]' default " | |
| "value must be at least 1."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %i32 = OpTypeInt 32 0 | |
| %len = OpSpecConstant %i32 2 | |
| %ary = OpTypeArray %i32 %len)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %i32 = OpTypeInt 32 0 | |
| %c1 = OpConstant %i32 1 | |
| %c2 = OpConstant %i32 2 | |
| %len = OpSpecConstantOp %i32 IAdd %c1 %c2 | |
| %ary = OpTypeArray %i32 %len)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeRuntimeArray %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 0 | |
| %3 = OpTypeRuntimeArray %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeRuntimeArray Element Type <id> '2[%uint_0]' is not a " | |
| "type."))); | |
| } | |
| // TODO: Object of this type can only be created with OpVariable using the | |
| // Uniform Storage Class | |
| TEST_P(ValidateIdWithMessage, OpTypeStructGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeFloat 64 | |
| %3 = OpTypePointer Input %1 | |
| %4 = OpTypeStruct %1 %2 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeStructMemberTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeFloat 64 | |
| %3 = OpConstant %2 0.0 | |
| %4 = OpTypeStruct %1 %2 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeStruct Member Type <id> '3[%double_0]' is not " | |
| "a type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Vertex %main "main" | |
| %1 = OpTypeSampler | |
| %2 = OpTypeStruct %1 | |
| %void = OpTypeVoid | |
| %3 = OpTypeFunction %void | |
| %main = OpFunction %void None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04667")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpTypeStruct must not contain an opaque type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypePointerGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer Input %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypePointerBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 0 | |
| %3 = OpTypePointer Input %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpTypePointer Type <id> '2[%uint_0]' is not a " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeFunctionGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 0 | |
| %3 = OpTypeFunction %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeFunction Return Type <id> '2[%uint_0]' is not " | |
| "a type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 0 | |
| %4 = OpTypeFunction %1 %2 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeFunction Parameter Type <id> '3[%uint_0]' is not a " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %4 = OpTypeFunction %1 %2 %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpTypeFunction Parameter Type <id> '1[%void]' cannot " | |
| "be OpTypeVoid."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypePipeGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 16 | |
| %3 = OpTypePipe ReadOnly)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantTrueGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpConstantTrue %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantTrueBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpConstantTrue %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantTrue Result Type <id> '1[%void]' is not a boolean " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantFalseGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpConstantTrue %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantFalseBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpConstantFalse %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantFalse Result Type <id> '1[%void]' is not a boolean " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpConstant !1 !0)"; | |
| // The expected failure code is implementation dependent (currently | |
| // INVALID_BINARY because the binary parser catches these cases) and may | |
| // change over time, but this must always fail. | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpConstant %1 3.14 | |
| %4 = OpConstantComposite %2 %3 %3 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpConstant %1 3.14 | |
| %9 = OpUndef %1 | |
| %4 = OpConstantComposite %2 %3 %3 %3 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpConstant %1 3.14 | |
| %4 = OpConstantComposite %1 %3 %3 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Result Type <id> '1[%float]' is not a " | |
| "composite type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %4 = OpTypeInt 32 0 | |
| %3 = OpConstant %1 3.14 | |
| %5 = OpConstant %4 42 ; bad type for constant value | |
| %6 = OpConstantComposite %2 %3 %5 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '5[%uint_42]'s type " | |
| "does not match Result Type <id> '2[%v4float]'s vector " | |
| "element type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpConstantCompositeVectorConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %4 = OpTypeInt 32 0 | |
| %3 = OpConstant %1 3.14 | |
| %5 = OpUndef %4 ; bad type for undef value | |
| %6 = OpConstantComposite %2 %3 %5 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '5[%5]'s type does not " | |
| "match Result Type <id> '2[%v4float]'s vector element type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpConstant %1 0.0 | |
| %6 = OpConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpConstantComposite %2 %5 %5 %5 %4 | |
| %10 = OpConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpConstant %1 0.0 | |
| %6 = OpConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpUndef %2 | |
| %10 = OpConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %11 = OpTypeVector %1 3 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpConstant %1 0.0 | |
| %6 = OpConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpConstantComposite %11 %5 %5 %5 | |
| %10 = OpConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '10[%10]' vector " | |
| "component count does not match Result Type <id> " | |
| "'4[%mat4v4float]'s vector component count."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpConstantCompositeMatrixConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %11 = OpTypeVector %1 3 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpConstant %1 0.0 | |
| %6 = OpConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpUndef %11 | |
| %10 = OpConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '10[%10]' vector " | |
| "component count does not match Result Type <id> " | |
| "'4[%mat4v4float]'s vector component count."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpConstantComposite %3 %2 %2 %2 %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %9 = OpUndef %1 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpConstantComposite %3 %2 %2 %2 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpConstantComposite %3 %2 %2 %2 %1)"; // Uses a type as operand | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '1[%uint]' cannot be a " | |
| "type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpTypePointer Uniform %1 | |
| %5 = OpVariable %4 Uniform | |
| %6 = OpConstantComposite %3 %2 %2 %2 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '5[%5]' is not a " | |
| "constant or undef."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpConstant %5 3.14 ; bad type for const value | |
| %4 = OpConstantComposite %3 %2 %2 %2 %6)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> " | |
| "'5[%float_3_1400001]'s type does not match Result " | |
| "Type <id> '3[%_arr_uint_uint_4]'s array element " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpUndef %5 ; bad type for undef | |
| %4 = OpConstantComposite %3 %2 %2 %2 %6)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpConstantComposite Constituent <id> " | |
| "'5[%5]'s type does not match Result " | |
| "Type <id> '3[%_arr_uint_uint_4]'s array element " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeStructGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpConstant %2 4300000000 | |
| %6 = OpConstantComposite %3 %4 %4 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpUndef %2 | |
| %6 = OpConstantComposite %3 %4 %4 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpConstant %2 4300000000 | |
| %6 = OpConstantComposite %3 %4 %5 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> " | |
| "'5[%ulong_4300000000]' type does not match the " | |
| "Result Type <id> '3[%_struct_3]'s member type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpUndef %2 | |
| %6 = OpConstantComposite %3 %4 %5 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantComposite Constituent <id> '5[%5]' type " | |
| "does not match the Result Type <id> '3[%_struct_3]'s " | |
| "member type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantSamplerGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %float = OpTypeFloat 32 | |
| %samplerType = OpTypeSampler | |
| %3 = OpConstantSampler %samplerType ClampToEdge 0 Nearest)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpConstantSampler %1 Clamp 0 Nearest)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantSampler Result Type <id> '1[%float]' is not a sampler " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantNullGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpConstantNull %1 | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpConstantNull %3 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpConstantNull %5 | |
| %7 = OpTypePointer UniformConstant %3 | |
| %8 = OpConstantNull %7 | |
| %9 = OpTypeEvent | |
| %10 = OpConstantNull %9 | |
| %11 = OpTypeDeviceEvent | |
| %12 = OpConstantNull %11 | |
| %13 = OpTypeReserveId | |
| %14 = OpConstantNull %13 | |
| %15 = OpTypeQueue | |
| %16 = OpConstantNull %15 | |
| %17 = OpTypeVector %5 2 | |
| %18 = OpConstantNull %17 | |
| %19 = OpTypeMatrix %17 2 | |
| %20 = OpConstantNull %19 | |
| %25 = OpConstant %3 8 | |
| %21 = OpTypeArray %3 %25 | |
| %22 = OpConstantNull %21 | |
| %23 = OpTypeStruct %3 %5 %1 | |
| %24 = OpConstantNull %23 | |
| %26 = OpTypeArray %17 %25 | |
| %27 = OpConstantNull %26 | |
| %28 = OpTypeStruct %7 %26 %26 %1 | |
| %29 = OpConstantNull %28 | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantNullBasicBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpConstantNull %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantNull Result Type <id> '1[%void]' cannot have a null " | |
| "value."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantNullArrayBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeSampler | |
| %4 = OpConstant %2 4 | |
| %5 = OpTypeArray %3 %4 | |
| %6 = OpConstantNull %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantNull Result Type <id> '4[%_arr_2_uint_4]' cannot have a " | |
| "null value."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantNullStructBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %2 = OpTypeSampler | |
| %3 = OpTypeStruct %2 %2 | |
| %4 = OpConstantNull %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpConstantNull Result Type <id> '2[%_struct_2]' " | |
| "cannot have a null value."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %bool = OpTypeBool | |
| %array = OpTypeRuntimeArray %bool | |
| %null = OpConstantNull %array)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpConstantNull Result Type <id> '2[%_runtimearr_bool]' cannot have " | |
| "a null value."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantTrueGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpSpecConstantTrue %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantTrueBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpSpecConstantTrue %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantTrue Result Type <id> '1[%void]' is not " | |
| "a boolean type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantFalseGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeBool | |
| %2 = OpSpecConstantFalse %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantFalseBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpSpecConstantFalse %1)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantFalse Result Type <id> '1[%void]' is not " | |
| "a boolean type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpSpecConstant %1 42)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpSpecConstant !1 !4)"; | |
| // The expected failure code is implementation dependent (currently | |
| // INVALID_BINARY because the binary parser catches these cases) and may | |
| // change over time, but this must always fail. | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Type Id 1 is not a scalar numeric type"))); | |
| } | |
| // Valid: SpecConstantComposite specializes to a vector. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpSpecConstant %1 3.14 | |
| %4 = OpConstant %1 3.14 | |
| %5 = OpSpecConstantComposite %2 %3 %3 %4 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Valid: Vector of floats and Undefs. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpSpecConstant %1 3.14 | |
| %5 = OpConstant %1 3.14 | |
| %9 = OpUndef %1 | |
| %4 = OpSpecConstantComposite %2 %3 %5 %3 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: result type is float. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpSpecConstant %1 3.14 | |
| %4 = OpSpecConstantComposite %1 %3 %3 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("is not a composite type"))); | |
| } | |
| // Invalid: Vector contains a mix of Int and Float. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %4 = OpTypeInt 32 0 | |
| %3 = OpSpecConstant %1 3.14 | |
| %5 = OpConstant %4 42 ; bad type for constant value | |
| %6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> " | |
| "'5[%uint_42]'s type does not match Result Type <id> " | |
| "'2[%v4float]'s vector element type."))); | |
| } | |
| // Invalid: Constituent is not a constant | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeVectorConstituentNotConstantBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpSpecConstant %1 3.14 | |
| %5 = OpTypePointer Uniform %1 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpSpecConstantComposite %2 %6 %4 %4 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '6[%6]' is " | |
| "not a constant or undef."))); | |
| } | |
| // Invalid: Vector contains a mix of Undef-int and Float. | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeVectorConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %4 = OpTypeInt 32 0 | |
| %3 = OpSpecConstant %1 3.14 | |
| %5 = OpUndef %4 ; bad type for undef value | |
| %6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]'s " | |
| "type does not match Result Type <id> '2[%v4float]'s " | |
| "vector element type."))); | |
| } | |
| // Invalid: Vector expects 3 components, but 4 specified. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 3 | |
| %3 = OpConstant %1 3.14 | |
| %5 = OpSpecConstant %1 4.0 | |
| %6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> count does " | |
| "not match Result Type <id> '2[%v3float]'s vector " | |
| "component count."))); | |
| } | |
| // Valid: 4x4 matrix of floats | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpSpecConstant %1 0.0 | |
| %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpSpecConstantComposite %2 %5 %5 %5 %4 | |
| %10 = OpSpecConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Valid: Matrix in which one column is Undef | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpConstant %1 1.0 | |
| %5 = OpSpecConstant %1 0.0 | |
| %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 | |
| %9 = OpUndef %2 | |
| %10 = OpSpecConstantComposite %3 %6 %7 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: Matrix in which the sizes of column vectors are not equal. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeVector %1 3 | |
| %4 = OpTypeMatrix %2 4 | |
| %5 = OpSpecConstant %1 1.0 | |
| %6 = OpConstant %1 0.0 | |
| %7 = OpSpecConstantComposite %2 %5 %6 %6 %6 | |
| %8 = OpSpecConstantComposite %2 %6 %5 %6 %6 | |
| %9 = OpSpecConstantComposite %2 %6 %6 %5 %6 | |
| %10 = OpSpecConstantComposite %3 %6 %6 %6 | |
| %11 = OpSpecConstantComposite %4 %7 %8 %9 %10)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '10[%10]' " | |
| "vector component count does not match Result Type " | |
| "<id> '4[%mat4v4float]'s vector component count."))); | |
| } | |
| // Invalid: Matrix type expects 4 columns but only 3 specified. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeMatrix %2 4 | |
| %4 = OpSpecConstant %1 1.0 | |
| %5 = OpConstant %1 0.0 | |
| %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 | |
| %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 | |
| %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 | |
| %10 = OpSpecConstantComposite %3 %6 %7 %8)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> count does " | |
| "not match Result Type <id> '3[%mat4v4float]'s matrix column " | |
| "count."))); | |
| } | |
| // Invalid: Composite contains a non-const/undef component | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeMatrixConstituentNotConstBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpConstant %1 0.0 | |
| %3 = OpTypeVector %1 4 | |
| %4 = OpTypeMatrix %3 4 | |
| %5 = OpSpecConstantComposite %3 %2 %2 %2 %2 | |
| %6 = OpTypePointer Uniform %1 | |
| %7 = OpVariable %6 Uniform | |
| %8 = OpSpecConstantComposite %4 %5 %5 %5 %7)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '7[%7]' is " | |
| "not a constant or undef."))); | |
| } | |
| // Invalid: Composite contains a column that is *not* a vector (it's an array) | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpSpecConstant %2 4 | |
| %4 = OpConstant %1 0.0 | |
| %5 = OpTypeVector %1 4 | |
| %6 = OpTypeArray %2 %3 | |
| %7 = OpTypeMatrix %5 4 | |
| %8 = OpSpecConstantComposite %6 %3 %3 %3 %3 | |
| %9 = OpSpecConstantComposite %5 %4 %4 %4 %4 | |
| %10 = OpSpecConstantComposite %7 %9 %9 %9 %8)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '8[%8]' type " | |
| "does not match Result Type <id> '7[%mat4v4float]'s " | |
| "matrix column type."))); | |
| } | |
| // Invalid: Matrix with an Undef column of the wrong size. | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeMatrixConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeVector %1 4 | |
| %3 = OpTypeVector %1 3 | |
| %4 = OpTypeMatrix %2 4 | |
| %5 = OpSpecConstant %1 1.0 | |
| %6 = OpSpecConstant %1 0.0 | |
| %7 = OpSpecConstantComposite %2 %5 %6 %6 %6 | |
| %8 = OpSpecConstantComposite %2 %6 %5 %6 %6 | |
| %9 = OpSpecConstantComposite %2 %6 %6 %5 %6 | |
| %10 = OpUndef %3 | |
| %11 = OpSpecConstantComposite %4 %7 %8 %9 %10)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '10[%10]' " | |
| "vector component count does not match Result Type " | |
| "<id> '4[%mat4v4float]'s vector component count."))); | |
| } | |
| // Invalid: Matrix in which some columns are Int and some are Float. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeFloat 32 | |
| %3 = OpTypeVector %1 2 | |
| %4 = OpTypeVector %2 2 | |
| %5 = OpTypeMatrix %4 2 | |
| %6 = OpSpecConstant %1 42 | |
| %7 = OpConstant %2 3.14 | |
| %8 = OpSpecConstantComposite %3 %6 %6 | |
| %9 = OpSpecConstantComposite %4 %7 %7 | |
| %10 = OpSpecConstantComposite %5 %8 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '8[%8]' " | |
| "component type does not match Result Type <id> " | |
| "'5[%mat2v2float]'s matrix column component type."))); | |
| } | |
| // Valid: Array of integers | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpSpecConstant %1 4 | |
| %5 = OpConstant %1 5 | |
| %3 = OpTypeArray %1 %2 | |
| %6 = OpTypeArray %1 %5 | |
| %4 = OpSpecConstantComposite %3 %2 %2 %2 %2 | |
| %7 = OpSpecConstantComposite %3 %5 %5 %5 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: Expecting an array of 4 components, but 3 specified. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpSpecConstantComposite %3 %2 %2 %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent count does not " | |
| "match Result Type <id> '3[%_arr_uint_uint_4]'s array " | |
| "length."))); | |
| } | |
| // Valid: Array of Integers and Undef-int | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpSpecConstant %1 4 | |
| %9 = OpUndef %1 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpSpecConstantComposite %3 %2 %2 %2 %9)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: Array uses a type as operand. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpTypePointer Uniform %1 | |
| %5 = OpVariable %4 Uniform | |
| %6 = OpSpecConstantComposite %3 %2 %2 %2 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]' is " | |
| "not a constant or undef."))); | |
| } | |
| // Invalid: Array has a mix of Int and Float components. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %4 = OpTypeFloat 32 | |
| %5 = OpSpecConstant %4 3.14 ; bad type for const value | |
| %6 = OpSpecConstantComposite %3 %2 %2 %2 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]'s " | |
| "type does not match Result Type <id> " | |
| "'3[%_arr_uint_uint_4]'s array element type."))); | |
| } | |
| // Invalid: Array has a mix of Int and Undef-float. | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeArrayConstituentUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpSpecConstant %1 4 | |
| %3 = OpTypeArray %1 %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpUndef %5 ; bad type for undef | |
| %4 = OpSpecConstantComposite %3 %2 %2 %2 %6)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]'s " | |
| "type does not match Result Type <id> " | |
| "'3[%_arr_uint_2]'s array element type."))); | |
| } | |
| // Valid: Struct of {Int32,Int32,Int64}. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpSpecConstant %2 4300000000 | |
| %6 = OpSpecConstantComposite %3 %4 %4 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: missing one int32 struct member. | |
| TEST_P(ValidateIdWithMessage, | |
| OpSpecConstantCompositeStructMissingComponentBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %3 = OpTypeStruct %1 %1 %1 | |
| %4 = OpConstant %1 42 | |
| %5 = OpSpecConstant %1 430 | |
| %6 = OpSpecConstantComposite %3 %4 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpSpecConstantComposite Constituent <id> " | |
| "'2[%_struct_2]' count does not match Result Type " | |
| "<id> '2[%_struct_2]'s struct member count."))); | |
| } | |
| // Valid: Struct uses Undef-int64. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpSpecConstant %1 42 | |
| %5 = OpUndef %2 | |
| %6 = OpSpecConstantComposite %3 %4 %4 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: Composite contains non-const/undef component. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpSpecConstant %1 42 | |
| %5 = OpUndef %2 | |
| %6 = OpTypePointer Uniform %1 | |
| %7 = OpVariable %6 Uniform | |
| %8 = OpSpecConstantComposite %3 %4 %7 %5)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '7[%7]' is " | |
| "not a constant or undef."))); | |
| } | |
| // Invalid: Struct component type does not match expected specialization type. | |
| // Second component was expected to be Int32, but got Int64. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpConstant %1 42 | |
| %5 = OpSpecConstant %2 4300000000 | |
| %6 = OpSpecConstantComposite %3 %4 %5 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]' type " | |
| "does not match the Result Type <id> '3[%_struct_3]'s " | |
| "member type."))); | |
| } | |
| // Invalid: Undef-int64 used when Int32 was expected. | |
| TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpTypeStruct %1 %1 %2 | |
| %4 = OpSpecConstant %1 42 | |
| %5 = OpUndef %2 | |
| %6 = OpSpecConstantComposite %3 %4 %5 %4)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpSpecConstantComposite Constituent <id> '5[%5]' type " | |
| "does not match the Result Type <id> '3[%_struct_3]'s " | |
| "member type."))); | |
| } | |
| // TODO: OpSpecConstantOp | |
| TEST_P(ValidateIdWithMessage, OpVariableGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer Input %1 | |
| %3 = OpVariable %2 Input)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableInitializerConstantGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer Output %1 | |
| %3 = OpConstant %1 42 | |
| %4 = OpVariable %2 Output %3)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer Uniform %1 | |
| %3 = OpVariable %2 Uniform | |
| %4 = OpTypePointer Private %2 ; pointer to pointer | |
| %5 = OpVariable %4 Private %3 | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // TODO: Positive test OpVariable with OpConstantNull of OpTypePointer | |
| TEST_P(ValidateIdWithMessage, OpVariableResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpVariable %1 Input)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpVariable Result Type <id> '1[%uint]' is not a pointer " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypePointer Input %1 | |
| %3 = OpVariable %2 Input %2)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '2[%_ptr_Input_uint]' " | |
| "cannot be a type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ptrint = OpTypePointer Function %int | |
| %ptrptrint = OpTypePointer Function %ptrint | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %var = OpVariable %ptrint Function | |
| %varinit = OpVariable %ptrptrint Function %var ; Can't initialize function variable. | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpVariable Initializer <id> '8[%8]' is not a constant " | |
| "or module-scope variable"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ptrint = OpTypePointer Uniform %int | |
| %mvar = OpVariable %ptrint Uniform | |
| %ptrptrint = OpTypePointer Function %ptrint | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %goodvar = OpVariable %ptrptrint Function %mvar ; This is ok | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsBoolBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %bool = OpTypeBool | |
| %int = OpTypeInt 32 0 | |
| %block = OpTypeStruct %bool %int | |
| %_ptr_Uniform_block = OpTypePointer Uniform %block | |
| %var = OpVariable %_ptr_Uniform_block Uniform | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %block %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "If OpTypeBool is stored in conjunction with OpVariable, it can only " | |
| "be used with non-externally visible shader Storage Classes: " | |
| "Workgroup, CrossWorkgroup, Private, Function, Input, Output, " | |
| "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, " | |
| "CallableDataKHR, or IncomingCallableDataKHR"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %bool = OpTypeBool | |
| %int = OpTypeInt 32 0 | |
| %block = OpTypeStruct %bool %int | |
| %_ptr_Private_block = OpTypePointer Private %block | |
| %var = OpVariable %_ptr_Private_block Private | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %block %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %bool = OpTypeBool | |
| %boolptr = OpTypePointer Uniform %bool | |
| %int = OpTypeInt 32 0 | |
| %block = OpTypeStruct %boolptr %int | |
| %_ptr_Uniform_block = OpTypePointer Uniform %block | |
| %var = OpVariable %_ptr_Uniform_block Uniform | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %block %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %input 0 BuiltIn FrontFacing | |
| %bool = OpTypeBool | |
| %input = OpTypeStruct %bool | |
| %_ptr_input = OpTypePointer Input %input | |
| %var = OpVariable %_ptr_input Input | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %input %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %bool = OpTypeBool | |
| %input = OpTypeStruct %bool | |
| %_ptr_input = OpTypePointer Input %input | |
| %var = OpVariable %_ptr_input Input | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %input %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "If OpTypeBool is stored in conjunction with OpVariable using Input " | |
| "or Output Storage Classes it requires a BuiltIn decoration"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBadVulkan) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %main "main" %var | |
| OpExecutionMode %main OriginUpperLeft | |
| %bool = OpTypeBool | |
| %input = OpTypeStruct %bool | |
| %_ptr_input = OpTypePointer Input %input | |
| %var = OpVariable %_ptr_input Input | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %input %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-Input-07290")); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "If OpTypeBool is stored in conjunction with OpVariable using Input " | |
| "or Output Storage Classes it requires a BuiltIn decoration"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) { | |
| std::string spirv = R"( | |
| OpCapability RayTracingNV | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpExtension "SPV_NV_ray_tracing" | |
| OpMemoryModel Logical GLSL450 | |
| %bool = OpTypeBool | |
| %PerRayData = OpTypeStruct %bool | |
| %_ptr_PerRayData = OpTypePointer RayPayloadNV %PerRayData | |
| %var = OpVariable %_ptr_PerRayData RayPayloadNV | |
| %void = OpTypeVoid | |
| %fnty = OpTypeFunction %void | |
| %main = OpFunction %void None %fnty | |
| %entry = OpLabel | |
| %load = OpLoad %PerRayData %var | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %void = OpTypeVoid | |
| %int = OpTypeInt 32 0 | |
| %_ptr_workgroup_int = OpTypePointer Workgroup %int | |
| %_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int | |
| %voidfn = OpTypeFunction %void | |
| %func = OpFunction %void None %voidfn | |
| %entry = OpLabel | |
| %var = OpVariable %_ptr_function_ptr Function | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "In Logical addressing, variables may not allocate a pointer type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpVariablePointerNoVariablePointersRelaxedLogicalGood) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %void = OpTypeVoid | |
| %int = OpTypeInt 32 0 | |
| %_ptr_workgroup_int = OpTypePointer Workgroup %int | |
| %_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int | |
| %voidfn = OpTypeFunction %void | |
| %func = OpFunction %void None %voidfn | |
| %entry = OpLabel | |
| %var = OpVariable %_ptr_function_ptr Function | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| auto options = getValidatorOptions(); | |
| options->relax_logical_pointer = true; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) { | |
| // DXC generates code that looks like when given something like: | |
| // T t; | |
| // t.s.fn_1(); | |
| // This needs to be accepted before legalization takes place, so we | |
| // will include it with the relaxed logical pointer. | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Vertex %1 "main" | |
| OpSource HLSL 600 | |
| %int = OpTypeInt 32 1 | |
| %int_0 = OpConstant %int 0 | |
| %void = OpTypeVoid | |
| %9 = OpTypeFunction %void | |
| %_struct_5 = OpTypeStruct | |
| %_struct_6 = OpTypeStruct %_struct_5 | |
| %_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 | |
| %_ptr_Function__struct_5 = OpTypePointer Function %_struct_5 | |
| %23 = OpTypeFunction %void %_ptr_Function__struct_5 | |
| %1 = OpFunction %void None %9 | |
| %10 = OpLabel | |
| %11 = OpVariable %_ptr_Function__struct_6 Function | |
| %20 = OpAccessChain %_ptr_Function__struct_5 %11 %int_0 | |
| %21 = OpFunctionCall %void %12 %20 | |
| OpReturn | |
| OpFunctionEnd | |
| %12 = OpFunction %void None %23 | |
| %13 = OpFunctionParameter %_ptr_Function__struct_5 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| auto options = getValidatorOptions(); | |
| options->relax_logical_pointer = true; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| OpVariablePointerVariablePointersStorageBufferGood) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VariablePointersStorageBuffer | |
| OpExtension "SPV_KHR_variable_pointers" | |
| OpMemoryModel Logical GLSL450 | |
| %void = OpTypeVoid | |
| %int = OpTypeInt 32 0 | |
| %_ptr_workgroup_int = OpTypePointer Workgroup %int | |
| %_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int | |
| %voidfn = OpTypeFunction %void | |
| %func = OpFunction %void None %voidfn | |
| %entry = OpLabel | |
| %var = OpVariable %_ptr_function_ptr Function | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VariablePointers | |
| OpExtension "SPV_KHR_variable_pointers" | |
| OpMemoryModel Logical GLSL450 | |
| %void = OpTypeVoid | |
| %int = OpTypeInt 32 0 | |
| %_ptr_workgroup_int = OpTypePointer Workgroup %int | |
| %_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int | |
| %voidfn = OpTypeFunction %void | |
| %func = OpFunction %void None %voidfn | |
| %entry = OpLabel | |
| %var = OpVariable %_ptr_function_ptr Function | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VariablePointers | |
| OpExtension "SPV_KHR_variable_pointers" | |
| OpMemoryModel Logical GLSL450 | |
| %void = OpTypeVoid | |
| %int = OpTypeInt 32 0 | |
| %_ptr_workgroup_int = OpTypePointer Workgroup %int | |
| %_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int | |
| %var = OpVariable %_ptr_uniform_ptr Uniform | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "In Logical addressing with variable pointers, " | |
| "variables that allocate pointers must be in Function " | |
| "or Private storage classes"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLoadGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpVariable %3 UniformConstant | |
| %6 = OpFunction %1 None %4 | |
| %7 = OpLabel | |
| %8 = OpLoad %2 %5 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // TODO: Add tests that exercise VariablePointersStorageBuffer instead of | |
| // VariablePointers. | |
| void createVariablePointerSpirvProgram(std::ostringstream* spirv, | |
| std::string result_strategy, | |
| bool use_varptr_cap, | |
| bool add_helper_function) { | |
| *spirv << "OpCapability Shader "; | |
| if (use_varptr_cap) { | |
| *spirv << "OpCapability VariablePointers "; | |
| *spirv << "OpExtension \"SPV_KHR_variable_pointers\" "; | |
| } | |
| *spirv << "OpExtension \"SPV_KHR_storage_buffer_storage_class\" "; | |
| *spirv << R"( | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %main "main" | |
| %void = OpTypeVoid | |
| %voidf = OpTypeFunction %void | |
| %bool = OpTypeBool | |
| %i32 = OpTypeInt 32 1 | |
| %f32 = OpTypeFloat 32 | |
| %f32ptr = OpTypePointer StorageBuffer %f32 | |
| %i = OpConstant %i32 1 | |
| %zero = OpConstant %i32 0 | |
| %float_1 = OpConstant %f32 1.0 | |
| %ptr1 = OpVariable %f32ptr StorageBuffer | |
| %ptr2 = OpVariable %f32ptr StorageBuffer | |
| )"; | |
| if (add_helper_function) { | |
| *spirv << R"( | |
| ; //////////////////////////////////////////////////////////// | |
| ;;;; Function that returns a pointer | |
| ; //////////////////////////////////////////////////////////// | |
| %selector_func_type = OpTypeFunction %f32ptr %bool %f32ptr %f32ptr | |
| %choose_input_func = OpFunction %f32ptr None %selector_func_type | |
| %is_neg_param = OpFunctionParameter %bool | |
| %first_ptr_param = OpFunctionParameter %f32ptr | |
| %second_ptr_param = OpFunctionParameter %f32ptr | |
| %selector_func_begin = OpLabel | |
| %result_ptr = OpSelect %f32ptr %is_neg_param %first_ptr_param %second_ptr_param | |
| OpReturnValue %result_ptr | |
| OpFunctionEnd | |
| )"; | |
| } | |
| *spirv << R"( | |
| %main = OpFunction %void None %voidf | |
| %label = OpLabel | |
| )"; | |
| *spirv << result_strategy; | |
| *spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| } | |
| // With the VariablePointer Capability, OpLoad should allow loading a | |
| // VaiablePointer. In this test the variable pointer is obtained by an OpSelect | |
| TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { | |
| std::string result_strategy = R"( | |
| %isneg = OpSLessThan %bool %i %zero | |
| %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 | |
| %result = OpLoad %f32 %varptr | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| true /* Add VariablePointers Capability? */, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Without the VariablePointers Capability, OpLoad will not allow loading | |
| // through a variable pointer. | |
| // Disabled since using OpSelect with pointers without VariablePointers will | |
| // fail LogicalsPass. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { | |
| std::string result_strategy = R"( | |
| %isneg = OpSLessThan %bool %i %zero | |
| %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 | |
| %result = OpLoad %f32 %varptr | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| false /* Add VariablePointers Capability?*/, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("is not a logical pointer."))); | |
| } | |
| // With the VariablePointer Capability, OpLoad should allow loading a | |
| // VaiablePointer. In this test the variable pointer is obtained by an OpPhi | |
| TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) { | |
| std::string result_strategy = R"( | |
| %is_neg = OpSLessThan %bool %i %zero | |
| OpSelectionMerge %end_label None | |
| OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2 | |
| %take_ptr_1 = OpLabel | |
| OpBranch %end_label | |
| %take_ptr_2 = OpLabel | |
| OpBranch %end_label | |
| %end_label = OpLabel | |
| %varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2 | |
| %result = OpLoad %f32 %varptr | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| true /* Add VariablePointers Capability?*/, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Without the VariablePointers Capability, OpPhi can have a pointer result | |
| // type. | |
| TEST_P(ValidateIdWithMessage, OpPhiBad) { | |
| std::string result_strategy = R"( | |
| %is_neg = OpSLessThan %bool %i %zero | |
| OpSelectionMerge %end_label None | |
| OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2 | |
| %take_ptr_1 = OpLabel | |
| OpBranch %end_label | |
| %take_ptr_2 = OpLabel | |
| OpBranch %end_label | |
| %end_label = OpLabel | |
| %varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2 | |
| %result = OpLoad %f32 %varptr | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| false /* Add VariablePointers Capability?*/, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Using pointers with OpPhi requires capability " | |
| "VariablePointers or VariablePointersStorageBuffer"))); | |
| } | |
| // With the VariablePointer Capability, OpLoad should allow loading through a | |
| // VaiablePointer. In this test the variable pointer is obtained from an | |
| // OpFunctionCall (return value from a function) | |
| TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) { | |
| std::ostringstream spirv; | |
| std::string result_strategy = R"( | |
| %isneg = OpSLessThan %bool %i %zero | |
| %varptr = OpFunctionCall %f32ptr %choose_input_func %isneg %ptr1 %ptr2 | |
| %result = OpLoad %f32 %varptr | |
| )"; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| true /* Add VariablePointers Capability?*/, | |
| true /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLoadResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpVariable %3 UniformConstant | |
| %6 = OpFunction %1 None %4 | |
| %7 = OpLabel | |
| %8 = OpLoad %3 %5 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpLoad Result Type <id> " | |
| "'3[%_ptr_UniformConstant_uint]' does not match " | |
| "Pointer <id> '5[%5]'s type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLoadPointerBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpLabel | |
| %7 = OpLoad %2 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| // Prove that SSA checks trigger for a bad Id value. | |
| // The next test case show the not-a-logical-pointer case. | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '8[%8]' has not been " | |
| "defined"))); | |
| } | |
| // Disabled as bitcasting type to object is now not valid. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypePointer UniformConstant %2 | |
| %5 = OpTypePointer UniformConstant %3 | |
| %6 = OpTypeFunction %1 | |
| %7 = OpFunction %1 None %6 | |
| %8 = OpLabel | |
| %9 = OpBitcast %5 %4 ; Not valid in logical addressing | |
| %10 = OpLoad %3 %9 ; Should trigger message | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| // Once we start checking bitcasts, we might catch that | |
| // as the error first, instead of catching it here. | |
| // I don't know if it's possible to generate a bad case | |
| // if/when the validator is complete. | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpLoad Pointer <id> '9' is not a logical pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 | |
| %6 = OpVariable %3 Uniform | |
| %7 = OpFunction %1 None %4 | |
| %8 = OpLabel | |
| OpStore %6 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStorePointerBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 | |
| %6 = OpVariable %3 UniformConstant | |
| %7 = OpConstant %2 0 | |
| %8 = OpFunction %1 None %4 | |
| %9 = OpLabel | |
| OpStore %7 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '7[%uint_0]' is not a logical " | |
| "pointer."))); | |
| } | |
| // Disabled as bitcasting type to object is now not valid. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypePointer UniformConstant %2 | |
| %5 = OpTypePointer UniformConstant %3 | |
| %6 = OpTypeFunction %1 | |
| %7 = OpConstantNull %5 | |
| %8 = OpFunction %1 None %6 | |
| %9 = OpLabel | |
| %10 = OpBitcast %5 %4 ; Not valid in logical addressing | |
| %11 = OpStore %10 %7 ; Should trigger message | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '10' is not a logical pointer."))); | |
| } | |
| // Without the VariablePointer Capability, OpStore should may not store | |
| // through a variable pointer. | |
| // Disabled since using OpSelect with pointers without VariablePointers will | |
| // fail LogicalsPass. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { | |
| std::string result_strategy = R"( | |
| %isneg = OpSLessThan %bool %i %zero | |
| %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 | |
| OpStore %varptr %float_1 | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram( | |
| &spirv, result_strategy, false /* Add VariablePointers Capability? */, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("is not a logical pointer."))); | |
| } | |
| // With the VariablePointer Capability, OpStore should allow storing through a | |
| // variable pointer. | |
| TEST_P(ValidateIdWithMessage, OpStoreVarPtrGood) { | |
| std::string result_strategy = R"( | |
| %isneg = OpSLessThan %bool %i %zero | |
| %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 | |
| OpStore %varptr %float_1 | |
| )"; | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, result_strategy, | |
| true /* Add VariablePointers Capability? */, | |
| false /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreObjectGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 | |
| %6 = OpVariable %3 Uniform | |
| %7 = OpFunction %1 None %4 | |
| %8 = OpLabel | |
| %9 = OpFunctionCall %1 %10 | |
| OpStore %6 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpStore Object <id> '9[%9]'s type is void."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %9 = OpTypeFloat 32 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %9 3.14 | |
| %6 = OpVariable %3 Uniform | |
| %7 = OpFunction %1 None %4 | |
| %8 = OpLabel | |
| OpStore %6 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '7[%7]'s type does not match " | |
| "Object <id> '6[%float_3_1400001]'s type."))); | |
| } | |
| // The next series of test check test a relaxation of the rules for stores to | |
| // structs. The first test checks that we get a failure when the option is not | |
| // set to relax the rule. | |
| // TODO: Add tests for layout compatible arrays and matricies when the validator | |
| // relaxes the rules for them as well. Also need test to check for layout | |
| // decorations specific to those types. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeBadStruct) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 0 | |
| OpMemberDecorate %1 1 Offset 4 | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 4 | |
| %3 = OpTypeVoid | |
| %4 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %4 %4 | |
| %5 = OpTypePointer Uniform %1 | |
| %2 = OpTypeStruct %4 %4 | |
| %6 = OpTypeFunction %3 | |
| %7 = OpConstant %4 3.14 | |
| %8 = OpVariable %5 Uniform | |
| %9 = OpFunction %3 None %6 | |
| %10 = OpLabel | |
| %11 = OpCompositeConstruct %2 %7 %7 | |
| OpStore %8 %11 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '8[%8]'s type does not match " | |
| "Object <id> '11[%11]'s type."))); | |
| } | |
| // Same code as the last test. The difference is that we relax the rule. | |
| // Because the structs %3 and %5 are defined the same way. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 0 | |
| OpMemberDecorate %1 1 Offset 4 | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 4 | |
| %3 = OpTypeVoid | |
| %4 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %4 %4 | |
| %5 = OpTypePointer Uniform %1 | |
| %2 = OpTypeStruct %4 %4 | |
| %6 = OpTypeFunction %3 | |
| %7 = OpConstant %4 3.14 | |
| %8 = OpVariable %5 Uniform | |
| %9 = OpFunction %3 None %6 | |
| %10 = OpLabel | |
| %11 = OpCompositeConstruct %2 %7 %7 | |
| OpStore %8 %11 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxStoreStruct(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Same code as the last test except for an extra decoration on one of the | |
| // members. With the relaxed rules, the code is still valid. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 0 | |
| OpMemberDecorate %1 1 Offset 4 | |
| OpMemberDecorate %1 0 RelaxedPrecision | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 4 | |
| %3 = OpTypeVoid | |
| %4 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %4 %4 | |
| %5 = OpTypePointer Uniform %1 | |
| %2 = OpTypeStruct %4 %4 | |
| %6 = OpTypeFunction %3 | |
| %7 = OpConstant %4 3.14 | |
| %8 = OpVariable %5 Uniform | |
| %9 = OpFunction %3 None %6 | |
| %10 = OpLabel | |
| %11 = OpCompositeConstruct %2 %7 %7 | |
| OpStore %8 %11 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxStoreStruct(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // This test check that we recursively traverse the struct to check if they are | |
| // interchangable. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 0 | |
| OpMemberDecorate %1 1 Offset 4 | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 8 | |
| OpMemberDecorate %3 0 Offset 0 | |
| OpMemberDecorate %3 1 Offset 4 | |
| OpMemberDecorate %4 0 Offset 0 | |
| OpMemberDecorate %4 1 Offset 8 | |
| %5 = OpTypeVoid | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %7 %6 | |
| %2 = OpTypeStruct %1 %1 | |
| %8 = OpTypePointer Uniform %2 | |
| %3 = OpTypeStruct %7 %6 | |
| %4 = OpTypeStruct %3 %3 | |
| %9 = OpTypeFunction %5 | |
| %10 = OpConstant %6 7 | |
| %11 = OpConstant %7 3.14 | |
| %12 = OpConstantComposite %3 %11 %10 | |
| %13 = OpVariable %8 Uniform | |
| %14 = OpFunction %5 None %9 | |
| %15 = OpLabel | |
| %16 = OpCompositeConstruct %4 %12 %12 | |
| OpStore %13 %16 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxStoreStruct(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // This test check that the even with the relaxed rules an error is identified | |
| // if the members of the struct are in a different order. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 0 | |
| OpMemberDecorate %1 1 Offset 4 | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 8 | |
| OpMemberDecorate %3 0 Offset 0 | |
| OpMemberDecorate %3 1 Offset 4 | |
| OpMemberDecorate %4 0 Offset 0 | |
| OpMemberDecorate %4 1 Offset 8 | |
| %5 = OpTypeVoid | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %6 %7 | |
| %2 = OpTypeStruct %1 %1 | |
| %8 = OpTypePointer Uniform %2 | |
| %3 = OpTypeStruct %7 %6 | |
| %4 = OpTypeStruct %3 %3 | |
| %9 = OpTypeFunction %5 | |
| %10 = OpConstant %6 7 | |
| %11 = OpConstant %7 3.14 | |
| %12 = OpConstantComposite %3 %11 %10 | |
| %13 = OpVariable %8 Uniform | |
| %14 = OpFunction %5 None %9 | |
| %15 = OpLabel | |
| %16 = OpCompositeConstruct %4 %12 %12 | |
| OpStore %13 %16 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxStoreStruct(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '13[%13]'s layout does not match Object " | |
| "<id> '16[%16]'s layout."))); | |
| } | |
| // This test check that the even with the relaxed rules an error is identified | |
| // if the members of the struct are at different offsets. | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpMemberDecorate %1 0 Offset 4 | |
| OpMemberDecorate %1 1 Offset 0 | |
| OpMemberDecorate %2 0 Offset 0 | |
| OpMemberDecorate %2 1 Offset 8 | |
| OpMemberDecorate %3 0 Offset 0 | |
| OpMemberDecorate %3 1 Offset 4 | |
| OpMemberDecorate %4 0 Offset 0 | |
| OpMemberDecorate %4 1 Offset 8 | |
| %5 = OpTypeVoid | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypeFloat 32 | |
| %1 = OpTypeStruct %7 %6 | |
| %2 = OpTypeStruct %1 %1 | |
| %8 = OpTypePointer Uniform %2 | |
| %3 = OpTypeStruct %7 %6 | |
| %4 = OpTypeStruct %3 %3 | |
| %9 = OpTypeFunction %5 | |
| %10 = OpConstant %6 7 | |
| %11 = OpConstant %7 3.14 | |
| %12 = OpConstantComposite %3 %11 %10 | |
| %13 = OpVariable %8 Uniform | |
| %14 = OpFunction %5 None %9 | |
| %15 = OpLabel | |
| %16 = OpCompositeConstruct %4 %12 %12 | |
| OpStore %13 %16 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxStoreStruct(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore Pointer <id> '13[%13]'s layout does not match Object " | |
| "<id> '16[%16]'s layout."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %1 = OpTypeInt 32 1 | |
| %2 = OpTypePointer Function %1 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpFunctionParameter %2 | |
| %6 = OpLabel | |
| OpReturnValue %5 | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxLogicalPointer(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) { | |
| const std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpMemoryModel Logical GLSL450 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 1 | |
| %3 = OpTypeFunction %1 ; void(void) | |
| %4 = OpTypePointer Uniform %2 ; int* | |
| %5 = OpTypePointer Private %4 ; int** (Private) | |
| %6 = OpTypePointer Function %4 ; int** (Function) | |
| %7 = OpVariable %5 Private | |
| %8 = OpFunction %1 None %3 | |
| %9 = OpLabel | |
| %10 = OpVariable %6 Function | |
| OpReturn | |
| OpFunctionEnd)"; | |
| spvValidatorOptionsSetRelaxLogicalPointer(options_, true); | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreVoid) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 | |
| %6 = OpVariable %3 Uniform | |
| %7 = OpFunction %1 None %4 | |
| %8 = OpLabel | |
| %9 = OpFunctionCall %1 %7 | |
| OpStore %6 %9 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpStore Object <id> '8[%8]'s type is void."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreLabel) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 | |
| %6 = OpVariable %3 Uniform | |
| %7 = OpFunction %1 None %4 | |
| %8 = OpLabel | |
| OpStore %6 %8 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '7[%7]' requires a type"))); | |
| } | |
| // TODO: enable when this bug is fixed: | |
| // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpStoreFunction) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypeFunction %2 | |
| %5 = OpConstant %2 123 | |
| %6 = OpVariable %3 UniformConstant | |
| %7 = OpFunction %2 None %4 | |
| %8 = OpLabel | |
| OpStore %6 %7 | |
| OpReturnValue %5 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreBuiltin) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID | |
| OpExecutionMode %main LocalSize 1 1 1 | |
| OpSource GLSL 450 | |
| OpName %main "main" | |
| OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" | |
| OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId | |
| %int = OpTypeInt 32 1 | |
| %uint = OpTypeInt 32 0 | |
| %v3uint = OpTypeVector %uint 3 | |
| %_ptr_Input_v3uint = OpTypePointer Input %v3uint | |
| %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input | |
| %zero = OpConstant %uint 0 | |
| %v3uint_000 = OpConstantComposite %v3uint %zero %zero %zero | |
| %void = OpTypeVoid | |
| %voidfunc = OpTypeFunction %void | |
| %main = OpFunction %void None %voidfunc | |
| %lmain = OpLabel | |
| OpStore %gl_GlobalInvocationID %v3uint_000 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("storage class is read-only"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpConstant %2 42 | |
| %5 = OpVariable %3 UniformConstant %4 | |
| %6 = OpTypePointer Function %2 | |
| %7 = OpTypeFunction %1 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| %10 = OpVariable %6 Function | |
| OpCopyMemory %10 %5 None | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 %2 %3 | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpFunctionParameter %2 | |
| %7 = OpFunctionParameter %3 | |
| %8 = OpLabel | |
| OpCopyMemory %6 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Target operand <id> '6[%6]' is not a pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpTypeFunction %1 %2 %3 | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpFunctionParameter %2 | |
| %7 = OpFunctionParameter %3 | |
| %8 = OpLabel | |
| OpCopyMemory %7 %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Source operand <id> '6[%6]' is not a pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpConstant %2 42 | |
| %5 = OpVariable %3 UniformConstant %4 | |
| %11 = OpTypeFloat 32 | |
| %6 = OpTypePointer Function %11 | |
| %7 = OpTypeFunction %1 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| %10 = OpVariable %6 Function | |
| OpCopyMemory %10 %5 None | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Target <id> '5[%5]'s type does not match " | |
| "Source <id> '2[%uint]'s type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidTarget) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %1 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFunction %1 %3 %4 | |
| %6 = OpFunction %1 None %5 | |
| %7 = OpFunctionParameter %3 | |
| %8 = OpFunctionParameter %4 | |
| %9 = OpLabel | |
| OpCopyMemory %7 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Target operand <id> '7[%7]' cannot be a void " | |
| "pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidSource) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %1 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFunction %1 %3 %4 | |
| %6 = OpFunction %1 None %5 | |
| %7 = OpFunctionParameter %3 | |
| %8 = OpFunctionParameter %4 | |
| %9 = OpLabel | |
| OpCopyMemory %8 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Source operand <id> '7[%7]' cannot be a void " | |
| "pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypePointer Function %2 | |
| %5 = OpConstant %2 4 | |
| %6 = OpVariable %3 UniformConstant %5 | |
| %7 = OpTypeFunction %1 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| %10 = OpVariable %4 Function | |
| OpCopyMemorySized %10 %6 %5 None | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypePointer Function %2 | |
| %5 = OpConstant %2 4 | |
| %6 = OpVariable %3 UniformConstant %5 | |
| %7 = OpTypeFunction %1 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| OpCopyMemorySized %5 %5 %5 None | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Target operand <id> '5[%uint_4]' is not a pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypePointer Function %2 | |
| %5 = OpConstant %2 4 | |
| %6 = OpTypeFunction %1 | |
| %7 = OpFunction %1 None %6 | |
| %8 = OpLabel | |
| %9 = OpVariable %4 Function | |
| OpCopyMemorySized %9 %5 %5 None | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Source operand <id> '5[%uint_4]' is not a pointer."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypePointer Function %2 | |
| %5 = OpConstant %2 4 | |
| %6 = OpVariable %3 UniformConstant %5 | |
| %7 = OpTypeFunction %1 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| %10 = OpVariable %4 Function | |
| OpCopyMemorySized %10 %6 %6 None | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '6[%6]' must be a scalar integer type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer UniformConstant %2 | |
| %4 = OpTypePointer Function %2 | |
| %5 = OpConstant %2 4 | |
| %6 = OpVariable %3 UniformConstant %5 | |
| %7 = OpTypeFunction %1 | |
| %11 = OpTypeFloat 32 | |
| %12 = OpConstant %11 1.0 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpLabel | |
| %10 = OpVariable %4 Function | |
| OpCopyMemorySized %10 %6 %12 None | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '9[%float_1]' must be a scalar integer " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstantNull %2 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Size operand <id> '3[%3]' cannot be a constant " | |
| "zero."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 0 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '3[%uint_0]' cannot be a constant " | |
| "zero."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpConstant %2 0 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '3[%ulong_0]' cannot be a constant " | |
| "zero."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 1 | |
| %3 = OpConstant %2 -1 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '3[%int_n1]' cannot have the sign bit set " | |
| "to 1."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 64 1 | |
| %3 = OpConstant %2 -1 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Size operand <id> '3[%long_n1]' cannot have the sign bit set " | |
| "to 1."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 2147483648 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 64 0 | |
| %3 = OpConstant %2 9223372036854775808 | |
| %4 = OpTypePointer Uniform %2 | |
| %5 = OpTypeFloat 32 | |
| %6 = OpTypePointer UniformConstant %5 | |
| %7 = OpTypeFunction %1 %4 %6 | |
| %8 = OpFunction %1 None %7 | |
| %9 = OpFunctionParameter %4 | |
| %10 = OpFunctionParameter %6 | |
| %11 = OpLabel | |
| OpCopyMemorySized %9 %10 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| const char kDeeplyNestedStructureSetup[] = R"( | |
| %void = OpTypeVoid | |
| %void_f = OpTypeFunction %void | |
| %int = OpTypeInt 32 0 | |
| %float = OpTypeFloat 32 | |
| %v3float = OpTypeVector %float 3 | |
| %mat4x3 = OpTypeMatrix %v3float 4 | |
| %_ptr_Private_mat4x3 = OpTypePointer Private %mat4x3 | |
| %_ptr_Private_float = OpTypePointer Private %float | |
| %my_matrix = OpVariable %_ptr_Private_mat4x3 Private | |
| %my_float_var = OpVariable %_ptr_Private_float Private | |
| %_ptr_Function_float = OpTypePointer Function %float | |
| %int_0 = OpConstant %int 0 | |
| %int_1 = OpConstant %int 1 | |
| %int_2 = OpConstant %int 2 | |
| %int_3 = OpConstant %int 3 | |
| %int_5 = OpConstant %int 5 | |
| ; Making the following nested structures. | |
| ; | |
| ; struct S { | |
| ; bool b; | |
| ; vec4 v[5]; | |
| ; int i; | |
| ; mat4x3 m[5]; | |
| ; } | |
| ; uniform blockName { | |
| ; S s; | |
| ; bool cond; | |
| ; RunTimeArray arr; | |
| ; } | |
| %f32arr = OpTypeRuntimeArray %float | |
| %v4float = OpTypeVector %float 4 | |
| %array5_mat4x3 = OpTypeArray %mat4x3 %int_5 | |
| %array5_vec4 = OpTypeArray %v4float %int_5 | |
| %_ptr_Uniform_float = OpTypePointer Uniform %float | |
| %_ptr_Function_vec4 = OpTypePointer Function %v4float | |
| %_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float | |
| %struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 | |
| %struct_blockName = OpTypeStruct %struct_s %int %f32arr | |
| %_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName | |
| %_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s | |
| %_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 | |
| %_ptr_Uniform_mat4x3 = OpTypePointer Uniform %mat4x3 | |
| %_ptr_Uniform_v3float = OpTypePointer Uniform %v3float | |
| %blockName_var = OpVariable %_ptr_Uniform_blockName Uniform | |
| %spec_int = OpSpecConstant %int 2 | |
| %float_0 = OpConstant %float 0 | |
| %func = OpFunction %void None %void_f | |
| %my_label = OpLabel | |
| )"; | |
| // In what follows, Access Chain Instruction refers to one of the following: | |
| // OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, and | |
| // OpInBoundsPtrAccessChain | |
| using AccessChainInstructionTest = spvtest::ValidateBase<std::string>; | |
| // Determines whether the access chain instruction requires the 'element id' | |
| // argument. | |
| bool AccessChainRequiresElemId(const std::string& instr) { | |
| return (instr == "OpPtrAccessChain" || instr == "OpInBoundsPtrAccessChain"); | |
| } | |
| // Valid: Access a float in a matrix using an access chain instruction. | |
| TEST_P(AccessChainInstructionTest, AccessChainGood) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + | |
| "%float_entry = " + instr + | |
| R"( %_ptr_Private_float %my_matrix )" + elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid. The result type of an access chain instruction must be a pointer. | |
| TEST_P(AccessChainInstructionTest, AccessChainResultTypeBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %float_entry = )" + | |
| instr + | |
| R"( %float %my_matrix )" + elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = "The Result Type of " + instr + | |
| " <id> '36[%36]' must be " | |
| "OpTypePointer. Found OpTypeFloat."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid. The base type of an access chain instruction must be a pointer. | |
| TEST_P(AccessChainInstructionTest, AccessChainBaseTypeVoidBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %float_entry = )" + | |
| instr + " %_ptr_Private_float %void " + elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a " | |
| "type")); | |
| } | |
| // Invalid. The base type of an access chain instruction must be a pointer. | |
| TEST_P(AccessChainInstructionTest, AccessChainBaseTypeNonPtrVariableBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_float %_ptr_Private_float )" + | |
| elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("Operand '8[%_ptr_Private_float]' cannot be a type")); | |
| } | |
| // Invalid: The storage class of Base and Result do not match. | |
| TEST_P(AccessChainInstructionTest, | |
| AccessChainResultAndBaseStorageClassDoesntMatchBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Function_float %my_matrix )" + elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = | |
| "The result pointer storage class and base pointer storage class in " + | |
| instr + " do not match."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid. The base type of an access chain instruction must point to a | |
| // composite object. | |
| TEST_P(AccessChainInstructionTest, | |
| AccessChainBasePtrNotPointingToCompositeBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_float %my_float_var )" + elem + | |
| R"(%int_0 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = instr + | |
| " reached non-composite type while " | |
| "indexes still remain to be traversed."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Valid. No Indexes were passed to the access chain instruction. The Result | |
| // Type is the same as the Base type. | |
| TEST_P(AccessChainInstructionTest, AccessChainNoIndexesGood) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_float %my_float_var )" + elem + | |
| R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid. No Indexes were passed to the access chain instruction, but the | |
| // Result Type is different from the Base type. | |
| TEST_P(AccessChainInstructionTest, AccessChainNoIndexesBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem + | |
| R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr("result type (OpTypeMatrix) does not match the type that " | |
| "results from indexing into the base <id> (OpTypeFloat).")); | |
| } | |
| // Valid: 255 indexes passed to the access chain instruction. Limit is 255. | |
| TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; | |
| const std::string arrayStride = | |
| " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 "; | |
| int depth = 255; | |
| std::string header = | |
| kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup; | |
| header.erase(header.find("%func")); | |
| std::ostringstream spirv; | |
| spirv << header << "\n"; | |
| // Build nested structures. Struct 'i' contains struct 'i-1' | |
| spirv << "%s_depth_1 = OpTypeStruct %float\n"; | |
| for (int i = 2; i <= depth; ++i) { | |
| spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n"; | |
| } | |
| // Define Pointer and Variable to use for the AccessChain instruction. | |
| spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_" | |
| << depth << "\n"; | |
| spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n"; | |
| // Function Start | |
| spirv << R"( | |
| %func = OpFunction %void None %void_f | |
| %my_label = OpLabel | |
| )"; | |
| // AccessChain with 'n' indexes (n = depth) | |
| spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem; | |
| for (int i = 0; i < depth; ++i) { | |
| spirv << " %int_0"; | |
| } | |
| // Function end | |
| spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: 256 indexes passed to the access chain instruction. Limit is 255. | |
| TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; | |
| std::ostringstream spirv; | |
| spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup; | |
| spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem; | |
| for (int i = 0; i < 256; ++i) { | |
| spirv << " %int_0"; | |
| } | |
| spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = "The number of indexes in " + instr + | |
| " may not exceed 255. Found 256 indexes."; | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Valid: 10 indexes passed to the access chain instruction. (Custom limit: 10) | |
| TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesGood) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; | |
| const std::string arrayStride = | |
| " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 "; | |
| int depth = 10; | |
| std::string header = | |
| kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup; | |
| header.erase(header.find("%func")); | |
| std::ostringstream spirv; | |
| spirv << header << "\n"; | |
| // Build nested structures. Struct 'i' contains struct 'i-1' | |
| spirv << "%s_depth_1 = OpTypeStruct %float\n"; | |
| for (int i = 2; i <= depth; ++i) { | |
| spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n"; | |
| } | |
| // Define Pointer and Variable to use for the AccessChain instruction. | |
| spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_" | |
| << depth << "\n"; | |
| spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n"; | |
| // Function Start | |
| spirv << R"( | |
| %func = OpFunction %void None %void_f | |
| %my_label = OpLabel | |
| )"; | |
| // AccessChain with 'n' indexes (n = depth) | |
| spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem; | |
| for (int i = 0; i < depth; ++i) { | |
| spirv << " %int_0"; | |
| } | |
| // Function end | |
| spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_access_chain_indexes, 10u); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: 11 indexes passed to the access chain instruction. Custom Limit:10 | |
| TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; | |
| std::ostringstream spirv; | |
| spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup; | |
| spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem; | |
| for (int i = 0; i < 11; ++i) { | |
| spirv << " %int_0"; | |
| } | |
| spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = "The number of indexes in " + instr + | |
| " may not exceed 10. Found 11 indexes."; | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_access_chain_indexes, 10u); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: Index passed to the access chain instruction is float (must be | |
| // integer). | |
| TEST_P(AccessChainInstructionTest, AccessChainUndefinedIndexBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_float %my_matrix )" + elem + | |
| R"(%float_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = | |
| "Indexes passed to " + instr + " must be of type integer."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: The index argument that indexes into a struct must be of type | |
| // OpConstant. | |
| TEST_P(AccessChainInstructionTest, AccessChainStructIndexNotConstantBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %f = )" + | |
| instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_0 %spec_int %int_2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = | |
| "The <id> passed to " + instr + | |
| " to index into a structure must be an OpConstant."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: Indexing up to a vec4 granularity, but result type expected float. | |
| TEST_P(AccessChainInstructionTest, | |
| AccessChainStructResultTypeDoesntMatchIndexedTypeBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_0 %int_1 %int_2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = instr + | |
| " result type (OpTypeFloat) does not match " | |
| "the type that results from indexing into " | |
| "the base <id> (OpTypeVector)."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: Reach non-composite type (bool) when unused indexes remain. | |
| TEST_P(AccessChainInstructionTest, AccessChainStructTooManyIndexesBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_0 %int_2 %int_2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = instr + | |
| " reached non-composite type while " | |
| "indexes still remain to be traversed."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: Trying to find index 3 of the struct that has only 3 members. | |
| TEST_P(AccessChainInstructionTest, AccessChainStructIndexOutOfBoundBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_3 %int_2 %int_2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = "Index is out of bounds: " + instr + | |
| " can not find index 3 into the structure " | |
| "<id> '25[%_struct_25]'. This structure " | |
| "has 3 members. Largest valid index is 2."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Valid: Tests that we can index into Struct, Array, Matrix, and Vector! | |
| TEST_P(AccessChainInstructionTest, AccessChainIndexIntoAllTypesGood) { | |
| // indexes that we are passing are: 0, 3, 1, 2, 0 | |
| // 0 will select the struct_s within the base struct (blockName) | |
| // 3 will select the Array that contains 5 matrices | |
| // 1 will select the Matrix that is at index 1 of the array | |
| // 2 will select the column (which is a vector) within the matrix at index 2 | |
| // 0 will select the element at the index 0 of the vector. (which is a float). | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| const std::string arrayStride = | |
| " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 "; | |
| std::ostringstream spirv; | |
| spirv << kGLSL450MemoryModel << arrayStride << kDeeplyNestedStructureSetup | |
| << std::endl; | |
| spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var " | |
| << elem << "%int_0" << std::endl; | |
| spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var " | |
| << elem << "%int_0 %int_3" << std::endl; | |
| spirv << "%sm = " << instr << " %_ptr_Uniform_mat4x3 %blockName_var " << elem | |
| << "%int_0 %int_3 %int_1" << std::endl; | |
| spirv << "%sc = " << instr << " %_ptr_Uniform_v3float %blockName_var " << elem | |
| << "%int_0 %int_3 %int_1 %int_2" << std::endl; | |
| spirv << "%entry = " << instr << " %_ptr_Uniform_float %blockName_var " | |
| << elem << "%int_0 %int_3 %int_1 %int_2 %int_0" << std::endl; | |
| spirv << R"( | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Valid: Access an element of OpTypeRuntimeArray. | |
| TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| const std::string arrayStride = | |
| " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 "; | |
| std::string spirv = kGLSL450MemoryModel + arrayStride + | |
| kDeeplyNestedStructureSetup + R"( | |
| %runtime_arr_entry = )" + instr + | |
| R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_2 %int_0 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: Unused index when accessing OpTypeRuntimeArray. | |
| TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %runtime_arr_entry = )" + | |
| instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + | |
| R"(%int_2 %int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = | |
| instr + | |
| " reached non-composite type while indexes still remain to be traversed."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: Reached scalar type before arguments to the access chain instruction | |
| // finished. | |
| TEST_P(AccessChainInstructionTest, AccessChainMatrixMoreArgsThanNeededBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_float %my_matrix )" + elem + | |
| R"(%int_0 %int_1 %int_0 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = instr + | |
| " reached non-composite type while " | |
| "indexes still remain to be traversed."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Invalid: The result type and the type indexed into do not match. | |
| TEST_P(AccessChainInstructionTest, | |
| AccessChainResultTypeDoesntMatchIndexedTypeBad) { | |
| const std::string instr = GetParam(); | |
| const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; | |
| std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( | |
| %entry = )" + | |
| instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem + | |
| R"(%int_0 %int_1 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_err = instr + | |
| " result type (OpTypeMatrix) does not match " | |
| "the type that results from indexing into " | |
| "the base <id> (OpTypeFloat)."; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); | |
| } | |
| // Run tests for Access Chain Instructions. | |
| INSTANTIATE_TEST_SUITE_P( | |
| CheckAccessChainInstructions, AccessChainInstructionTest, | |
| ::testing::Values("OpAccessChain", "OpInBoundsAccessChain", | |
| "OpPtrAccessChain", "OpInBoundsPtrAccessChain")); | |
| // TODO: OpArrayLength | |
| // TODO: OpImagePointer | |
| // TODO: OpGenericPtrMemSemantics | |
| TEST_P(ValidateIdWithMessage, OpFunctionGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %1 %2 %2 | |
| %4 = OpFunction %1 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpConstant %2 42 | |
| %4 = OpTypeFunction %1 %2 %2 | |
| %5 = OpFunction %2 None %4 | |
| %6 = OpLabel | |
| OpReturnValue %3 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpFunction Result Type <id> '2[%uint]' does not " | |
| "match the Function Type's return type <id> " | |
| "'1[%void]'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeInt 32 0 | |
| %2 = OpTypeFloat 32 | |
| %3 = OpConstant %2 0 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpFunction %1 None %4 | |
| %6 = OpLabel | |
| OpReturnValue %3 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpReturnValue Value <id> '3[%float_0]'s type does " | |
| "not match OpFunction's return type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionFunctionTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %4 = OpFunction %1 None %2 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpFunction Function Type <id> '2[%uint]' is not a function " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionUseBad) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeFloat 32 | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| OpReturnValue %3 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Invalid use of function result id '3[%3]'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionParameterGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %1 %2 | |
| %4 = OpFunction %1 None %3 | |
| %5 = OpFunctionParameter %2 | |
| %6 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionParameterMultipleGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %1 %2 %2 | |
| %4 = OpFunction %1 None %3 | |
| %5 = OpFunctionParameter %2 | |
| %6 = OpFunctionParameter %2 | |
| %7 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %1 %2 | |
| %4 = OpFunction %1 None %3 | |
| %5 = OpFunctionParameter %1 | |
| %6 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpFunctionParameter Result Type <id> '1[%void]' does not " | |
| "match the OpTypeFunction parameter type of the same index."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionCallGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 ;21 | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpFunctionParameter %2 | |
| %8 = OpLabel | |
| OpReturnValue %7 | |
| OpFunctionEnd | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| %12 = OpFunctionCall %2 %6 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionCallResultTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 ;21 | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpFunctionParameter %2 | |
| %8 = OpLabel | |
| %9 = OpIAdd %2 %7 %7 | |
| OpReturnValue %9 | |
| OpFunctionEnd | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| %12 = OpFunctionCall %1 %6 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpFunctionCall Result Type <id> '1[%void]'s type " | |
| "does not match Function <id> '2[%uint]'s return " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionCallFunctionBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 ;21 | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| %12 = OpFunctionCall %2 %5 %5 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpFunctionCall Function <id> '5[%uint_42]' is not a " | |
| "function."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 | |
| %13 = OpTypeFloat 32 | |
| %14 = OpConstant %13 3.14 | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpFunctionParameter %2 | |
| %8 = OpLabel | |
| %9 = OpIAdd %2 %7 %7 | |
| OpReturnValue %9 | |
| OpFunctionEnd | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| %12 = OpFunctionCall %2 %6 %14 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpFunctionCall Argument <id> '7[%float_3_1400001]'s " | |
| "type does not match Function <id> '2[%uint]'s " | |
| "parameter type."))); | |
| } | |
| // Valid: OpSampledImage result <id> is used in the same block by | |
| // OpImageSampleImplictLod | |
| TEST_P(ValidateIdWithMessage, OpSampledImageGood) { | |
| std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( | |
| %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst | |
| %si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Invalid: OpSampledImage result <id> is defined in one block and used in a | |
| // different block. | |
| TEST_P(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) { | |
| std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( | |
| %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst | |
| OpBranch %label_2 | |
| %label_2 = OpLabel | |
| %si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "All OpSampledImage instructions must be in the same block in " | |
| "which their Result <id> are consumed. OpSampledImage Result " | |
| "Type <id> '23[%23]' has a consumer in a different basic " | |
| "block. The consumer instruction <id> is '25[%25]'."))); | |
| } | |
| // Invalid: OpSampledImage result <id> is used by OpSelect | |
| // Note: According to the Spec, OpSelect parameters must be either a scalar or a | |
| // vector. Therefore, OpTypeSampledImage is an illegal parameter for OpSelect. | |
| // However, the OpSelect validation does not catch this today. Therefore, it is | |
| // caught by the OpSampledImage validation. If the OpSelect validation code is | |
| // updated, the error message for this test may change. | |
| // | |
| // Disabled since OpSelect catches this now. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) { | |
| std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( | |
| %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst | |
| %select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Result <id> from OpSampledImage instruction must not " | |
| "appear as operands of OpSelect. Found result <id> " | |
| "'23' as an operand of <id> '24'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpCopyObjectSampledImageGood) { | |
| std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( | |
| %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst | |
| %smpld_img2 = OpCopyObject %sampled_image_type %smpld_img | |
| %image_inst2 = OpCopyObject %image_type %image_inst | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Valid: Get a float in a matrix using CompositeExtract. | |
| // Valid: Insert float into a matrix using CompositeInsert. | |
| TEST_P(ValidateIdWithMessage, CompositeExtractInsertGood) { | |
| std::ostringstream spirv; | |
| spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl; | |
| spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; | |
| spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl; | |
| // To test CompositeInsert, insert the object back in after extraction. | |
| spirv << "%new_composite = OpCompositeInsert %mat4x3 %float_entry %matrix 0 1" | |
| << std::endl; | |
| spirv << R"(OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| #if 0 | |
| TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) { | |
| const char *spirv = R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 %2 | |
| %4 = OpTypeFunction %1 | |
| %5 = OpConstant %2 42 ;21 | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpFunctionParameter %2 | |
| %8 = OpLabel | |
| %9 = OpLoad %2 %7 | |
| OpReturnValue %9 | |
| OpFunctionEnd | |
| %10 = OpFunction %1 None %4 | |
| %11 = OpLabel | |
| OpReturn | |
| %12 = OpFunctionCall %2 %6 %5 | |
| OpFunctionEnd)"; | |
| CHECK(spirv, SPV_ERROR_INVALID_ID); | |
| } | |
| #endif | |
| // TODO: The many things that changed with how images are used. | |
| // TODO: OpTextureSample | |
| // TODO: OpTextureSampleDref | |
| // TODO: OpTextureSampleLod | |
| // TODO: OpTextureSampleProj | |
| // TODO: OpTextureSampleGrad | |
| // TODO: OpTextureSampleOffset | |
| // TODO: OpTextureSampleProjLod | |
| // TODO: OpTextureSampleProjGrad | |
| // TODO: OpTextureSampleLodOffset | |
| // TODO: OpTextureSampleProjOffset | |
| // TODO: OpTextureSampleGradOffset | |
| // TODO: OpTextureSampleProjLodOffset | |
| // TODO: OpTextureSampleProjGradOffset | |
| // TODO: OpTextureFetchTexelLod | |
| // TODO: OpTextureFetchTexelOffset | |
| // TODO: OpTextureFetchSample | |
| // TODO: OpTextureFetchTexel | |
| // TODO: OpTextureGather | |
| // TODO: OpTextureGatherOffset | |
| // TODO: OpTextureGatherOffsets | |
| // TODO: OpTextureQuerySizeLod | |
| // TODO: OpTextureQuerySize | |
| // TODO: OpTextureQueryLevels | |
| // TODO: OpTextureQuerySamples | |
| // TODO: OpConvertUToF | |
| // TODO: OpConvertFToS | |
| // TODO: OpConvertSToF | |
| // TODO: OpConvertUToF | |
| // TODO: OpUConvert | |
| // TODO: OpSConvert | |
| // TODO: OpFConvert | |
| // TODO: OpConvertPtrToU | |
| // TODO: OpConvertUToPtr | |
| // TODO: OpPtrCastToGeneric | |
| // TODO: OpGenericCastToPtr | |
| // TODO: OpBitcast | |
| // TODO: OpGenericCastToPtrExplicit | |
| // TODO: OpSatConvertSToU | |
| // TODO: OpSatConvertUToS | |
| // TODO: OpVectorExtractDynamic | |
| // TODO: OpVectorInsertDynamic | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleIntGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec3 = OpTypeVector %int 3 | |
| %ivec4 = OpTypeVector %int 4 | |
| %ptr_ivec3 = OpTypePointer Function %ivec3 | |
| %undef = OpUndef %ivec4 | |
| %int_42 = OpConstant %int 42 | |
| %int_0 = OpConstant %int 0 | |
| %int_2 = OpConstant %int 2 | |
| %1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 | |
| %2 = OpTypeFunction %ivec3 | |
| %3 = OpFunction %ivec3 None %2 | |
| %4 = OpLabel | |
| %var = OpVariable %ptr_ivec3 Function %1 | |
| %5 = OpLoad %ivec3 %var | |
| %6 = OpVectorShuffle %ivec3 %5 %undef 2 1 0 | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleFloatGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %float = OpTypeFloat 32 | |
| %vec2 = OpTypeVector %float 2 | |
| %vec3 = OpTypeVector %float 3 | |
| %vec4 = OpTypeVector %float 4 | |
| %ptr_vec2 = OpTypePointer Function %vec2 | |
| %ptr_vec3 = OpTypePointer Function %vec3 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %1 = OpConstantComposite %vec2 %float_2 %float_1 | |
| %2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 | |
| %3 = OpTypeFunction %vec4 | |
| %4 = OpFunction %vec4 None %3 | |
| %5 = OpLabel | |
| %var = OpVariable %ptr_vec2 Function %1 | |
| %var2 = OpVariable %ptr_vec3 Function %2 | |
| %6 = OpLoad %vec2 %var | |
| %7 = OpLoad %vec3 %var2 | |
| %8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff | |
| OpReturnValue %8 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %float = OpTypeFloat 32 | |
| %vec2 = OpTypeVector %float 2 | |
| %ptr_vec2 = OpTypePointer Function %vec2 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %1 = OpConstantComposite %vec2 %float_2 %float_1 | |
| %2 = OpTypeFunction %float | |
| %3 = OpFunction %float None %2 | |
| %4 = OpLabel | |
| %var = OpVariable %ptr_vec2 Function %1 | |
| %5 = OpLoad %vec2 %var | |
| %6 = OpVectorShuffle %float %5 %5 0 | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Result Type of OpVectorShuffle must be OpTypeVector."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec3 = OpTypeVector %int 3 | |
| %ptr_ivec3 = OpTypePointer Function %ivec3 | |
| %int_42 = OpConstant %int 42 | |
| %int_0 = OpConstant %int 0 | |
| %int_2 = OpConstant %int 2 | |
| %1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 | |
| %2 = OpTypeFunction %ivec3 | |
| %3 = OpFunction %ivec3 None %2 | |
| %4 = OpLabel | |
| %var = OpVariable %ptr_ivec3 Function %1 | |
| %5 = OpLoad %ivec3 %var | |
| %6 = OpVectorShuffle %ivec3 %5 %5 0 1 | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpVectorShuffle component literals count does not match " | |
| "Result Type <id> '2[%v3uint]'s vector component count."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1Type) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec2 = OpTypeVector %int 2 | |
| %ptr_int = OpTypePointer Function %int | |
| %undef = OpUndef %ivec2 | |
| %int_42 = OpConstant %int 42 | |
| %2 = OpTypeFunction %ivec2 | |
| %3 = OpFunction %ivec2 None %2 | |
| %4 = OpLabel | |
| %var = OpVariable %ptr_int Function %int_42 | |
| %5 = OpLoad %int %var | |
| %6 = OpVectorShuffle %ivec2 %5 %undef 0 0 | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("The type of Vector 1 must be OpTypeVector."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec2 = OpTypeVector %int 2 | |
| %ptr_ivec2 = OpTypePointer Function %ivec2 | |
| %undef = OpUndef %int | |
| %int_42 = OpConstant %int 42 | |
| %1 = OpConstantComposite %ivec2 %int_42 %int_42 | |
| %2 = OpTypeFunction %ivec2 | |
| %3 = OpFunction %ivec2 None %2 | |
| %4 = OpLabel | |
| %var = OpVariable %ptr_ivec2 Function %1 | |
| %5 = OpLoad %ivec2 %var | |
| %6 = OpVectorShuffle %ivec2 %5 %undef 0 1 | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("The type of Vector 2 must be OpTypeVector."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec3 = OpTypeVector %int 3 | |
| %ptr_ivec3 = OpTypePointer Function %ivec3 | |
| %int_42 = OpConstant %int 42 | |
| %int_0 = OpConstant %int 0 | |
| %int_2 = OpConstant %int 2 | |
| %float = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %float 3 | |
| %vec4 = OpTypeVector %float 4 | |
| %ptr_vec3 = OpTypePointer Function %vec3 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 | |
| %2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 | |
| %3 = OpTypeFunction %vec4 | |
| %4 = OpFunction %vec4 None %3 | |
| %5 = OpLabel | |
| %var = OpVariable %ptr_ivec3 Function %1 | |
| %var2 = OpVariable %ptr_vec3 Function %2 | |
| %6 = OpLoad %ivec3 %var | |
| %7 = OpLoad %vec3 %var2 | |
| %8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0 | |
| OpReturnValue %8 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "The Component Type of Vector 1 must be the same as " | |
| "ResultType."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %int = OpTypeInt 32 0 | |
| %ivec3 = OpTypeVector %int 3 | |
| %ptr_ivec3 = OpTypePointer Function %ivec3 | |
| %int_42 = OpConstant %int 42 | |
| %int_0 = OpConstant %int 0 | |
| %int_2 = OpConstant %int 2 | |
| %float = OpTypeFloat 32 | |
| %vec3 = OpTypeVector %float 3 | |
| %vec4 = OpTypeVector %float 4 | |
| %ptr_vec3 = OpTypePointer Function %vec3 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 | |
| %2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 | |
| %3 = OpTypeFunction %vec4 | |
| %4 = OpFunction %vec4 None %3 | |
| %5 = OpLabel | |
| %var = OpVariable %ptr_ivec3 Function %1 | |
| %var2 = OpVariable %ptr_vec3 Function %2 | |
| %6 = OpLoad %vec3 %var2 | |
| %7 = OpLoad %ivec3 %var | |
| %8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0 | |
| OpReturnValue %8 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "The Component Type of Vector 2 must be the same as " | |
| "ResultType."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpVectorShuffleLiterals) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %float = OpTypeFloat 32 | |
| %vec2 = OpTypeVector %float 2 | |
| %vec3 = OpTypeVector %float 3 | |
| %vec4 = OpTypeVector %float 4 | |
| %ptr_vec2 = OpTypePointer Function %vec2 | |
| %ptr_vec3 = OpTypePointer Function %vec3 | |
| %float_1 = OpConstant %float 1 | |
| %float_2 = OpConstant %float 2 | |
| %1 = OpConstantComposite %vec2 %float_2 %float_1 | |
| %2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 | |
| %3 = OpTypeFunction %vec4 | |
| %4 = OpFunction %vec4 None %3 | |
| %5 = OpLabel | |
| %var = OpVariable %ptr_vec2 Function %1 | |
| %var2 = OpVariable %ptr_vec3 Function %2 | |
| %6 = OpLoad %vec2 %var | |
| %7 = OpLoad %vec3 %var2 | |
| %8 = OpVectorShuffle %vec4 %6 %7 0 8 2 6 | |
| OpReturnValue %8 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Component index 8 is out of bounds for combined (Vector1 + Vector2) " | |
| "size of 5."))); | |
| } | |
| // TODO: OpCompositeConstruct | |
| // TODO: OpCompositeExtract | |
| // TODO: OpCompositeInsert | |
| // TODO: OpCopyObject | |
| // TODO: OpTranspose | |
| // TODO: OpSNegate | |
| // TODO: OpFNegate | |
| // TODO: OpNot | |
| // TODO: OpIAdd | |
| // TODO: OpFAdd | |
| // TODO: OpISub | |
| // TODO: OpFSub | |
| // TODO: OpIMul | |
| // TODO: OpFMul | |
| // TODO: OpUDiv | |
| // TODO: OpSDiv | |
| // TODO: OpFDiv | |
| // TODO: OpUMod | |
| // TODO: OpSRem | |
| // TODO: OpSMod | |
| // TODO: OpFRem | |
| // TODO: OpFMod | |
| // TODO: OpVectorTimesScalar | |
| // TODO: OpMatrixTimesScalar | |
| // TODO: OpVectorTimesMatrix | |
| // TODO: OpMatrixTimesVector | |
| // TODO: OpMatrixTimesMatrix | |
| // TODO: OpOuterProduct | |
| // TODO: OpDot | |
| // TODO: OpShiftRightLogical | |
| // TODO: OpShiftRightArithmetic | |
| // TODO: OpShiftLeftLogical | |
| // TODO: OpBitwiseOr | |
| // TODO: OpBitwiseXor | |
| // TODO: OpBitwiseAnd | |
| // TODO: OpAny | |
| // TODO: OpAll | |
| // TODO: OpIsNan | |
| // TODO: OpIsInf | |
| // TODO: OpIsFinite | |
| // TODO: OpIsNormal | |
| // TODO: OpSignBitSet | |
| // TODO: OpLessOrGreater | |
| // TODO: OpOrdered | |
| // TODO: OpUnordered | |
| // TODO: OpLogicalOr | |
| // TODO: OpLogicalXor | |
| // TODO: OpLogicalAnd | |
| // TODO: OpSelect | |
| // TODO: OpIEqual | |
| // TODO: OpFOrdEqual | |
| // TODO: OpFUnordEqual | |
| // TODO: OpINotEqual | |
| // TODO: OpFOrdNotEqual | |
| // TODO: OpFUnordNotEqual | |
| // TODO: OpULessThan | |
| // TODO: OpSLessThan | |
| // TODO: OpFOrdLessThan | |
| // TODO: OpFUnordLessThan | |
| // TODO: OpUGreaterThan | |
| // TODO: OpSGreaterThan | |
| // TODO: OpFOrdGreaterThan | |
| // TODO: OpFUnordGreaterThan | |
| // TODO: OpULessThanEqual | |
| // TODO: OpSLessThanEqual | |
| // TODO: OpFOrdLessThanEqual | |
| // TODO: OpFUnordLessThanEqual | |
| // TODO: OpUGreaterThanEqual | |
| // TODO: OpSGreaterThanEqual | |
| // TODO: OpFOrdGreaterThanEqual | |
| // TODO: OpFUnordGreaterThanEqual | |
| // TODO: OpDPdx | |
| // TODO: OpDPdy | |
| // TODO: OpFWidth | |
| // TODO: OpDPdxFine | |
| // TODO: OpDPdyFine | |
| // TODO: OpFwidthFine | |
| // TODO: OpDPdxCoarse | |
| // TODO: OpDPdyCoarse | |
| // TODO: OpFwidthCoarse | |
| // TODO: OpLoopMerge | |
| // TODO: OpSelectionMerge | |
| // TODO: OpBranch | |
| TEST_P(ValidateIdWithMessage, OpPhiNotAType) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranch %8 | |
| %8 = OpLabel | |
| %9 = OpPhi %3 %3 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '3[%true]' is not a type " | |
| "id"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiSamePredecessor) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranchConditional %3 %8 %8 | |
| %8 = OpLabel | |
| %9 = OpPhi %2 %3 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiOddArgumentNumber) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranch %8 | |
| %8 = OpLabel | |
| %9 = OpPhi %2 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpPhi does not have an equal number of incoming " | |
| "values and basic blocks."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiTooFewPredecessors) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranch %8 | |
| %8 = OpLabel | |
| %9 = OpPhi %2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpPhi's number of incoming blocks (0) does not match " | |
| "block's predecessor count (1)."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiTooManyPredecessors) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranch %8 | |
| %9 = OpLabel | |
| OpReturn | |
| %8 = OpLabel | |
| %10 = OpPhi %2 %3 %7 %3 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpPhi's number of incoming blocks (2) does not match " | |
| "block's predecessor count (1)."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiMismatchedTypes) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeInt 32 0 | |
| %6 = OpConstant %5 0 | |
| %7 = OpTypeFunction %4 | |
| %8 = OpFunction %4 None %7 | |
| %9 = OpLabel | |
| OpBranchConditional %3 %10 %11 | |
| %11 = OpLabel | |
| OpBranch %10 | |
| %10 = OpLabel | |
| %12 = OpPhi %2 %3 %9 %6 %11 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpPhi's result type <id> '2[%bool]' does not match " | |
| "incoming value <id> '6[%uint_0]' type <id> " | |
| "'5[%uint]'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiPredecessorNotABlock) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranchConditional %3 %8 %9 | |
| %9 = OpLabel | |
| OpBranch %11 | |
| %11 = OpLabel | |
| OpBranch %8 | |
| %8 = OpLabel | |
| %10 = OpPhi %2 %3 %7 %3 %3 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpPhi's incoming basic block <id> '3[%true]' is not an " | |
| "OpLabel."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPhiNotAPredecessor) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| %2 = OpTypeBool | |
| %3 = OpConstantTrue %2 | |
| %4 = OpTypeVoid | |
| %5 = OpTypeFunction %4 | |
| %6 = OpFunction %4 None %5 | |
| %7 = OpLabel | |
| OpBranchConditional %3 %8 %9 | |
| %9 = OpLabel | |
| OpBranch %11 | |
| %11 = OpLabel | |
| OpBranch %8 | |
| %8 = OpLabel | |
| %10 = OpPhi %2 %3 %7 %3 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpPhi's incoming basic block <id> '9[%9]' is not a " | |
| "predecessor of <id> '8[%8]'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditionalGood) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| %branch_cond = OpINotEqual %bool %i0 %i1 | |
| OpSelectionMerge %end None | |
| OpBranchConditional %branch_cond %target_t %target_f | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| %branch_cond = OpINotEqual %bool %i0 %i1 | |
| OpSelectionMerge %end None | |
| OpBranchConditional %branch_cond %target_t %target_f 1 1 | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| OpSelectionMerge %end None | |
| OpBranchConditional %i0 %target_t %target_f | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Condition operand for OpBranchConditional must " | |
| "be of boolean type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| OpSelectionMerge %end None | |
| OpBranchConditional %true %i0 %target_f | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "The 'True Label' operand for OpBranchConditional must " | |
| "be the ID of an OpLabel instruction"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| OpSelectionMerge %end None | |
| OpBranchConditional %true %target_t %i0 | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "The 'False Label' operand for OpBranchConditional " | |
| "must be the ID of an OpLabel instruction"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| %branch_cond = OpINotEqual %bool %i0 %i1 | |
| OpSelectionMerge %end None | |
| OpBranchConditional %branch_cond %target_t %target_f 1 | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpBranchConditional requires either 3 or 5 parameters"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| %branch_cond = OpINotEqual %bool %i0 %i1 | |
| OpSelectionMerge %end None | |
| OpBranchConditional %branch_cond %target_t %target_f 1 2 3 | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpBranchConditional requires either 3 or 5 parameters"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) { | |
| std::string spirv = BranchConditionalSetup + R"( | |
| OpBranchConditional %bool %target_t %target_f | |
| )" + BranchConditionalTail; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '3[%bool]' cannot be a " | |
| "type"))); | |
| } | |
| // TODO: OpSwitch | |
| TEST_P(ValidateIdWithMessage, OpReturnValueConstantGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 | |
| %4 = OpConstant %2 42 | |
| %5 = OpFunction %2 None %3 | |
| %6 = OpLabel | |
| OpReturnValue %4 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueVariableGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 ;10 | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypePointer Function %2 ;18 | |
| %4 = OpConstant %2 42 ;22 | |
| %5 = OpFunction %2 None %3 ;27 | |
| %6 = OpLabel ;29 | |
| %7 = OpVariable %8 Function %4 ;34 | |
| %9 = OpLoad %2 %7 | |
| OpReturnValue %9 ;36 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueExpressionGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 | |
| %4 = OpConstant %2 42 | |
| %5 = OpFunction %2 None %3 | |
| %6 = OpLabel | |
| %7 = OpIAdd %2 %4 %4 | |
| OpReturnValue %7 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueIsType) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 | |
| %5 = OpFunction %2 None %3 | |
| %6 = OpLabel | |
| OpReturnValue %1 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '1[%void]' cannot be a " | |
| "type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueIsLabel) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 | |
| %5 = OpFunction %2 None %3 | |
| %6 = OpLabel | |
| OpReturnValue %6 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '5[%5]' requires a type"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueIsVoid) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %1 | |
| %5 = OpFunction %1 None %3 | |
| %6 = OpLabel | |
| %7 = OpFunctionCall %1 %5 | |
| OpReturnValue %7 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpReturnValue value's type <id> '1[%void]' is missing or " | |
| "void."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) { | |
| // It's valid to return a pointer in a physical addressing model. | |
| std::string spirv = kOpCapabilitySetup + R"( | |
| OpMemoryModel Physical32 OpenCL | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Function %2 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %3 None %4 | |
| %6 = OpLabel | |
| %7 = OpVariable %3 Function | |
| OpReturnValue %7 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) { | |
| // It's invalid to return a pointer in a physical addressing model. | |
| std::string spirv = kOpCapabilitySetup + R"( | |
| OpMemoryModel Logical GLSL450 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Function %2 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %3 None %4 | |
| %6 = OpLabel | |
| %7 = OpVariable %3 Function | |
| OpReturnValue %7 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("OpReturnValue value's type <id> " | |
| "'3[%_ptr_Function_uint]' is a pointer, which is " | |
| "invalid in the Logical addressing model."))); | |
| } | |
| // With the VariablePointer Capability, the return value of a function is | |
| // allowed to be a pointer. | |
| TEST_P(ValidateIdWithMessage, OpReturnValueVarPtrGood) { | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, | |
| "" /* Instructions to add to "main" */, | |
| true /* Add VariablePointers Capability?*/, | |
| true /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| // Without the VariablePointer Capability, the return value of a function is | |
| // *not* allowed to be a pointer. | |
| // Disabled since using OpSelect with pointers without VariablePointers will | |
| // fail LogicalsPass. | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { | |
| std::ostringstream spirv; | |
| createVariablePointerSpirvProgram(&spirv, | |
| "" /* Instructions to add to "main" */, | |
| false /* Add VariablePointers Capability?*/, | |
| true /* Use Helper Function? */); | |
| CompileSuccessfully(spirv.str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpReturnValue value's type <id> '7' is a pointer, " | |
| "which is invalid in the Logical addressing model."))); | |
| } | |
| // TODO: enable when this bug is fixed: | |
| // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 | |
| TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypeFunction %2 | |
| %5 = OpFunction %2 None %3 | |
| %6 = OpLabel | |
| OpReturnValue %5 | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, UndefinedTypeId) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %s = OpTypeStruct %i32 | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Operand '2[%2]' requires a previous definition"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, UndefinedIdScope) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %u32 = OpTypeInt 32 0 | |
| %memsem = OpConstant %u32 0 | |
| %void = OpTypeVoid | |
| %void_f = OpTypeFunction %void | |
| %f = OpFunction %void None %void_f | |
| %l = OpLabel | |
| OpMemoryBarrier %undef %memsem | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '7[%7]' has not been " | |
| "defined"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, UndefinedIdMemSem) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %u32 = OpTypeInt 32 0 | |
| %scope = OpConstant %u32 0 | |
| %void = OpTypeVoid | |
| %void_f = OpTypeFunction %void | |
| %f = OpFunction %void None %void_f | |
| %l = OpLabel | |
| OpMemoryBarrier %scope %undef | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '7[%7]' has not been " | |
| "defined"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| KernelOpEntryPointAndOpInBoundsPtrAccessChainGood) { | |
| std::string spirv = kOpenCLMemoryModel32 + R"( | |
| OpEntryPoint Kernel %2 "simple_kernel" | |
| OpSource OpenCL_C 200000 | |
| OpDecorate %3 BuiltIn GlobalInvocationId | |
| OpDecorate %3 Constant | |
| OpDecorate %4 FuncParamAttr NoCapture | |
| OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import | |
| %5 = OpTypeInt 32 0 | |
| %6 = OpTypeVector %5 3 | |
| %7 = OpTypePointer UniformConstant %6 | |
| %3 = OpVariable %7 UniformConstant | |
| %8 = OpTypeVoid | |
| %9 = OpTypeStruct %5 | |
| %10 = OpTypePointer CrossWorkgroup %9 | |
| %11 = OpTypeFunction %8 %10 | |
| %12 = OpConstant %5 0 | |
| %13 = OpTypePointer CrossWorkgroup %5 | |
| %14 = OpConstant %5 42 | |
| %2 = OpFunction %8 None %11 | |
| %4 = OpFunctionParameter %10 | |
| %15 = OpLabel | |
| %16 = OpLoad %6 %3 Aligned 0 | |
| %17 = OpCompositeExtract %5 %16 0 | |
| %18 = OpInBoundsPtrAccessChain %13 %4 %17 %12 | |
| OpStore %18 %14 Aligned 4 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpPtrAccessChainGood) { | |
| std::string spirv = kOpenCLMemoryModel64 + R"( | |
| OpEntryPoint Kernel %2 "another_kernel" | |
| OpSource OpenCL_C 200000 | |
| OpDecorate %3 BuiltIn GlobalInvocationId | |
| OpDecorate %3 Constant | |
| OpDecorate %4 FuncParamAttr NoCapture | |
| OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import | |
| %5 = OpTypeInt 64 0 | |
| %6 = OpTypeVector %5 3 | |
| %7 = OpTypePointer UniformConstant %6 | |
| %3 = OpVariable %7 UniformConstant | |
| %8 = OpTypeVoid | |
| %9 = OpTypeInt 32 0 | |
| %10 = OpTypeStruct %9 | |
| %11 = OpTypePointer CrossWorkgroup %10 | |
| %12 = OpTypeFunction %8 %11 | |
| %13 = OpConstant %5 4294967295 | |
| %14 = OpConstant %9 0 | |
| %15 = OpTypePointer CrossWorkgroup %9 | |
| %16 = OpConstant %9 42 | |
| %2 = OpFunction %8 None %12 | |
| %4 = OpFunctionParameter %11 | |
| %17 = OpLabel | |
| %18 = OpLoad %6 %3 Aligned 0 | |
| %19 = OpCompositeExtract %5 %18 0 | |
| %20 = OpBitwiseAnd %5 %19 %13 | |
| %21 = OpPtrAccessChain %15 %4 %20 %14 | |
| OpStore %21 %16 Aligned 4 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VariablePointersStorageBuffer | |
| OpExtension "SPV_KHR_variable_pointers" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint GLCompute %3 "" | |
| OpDecorate %ptr ArrayStride 8 | |
| %int = OpTypeInt 32 0 | |
| %int_2 = OpConstant %int 2 | |
| %int_4 = OpConstant %int 4 | |
| %struct = OpTypeStruct %int | |
| %array = OpTypeArray %struct %int_4 | |
| %ptr = OpTypePointer StorageBuffer %array | |
| %var = OpVariable %ptr StorageBuffer | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpFunction %1 None %2 | |
| %4 = OpLabel | |
| %5 = OpPtrAccessChain %ptr %var %int_2 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLoadBitcastPointerGood) { | |
| std::string spirv = kOpenCLMemoryModel64 + R"( | |
| %2 = OpTypeVoid | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypeFloat 32 | |
| %5 = OpTypePointer UniformConstant %3 | |
| %6 = OpTypePointer UniformConstant %4 | |
| %7 = OpVariable %5 UniformConstant | |
| %8 = OpTypeFunction %2 | |
| %9 = OpFunction %2 None %8 | |
| %10 = OpLabel | |
| %11 = OpBitcast %6 %7 | |
| %12 = OpLoad %4 %11 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) { | |
| std::string spirv = kOpenCLMemoryModel64 + R"( | |
| %2 = OpTypeVoid | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypeFloat 32 | |
| %5 = OpTypePointer UniformConstant %3 | |
| %6 = OpTypeFunction %2 | |
| %7 = OpVariable %5 UniformConstant | |
| %8 = OpFunction %2 None %6 | |
| %9 = OpLabel | |
| %10 = OpLoad %3 %7 | |
| %11 = OpBitcast %4 %10 | |
| %12 = OpLoad %3 %11 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpLoad type for pointer <id> '11[%11]' is not a pointer " | |
| "type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreBitcastPointerGood) { | |
| std::string spirv = kOpenCLMemoryModel64 + R"( | |
| %2 = OpTypeVoid | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypeFloat 32 | |
| %5 = OpTypePointer Function %3 | |
| %6 = OpTypePointer Function %4 | |
| %7 = OpTypeFunction %2 | |
| %8 = OpConstant %3 42 | |
| %9 = OpFunction %2 None %7 | |
| %10 = OpLabel | |
| %11 = OpVariable %6 Function | |
| %12 = OpBitcast %5 %11 | |
| OpStore %12 %8 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) { | |
| std::string spirv = kOpenCLMemoryModel64 + R"( | |
| %2 = OpTypeVoid | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypeFloat 32 | |
| %5 = OpTypePointer Function %4 | |
| %6 = OpTypeFunction %2 | |
| %7 = OpConstant %4 42 | |
| %8 = OpFunction %2 None %6 | |
| %9 = OpLabel | |
| %10 = OpVariable %5 Function | |
| %11 = OpBitcast %3 %7 | |
| OpStore %11 %7 | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "OpStore type for pointer <id> '11[%11]' is not a pointer " | |
| "type."))); | |
| } | |
| // Result <id> resulting from an instruction within a function may not be used | |
| // outside that function. | |
| TEST_P(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeInt 32 0 | |
| %4 = OpTypePointer Function %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| %7 = OpVariable %4 Function | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %1 None %2 | |
| %9 = OpLabel | |
| %10 = OpLoad %3 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '7[%7]' defined in block '6[%6]' does " | |
| "not dominate its use in block " | |
| "'9[%9]'"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %1 SpecId 200 | |
| %void = OpTypeVoid | |
| %2 = OpTypeFunction %void | |
| %int = OpTypeInt 32 0 | |
| %1 = OpConstant %int 3 | |
| %main = OpFunction %void None %2 | |
| %4 = OpLabel | |
| OpReturnValue %1 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("SpecId decoration on target <id> " | |
| "'1[%uint_3]' must be a scalar specialization " | |
| "constant"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %1 SpecId 200 | |
| %void = OpTypeVoid | |
| %2 = OpTypeFunction %void | |
| %int = OpTypeInt 32 0 | |
| %3 = OpConstant %int 1 | |
| %4 = OpConstant %int 2 | |
| %1 = OpSpecConstantOp %int IAdd %3 %4 | |
| %main = OpFunction %void None %2 | |
| %6 = OpLabel | |
| OpReturnValue %3 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' " | |
| "must be a scalar specialization constant"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %1 SpecId 200 | |
| %void = OpTypeVoid | |
| %2 = OpTypeFunction %void | |
| %int = OpTypeInt 32 0 | |
| %3 = OpConstant %int 1 | |
| %1 = OpSpecConstantComposite %int | |
| %main = OpFunction %void None %2 | |
| %4 = OpLabel | |
| OpReturnValue %3 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' " | |
| "must be a scalar specialization constant"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, SpecIdTargetGood) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| OpDecorate %3 SpecId 200 | |
| OpDecorate %4 SpecId 201 | |
| OpDecorate %5 SpecId 202 | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %int = OpTypeInt 32 0 | |
| %bool = OpTypeBool | |
| %3 = OpSpecConstant %int 3 | |
| %4 = OpSpecConstantTrue %bool | |
| %5 = OpSpecConstantFalse %bool | |
| %main = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_P(ValidateIdWithMessage, CorrectErrorForShuffle) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %uint = OpTypeInt 32 0 | |
| %float = OpTypeFloat 32 | |
| %v4float = OpTypeVector %float 4 | |
| %v2float = OpTypeVector %float 2 | |
| %void = OpTypeVoid | |
| %548 = OpTypeFunction %void | |
| %CS = OpFunction %void None %548 | |
| %550 = OpLabel | |
| %6275 = OpUndef %v2float | |
| %6280 = OpUndef %v2float | |
| %6282 = OpVectorShuffle %v4float %6275 %6280 0 1 4 5 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv.c_str()); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Component index 4 is out of bounds for combined (Vector1 + Vector2) " | |
| "size of 4."))); | |
| EXPECT_EQ(25, getErrorPosition().index); | |
| } | |
| TEST_P(ValidateIdWithMessage, VoidStructMember) { | |
| const std::string spirv = kGLSL450MemoryModel + R"( | |
| %void = OpTypeVoid | |
| %struct = OpTypeStruct %void | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("Structures cannot contain a void type."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, TypeFunctionBadUse) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypePointer Function %2 | |
| %4 = OpFunction %1 None %2 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd)"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Invalid use of function type result id '2[%2]'."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, BadTypeId) { | |
| std::string spirv = kGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpConstant %3 0 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| %7 = OpUndef %4 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("ID '4[%float_0]' is not a type " | |
| "id"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 2 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| %9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerVisibleKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelLoadMakePointerVisibleMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 2 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| %9 = OpLoad %2 %4 MakePointerVisibleKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerVisibleKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelLoadNonPrivatePointerBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Private %2 | |
| %4 = OpVariable %3 Private | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 2 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| %9 = OpLoad %2 %4 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelLoadMakePointerAvailableCannotBeUsed) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 2 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| %9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerAvailableKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "MakePointerAvailableKHR cannot be used with OpLoad"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpVariable %3 Uniform | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 5 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| OpStore %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelStoreMakePointerAvailableMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpVariable %3 Uniform | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 5 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| OpStore %4 %6 MakePointerAvailableKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerAvailableKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelStoreNonPrivatePointerBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Output %2 | |
| %4 = OpVariable %3 Output | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 5 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| OpStore %4 %6 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelStoreMakePointerVisibleCannotBeUsed) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability VulkanMemoryModelKHR | |
| OpCapability Linkage | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Uniform %2 | |
| %4 = OpVariable %3 Uniform | |
| %5 = OpTypeFunction %1 | |
| %6 = OpConstant %2 5 | |
| %7 = OpFunction %1 None %5 | |
| %8 = OpLabel | |
| OpStore %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "MakePointerVisibleKHR cannot be used with OpStore."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemoryAvailableMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 MakePointerAvailableKHR %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerAvailableKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemoryVisibleMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 MakePointerVisibleKHR %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerVisibleKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemoryAvailableBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Output %2 | |
| %4 = OpVariable %3 Output | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemoryVisibleBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Input %2 | |
| %6 = OpVariable %5 Input | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemory %4 %6 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerVisibleKHR %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemorySizedAvailableAndVisible) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemorySizedAvailableMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 MakePointerAvailableKHR %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerAvailableKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemorySizedVisibleMissingNonPrivatePointer) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 MakePointerVisibleKHR %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message("NonPrivatePointerKHR must be specified if " | |
| "MakePointerVisibleKHR is specified."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemorySizedAvailableBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Output %2 | |
| %4 = OpVariable %3 Output | |
| %5 = OpTypePointer Uniform %2 | |
| %6 = OpVariable %5 Uniform | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, | |
| VulkanMemoryModelCopyMemorySizedVisibleBadStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability Linkage | |
| OpCapability Addresses | |
| OpCapability VulkanMemoryModelKHR | |
| OpExtension "SPV_KHR_vulkan_memory_model" | |
| OpMemoryModel Logical VulkanKHR | |
| %1 = OpTypeVoid | |
| %2 = OpTypeInt 32 0 | |
| %3 = OpTypePointer Workgroup %2 | |
| %4 = OpVariable %3 Workgroup | |
| %5 = OpTypePointer Input %2 | |
| %6 = OpVariable %5 Input | |
| %7 = OpConstant %2 2 | |
| %8 = OpConstant %2 5 | |
| %9 = OpTypeFunction %1 | |
| %10 = OpFunction %1 None %9 | |
| %11 = OpLabel | |
| OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "NonPrivatePointerKHR requires a pointer in Uniform, " | |
| "Workgroup, CrossWorkgroup, Generic, Image or " | |
| "StorageBuffer storage classes."))); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock1) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| %7 = OpLabel | |
| %8 = OpFunctionCall %3 %9 | |
| OpUnreachable | |
| OpFunctionEnd | |
| %9 = OpFunction %3 None %4 | |
| %10 = OpLabel | |
| OpReturnValue %8 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "ID '8[%8]' defined in block '7[%7]' does not dominate its " | |
| "use in block '10[%10]'\n %10 = OpLabel"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock2) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| %7 = OpLabel | |
| %8 = OpFunctionCall %3 %9 | |
| OpUnreachable | |
| OpFunctionEnd | |
| %9 = OpFunction %3 None %4 | |
| %10 = OpLabel | |
| OpReturnValue %8 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "ID '8[%8]' defined in block '7[%7]' does not dominate its " | |
| "use in block '10[%10]'\n %10 = OpLabel"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock3) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| %7 = OpLabel | |
| %8 = OpFunctionCall %3 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| %9 = OpFunction %3 None %4 | |
| %10 = OpLabel | |
| OpReturnValue %8 | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "ID '8[%8]' defined in block '7[%7]' does not dominate its " | |
| "use in block '10[%10]'\n %10 = OpLabel"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock4) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| %7 = OpLabel | |
| %8 = OpUndef %3 | |
| %9 = OpCopyObject %3 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock5) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpReturn | |
| %7 = OpLabel | |
| %8 = OpUndef %3 | |
| OpBranch %9 | |
| %9 = OpLabel | |
| %10 = OpCopyObject %3 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock6) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| OpBranch %7 | |
| %8 = OpLabel | |
| %9 = OpUndef %3 | |
| OpBranch %7 | |
| %7 = OpLabel | |
| %10 = OpCopyObject %3 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "ID '9[%9]' defined in block '8[%8]' does not dominate its " | |
| "use in block '7[%7]'\n %7 = OpLabel"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, ReachableDefUnreachableUse) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %1 = OpTypeVoid | |
| %2 = OpTypeFunction %1 | |
| %3 = OpTypeFloat 32 | |
| %4 = OpTypeFunction %3 | |
| %5 = OpFunction %1 None %2 | |
| %6 = OpLabel | |
| %7 = OpUndef %3 | |
| OpReturn | |
| %8 = OpLabel | |
| %9 = OpCopyObject %3 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| TEST_P(ValidateIdWithMessage, UnreachableDefUsedInPhi) { | |
| const std::string spirv = kNoKernelGLSL450MemoryModel + R"( | |
| %void = OpTypeVoid | |
| %3 = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %bool = OpTypeBool | |
| %6 = OpTypeFunction %float | |
| %1 = OpFunction %void None %3 | |
| %7 = OpLabel | |
| %8 = OpUndef %bool | |
| OpSelectionMerge %9 None | |
| OpBranchConditional %8 %10 %9 | |
| %10 = OpLabel | |
| %11 = OpUndef %float | |
| OpBranch %9 | |
| %12 = OpLabel | |
| %13 = OpUndef %float | |
| OpUnreachable | |
| %9 = OpLabel | |
| %14 = OpPhi %float %11 %10 %13 %7 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT( | |
| getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "In OpPhi instruction '14[%14]', ID '13[%13]' definition does not " | |
| "dominate its parent '7[%7]'\n %14 = OpPhi %float %11 %10 %13 " | |
| "%7"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) { | |
| std::string spirv = R"( | |
| OpCapability GenericPointer | |
| OpCapability VariablePointersStorageBuffer | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %1 "main" | |
| OpExecutionMode %1 OriginLowerLeft | |
| OpTypeForwardPointer %2 CrossWorkgroup | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %1 = OpFunction %2 DontInline %3 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Pointer type in OpTypeForwardPointer is not a pointer " | |
| "type.\n OpTypeForwardPointer %void CrossWorkgroup"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) { | |
| std::string spirv = R"( | |
| OpCapability GenericPointer | |
| OpCapability VariablePointersStorageBuffer | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %1 "main" | |
| OpExecutionMode %1 OriginLowerLeft | |
| OpTypeForwardPointer %2 CrossWorkgroup | |
| %int = OpTypeInt 32 1 | |
| %2 = OpTypePointer Function %int | |
| %void = OpTypeVoid | |
| %3 = OpTypeFunction %void | |
| %1 = OpFunction %void None %3 | |
| %4 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message( | |
| "Storage class in OpTypeForwardPointer does not match the " | |
| "pointer definition.\n OpTypeForwardPointer " | |
| "%_ptr_Function_int CrossWorkgroup"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, MissingForwardPointer) { | |
| const std::string spirv = R"( | |
| OpCapability Linkage | |
| OpCapability Shader | |
| OpMemoryModel Logical Simple | |
| %float = OpTypeFloat 32 | |
| %_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9 | |
| %_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 | |
| %1278 = OpVariable %_ptr_Uniform__struct_9 Uniform | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr(make_message("Operand '3[%_ptr_Uniform__struct_2]' " | |
| "requires a previous definition"))); | |
| } | |
| TEST_P(ValidateIdWithMessage, NVBindlessSamplerInStruct) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpCapability BindlessTextureNV | |
| OpExtension "SPV_NV_bindless_texture" | |
| OpMemoryModel Logical GLSL450 | |
| OpSamplerImageAddressingModeNV 64 | |
| OpEntryPoint Fragment %main "main" | |
| OpExecutionMode %main OriginUpperLeft | |
| %void = OpTypeVoid | |
| %3 = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %7 = OpTypeImage %float 2D 0 0 0 1 Unknown | |
| %8 = OpTypeSampledImage %7 | |
| %9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f | |
| %10 = OpTypeSampler | |
| %UBO = OpTypeStruct %8 %9 %10 | |
| %_ptr_Uniform_UBO = OpTypePointer Uniform %UBO | |
| %_ = OpVariable %_ptr_Uniform_UBO Uniform | |
| %main = OpFunction %void None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); | |
| } | |
| INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool()); | |
| } // namespace | |
| } // namespace val | |
| } // namespace spvtools |