1// Copyright (c) 2017 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include <string> 16 17#include "gmock/gmock.h" 18#include "source/opt/value_number_table.h" 19#include "test/opt/assembly_builder.h" 20#include "test/opt/pass_fixture.h" 21#include "test/opt/pass_utils.h" 22 23namespace spvtools { 24namespace opt { 25namespace { 26 27using ::testing::HasSubstr; 28using ::testing::MatchesRegex; 29using PrivateToLocalTest = PassTest<::testing::Test>; 30 31TEST_F(PrivateToLocalTest, ChangeToLocal) { 32 // Change the private variable to a local, and change the types accordingly. 33 const std::string text = R"( 34 OpCapability Shader 35 %1 = OpExtInstImport "GLSL.std.450" 36 OpMemoryModel Logical GLSL450 37 OpEntryPoint Fragment %2 "main" 38 OpExecutionMode %2 OriginUpperLeft 39 OpSource GLSL 430 40 %3 = OpTypeVoid 41 %4 = OpTypeFunction %3 42; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 43 %5 = OpTypeFloat 32 44; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] 45 %6 = OpTypePointer Private %5 46; CHECK-NOT: OpVariable [[.+]] Private 47 %8 = OpVariable %6 Private 48; CHECK: OpFunction 49 %2 = OpFunction %3 None %4 50; CHECK: OpLabel 51 %7 = OpLabel 52; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function 53; CHECK: OpLoad [[float]] [[newvar]] 54 %9 = OpLoad %5 %8 55 OpReturn 56 OpFunctionEnd 57 )"; 58 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 59} 60 61TEST_F(PrivateToLocalTest, ReuseExistingType) { 62 // Change the private variable to a local, and change the types accordingly. 63 const std::string text = R"( 64 OpCapability Shader 65 %1 = OpExtInstImport "GLSL.std.450" 66 OpMemoryModel Logical GLSL450 67 OpEntryPoint Fragment %2 "main" 68 OpExecutionMode %2 OriginUpperLeft 69 OpSource GLSL 430 70 %3 = OpTypeVoid 71 %4 = OpTypeFunction %3 72; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 73 %5 = OpTypeFloat 32 74 %func_ptr = OpTypePointer Function %5 75; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] 76; CHECK-NOT: [[%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] 77 %6 = OpTypePointer Private %5 78; CHECK-NOT: OpVariable [[.+]] Private 79 %8 = OpVariable %6 Private 80; CHECK: OpFunction 81 %2 = OpFunction %3 None %4 82; CHECK: OpLabel 83 %7 = OpLabel 84; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function 85; CHECK: OpLoad [[float]] [[newvar]] 86 %9 = OpLoad %5 %8 87 OpReturn 88 OpFunctionEnd 89 )"; 90 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 91} 92 93TEST_F(PrivateToLocalTest, UpdateAccessChain) { 94 // Change the private variable to a local, and change the AccessChain. 95 const std::string text = R"( 96 OpCapability Shader 97 %1 = OpExtInstImport "GLSL.std.450" 98 OpMemoryModel Logical GLSL450 99 OpEntryPoint Fragment %2 "main" 100 OpExecutionMode %2 OriginUpperLeft 101 OpSource GLSL 430 102 %uint = OpTypeInt 32 0 103 %uint_0 = OpConstant %uint 0 104 %void = OpTypeVoid 105 %6 = OpTypeFunction %void 106; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 107 %float = OpTypeFloat 32 108; CHECK: [[struct:%[a-zA-Z_\d]+]] = OpTypeStruct 109 %_struct_8 = OpTypeStruct %float 110%_ptr_Private_float = OpTypePointer Private %float 111; CHECK: [[new_struct_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[struct]] 112; CHECK: [[new_float_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] 113%_ptr_Private__struct_8 = OpTypePointer Private %_struct_8 114; CHECK-NOT: OpVariable [[.+]] Private 115 %11 = OpVariable %_ptr_Private__struct_8 Private 116; CHECK: OpFunction 117 %2 = OpFunction %void None %6 118; CHECK: OpLabel 119 %12 = OpLabel 120; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[new_struct_type]] Function 121; CHECK: [[member:%[a-zA-Z_\d]+]] = OpAccessChain [[new_float_type]] [[newvar]] 122 %13 = OpAccessChain %_ptr_Private_float %11 %uint_0 123; CHECK: OpLoad [[float]] [[member]] 124 %14 = OpLoad %float %13 125 OpReturn 126 OpFunctionEnd 127 )"; 128 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 129} 130 131TEST_F(PrivateToLocalTest, UseTexelPointer) { 132 // Change the private variable to a local, and change the OpImageTexelPointer. 133 const std::string text = R"( 134OpCapability SampledBuffer 135 OpCapability StorageImageExtendedFormats 136 OpCapability ImageBuffer 137 OpCapability Shader 138 %1 = OpExtInstImport "GLSL.std.450" 139 OpMemoryModel Logical GLSL450 140 OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID 141 OpExecutionMode %2 LocalSize 64 1 1 142 OpSource HLSL 600 143 OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId 144 OpDecorate %4 DescriptorSet 4 145 OpDecorate %4 Binding 70 146 %uint = OpTypeInt 32 0 147 %6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui 148%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 149%_ptr_Private_6 = OpTypePointer Private %6 150 %void = OpTypeVoid 151 %10 = OpTypeFunction %void 152 %uint_0 = OpConstant %uint 0 153 %uint_1 = OpConstant %uint 1 154 %v3uint = OpTypeVector %uint 3 155%_ptr_Input_v3uint = OpTypePointer Input %v3uint 156%_ptr_Image_uint = OpTypePointer Image %uint 157 %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant 158 %16 = OpVariable %_ptr_Private_6 Private 159%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input 160 %2 = OpFunction %void None %10 161 %17 = OpLabel 162; Make sure the variable was moved. 163; CHECK: OpFunction 164; CHECK-NEXT: OpLabel 165; CHECK-NEXT: OpVariable %_ptr_Function_6 Function 166 %18 = OpLoad %6 %4 167 OpStore %16 %18 168 %19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0 169 %20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 170 OpReturn 171 OpFunctionEnd 172 )"; 173 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 174} 175 176TEST_F(PrivateToLocalTest, UsedInTwoFunctions) { 177 // Should not change because it is used in multiple functions. 178 const std::string text = R"( 179 OpCapability Shader 180 %1 = OpExtInstImport "GLSL.std.450" 181 OpMemoryModel Logical GLSL450 182 OpEntryPoint Fragment %2 "main" 183 OpExecutionMode %2 OriginUpperLeft 184 OpSource GLSL 430 185 %3 = OpTypeVoid 186 %4 = OpTypeFunction %3 187 %5 = OpTypeFloat 32 188 %6 = OpTypePointer Private %5 189 %8 = OpVariable %6 Private 190 %2 = OpFunction %3 None %4 191 %7 = OpLabel 192 %9 = OpLoad %5 %8 193 OpReturn 194 OpFunctionEnd 195 %10 = OpFunction %3 None %4 196 %11 = OpLabel 197 %12 = OpLoad %5 %8 198 OpReturn 199 OpFunctionEnd 200 )"; 201 auto result = SinglePassRunAndDisassemble<StrengthReductionPass>( 202 text, /* skip_nop = */ true, /* do_validation = */ false); 203 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); 204} 205 206TEST_F(PrivateToLocalTest, UsedInFunctionCall) { 207 // Should not change because it is used in a function call. Changing the 208 // signature of the function would require cloning the function, which is not 209 // worth it. 210 const std::string text = R"( 211 OpCapability Shader 212 %1 = OpExtInstImport "GLSL.std.450" 213 OpMemoryModel Logical GLSL450 214 OpEntryPoint Fragment %2 "main" 215 OpExecutionMode %2 OriginUpperLeft 216 OpSource GLSL 430 217 %void = OpTypeVoid 218 %4 = OpTypeFunction %void 219 %float = OpTypeFloat 32 220%_ptr_Private_float = OpTypePointer Private %float 221 %7 = OpTypeFunction %void %_ptr_Private_float 222 %8 = OpVariable %_ptr_Private_float Private 223 %2 = OpFunction %void None %4 224 %9 = OpLabel 225 %10 = OpFunctionCall %void %11 %8 226 OpReturn 227 OpFunctionEnd 228 %11 = OpFunction %void None %7 229 %12 = OpFunctionParameter %_ptr_Private_float 230 %13 = OpLabel 231 %14 = OpLoad %float %12 232 OpReturn 233 OpFunctionEnd 234 )"; 235 auto result = SinglePassRunAndDisassemble<StrengthReductionPass>( 236 text, /* skip_nop = */ true, /* do_validation = */ false); 237 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); 238} 239 240TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct1) { 241 // Test that the correct pointer type is picked up. 242 const std::string text = R"( 243; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct 244; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct 245; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct1]] 246; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] 247; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] 248; CHECK: OpFunction 249; CHECK: OpLabel 250; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr1]] Function 251; CHECK: OpLoad [[struct1]] [[newvar]] 252 OpCapability Shader 253 %1 = OpExtInstImport "GLSL.std.450" 254 OpMemoryModel Logical GLSL450 255 OpEntryPoint Fragment %2 "main" 256 OpExecutionMode %2 OriginUpperLeft 257 OpSource GLSL 430 258 %3 = OpTypeVoid 259 %4 = OpTypeFunction %3 260 %5 = OpTypeFloat 32 261 %struct1 = OpTypeStruct %5 262 %struct2 = OpTypeStruct %5 263 %6 = OpTypePointer Private %struct1 264 %func_ptr2 = OpTypePointer Function %struct2 265 %8 = OpVariable %6 Private 266 %2 = OpFunction %3 None %4 267 %7 = OpLabel 268 %9 = OpLoad %struct1 %8 269 OpReturn 270 OpFunctionEnd 271 )"; 272 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 273} 274 275TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct2) { 276 // Test that the correct pointer type is picked up. 277 const std::string text = R"( 278; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct 279; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct 280; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct2]] 281; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] 282; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] 283; CHECK: OpFunction 284; CHECK: OpLabel 285; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr2]] Function 286; CHECK: OpLoad [[struct2]] [[newvar]] 287 OpCapability Shader 288 %1 = OpExtInstImport "GLSL.std.450" 289 OpMemoryModel Logical GLSL450 290 OpEntryPoint Fragment %2 "main" 291 OpExecutionMode %2 OriginUpperLeft 292 OpSource GLSL 430 293 %3 = OpTypeVoid 294 %4 = OpTypeFunction %3 295 %5 = OpTypeFloat 32 296 %struct1 = OpTypeStruct %5 297 %struct2 = OpTypeStruct %5 298 %6 = OpTypePointer Private %struct2 299 %func_ptr2 = OpTypePointer Function %struct1 300 %8 = OpVariable %6 Private 301 %2 = OpFunction %3 None %4 302 %7 = OpLabel 303 %9 = OpLoad %struct2 %8 304 OpReturn 305 OpFunctionEnd 306 )"; 307 SinglePassRunAndMatch<PrivateToLocalPass>(text, false); 308} 309 310TEST_F(PrivateToLocalTest, SPV14RemoveFromInterface) { 311 const std::string text = R"( 312; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv 313; CHECK: OpEntryPoint GLCompute %foo "foo" %in 314; CHECK: %priv = OpVariable {{%\w+}} Function 315OpCapability Shader 316OpMemoryModel Logical GLSL450 317OpEntryPoint GLCompute %foo "foo" %in %priv 318OpExecutionMode %foo LocalSize 1 1 1 319OpName %foo "foo" 320OpName %in "in" 321OpName %priv "priv" 322%void = OpTypeVoid 323%int = OpTypeInt 32 0 324%ptr_ssbo_int = OpTypePointer StorageBuffer %int 325%ptr_private_int = OpTypePointer Private %int 326%in = OpVariable %ptr_ssbo_int StorageBuffer 327%priv = OpVariable %ptr_private_int Private 328%void_fn = OpTypeFunction %void 329%foo = OpFunction %void None %void_fn 330%entry = OpLabel 331%ld = OpLoad %int %in 332OpStore %priv %ld 333OpReturn 334OpFunctionEnd 335)"; 336 337 SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); 338 SinglePassRunAndMatch<PrivateToLocalPass>(text, true); 339} 340 341TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleEntryPoints) { 342 const std::string text = R"( 343; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv 344; CHECK-NOT: OpEntryPoint GLCompute %foo "bar" %in %priv 345; CHECK: OpEntryPoint GLCompute %foo "foo" %in 346; CHECK: OpEntryPoint GLCompute %foo "bar" %in 347; CHECK: %priv = OpVariable {{%\w+}} Function 348OpCapability Shader 349OpMemoryModel Logical GLSL450 350OpEntryPoint GLCompute %foo "foo" %in %priv 351OpEntryPoint GLCompute %foo "bar" %in %priv 352OpExecutionMode %foo LocalSize 1 1 1 353OpName %foo "foo" 354OpName %in "in" 355OpName %priv "priv" 356%void = OpTypeVoid 357%int = OpTypeInt 32 0 358%ptr_ssbo_int = OpTypePointer StorageBuffer %int 359%ptr_private_int = OpTypePointer Private %int 360%in = OpVariable %ptr_ssbo_int StorageBuffer 361%priv = OpVariable %ptr_private_int Private 362%void_fn = OpTypeFunction %void 363%foo = OpFunction %void None %void_fn 364%entry = OpLabel 365%ld = OpLoad %int %in 366OpStore %priv %ld 367OpReturn 368OpFunctionEnd 369)"; 370 371 SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); 372 SinglePassRunAndMatch<PrivateToLocalPass>(text, true); 373} 374 375TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleVariables) { 376 const std::string text = R"( 377; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 378; CHECK: OpEntryPoint GLCompute %foo "foo" %in 379; CHECK: %priv1 = OpVariable {{%\w+}} Function 380; CHECK: %priv2 = OpVariable {{%\w+}} Function 381OpCapability Shader 382OpMemoryModel Logical GLSL450 383OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 384OpExecutionMode %foo LocalSize 1 1 1 385OpName %foo "foo" 386OpName %in "in" 387OpName %priv1 "priv1" 388OpName %priv2 "priv2" 389%void = OpTypeVoid 390%int = OpTypeInt 32 0 391%ptr_ssbo_int = OpTypePointer StorageBuffer %int 392%ptr_private_int = OpTypePointer Private %int 393%in = OpVariable %ptr_ssbo_int StorageBuffer 394%priv1 = OpVariable %ptr_private_int Private 395%priv2 = OpVariable %ptr_private_int Private 396%void_fn = OpTypeFunction %void 397%foo = OpFunction %void None %void_fn 398%entry = OpLabel 399%1 = OpFunctionCall %void %bar1 400%2 = OpFunctionCall %void %bar2 401OpReturn 402OpFunctionEnd 403%bar1 = OpFunction %void None %void_fn 404%3 = OpLabel 405%ld1 = OpLoad %int %in 406OpStore %priv1 %ld1 407OpReturn 408OpFunctionEnd 409%bar2 = OpFunction %void None %void_fn 410%4 = OpLabel 411%ld2 = OpLoad %int %in 412OpStore %priv2 %ld2 413OpReturn 414OpFunctionEnd 415)"; 416 417 SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); 418 SinglePassRunAndMatch<PrivateToLocalPass>(text, true); 419} 420 421TEST_F(PrivateToLocalTest, IdBoundOverflow1) { 422 const std::string text = R"( 423 OpCapability Shader 424 OpMemoryModel Logical GLSL450 425 OpEntryPoint Fragment %4 "main" 426 OpExecutionMode %4 OriginLowerLeft 427 OpSource HLSL 84 428 %2 = OpTypeVoid 429 %3 = OpTypeFunction %2 430 %6 = OpTypeFloat 32 431 %7 = OpTypeVector %6 4 432 %8 = OpTypeStruct %7 433 %4194302 = OpTypeStruct %8 %8 434 %9 = OpTypeStruct %8 %8 435 %11 = OpTypePointer Private %7 436 %18 = OpTypeStruct %6 %9 437 %12 = OpVariable %11 Private 438 %4 = OpFunction %2 None %3 439 %5 = OpLabel 440 %13 = OpLoad %7 %12 441 OpReturn 442 OpFunctionEnd 443 )"; 444 445 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 446 447 std::vector<Message> messages = { 448 {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; 449 SetMessageConsumer(GetTestMessageConsumer(messages)); 450 auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true); 451 EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); 452} 453 454TEST_F(PrivateToLocalTest, DebugPrivateToLocal) { 455 // Debug instructions must not have any impact on changing the private 456 // variable to a local. 457 const std::string text = R"( 458 OpCapability Shader 459 %1 = OpExtInstImport "GLSL.std.450" 460 %10 = OpExtInstImport "OpenCL.DebugInfo.100" 461 OpMemoryModel Logical GLSL450 462 OpEntryPoint Fragment %2 "main" 463 OpExecutionMode %2 OriginUpperLeft 464 %11 = OpString "test" 465 OpSource GLSL 430 466 %13 = OpTypeInt 32 0 467 %14 = OpConstant %13 32 468 %3 = OpTypeVoid 469 %4 = OpTypeFunction %3 470; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 471 %5 = OpTypeFloat 32 472; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] 473 %6 = OpTypePointer Private %5 474; CHECK-NOT: OpVariable [[.+]] Private 475 %8 = OpVariable %6 Private 476 477 %12 = OpExtInst %3 %10 DebugTypeBasic %11 %14 Float 478 %15 = OpExtInst %3 %10 DebugSource %11 479 %16 = OpExtInst %3 %10 DebugCompilationUnit 1 4 %15 GLSL 480; CHECK-NOT: DebugGlobalVariable 481; CHECK: [[dbg_newvar:%[a-zA-Z_\d]+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable 482 %17 = OpExtInst %3 %10 DebugGlobalVariable %11 %12 %15 0 0 %16 %11 %8 FlagIsDefinition 483 484; CHECK: OpFunction 485 %2 = OpFunction %3 None %4 486; CHECK: OpLabel 487 %7 = OpLabel 488; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function 489; CHECK-NEXT: DebugDeclare [[dbg_newvar]] [[newvar]] 490; CHECK: OpLoad [[float]] [[newvar]] 491 %9 = OpLoad %5 %8 492 OpReturn 493 OpFunctionEnd 494 )"; 495 SinglePassRunAndMatch<PrivateToLocalPass>(text, true); 496} 497 498} // namespace 499} // namespace opt 500} // namespace spvtools 501