// Copyright (c) 2025 Google LLC // // 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 #include #include "gmock/gmock.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" namespace spvtools { namespace val { namespace { using ::testing::Combine; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Values; using ::testing::ValuesIn; using ValidateLogicalPointersTest = spvtest::ValidateBase; enum class MatrixTrace : uint32_t { kNotAMatrix, kColumn, kComponent, }; const MatrixTrace traces[] = {MatrixTrace::kNotAMatrix, MatrixTrace::kColumn, MatrixTrace::kComponent}; using MatrixTraceTypedTest = spvtest::ValidateBase; TEST_P(MatrixTraceTypedTest, PhiLoopOp1) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpBranch %loop %loop = OpLabel %phi = OpPhi )" + type + R"( %gep %entry %copy %continue OpLoopMerge %merge %continue None OpBranchConditional %bool_cond %merge %continue %continue = OpLabel %copy = OpCopyObject )" + type + R"( %phi OpBranch %loop %merge = OpLabel OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, PhiLoopOp2) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpBranch %loop %loop = OpLabel %phi = OpPhi )" + type + R"( %copy %continue %gep %entry OpLoopMerge %merge %continue None OpBranchConditional %bool_cond %merge %continue %continue = OpLabel %copy = OpCopyObject )" + type + R"( %phi OpBranch %loop %merge = OpLabel OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, SelectOp1) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject )" + type + R"( %gep %sel = OpSelect )" + type + R"( %bool_cond %copy %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, SelectOp2) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject )" + type + R"( %gep %sel = OpSelect )" + type + R"( %bool_cond %null %copy OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionVariable) { const auto trace_type = GetParam(); std::string gep, type, ld_type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_func_wg_v2float"; ld_type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; break; case MatrixTrace::kComponent: type = "%ptr_func_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; break; default: type = "%ptr_func_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_float = OpTypePointer Workgroup %float %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 %ptr_func_wg_float = OpTypePointer Function %ptr_wg_float %ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float %var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup %var_float = OpVariable %ptr_wg_float Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %func_var = OpVariable )" + type + R"( Function %gep = )" + gep + R"( OpStore %func_var %gep %ld = OpLoad )" + ld_type + R"( %func_var OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, PrivateVariable) { const auto trace_type = GetParam(); std::string gep, type, ld_type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_priv_wg_v2float"; ld_type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; break; case MatrixTrace::kComponent: type = "%ptr_priv_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; break; default: type = "%ptr_priv_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_float = OpTypePointer Workgroup %float %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 %ptr_priv_wg_float = OpTypePointer Private %ptr_wg_float %ptr_priv_wg_v2float = OpTypePointer Private %ptr_wg_v2float %var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup %var_float = OpVariable %ptr_wg_float Workgroup %priv_var = OpVariable )" + type + R"( Private %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpStore %priv_var %gep %ld = OpLoad )" + ld_type + R"( %priv_var OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionVariableAggregate) { const auto trace_type = GetParam(); std::string gep, var_type, gep_type, ld_type; switch (trace_type) { case MatrixTrace::kColumn: var_type = "%ptr_func_wg_struct_v2float"; gep_type = "%ptr_func_wg_v2float"; ld_type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; break; case MatrixTrace::kComponent: var_type = "%ptr_func_wg_struct_float"; gep_type = "%ptr_func_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; break; default: var_type = "%ptr_func_wg_struct_float"; gep_type = "%ptr_func_wg_float"; ld_type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_float = OpTypePointer Workgroup %float %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 %func_array_float = OpTypeArray %ptr_wg_float %int_1 %func_array_v2float = OpTypeArray %ptr_wg_v2float %int_1 %func_struct_float = OpTypeStruct %func_array_float %func_struct_v2float = OpTypeStruct %func_array_v2float %ptr_func_wg_struct_float = OpTypePointer Function %func_struct_float %ptr_func_wg_struct_v2float = OpTypePointer Function %func_struct_v2float %ptr_func_wg_float = OpTypePointer Function %ptr_wg_float %ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float %var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup %var_float = OpVariable %ptr_wg_float Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %func_var = OpVariable )" + var_type + R"( Function %gep = )" + gep + R"( %store_gep = OpAccessChain )" + gep_type + R"( %func_var %int_0 %index %store_gep_copy = OpCopyObject )" + gep_type + R"( %store_gep OpStore %store_gep_copy %gep %ld_gep = OpAccessChain )" + gep_type + R"( %func_var %int_0 %index %ld = OpLoad )" + ld_type + R"( %ld_gep OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionCallParam1) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %foo_fn = OpTypeFunction %void )" + type + R"( %bool %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject )" + type + R"( %gep %call = OpFunctionCall %void %foo %copy %bool_cond OpReturn OpFunctionEnd %foo = OpFunction %void None %foo_fn %ptr_param = OpFunctionParameter )" + type + R"( %bool_param = OpFunctionParameter %bool %foo_entry = OpLabel %sel = OpSelect )" + type + R"( %bool_param %ptr_param %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionCallParam2) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %foo_fn = OpTypeFunction %void %bool )" + type + R"( %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject )" + type + R"( %gep %call = OpFunctionCall %void %foo %bool_cond %copy OpReturn OpFunctionEnd %foo = OpFunction %void None %foo_fn %bool_param = OpFunctionParameter %bool %ptr_param = OpFunctionParameter )" + type + R"( %foo_entry = OpLabel %sel = OpSelect )" + type + R"( %bool_param %ptr_param %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionCall) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction )" + type + R"( %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall )" + type + R"( %foo OpReturn OpFunctionEnd %foo = OpFunction )" + type + R"( None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpReturnValue %gep OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn1) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction )" + type + R"( %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall )" + type + R"( %foo OpReturn OpFunctionEnd %foo = OpFunction )" + type + R"( None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpReturnValue %gep %merge = OpLabel OpReturnValue %null OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn2) { const auto trace_type = GetParam(); std::string gep, type; switch (trace_type) { case MatrixTrace::kColumn: type = "%ptr_wg_v2float"; gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: type = "%ptr_wg_float"; gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull )" + type + R"( %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction )" + type + R"( %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall )" + type + R"( %foo OpReturn OpFunctionEnd %foo = OpFunction )" + type + R"( None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpReturnValue %null %merge = OpLabel OpReturnValue %gep OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceTyped, MatrixTraceTypedTest, ValuesIn(traces)); using MatrixTraceUntypedTest = spvtest::ValidateBase; TEST_P(MatrixTraceUntypedTest, PhiLoopOp1) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpBranch %loop %loop = OpLabel %phi = OpPhi %ptr_wg %gep %entry %copy %continue OpLoopMerge %merge %continue None OpBranchConditional %bool_cond %merge %continue %continue = OpLabel %copy = OpCopyObject %ptr_wg %phi OpBranch %loop %merge = OpLabel OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, PhiLoopOp2) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpBranch %loop %loop = OpLabel %phi = OpPhi %ptr_wg %copy %continue %gep %entry OpLoopMerge %merge %continue None OpBranchConditional %bool_cond %merge %continue %continue = OpLabel %copy = OpCopyObject %ptr_wg %phi OpBranch %loop %merge = OpLabel OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, SelectOp1) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject %ptr_wg %gep %sel = OpSelect %ptr_wg %bool_cond %copy %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, SelectOp2) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject %ptr_wg %gep %sel = OpSelect %ptr_wg %bool_cond %null %copy OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionVariable) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %ptr_func_wg = OpTypePointer Function %ptr_wg %var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 %var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %func_var = OpVariable %ptr_func_wg Function %gep = )" + gep + R"( OpStore %func_var %gep %ld = OpLoad %ptr_wg %func_var OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, PrivateVariable) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %ptr_priv_wg = OpTypePointer Private %ptr_wg %var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 %var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float %priv_var = OpVariable %ptr_priv_wg Private %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( OpStore %priv_var %gep %ld = OpLoad %ptr_wg %priv_var OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionVariableAggregate) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %func_array = OpTypeArray %ptr_wg %int_1 %func_struct = OpTypeStruct %func_array %ptr_func_wg_struct = OpTypePointer Function %func_struct %ptr_func_wg = OpTypePointer Function %ptr_wg %var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 %var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %func_var = OpVariable %ptr_func_wg_struct Function %gep = )" + gep + R"( %store_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index %store_gep_copy = OpCopyObject %ptr_func_wg %store_gep OpStore %store_gep_copy %gep %ld_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index %ld = OpLoad %ptr_wg %ld_gep OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionCallParam1) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %foo_fn = OpTypeFunction %void %ptr_wg %bool %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject %ptr_wg %gep %call = OpFunctionCall %void %foo %copy %bool_cond OpReturn OpFunctionEnd %foo = OpFunction %void None %foo_fn %ptr_param = OpFunctionParameter %ptr_wg %bool_param = OpFunctionParameter %bool %foo_entry = OpLabel %sel = OpSelect %ptr_wg %bool_param %ptr_param %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionCallParam2) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %foo_fn = OpTypeFunction %void %bool %ptr_wg %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpCopyObject %ptr_wg %gep %call = OpFunctionCall %void %foo %bool_cond %copy OpReturn OpFunctionEnd %foo = OpFunction %void None %foo_fn %bool_param = OpFunctionParameter %bool %ptr_param = OpFunctionParameter %ptr_wg %foo_entry = OpLabel %sel = OpSelect %ptr_wg %bool_param %ptr_param %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionCall) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction %ptr_wg %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %ptr_wg %foo OpReturn OpFunctionEnd %foo = OpFunction %ptr_wg None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpReturnValue %gep OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn1) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction %ptr_wg %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %ptr_wg %foo OpReturn OpFunctionEnd %foo = OpFunction %ptr_wg None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpReturnValue %gep %merge = OpLabel OpReturnValue %null OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn2) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " "%index"; break; default: gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %null = OpConstantNull %ptr_wg %var = OpUntypedVariableKHR %ptr_wg Workgroup %struct %void_fn = OpTypeFunction %void %foo_ty = OpTypeFunction %ptr_wg %main = OpFunction %void None %void_fn %entry = OpLabel %call = OpFunctionCall %ptr_wg %foo OpReturn OpFunctionEnd %foo = OpFunction %ptr_wg None %foo_ty %foo_entry = OpLabel %gep = )" + gep + R"( OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpReturnValue %null %merge = OpLabel OpReturnValue %gep OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } TEST_P(MatrixTraceUntypedTest, MixedTypes) { const auto trace_type = GetParam(); std::string gep; switch (trace_type) { case MatrixTrace::kColumn: gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; break; case MatrixTrace::kComponent: gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; break; default: gep = "OpAccessChain %ptr_wg_float %var %int_1"; break; } const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %index = OpUndef %int %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %array = OpTypeArray %mat2x2 %int_2 %struct = OpTypeStruct %array %float %ptr_wg = OpTypeUntypedPointerKHR Workgroup %ptr_wg_struct = OpTypePointer Workgroup %struct %ptr_wg_v2float = OpTypePointer Workgroup %v2float %ptr_wg_float = OpTypePointer Workgroup %float %null = OpConstantNull %ptr_wg %var = OpVariable %ptr_wg_struct Workgroup %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = )" + gep + R"( %copy = OpUntypedAccessChainKHR %ptr_wg %struct %gep %sel = OpSelect %ptr_wg %bool_cond %copy %null OpReturn OpFunctionEnd )"; const auto expected = trace_type == MatrixTrace::kNotAMatrix ? SPV_SUCCESS : SPV_ERROR_INVALID_DATA; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); if (expected) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to a column or a " "component of a column of a matrix")); } } INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceUntyped, MatrixTraceUntypedTest, ValuesIn(traces)); TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersTyped) { const std::string spirv = R"( OpCapability VariablePointersStorageBuffer OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %struct = OpTypeStruct %int %ptr_struct = OpTypePointer StorageBuffer %struct %v1 = OpVariable %ptr_struct StorageBuffer %v2 = OpVariable %ptr_struct StorageBuffer %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %select = OpSelect %ptr_struct %bool_cond %v1 %v2 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointers must point into the same structure " "(or OpConstantNull)")); } TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersUntyped) { const std::string spirv = R"( OpCapability VariablePointersStorageBuffer OpCapability Shader OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %struct = OpTypeStruct %int %ptr = OpTypeUntypedPointerKHR StorageBuffer %v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct %v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %select = OpSelect %ptr %bool_cond %v1 %v2 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointers must point into the same structure " "(or OpConstantNull)")); } TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersTyped) { const std::string spirv = R"( OpCapability VariablePointersStorageBuffer OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %struct = OpTypeStruct %int %ptr_struct = OpTypePointer StorageBuffer %struct %v1 = OpVariable %ptr_struct StorageBuffer %v2 = OpVariable %ptr_struct StorageBuffer %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpBranch %merge %merge = OpLabel %phi = OpPhi %ptr_struct %v1 %entry %v2 %then OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointers must point into the same structure " "(or OpConstantNull)")); } TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersUntyped) { const std::string spirv = R"( OpCapability VariablePointersStorageBuffer OpCapability Shader OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %bool = OpTypeBool %bool_cond = OpUndef %bool %int = OpTypeInt 32 0 %struct = OpTypeStruct %int %ptr = OpTypeUntypedPointerKHR StorageBuffer %v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct %v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpSelectionMerge %merge None OpBranchConditional %bool_cond %then %merge %then = OpLabel OpBranch %merge %merge = OpLabel %phi = OpPhi %ptr %v1 %entry %v2 %then OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointers must point into the same structure " "(or OpConstantNull)")); } TEST_F(ValidateLogicalPointersTest, BlockArrayTyped) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_4 = OpConstant %int 4 %bool = OpTypeBool %bool_cond = OpUndef %bool %struct = OpTypeStruct %int %array = OpTypeArray %struct %int_4 %ptr_array = OpTypePointer StorageBuffer %array %ptr_int = OpTypePointer StorageBuffer %int %var = OpVariable %ptr_array StorageBuffer %null = OpConstantNull %ptr_array %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %select = OpSelect %ptr_array %bool_cond %null %var OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to an array of Block- " "or BufferBlock-decorated structs")); } TEST_F(ValidateLogicalPointersTest, BlockArrayUntyped) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_4 = OpConstant %int 4 %bool = OpTypeBool %bool_cond = OpUndef %bool %struct = OpTypeStruct %int %array = OpTypeArray %struct %int_4 %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %array %null = OpConstantNull %ptr %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %select = OpSelect %ptr %bool_cond %null %var OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to an array of Block- " "or BufferBlock-decorated structs")); } TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoad) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %struct = OpTypeStruct %mat2x2 %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %ld = OpLoad %struct %var OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoadVariablePointer) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %struct = OpTypeStruct %mat2x2 %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %struct %null = OpConstantNull %ptr %bool = OpTypeBool %cond = OpUndef %bool %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %sel = OpSelect %ptr %cond %var %null %ld = OpLoad %struct %sel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to an object that is " "or contains a matrix")); } TEST_F(ValidateLogicalPointersTest, UntypedMatrixStore) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %zero = OpConstantNull %mat2x2 %struct = OpTypeStruct %mat2x2 %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel OpStore %var %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateLogicalPointersTest, UntypedMatrixStoreVariablePointer) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %float = OpTypeFloat 32 %v2float = OpTypeVector %float 2 %mat2x2 = OpTypeMatrix %v2float 2 %zero = OpConstantNull %mat2x2 %struct = OpTypeStruct %mat2x2 %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %struct %null = OpConstantNull %ptr %bool = OpTypeBool %cond = OpUndef %bool %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %sel = OpSelect %ptr %cond %var %null OpStore %sel %zero OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Variable pointer must not point to an object that is " "or contains a matrix")); } TEST_F(ValidateLogicalPointersTest, LogicalPointerOperandFailure) { const std::string spirv = R"( OpCapability Shader OpCapability Kernel OpCapability Addresses OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %v2int = OpTypeVector %int 2 %ptr_v2int = OpTypePointer StorageBuffer %v2int %var = OpVariable %ptr_v2int StorageBuffer %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %gep = OpInBoundsPtrAccessChain %ptr_v2int %var %int_0 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Instruction may not have a logical pointer operand")); } TEST_F(ValidateLogicalPointersTest, LogicalPointerOperandVariablePointerStorageBuffer) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage OpCapability Addresses OpMemoryModel Logical GLSL450 OpDecorate %ptr_int ArrayStride 4 %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_4 = OpConstant %int 4 %array = OpTypeArray %int %int_4 %ptr_array = OpTypePointer StorageBuffer %array %ptr_int = OpTypePointer StorageBuffer %int %var = OpVariable %ptr_array StorageBuffer %void = OpTypeVoid %void_fn = OpTypeFunction %void %foo = OpFunction %void None %void_fn %entry = OpLabel %gep = OpAccessChain %ptr_int %var %int_0 %ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Instruction may only have a logical pointer operand in the " "StorageBuffer or " "Workgroup storage classes with appropriate variable pointers " "capability")); } TEST_F(ValidateLogicalPointersTest, LogicalPointerOperandVariablePointerWorkgroup) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage OpCapability VariablePointersStorageBuffer OpMemoryModel Logical GLSL450 %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %int_4 = OpConstant %int 4 %array = OpTypeArray %int %int_4 %ptr_array = OpTypePointer Workgroup %array %ptr_int = OpTypePointer Workgroup %int %var = OpVariable %ptr_array Workgroup %void = OpTypeVoid %void_fn = OpTypeFunction %void %foo = OpFunction %void None %void_fn %entry = OpLabel %gep = OpAccessChain %ptr_int %var %int_0 %ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Instruction may only have a logical pointer operand in the " "StorageBuffer or " "Workgroup storage classes with appropriate variable pointers " "capability")); } TEST_F(ValidateLogicalPointersTest, LogicalPointerReturnVariablePointerStorageBuffer) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %int = OpTypeInt 32 0 %ptr = OpTypePointer StorageBuffer %int %null = OpConstantNull %ptr )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Instruction may only return a logical pointer in the " "StorageBuffer or " "Workgroup storage classes with appropriate variable pointers " "capability")); } TEST_F(ValidateLogicalPointersTest, LogicalPointerReturnVariablePointerWorkgroup) { const std::string spirv = R"( OpCapability Shader OpCapability Linkage OpCapability VariablePointersStorageBuffer OpMemoryModel Logical GLSL450 %int = OpTypeInt 32 0 %ptr = OpTypePointer Workgroup %int %null = OpConstantNull %ptr )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Instruction may only return a logical pointer in the " "StorageBuffer or " "Workgroup storage classes with appropriate variable pointers " "capability")); } TEST_F(ValidateLogicalPointersTest, ArrayLengthInvalidVariablePointer) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %array = OpTypeRuntimeArray %int %struct = OpTypeStruct %array %ptr = OpTypePointer StorageBuffer %struct %var = OpVariable %ptr StorageBuffer %null = OpConstantNull %ptr %bool = OpTypeBool %cond = OpUndef %bool %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %sel = OpSelect %ptr %cond %null %var %len = OpArrayLength %int %sel 0 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Pointer operand must not be a variable pointer")); } TEST_F(ValidateLogicalPointersTest, ArrayLengthUntypedInvalidVariablePointer) { const std::string spirv = R"( OpCapability Shader OpCapability VariablePointers OpCapability UntypedPointersKHR OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 1 1 1 %void = OpTypeVoid %int = OpTypeInt 32 0 %array = OpTypeRuntimeArray %int %struct = OpTypeStruct %array %ptr = OpTypeUntypedPointerKHR StorageBuffer %var = OpUntypedVariableKHR %ptr StorageBuffer %struct %null = OpConstantNull %ptr %bool = OpTypeBool %cond = OpUndef %bool %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel %sel = OpSelect %ptr %cond %null %var %len = OpUntypedArrayLengthKHR %int %struct %sel 0 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Pointer operand must not be a variable pointer")); } TEST_F(ValidateLogicalPointersTest, FunctionParameterOperandOfFunctionCall) { const std::string spirv = R"( OpCapability ClipDistance %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 " " OpExecutionMode %2 OriginUpperLeft %void = OpTypeVoid %32 = OpTypeFunction %void %float = OpTypeFloat 32 %_ptr_Function_float = OpTypePointer Function %float %35 = OpTypeFunction %float %_ptr_Function_float %float_1_35631564en19 = OpConstant %float 1.35631564e-19 %float_1_35631564en19_0 = OpConstant %float 1.35631564e-19 %2 = OpFunction %void None %32 %8447 = OpLabel OpUnreachable OpFunctionEnd %9 = OpFunction %float None %35 %10 = OpFunctionParameter %_ptr_Function_float %65535 = OpLabel %217 = OpVariable %_ptr_Function_float Function %218 = OpLoad %float %10 %2097407 = OpFunctionCall %float %9 %10 %231 = OpLoad %float %217 %232 = OpFDiv %float %218 %231 %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232 OpReturnValue %233 OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateLogicalPointersTest, RecursiveCalls) { const std::string spirv = R"( OpCapability ClipDistance %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 " " OpExecutionMode %2 OriginUpperLeft %void = OpTypeVoid %32 = OpTypeFunction %void %float = OpTypeFloat 32 %_ptr_Function_float = OpTypePointer Function %float %35 = OpTypeFunction %float %_ptr_Function_float %float_1_35631564en19 = OpConstant %float 1.35631564e-19 %float_1_35631564en19_0 = OpConstant %float 1.35631564e-19 %2 = OpFunction %void None %32 %8447 = OpLabel OpUnreachable OpFunctionEnd %9 = OpFunction %float None %35 %10 = OpFunctionParameter %_ptr_Function_float %65535 = OpLabel %217 = OpVariable %_ptr_Function_float Function %218 = OpLoad %float %10 %2097407 = OpFunctionCall %float %20 %10 %231 = OpLoad %float %217 %232 = OpFDiv %float %218 %231 %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232 OpReturnValue %233 OpFunctionEnd %20 = OpFunction %float None %35 %21 = OpFunctionParameter %_ptr_Function_float %22 = OpLabel %23 = OpFunctionCall %float %9 %21 OpReturnValue %float_1_35631564en19 OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } } // namespace } // namespace val } // namespace spvtools