1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals 2; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S -hoist-common-insts=true | FileCheck %s 3 4; ModuleID = 'cppeh-simplify.cpp' 5target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" 6target triple = "x86_64-pc-windows-msvc18.0.0" 7 8 9; This case arises when two objects with empty destructors are cleaned up. 10; 11; void f1() { 12; S a; 13; S b; 14; g(); 15; } 16; 17; In this case, both cleanup pads can be eliminated and the invoke can be 18; converted to a call. 19; 20define void @f1() personality ptr @__CxxFrameHandler3 { 21; CHECK-LABEL: @f1( 22; CHECK-NEXT: entry: 23; CHECK-NEXT: call void @g() 24; CHECK-NEXT: ret void 25; 26entry: 27 invoke void @g() to label %invoke.cont unwind label %ehcleanup 28 29invoke.cont: ; preds = %entry 30 ret void 31 32ehcleanup: ; preds = %entry 33 %0 = cleanuppad within none [] 34 cleanupret from %0 unwind label %ehcleanup.1 35 36ehcleanup.1: ; preds = %ehcleanup 37 %1 = cleanuppad within none [] 38 cleanupret from %1 unwind to caller 39} 40 41 42; This case arises when an object with an empty destructor must be cleaned up 43; outside of a try-block and an object with a non-empty destructor must be 44; cleaned up within the try-block. 45; 46; void f2() { 47; S a; 48; try { 49; S2 b; 50; g(); 51; } catch (...) {} 52; } 53; 54; In this case, the outermost cleanup pad can be eliminated and the catch block 55; should unwind to the caller (that is, exception handling continues with the 56; parent frame of the caller). 57; 58define void @f2() personality ptr @__CxxFrameHandler3 { 59; CHECK-LABEL: @f2( 60; CHECK-NEXT: entry: 61; CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_S2:%.*]], align 1 62; CHECK-NEXT: invoke void @g() 63; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[EHCLEANUP:%.*]] 64; CHECK: ehcleanup: 65; CHECK-NEXT: [[TMP0:%.*]] = cleanuppad within none [] 66; CHECK-NEXT: call void @"\01??1S2@@QEAA@XZ"(ptr [[B]]) 67; CHECK-NEXT: cleanupret from [[TMP0]] unwind label [[CATCH_DISPATCH:%.*]] 68; CHECK: catch.dispatch: 69; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 70; CHECK: catch: 71; CHECK-NEXT: [[TMP1:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 72; CHECK-NEXT: catchret from [[TMP1]] to label [[TRY_CONT]] 73; CHECK: try.cont: 74; CHECK-NEXT: ret void 75; 76entry: 77 %b = alloca %struct.S2, align 1 78 invoke void @g() to label %invoke.cont unwind label %ehcleanup 79 80invoke.cont: ; preds = %entry 81 br label %try.cont 82 83ehcleanup: ; preds = %entry 84 %0 = cleanuppad within none [] 85 call void @"\01??1S2@@QEAA@XZ"(ptr %b) 86 cleanupret from %0 unwind label %catch.dispatch 87 88catch.dispatch: ; preds = %ehcleanup 89 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 90 91catch: ; preds = %catch.dispatch 92 %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 93 catchret from %1 to label %catchret.dest 94 95catchret.dest: ; preds = %catch 96 br label %try.cont 97 98try.cont: ; preds = %catchret.dest, %invoke.cont 99 ret void 100 101ehcleanup.1: 102 %2 = cleanuppad within none [] 103 cleanupret from %2 unwind to caller 104} 105 106 107; This case arises when an object with a non-empty destructor must be cleaned up 108; outside of a try-block and an object with an empty destructor must be cleaned 109; within the try-block. 110; 111; void f3() { 112; S2 a; 113; try { 114; S b; 115; g(); 116; } catch (...) {} 117; } 118; 119; In this case the inner cleanup pad should be eliminated and the invoke of g() 120; should unwind directly to the catchpad. 121 122define void @f3() personality ptr @__CxxFrameHandler3 { 123; CHECK-LABEL: @f3( 124; CHECK-NEXT: entry: 125; CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_S2:%.*]], align 1 126; CHECK-NEXT: invoke void @g() 127; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 128; CHECK: catch.dispatch: 129; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[EHCLEANUP_1:%.*]] 130; CHECK: catch: 131; CHECK-NEXT: [[CP2:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 132; CHECK-NEXT: catchret from [[CP2]] to label [[TRY_CONT]] 133; CHECK: try.cont: 134; CHECK-NEXT: ret void 135; CHECK: ehcleanup.1: 136; CHECK-NEXT: [[CP3:%.*]] = cleanuppad within none [] 137; CHECK-NEXT: call void @"\01??1S2@@QEAA@XZ"(ptr [[A]]) 138; CHECK-NEXT: cleanupret from [[CP3]] unwind to caller 139; 140entry: 141 %a = alloca %struct.S2, align 1 142 invoke void @g() to label %invoke.cont unwind label %ehcleanup 143 144invoke.cont: ; preds = %entry 145 br label %try.cont 146 147ehcleanup: ; preds = %entry 148 %0 = cleanuppad within none [] 149 cleanupret from %0 unwind label %catch.dispatch 150 151catch.dispatch: ; preds = %ehcleanup 152 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 153 154catch: ; preds = %catch.dispatch 155 %cp2 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 156 catchret from %cp2 to label %catchret.dest 157 158catchret.dest: ; preds = %catch 159 br label %try.cont 160 161try.cont: ; preds = %catchret.dest, %invoke.cont 162 ret void 163 164ehcleanup.1: 165 %cp3 = cleanuppad within none [] 166 call void @"\01??1S2@@QEAA@XZ"(ptr %a) 167 cleanupret from %cp3 unwind to caller 168} 169 170 171; This case arises when an object with an empty destructor may require cleanup 172; from either inside or outside of a try-block. 173; 174; void f4() { 175; S a; 176; g(); 177; try { 178; g(); 179; } catch (...) {} 180; } 181; 182; In this case, the cleanuppad should be eliminated, the invoke outside of the 183; catch block should be converted to a call (that is, that is, exception 184; handling continues with the parent frame of the caller).) 185; 186; Note: The cleanuppad simplification will insert an unconditional branch here 187; but it will be eliminated, placing the following invoke in the entry BB. 188; 189define void @f4() personality ptr @__CxxFrameHandler3 { 190; CHECK-LABEL: @f4( 191; CHECK-NEXT: entry: 192; CHECK-NEXT: call void @g() 193; CHECK-NEXT: invoke void @g() 194; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 195; CHECK: catch.dispatch: 196; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 197; CHECK: catch: 198; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 199; CHECK-NEXT: catchret from [[TMP0]] to label [[TRY_CONT]] 200; CHECK: try.cont: 201; CHECK-NEXT: ret void 202; 203entry: 204 invoke void @g() 205 to label %invoke.cont unwind label %ehcleanup 206 207invoke.cont: ; preds = %entry 208 invoke void @g() 209 to label %try.cont unwind label %catch.dispatch 210 211catch.dispatch: ; preds = %invoke.cont 212 %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup 213 214catch: ; preds = %catch.dispatch 215 %0 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 216 catchret from %0 to label %try.cont 217 218try.cont: ; preds = %catch, %invoke.cont 219 ret void 220 221ehcleanup: 222 %cp2 = cleanuppad within none [] 223 cleanupret from %cp2 unwind to caller 224} 225 226; This case tests simplification of an otherwise empty cleanup pad that contains 227; a PHI node. 228; 229; int f6() { 230; int state = 1; 231; try { 232; S a; 233; g(); 234; state = 2; 235; g(); 236; } catch (...) { 237; return state; 238; } 239; return 0; 240; } 241; 242; In this case, the cleanup pad should be eliminated and the PHI node in the 243; cleanup pad should be sunk into the catch dispatch block. 244; 245define i32 @f6() personality ptr @__CxxFrameHandler3 { 246; CHECK-LABEL: @f6( 247; CHECK-NEXT: entry: 248; CHECK-NEXT: invoke void @g() 249; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 250; CHECK: invoke.cont: 251; CHECK-NEXT: invoke void @g() 252; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] 253; CHECK: catch.dispatch: 254; CHECK-NEXT: [[STATE_0:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ] 255; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 256; CHECK: catch: 257; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 258; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] 259; CHECK: return: 260; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[STATE_0]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT]] ] 261; CHECK-NEXT: ret i32 [[RETVAL_0]] 262; 263entry: 264 invoke void @g() 265 to label %invoke.cont unwind label %ehcleanup 266 267invoke.cont: ; preds = %entry 268 invoke void @g() 269 to label %return unwind label %ehcleanup 270 271ehcleanup: ; preds = %invoke.cont, %entry 272 %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 273 %0 = cleanuppad within none [] 274 cleanupret from %0 unwind label %catch.dispatch 275 276catch.dispatch: ; preds = %ehcleanup 277 %cs1 = catchswitch within none [label %catch] unwind to caller 278 279catch: ; preds = %catch.dispatch 280 %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 281 catchret from %1 to label %return 282 283return: ; preds = %invoke.cont, %catch 284 %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ] 285 ret i32 %retval.0 286} 287 288; This case tests another variation of simplification of an otherwise empty 289; cleanup pad that contains a PHI node. 290; 291; int f7() { 292; int state = 1; 293; try { 294; g(); 295; state = 2; 296; S a; 297; g(); 298; state = 3; 299; g(); 300; } catch (...) { 301; return state; 302; } 303; return 0; 304; } 305; 306; In this case, the cleanup pad should be eliminated and the PHI node in the 307; cleanup pad should be merged with the PHI node in the catch dispatch block. 308; 309define i32 @f7() personality ptr @__CxxFrameHandler3 { 310; CHECK-LABEL: @f7( 311; CHECK-NEXT: entry: 312; CHECK-NEXT: invoke void @g() 313; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 314; CHECK: invoke.cont: 315; CHECK-NEXT: invoke void @g() 316; CHECK-NEXT: to label [[INVOKE_CONT_1:%.*]] unwind label [[CATCH_DISPATCH]] 317; CHECK: invoke.cont.1: 318; CHECK-NEXT: invoke void @g() 319; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] 320; CHECK: catch.dispatch: 321; CHECK-NEXT: [[STATE_1:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 3, [[INVOKE_CONT_1]] ], [ 2, [[INVOKE_CONT]] ] 322; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 323; CHECK: catch: 324; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 325; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] 326; CHECK: return: 327; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[STATE_1]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT_1]] ] 328; CHECK-NEXT: ret i32 [[RETVAL_0]] 329; 330entry: 331 invoke void @g() 332 to label %invoke.cont unwind label %catch.dispatch 333 334invoke.cont: ; preds = %entry 335 invoke void @g() 336 to label %invoke.cont.1 unwind label %ehcleanup 337 338invoke.cont.1: ; preds = %invoke.cont 339 invoke void @g() 340 to label %return unwind label %ehcleanup 341 342ehcleanup: ; preds = %invoke.cont.1, %invoke.cont 343 %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] 344 %0 = cleanuppad within none [] 345 cleanupret from %0 unwind label %catch.dispatch 346 347catch.dispatch: ; preds = %ehcleanup, %entry 348 %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ] 349 %cs1 = catchswitch within none [label %catch] unwind to caller 350 351catch: ; preds = %catch.dispatch 352 %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 353 catchret from %1 to label %return 354 355return: ; preds = %invoke.cont.1, %catch 356 %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ] 357 ret i32 %retval.0 358} 359 360; This case tests a scenario where an empty cleanup pad is not dominated by all 361; of the predecessors of its successor, but the successor references a PHI node 362; in the empty cleanup pad. 363; 364; Conceptually, the case being modeled is something like this: 365; 366; int f8() { 367; int x = 1; 368; try { 369; S a; 370; g(); 371; x = 2; 372; retry: 373; g(); 374; return 375; } catch (...) { 376; use_x(x); 377; } 378; goto retry; 379; } 380; 381; While that C++ syntax isn't legal, the IR below is. 382; 383; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch 384; should have an incoming value entry for path from 'foo' that references the 385; PHI node itself. 386; 387define void @f8() personality ptr @__CxxFrameHandler3 { 388; CHECK-LABEL: @f8( 389; CHECK-NEXT: entry: 390; CHECK-NEXT: invoke void @g() 391; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 392; CHECK: invoke.cont: 393; CHECK-NEXT: invoke void @g() 394; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] 395; CHECK: catch.dispatch: 396; CHECK-NEXT: [[X:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ], [ [[X]], [[CATCH_CONT:%.*]] ] 397; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 398; CHECK: catch: 399; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 400; CHECK-NEXT: call void @use_x(i32 [[X]]) 401; CHECK-NEXT: catchret from [[TMP0]] to label [[CATCH_CONT]] 402; CHECK: catch.cont: 403; CHECK-NEXT: invoke void @g() 404; CHECK-NEXT: to label [[RETURN]] unwind label [[CATCH_DISPATCH]] 405; CHECK: return: 406; CHECK-NEXT: ret void 407; 408entry: 409 invoke void @g() 410 to label %invoke.cont unwind label %ehcleanup 411 412invoke.cont: ; preds = %entry 413 invoke void @g() 414 to label %return unwind label %ehcleanup 415 416ehcleanup: ; preds = %invoke.cont, %entry 417 %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] 418 %0 = cleanuppad within none [] 419 cleanupret from %0 unwind label %catch.dispatch 420 421catch.dispatch: ; preds = %ehcleanup, %catch.cont 422 %cs1 = catchswitch within none [label %catch] unwind to caller 423 424catch: ; preds = %catch.dispatch 425 %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 426 call void @use_x(i32 %x) 427 catchret from %1 to label %catch.cont 428 429catch.cont: ; preds = %catch 430 invoke void @g() 431 to label %return unwind label %catch.dispatch 432 433return: ; preds = %invoke.cont, %catch.cont 434 ret void 435} 436define i32 @f9() personality ptr @__CxxFrameHandler3 { 437; CHECK-LABEL: @f9( 438; CHECK-NEXT: entry: 439; CHECK-NEXT: [[S:%.*]] = alloca i8, align 1 440; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 1, ptr nonnull [[S]]) 441; CHECK-NEXT: invoke void @"\01??1S2@@QEAA@XZ"(ptr [[S]]) 442; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 443; CHECK: catch.dispatch: 444; CHECK-NEXT: [[CATCH_SWITCH:%.*]] = catchswitch within none [label %catch] unwind to caller 445; CHECK: catch: 446; CHECK-NEXT: [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [ptr null, i32 0, ptr null] 447; CHECK-NEXT: catchret from [[CATCH_PAD]] to label [[TRY_CONT]] 448; CHECK: try.cont: 449; CHECK-NEXT: ret i32 0 450; 451entry: 452 %s = alloca i8, align 1 453 call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %s) 454 invoke void @"\01??1S2@@QEAA@XZ"(ptr %s) 455 to label %try.cont unwind label %ehcleanup 456 457ehcleanup: 458 %cleanup.pad = cleanuppad within none [] 459 call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %s) 460 cleanupret from %cleanup.pad unwind label %catch.dispatch 461 462catch.dispatch: 463 %catch.switch = catchswitch within none [label %catch] unwind to caller 464 465catch: 466 %catch.pad = catchpad within %catch.switch [ptr null, i32 0, ptr null] 467 catchret from %catch.pad to label %try.cont 468 469try.cont: 470 ret i32 0 471} 472 473define void @f10(i32 %V) personality ptr @__CxxFrameHandler3 { 474; CHECK-LABEL: @f10( 475; CHECK-NEXT: entry: 476; CHECK-NEXT: call void @g() 477; CHECK-NEXT: unreachable 478; 479entry: 480 invoke void @g() 481 to label %unreachable unwind label %cleanup 482 483unreachable: 484 unreachable 485 486cleanup: 487 %cp = cleanuppad within none [] 488 switch i32 %V, label %cleanupret1 [ 489 i32 0, label %cleanupret2 490 ] 491 492cleanupret1: 493 cleanupret from %cp unwind to caller 494 495cleanupret2: 496 cleanupret from %cp unwind to caller 497} 498 499; This case tests the handling of an empty cleanup pad that 500; contains a lifetime_end intrinsic and does not dominate its 501; successor. 502define void @f11() personality ptr @__CxxFrameHandler3 { 503; CHECK-LABEL: @f11( 504; CHECK-NEXT: entry: 505; CHECK-NEXT: invoke void @g() 506; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]] 507; CHECK: invoke.cont: 508; CHECK-NEXT: invoke void @g() 509; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[CATCH_DISPATCH]] 510; CHECK: invoke.cont2: 511; CHECK-NEXT: invoke void @g() 512; CHECK-NEXT: to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]] 513; CHECK: catch.dispatch: 514; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller 515; CHECK: catch: 516; CHECK-NEXT: [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null] 517; CHECK-NEXT: catchret from [[TMP0]] to label [[RETURN]] 518; CHECK: return: 519; CHECK-NEXT: ret void 520; 521entry: 522 invoke void @g() 523 to label %invoke.cont unwind label %ehcleanup 524 525invoke.cont: ; preds = %entry 526 invoke void @g() 527 to label %invoke.cont2 unwind label %ehcleanup 528 529invoke.cont2: ; preds = %invoke.cont 530 invoke void @g() 531 to label %return unwind label %catch.dispatch 532 533ehcleanup: ; preds = %invoke.cont, %entry 534 %x = phi ptr [ undef, %invoke.cont ], [ undef, %entry ] 535 %0 = cleanuppad within none [] 536 call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %x) 537 cleanupret from %0 unwind label %catch.dispatch 538 539catch.dispatch: ; preds = %ehcleanup, %invoke.cont 540 %cs1 = catchswitch within none [label %catch] unwind to caller 541 542catch: ; preds = %catch.dispatch 543 %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null] 544 catchret from %1 to label %return 545 546return: ; preds = %invoke.cont, %catch.cont 547 ret void 548} 549 550%struct.S = type { i8 } 551%struct.S2 = type { i8 } 552declare void @"\01??1S2@@QEAA@XZ"(ptr) 553declare void @g() 554declare void @use_x(i32 %x) 555 556declare i32 @__CxxFrameHandler3(...) 557 558declare void @llvm.lifetime.start.p0(i64, ptr nocapture) 559declare void @llvm.lifetime.end.p0(i64, ptr nocapture) 560;. 561; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } 562;. 563