llvm/llvm/test/Transforms/SimplifyCFG/X86/empty-cleanuppad.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S -hoist-common-insts=true | FileCheck %s

; ModuleID = 'cppeh-simplify.cpp'
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc18.0.0"


; This case arises when two objects with empty destructors are cleaned up.
;
; void f1() {
;   S a;
;   S b;
;   g();
; }
;
; In this case, both cleanup pads can be eliminated and the invoke can be
; converted to a call.
;
define void @f1() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f1(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void @g()
; CHECK-NEXT:    ret void
;
entry:
  invoke void @g() to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  ret void

ehcleanup:                                        ; preds = %entry
  %0 = cleanuppad within none []
  cleanupret from %0 unwind label %ehcleanup.1

ehcleanup.1:                                      ; preds = %ehcleanup
  %1 = cleanuppad within none []
  cleanupret from %1 unwind to caller
}


; This case arises when an object with an empty destructor must be cleaned up
; outside of a try-block and an object with a non-empty destructor must be
; cleaned up within the try-block.
;
; void f2() {
;   S a;
;   try {
;     S2 b;
;     g();
;   } catch (...) {}
; }
;
; In this case, the outermost cleanup pad can be eliminated and the catch block
; should unwind to the caller (that is, exception handling continues with the
; parent frame of the caller).
;
define void @f2() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[B:%.*]] = alloca [[STRUCT_S2:%.*]], align 1
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[EHCLEANUP:%.*]]
; CHECK:       ehcleanup:
; CHECK-NEXT:    [[TMP0:%.*]] = cleanuppad within none []
; CHECK-NEXT:    call void @"\01??1S2@@QEAA@XZ"(ptr [[B]])
; CHECK-NEXT:    cleanupret from [[TMP0]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[TMP1]] to label [[TRY_CONT]]
; CHECK:       try.cont:
; CHECK-NEXT:    ret void
;
entry:
  %b = alloca %struct.S2, align 1
  invoke void @g() to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  br label %try.cont

ehcleanup:                                        ; preds = %entry
  %0 = cleanuppad within none []
  call void @"\01??1S2@@QEAA@XZ"(ptr %b)
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup
  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1

catch:                                            ; preds = %catch.dispatch
  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %1 to label %catchret.dest

catchret.dest:                                    ; preds = %catch
  br label %try.cont

try.cont:                                         ; preds = %catchret.dest, %invoke.cont
  ret void

ehcleanup.1:
  %2 = cleanuppad within none []
  cleanupret from %2 unwind to caller
}


; This case arises when an object with a non-empty destructor must be cleaned up
; outside of a try-block and an object with an empty destructor must be cleaned
; within the try-block.
;
; void f3() {
;   S2 a;
;   try {
;     S b;
;     g();
;   } catch (...) {}
; }
;
; In this case the inner cleanup pad should be eliminated and the invoke of g()
; should unwind directly to the catchpad.

define void @f3() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_S2:%.*]], align 1
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[EHCLEANUP_1:%.*]]
; CHECK:       catch:
; CHECK-NEXT:    [[CP2:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[CP2]] to label [[TRY_CONT]]
; CHECK:       try.cont:
; CHECK-NEXT:    ret void
; CHECK:       ehcleanup.1:
; CHECK-NEXT:    [[CP3:%.*]] = cleanuppad within none []
; CHECK-NEXT:    call void @"\01??1S2@@QEAA@XZ"(ptr [[A]])
; CHECK-NEXT:    cleanupret from [[CP3]] unwind to caller
;
entry:
  %a = alloca %struct.S2, align 1
  invoke void @g() to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  br label %try.cont

ehcleanup:                                        ; preds = %entry
  %0 = cleanuppad within none []
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup
  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1

catch:                                            ; preds = %catch.dispatch
  %cp2 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %cp2 to label %catchret.dest

catchret.dest:                                    ; preds = %catch
  br label %try.cont

try.cont:                                         ; preds = %catchret.dest, %invoke.cont
  ret void

ehcleanup.1:
  %cp3 = cleanuppad within none []
  call void @"\01??1S2@@QEAA@XZ"(ptr %a)
  cleanupret from %cp3 unwind to caller
}


