1// Copyright (c) 2018 Google LLC 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 "test/opt/pass_fixture.h" 18#include "test/opt/pass_utils.h" 19 20namespace spvtools { 21namespace opt { 22namespace { 23 24using IfConversionTest = PassTest<::testing::Test>; 25 26TEST_F(IfConversionTest, TestSimpleIfThenElse) { 27 const std::string text = R"( 28; CHECK: OpSelectionMerge [[merge:%\w+]] 29; CHECK: [[merge]] = OpLabel 30; CHECK-NOT: OpPhi 31; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 32; CHECK OpStore {{%\w+}} [[sel]] 33OpCapability Shader 34OpMemoryModel Logical GLSL450 35OpEntryPoint Vertex %1 "func" %2 36%void = OpTypeVoid 37%bool = OpTypeBool 38%true = OpConstantTrue %bool 39%uint = OpTypeInt 32 0 40%uint_0 = OpConstant %uint 0 41%uint_1 = OpConstant %uint 1 42%_ptr_Output_uint = OpTypePointer Output %uint 43%2 = OpVariable %_ptr_Output_uint Output 44%11 = OpTypeFunction %void 45%1 = OpFunction %void None %11 46%12 = OpLabel 47OpSelectionMerge %14 None 48OpBranchConditional %true %15 %16 49%15 = OpLabel 50OpBranch %14 51%16 = OpLabel 52OpBranch %14 53%14 = OpLabel 54%18 = OpPhi %uint %uint_0 %15 %uint_1 %16 55OpStore %2 %18 56OpReturn 57OpFunctionEnd 58)"; 59 60 SinglePassRunAndMatch<IfConversion>(text, true); 61} 62 63TEST_F(IfConversionTest, TestSimpleHalfIfTrue) { 64 const std::string text = R"( 65; CHECK: OpSelectionMerge [[merge:%\w+]] 66; CHECK: [[merge]] = OpLabel 67; CHECK-NOT: OpPhi 68; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 69; CHECK OpStore {{%\w+}} [[sel]] 70OpCapability Shader 71OpMemoryModel Logical GLSL450 72OpEntryPoint Vertex %1 "func" %2 73%void = OpTypeVoid 74%bool = OpTypeBool 75%true = OpConstantTrue %bool 76%uint = OpTypeInt 32 0 77%uint_0 = OpConstant %uint 0 78%uint_1 = OpConstant %uint 1 79%_ptr_Output_uint = OpTypePointer Output %uint 80%2 = OpVariable %_ptr_Output_uint Output 81%11 = OpTypeFunction %void 82%1 = OpFunction %void None %11 83%12 = OpLabel 84OpSelectionMerge %14 None 85OpBranchConditional %true %15 %14 86%15 = OpLabel 87OpBranch %14 88%14 = OpLabel 89%18 = OpPhi %uint %uint_0 %15 %uint_1 %12 90OpStore %2 %18 91OpReturn 92OpFunctionEnd 93)"; 94 95 SinglePassRunAndMatch<IfConversion>(text, true); 96} 97 98TEST_F(IfConversionTest, TestSimpleHalfIfExtraBlock) { 99 const std::string text = R"( 100; CHECK: OpSelectionMerge [[merge:%\w+]] 101; CHECK: [[merge]] = OpLabel 102; CHECK-NOT: OpPhi 103; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 104; CHECK OpStore {{%\w+}} [[sel]] 105OpCapability Shader 106OpMemoryModel Logical GLSL450 107OpEntryPoint Vertex %1 "func" %2 108%void = OpTypeVoid 109%bool = OpTypeBool 110%true = OpConstantTrue %bool 111%uint = OpTypeInt 32 0 112%uint_0 = OpConstant %uint 0 113%uint_1 = OpConstant %uint 1 114%_ptr_Output_uint = OpTypePointer Output %uint 115%2 = OpVariable %_ptr_Output_uint Output 116%11 = OpTypeFunction %void 117%1 = OpFunction %void None %11 118%12 = OpLabel 119OpSelectionMerge %14 None 120OpBranchConditional %true %15 %14 121%15 = OpLabel 122OpBranch %16 123%16 = OpLabel 124OpBranch %14 125%14 = OpLabel 126%18 = OpPhi %uint %uint_0 %15 %uint_1 %12 127OpStore %2 %18 128OpReturn 129OpFunctionEnd 130)"; 131 132 SinglePassRunAndMatch<IfConversion>(text, true); 133} 134 135TEST_F(IfConversionTest, TestSimpleHalfIfFalse) { 136 const std::string text = R"( 137; CHECK: OpSelectionMerge [[merge:%\w+]] 138; CHECK: [[merge]] = OpLabel 139; CHECK-NOT: OpPhi 140; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 141; CHECK OpStore {{%\w+}} [[sel]] 142OpCapability Shader 143OpMemoryModel Logical GLSL450 144OpEntryPoint Vertex %1 "func" %2 145%void = OpTypeVoid 146%bool = OpTypeBool 147%true = OpConstantTrue %bool 148%uint = OpTypeInt 32 0 149%uint_0 = OpConstant %uint 0 150%uint_1 = OpConstant %uint 1 151%_ptr_Output_uint = OpTypePointer Output %uint 152%2 = OpVariable %_ptr_Output_uint Output 153%11 = OpTypeFunction %void 154%1 = OpFunction %void None %11 155%12 = OpLabel 156OpSelectionMerge %14 None 157OpBranchConditional %true %14 %15 158%15 = OpLabel 159OpBranch %14 160%14 = OpLabel 161%18 = OpPhi %uint %uint_0 %12 %uint_1 %15 162OpStore %2 %18 163OpReturn 164OpFunctionEnd 165)"; 166 167 SinglePassRunAndMatch<IfConversion>(text, true); 168} 169 170TEST_F(IfConversionTest, TestVectorSplat) { 171 const std::string text = R"( 172; CHECK: [[bool_vec:%\w+]] = OpTypeVector %bool 2 173; CHECK: OpSelectionMerge [[merge:%\w+]] 174; CHECK: [[merge]] = OpLabel 175; CHECK-NOT: OpPhi 176; CHECK: [[comp:%\w+]] = OpCompositeConstruct [[bool_vec]] %true %true 177; CHECK: [[sel:%\w+]] = OpSelect {{%\w+}} [[comp]] 178; CHECK OpStore {{%\w+}} [[sel]] 179OpCapability Shader 180OpMemoryModel Logical GLSL450 181OpEntryPoint Vertex %1 "func" %2 182%void = OpTypeVoid 183%bool = OpTypeBool 184%true = OpConstantTrue %bool 185%uint = OpTypeInt 32 0 186%uint_0 = OpConstant %uint 0 187%uint_1 = OpConstant %uint 1 188%uint_vec2 = OpTypeVector %uint 2 189%vec2_01 = OpConstantComposite %uint_vec2 %uint_0 %uint_1 190%vec2_10 = OpConstantComposite %uint_vec2 %uint_1 %uint_0 191%_ptr_Output_uint = OpTypePointer Output %uint_vec2 192%2 = OpVariable %_ptr_Output_uint Output 193%11 = OpTypeFunction %void 194%1 = OpFunction %void None %11 195%12 = OpLabel 196OpSelectionMerge %14 None 197OpBranchConditional %true %15 %16 198%15 = OpLabel 199OpBranch %14 200%16 = OpLabel 201OpBranch %14 202%14 = OpLabel 203%18 = OpPhi %uint_vec2 %vec2_01 %15 %vec2_10 %16 204OpStore %2 %18 205OpReturn 206OpFunctionEnd 207)"; 208 209 SinglePassRunAndMatch<IfConversion>(text, true); 210} 211 212TEST_F(IfConversionTest, CodeMotionSameValue) { 213 const std::string text = R"( 214; CHECK: [[var:%\w+]] = OpVariable 215; CHECK: OpFunction 216; CHECK: OpLabel 217; CHECK-NOT: OpLabel 218; CHECK: [[add:%\w+]] = OpIAdd %uint %uint_0 %uint_1 219; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None 220; CHECK-NEXT: OpBranchConditional 221; CHECK: [[merge_lab]] = OpLabel 222; CHECK-NOT: OpLabel 223; CHECK: OpStore [[var]] [[add]] 224 OpCapability Shader 225 OpMemoryModel Logical GLSL450 226 OpEntryPoint Vertex %1 "func" %2 227 %void = OpTypeVoid 228 %uint = OpTypeInt 32 0 229 %uint_0 = OpConstant %uint 0 230 %uint_1 = OpConstant %uint 1 231%_ptr_Output_uint = OpTypePointer Output %uint 232 %2 = OpVariable %_ptr_Output_uint Output 233 %8 = OpTypeFunction %void 234 %bool = OpTypeBool 235 %true = OpConstantTrue %bool 236 %1 = OpFunction %void None %8 237 %11 = OpLabel 238 OpSelectionMerge %12 None 239 OpBranchConditional %true %13 %15 240 %13 = OpLabel 241 %14 = OpIAdd %uint %uint_0 %uint_1 242 OpBranch %12 243 %15 = OpLabel 244 %16 = OpIAdd %uint %uint_0 %uint_1 245 OpBranch %12 246 %12 = OpLabel 247 %17 = OpPhi %uint %16 %15 %14 %13 248 OpStore %2 %17 249 OpReturn 250 OpFunctionEnd 251)"; 252 253 SinglePassRunAndMatch<IfConversion>(text, true); 254} 255 256TEST_F(IfConversionTest, CodeMotionMultipleInstructions) { 257 const std::string text = R"( 258; CHECK: [[var:%\w+]] = OpVariable 259; CHECK: OpFunction 260; CHECK: OpLabel 261; CHECK-NOT: OpLabel 262; CHECK: [[a1:%\w+]] = OpIAdd %uint %uint_0 %uint_1 263; CHECK: [[a2:%\w+]] = OpIAdd %uint [[a1]] %uint_1 264; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None 265; CHECK-NEXT: OpBranchConditional 266; CHECK: [[merge_lab]] = OpLabel 267; CHECK-NOT: OpLabel 268; CHECK: OpStore [[var]] [[a2]] 269 OpCapability Shader 270 OpMemoryModel Logical GLSL450 271 OpEntryPoint Vertex %1 "func" %2 272 %void = OpTypeVoid 273 %uint = OpTypeInt 32 0 274 %uint_0 = OpConstant %uint 0 275 %uint_1 = OpConstant %uint 1 276%_ptr_Output_uint = OpTypePointer Output %uint 277 %2 = OpVariable %_ptr_Output_uint Output 278 %8 = OpTypeFunction %void 279 %bool = OpTypeBool 280 %true = OpConstantTrue %bool 281 %1 = OpFunction %void None %8 282 %11 = OpLabel 283 OpSelectionMerge %12 None 284 OpBranchConditional %true %13 %15 285 %13 = OpLabel 286 %a1 = OpIAdd %uint %uint_0 %uint_1 287 %a2 = OpIAdd %uint %a1 %uint_1 288 OpBranch %12 289 %15 = OpLabel 290 %b1 = OpIAdd %uint %uint_0 %uint_1 291 %b2 = OpIAdd %uint %b1 %uint_1 292 OpBranch %12 293 %12 = OpLabel 294 %17 = OpPhi %uint %b2 %15 %a2 %13 295 OpStore %2 %17 296 OpReturn 297 OpFunctionEnd 298)"; 299 300 SinglePassRunAndMatch<IfConversion>(text, true); 301} 302 303TEST_F(IfConversionTest, NoCommonDominator) { 304 const std::string text = R"(OpCapability Shader 305OpMemoryModel Logical GLSL450 306OpEntryPoint Vertex %1 "func" %2 307%void = OpTypeVoid 308%uint = OpTypeInt 32 0 309%uint_0 = OpConstant %uint 0 310%uint_1 = OpConstant %uint 1 311%_ptr_Output_uint = OpTypePointer Output %uint 312%2 = OpVariable %_ptr_Output_uint Output 313%8 = OpTypeFunction %void 314%1 = OpFunction %void None %8 315%9 = OpLabel 316OpBranch %10 317%11 = OpLabel 318OpBranch %10 319%10 = OpLabel 320%12 = OpPhi %uint %uint_0 %9 %uint_1 %11 321OpStore %2 %12 322OpReturn 323OpFunctionEnd 324)"; 325 326 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 327} 328 329TEST_F(IfConversionTest, DontFlatten) { 330 const std::string text = R"(OpCapability Shader 331OpMemoryModel Logical GLSL450 332OpEntryPoint Vertex %1 "func" %2 333%void = OpTypeVoid 334%bool = OpTypeBool 335%true = OpConstantTrue %bool 336%uint = OpTypeInt 32 0 337%uint_0 = OpConstant %uint 0 338%uint_1 = OpConstant %uint 1 339%v2uint = OpTypeVector %uint 2 340%10 = OpConstantComposite %v2uint %uint_0 %uint_1 341%11 = OpConstantComposite %v2uint %uint_1 %uint_0 342%_ptr_Output_v2uint = OpTypePointer Output %v2uint 343%2 = OpVariable %_ptr_Output_v2uint Output 344%13 = OpTypeFunction %void 345%1 = OpFunction %void None %13 346%14 = OpLabel 347OpSelectionMerge %15 DontFlatten 348OpBranchConditional %true %16 %17 349%16 = OpLabel 350OpBranch %15 351%17 = OpLabel 352OpBranch %15 353%15 = OpLabel 354%18 = OpPhi %v2uint %10 %16 %11 %17 355OpStore %2 %18 356OpReturn 357OpFunctionEnd 358)"; 359 360 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 361} 362 363TEST_F(IfConversionTest, LoopUntouched) { 364 const std::string text = R"(OpCapability Shader 365OpMemoryModel Logical GLSL450 366OpEntryPoint Vertex %1 "func" %2 367%void = OpTypeVoid 368%uint = OpTypeInt 32 0 369%uint_0 = OpConstant %uint 0 370%uint_1 = OpConstant %uint 1 371%_ptr_Output_uint = OpTypePointer Output %uint 372%2 = OpVariable %_ptr_Output_uint Output 373%8 = OpTypeFunction %void 374%bool = OpTypeBool 375%true = OpConstantTrue %bool 376%1 = OpFunction %void None %8 377%11 = OpLabel 378OpBranch %12 379%12 = OpLabel 380%13 = OpPhi %uint %uint_0 %11 %uint_1 %12 381OpLoopMerge %14 %12 None 382OpBranchConditional %true %14 %12 383%14 = OpLabel 384OpStore %2 %13 385OpReturn 386OpFunctionEnd 387)"; 388 389 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 390} 391 392TEST_F(IfConversionTest, TooManyPredecessors) { 393 const std::string text = R"(OpCapability Shader 394OpMemoryModel Logical GLSL450 395OpEntryPoint Vertex %1 "func" %2 396%void = OpTypeVoid 397%uint = OpTypeInt 32 0 398%uint_0 = OpConstant %uint 0 399%uint_1 = OpConstant %uint 1 400%_ptr_Output_uint = OpTypePointer Output %uint 401%2 = OpVariable %_ptr_Output_uint Output 402%8 = OpTypeFunction %void 403%bool = OpTypeBool 404%true = OpConstantTrue %bool 405%1 = OpFunction %void None %8 406%11 = OpLabel 407OpSelectionMerge %12 None 408OpBranchConditional %true %13 %12 409%13 = OpLabel 410OpBranchConditional %true %14 %12 411%14 = OpLabel 412OpBranch %12 413%12 = OpLabel 414%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14 415OpStore %2 %15 416OpReturn 417OpFunctionEnd 418)"; 419 420 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 421} 422 423TEST_F(IfConversionTest, NoCodeMotion) { 424 const std::string text = R"(OpCapability Shader 425OpMemoryModel Logical GLSL450 426OpEntryPoint Vertex %1 "func" %2 427%void = OpTypeVoid 428%uint = OpTypeInt 32 0 429%uint_0 = OpConstant %uint 0 430%uint_1 = OpConstant %uint 1 431%_ptr_Output_uint = OpTypePointer Output %uint 432%2 = OpVariable %_ptr_Output_uint Output 433%8 = OpTypeFunction %void 434%bool = OpTypeBool 435%true = OpConstantTrue %bool 436%1 = OpFunction %void None %8 437%11 = OpLabel 438OpSelectionMerge %12 None 439OpBranchConditional %true %13 %12 440%13 = OpLabel 441%14 = OpIAdd %uint %uint_0 %uint_1 442OpBranch %12 443%12 = OpLabel 444%15 = OpPhi %uint %uint_0 %11 %14 %13 445OpStore %2 %15 446OpReturn 447OpFunctionEnd 448)"; 449 450 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 451} 452 453TEST_F(IfConversionTest, NoCodeMotionImmovableInst) { 454 const std::string text = R"(OpCapability Shader 455OpMemoryModel Logical GLSL450 456OpEntryPoint Vertex %1 "func" %2 457%void = OpTypeVoid 458%uint = OpTypeInt 32 0 459%uint_0 = OpConstant %uint 0 460%uint_1 = OpConstant %uint 1 461%_ptr_Output_uint = OpTypePointer Output %uint 462%2 = OpVariable %_ptr_Output_uint Output 463%8 = OpTypeFunction %void 464%bool = OpTypeBool 465%true = OpConstantTrue %bool 466%1 = OpFunction %void None %8 467%11 = OpLabel 468OpSelectionMerge %12 None 469OpBranchConditional %true %13 %14 470%13 = OpLabel 471OpSelectionMerge %15 None 472OpBranchConditional %true %16 %15 473%16 = OpLabel 474%17 = OpIAdd %uint %uint_0 %uint_1 475OpBranch %15 476%15 = OpLabel 477%18 = OpPhi %uint %uint_0 %13 %17 %16 478%19 = OpIAdd %uint %18 %uint_1 479OpBranch %12 480%14 = OpLabel 481OpSelectionMerge %20 None 482OpBranchConditional %true %21 %20 483%21 = OpLabel 484%22 = OpIAdd %uint %uint_0 %uint_1 485OpBranch %20 486%20 = OpLabel 487%23 = OpPhi %uint %uint_0 %14 %22 %21 488%24 = OpIAdd %uint %23 %uint_1 489OpBranch %12 490%12 = OpLabel 491%25 = OpPhi %uint %24 %20 %19 %15 492OpStore %2 %25 493OpReturn 494OpFunctionEnd 495)"; 496 497 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 498} 499 500TEST_F(IfConversionTest, InvalidCommonDominator) { 501 const std::string text = R"(OpCapability Shader 502OpCapability Linkage 503OpMemoryModel Logical GLSL450 504%void = OpTypeVoid 505%float = OpTypeFloat 32 506%float_0 = OpConstant %float 0 507%float_1 = OpConstant %float 1 508%bool = OpTypeBool 509%true = OpConstantTrue %bool 510%1 = OpTypeFunction %void 511%2 = OpFunction %void None %1 512%3 = OpLabel 513OpBranch %4 514%4 = OpLabel 515OpLoopMerge %5 %6 None 516OpBranch %7 517%7 = OpLabel 518OpSelectionMerge %8 None 519OpBranchConditional %true %8 %9 520%9 = OpLabel 521OpSelectionMerge %10 None 522OpBranchConditional %true %10 %5 523%10 = OpLabel 524OpBranch %8 525%8 = OpLabel 526OpBranch %6 527%6 = OpLabel 528OpBranchConditional %true %4 %5 529%5 = OpLabel 530%11 = OpPhi %float %float_0 %6 %float_1 %9 531OpReturn 532OpFunctionEnd 533)"; 534 535 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 536 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 537} 538 539TEST_F(IfConversionTest, DebugInfoSimpleIfThenElse) { 540 // When it replaces an OpPhi with OpSelect, the new OpSelect must have 541 // the same scope and line information with the OpPhi. 542 const std::string text = R"( 543; CHECK: OpSelectionMerge [[merge:%\w+]] 544; CHECK: [[merge]] = OpLabel 545; CHECK-NOT: OpPhi 546; CHECK: DebugScope 547; CHECK-NEXT: OpLine {{%\w+}} 3 7 548; CHECK-NEXT: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 549; CHECK-NEXT: DebugValue {{%\w+}} [[sel]] 550; CHECK: OpStore {{%\w+}} [[sel]] 551OpCapability Shader 552%ext = OpExtInstImport "OpenCL.DebugInfo.100" 553OpMemoryModel Logical GLSL450 554OpEntryPoint Vertex %1 "func" %2 555%name = OpString "test" 556%void = OpTypeVoid 557%bool = OpTypeBool 558%true = OpConstantTrue %bool 559%uint = OpTypeInt 32 0 560%uint_0 = OpConstant %uint 0 561%uint_1 = OpConstant %uint 1 562%uint_32 = OpConstant %uint 32 563%_ptr_Output_uint = OpTypePointer Output %uint 564%2 = OpVariable %_ptr_Output_uint Output 565%11 = OpTypeFunction %void 566%null_expr = OpExtInst %void %ext DebugExpression 567%src = OpExtInst %void %ext DebugSource %name 568%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL 569%dbg_tf = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float 570%dbg_f = OpExtInst %void %ext DebugLocalVariable %name %dbg_tf %src 0 0 %cu FlagIsLocal 571%1 = OpFunction %void None %11 572%12 = OpLabel 573OpSelectionMerge %14 None 574OpBranchConditional %true %15 %16 575%15 = OpLabel 576OpBranch %14 577%16 = OpLabel 578OpBranch %14 579%14 = OpLabel 580%scope = OpExtInst %void %ext DebugScope %cu 581OpLine %name 3 7 582%18 = OpPhi %uint %uint_0 %15 %uint_1 %16 583%value = OpExtInst %void %ext DebugValue %dbg_f %18 %null_expr 584OpStore %2 %18 585OpReturn 586OpFunctionEnd 587)"; 588 589 SinglePassRunAndMatch<IfConversion>(text, true); 590} 591 592TEST_F(IfConversionTest, MultipleEdgesFromSameBlock) { 593 // If a block has two out going edges that go to the same block, then there 594 // can be an OpPhi instruction with fewer entries than the number of incoming 595 // edges. This must be handled. 596 const std::string text = R"(OpCapability Shader 597%1 = OpExtInstImport "GLSL.std.450" 598OpMemoryModel Logical GLSL450 599OpEntryPoint Fragment %2 "main" 600OpExecutionMode %2 OriginUpperLeft 601%void = OpTypeVoid 602%4 = OpTypeFunction %void 603%bool = OpTypeBool 604%true = OpConstantTrue %bool 605%true_0 = OpConstantTrue %bool 606%2 = OpFunction %void None %4 607%8 = OpLabel 608OpSelectionMerge %9 None 609OpBranchConditional %true_0 %9 %9 610%9 = OpLabel 611%10 = OpPhi %bool %true %8 612OpReturn 613OpFunctionEnd 614)"; 615 616 SinglePassRunAndCheck<IfConversion>(text, text, true, true); 617} 618 619} // namespace 620} // namespace opt 621} // namespace spvtools 622