; This case arises when an object with an empty destructor may require cleanup
; from either inside or outside of a try-block.
;
; void f4() {
;   S a;
;   g();
;   try {
;     g();
;   } catch (...) {}
; }
;
; In this case, the cleanuppad should be eliminated, the invoke outside of the
; catch block should be converted to a call (that is, that is, exception
; handling continues with the parent frame of the caller).)
;
; Note: The cleanuppad simplification will insert an unconditional branch here
;       but it will be eliminated, placing the following invoke in the entry BB.
;
define void @f4() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void @g()
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[TMP0]] to label [[TRY_CONT]]
; CHECK:       try.cont:
; CHECK-NEXT:    ret void
;
entry:
  invoke void @g()
  to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  invoke void @g()
  to label %try.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %invoke.cont
  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup

catch:                                            ; preds = %catch.dispatch
  %0 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %0 to label %try.cont

try.cont:                                         ; preds = %catch, %invoke.cont
  ret void

ehcleanup:
  %cp2 = cleanuppad within none []
  cleanupret from %cp2 unwind to caller
}

; This case tests simplification of an otherwise empty cleanup pad that contains
; a PHI node.
;
; int f6() {
;   int state = 1;
;   try {
;     S a;
;     g();
;     state = 2;
;     g();
;   } catch (...) {
;     return state;
;   }
;   return 0;
; }
;
; In this case, the cleanup pad should be eliminated and the PHI node in the
; cleanup pad should be sunk into the catch dispatch block.
;
define i32 @f6() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f6(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[STATE_0:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
; CHECK:       return:
; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i32 [ [[STATE_0]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT]] ]
; CHECK-NEXT:    ret i32 [[RETVAL_0]]
;
entry:
  invoke void @g()
  to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  invoke void @g()
  to label %return unwind label %ehcleanup

ehcleanup:                                        ; preds = %invoke.cont, %entry
  %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
  %0 = cleanuppad within none []
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup
  %cs1 = catchswitch within none [label %catch] unwind to caller

catch:                                            ; preds = %catch.dispatch
  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %1 to label %return

return:                                           ; preds = %invoke.cont, %catch
  %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ]
  ret i32 %retval.0
}

; This case tests another variation of simplification of an otherwise empty
; cleanup pad that contains a PHI node.
;
; int f7() {
;   int state = 1;
;   try {
;     g();
;     state = 2;
;     S a;
;     g();
;     state = 3;
;     g();
;   } catch (...) {
;     return state;
;   }
;   return 0;
; }
;
; In this case, the cleanup pad should be eliminated and the PHI node in the
; cleanup pad should be merged with the PHI node in the catch dispatch block.
;
define i32 @f7() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f7(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT_1:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       invoke.cont.1:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[STATE_1:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 3, [[INVOKE_CONT_1]] ], [ 2, [[INVOKE_CONT]] ]
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
; CHECK:       return:
; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i32 [ [[STATE_1]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT_1]] ]
; CHECK-NEXT:    ret i32 [[RETVAL_0]]
;
entry:
  invoke void @g()
  to label %invoke.cont unwind label %catch.dispatch

invoke.cont:                                      ; preds = %entry
  invoke void @g()
  to label %invoke.cont.1 unwind label %ehcleanup

invoke.cont.1:                                    ; preds = %invoke.cont
  invoke void @g()
  to label %return unwind label %ehcleanup

ehcleanup:                                        ; preds = %invoke.cont.1, %invoke.cont
  %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ]
  %0 = cleanuppad within none []
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup, %entry
  %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ]
  %cs1 = catchswitch within none [label %catch] unwind to caller

catch:                                            ; preds = %catch.dispatch
  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %1 to label %return

return:                                           ; preds = %invoke.cont.1, %catch
  %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ]
  ret i32 %retval.0
}

; This case tests a scenario where an empty cleanup pad is not dominated by all
; of the predecessors of its successor, but the successor references a PHI node
; in the empty cleanup pad.
;
; Conceptually, the case being modeled is something like this:
;
; int f8() {
;   int x = 1;
;   try {
;     S a;
;     g();
;     x = 2;
; retry:
;     g();
;     return
;   } catch (...) {
;     use_x(x);
;   }
;   goto retry;
; }
;
; While that C++ syntax isn't legal, the IR below is.
;
; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch
; should have an incoming value entry for path from 'foo' that references the
; PHI node itself.
;
define void @f8() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f8(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ], [ [[X]], [[CATCH_CONT:%.*]] ]
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    call void @use_x(i32 [[X]])
; CHECK-NEXT:    catchret from [[TMP0]] to label [[CATCH_CONT]]
; CHECK:       catch.cont:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[RETURN]] unwind label [[CATCH_DISPATCH]]
; CHECK:       return:
; CHECK-NEXT:    ret void
;
entry:
  invoke void @g()
  to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  invoke void @g()
  to label %return unwind label %ehcleanup

ehcleanup:                                        ; preds = %invoke.cont, %entry
  %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
  %0 = cleanuppad within none []
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup, %catch.cont
  %cs1 = catchswitch within none [label %catch] unwind to caller

catch:                                            ; preds = %catch.dispatch
  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  call void @use_x(i32 %x)
  catchret from %1 to label %catch.cont

catch.cont:                                       ; preds = %catch
  invoke void @g()
  to label %return unwind label %catch.dispatch

return:                                           ; preds = %invoke.cont, %catch.cont
  ret void
}
define i32 @f9() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f9(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[S:%.*]] = alloca i8, align 1
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 1, ptr nonnull [[S]])
; CHECK-NEXT:    invoke void @"\01??1S2@@QEAA@XZ"(ptr [[S]])
; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[CATCH_SWITCH:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [ptr null, i32 0, ptr null]
; CHECK-NEXT:    catchret from [[CATCH_PAD]] to label [[TRY_CONT]]
; CHECK:       try.cont:
; CHECK-NEXT:    ret i32 0
;
entry:
  %s = alloca i8, align 1
  call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %s)
  invoke void @"\01??1S2@@QEAA@XZ"(ptr %s)
  to label %try.cont unwind label %ehcleanup

ehcleanup:
  %cleanup.pad = cleanuppad within none []
  call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %s)
  cleanupret from %cleanup.pad unwind label %catch.dispatch

catch.dispatch:
  %catch.switch = catchswitch within none [label %catch] unwind to caller

catch:
  %catch.pad = catchpad within %catch.switch [ptr null, i32 0, ptr null]
  catchret from %catch.pad to label %try.cont

try.cont:
  ret i32 0
}

define void @f10(i32 %V) personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f10(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void @g()
; CHECK-NEXT:    unreachable
;
entry:
  invoke void @g()
  to label %unreachable unwind label %cleanup

unreachable:
  unreachable

cleanup:
  %cp = cleanuppad within none []
  switch i32 %V, label %cleanupret1 [
  i32 0, label %cleanupret2
  ]

cleanupret1:
  cleanupret from %cp unwind to caller

cleanupret2:
  cleanupret from %cp unwind to caller
}

;   This case tests the handling of an empty cleanup pad that
;   contains a lifetime_end intrinsic and does not dominate its
;   successor.
define void @f11() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f11(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[INVOKE_CONT2:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       invoke.cont2:
; CHECK-NEXT:    invoke void @g()
; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
; CHECK:       return:
; CHECK-NEXT:    ret void
;
entry:
  invoke void @g()
  to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  invoke void @g()
  to label %invoke.cont2 unwind label %ehcleanup

invoke.cont2:                                     ; preds = %invoke.cont
  invoke void @g()
  to label %return unwind label %catch.dispatch

ehcleanup:                                        ; preds = %invoke.cont, %entry
  %x = phi ptr [ undef, %invoke.cont ], [ undef, %entry ]
  %0 = cleanuppad within none []
  call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %x)
  cleanupret from %0 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %ehcleanup, %invoke.cont
  %cs1 = catchswitch within none [label %catch] unwind to caller

catch:                                            ; preds = %catch.dispatch
  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
  catchret from %1 to label %return

return:                                           ; preds = %invoke.cont, %catch.cont
  ret void
}

%struct.S = type { i8 }
%struct.S2 = type { i8 }
declare void @"\01??1S2@@QEAA@XZ"(ptr)
declare void @g()
declare void @use_x(i32 %x)

declare i32 @__CxxFrameHandler3(...)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture)
declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
;.