llvm/clang/test/CodeGen/exceptions-seh-leave.c

// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s

void g(void);

//////////////////////////////////////////////////////////////////////////////
// __leave with __except

// Nothing in the __try block can trap, so __try.cont isn't created.
int __leave_with___except_simple(void) {
  int myres = 0;
  __try {
    myres = 15;
    __leave;
    myres = 23;
  } __except (1) {
    return 0;
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @__leave_with___except_simple()
// CHECK: store i32 15, ptr %myres
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// CHECK: [[tryleave]]
// CHECK-NEXT: ret i32 1


// The "normal" case.
int __leave_with___except(void) {
  int myres = 0;
  __try {
    g();
    __leave;
    myres = 23;
  } __except (1) {
    return 0;
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @__leave_with___except()
// CHECK: invoke void @g()
// CHECK-NEXT:       to label %[[cont:.*]] unwind label %{{.*}}
// For __excepts, instead of an explicit __try.__leave label, we could use
// use invoke.cont as __leave jump target instead.  However, not doing this
// keeps the CodeGen code simpler, __leave is very rare, and SimplifyCFG will
// simplify this anyways.
// CHECK: [[cont]]
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// CHECK: [[tryleave]]
// CHECK-NEXT: br label %


//////////////////////////////////////////////////////////////////////////////
// __leave with __finally

void abort(void) __attribute__((noreturn));

// Nothing in the __try block can trap, so __finally.cont and friends aren't
// created.
int __leave_with___finally_simple(void) {
  int myres = 0;
  __try {
    myres = 15;
    __leave;
    myres = 23;
  } __finally {
    return 0;
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @__leave_with___finally_simple()
// CHECK: store i32 15, ptr %myres
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// CHECK: [[tryleave]]
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally_simple@@"(i8 noundef 0, ptr noundef %[[fp]])

// __finally block doesn't return, __finally.cont doesn't exist.
int __leave_with___finally_noreturn(void) {
  int myres = 0;
  __try {
    myres = 15;
    __leave;
    myres = 23;
  } __finally {
    abort();
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @__leave_with___finally_noreturn()
// CHECK: store i32 15, ptr %myres
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// CHECK: [[tryleave]]
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally_noreturn@@"(i8 noundef 0, ptr noundef %[[fp]])

// The "normal" case.
int __leave_with___finally(void) {
  int myres = 0;
  __try {
    g();
    __leave;
    myres = 23;
  } __finally {
    return 0;
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @__leave_with___finally()
// CHECK: invoke void @g()
// CHECK-NEXT:       to label %[[cont:.*]] unwind label %{{.*}}
// For __finally, there needs to be an explicit __try.__leave, because
// abnormal.termination.slot needs to be set there.
// CHECK: [[cont]]
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// CHECK: [[tryleave]]
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@__leave_with___finally@@"(i8 noundef 0, ptr noundef %[[fp]])


//////////////////////////////////////////////////////////////////////////////
// Mixed, nested cases.

int nested___except___finally(void) {
  int myres = 0;
  __try {
    __try {
      g();
    } __finally {
      g();
      __leave;  // Refers to the outer __try, not the __finally!
      myres = 23;
      return 0;
    }

    myres = 51;
  } __except (1) {
  }
  return 1;
}
// CHECK-LABEL: define dso_local i32 @nested___except___finally()

// CHECK-LABEL: invoke void @g()
// CHECK-NEXT:       to label %[[g1_cont1:.*]] unwind label %[[g1_lpad:.*]]

// CHECK: [[g1_cont1]]
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: invoke void @"?fin$0@0@nested___except___finally@@"(i8 noundef 0, ptr noundef %[[fp]])
// CHECK-NEXT:       to label %[[fin_cont:.*]] unwind label %[[g2_lpad:.*]]

// CHECK: [[fin_cont]]
// CHECK: store i32 51, ptr %
// CHECK-NEXT: br label %[[trycont:[^ ]*]]

// CHECK: [[g1_lpad]]
// CHECK-NEXT: cleanuppad
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: invoke void @"?fin$0@0@nested___except___finally@@"(i8 noundef 1, ptr noundef %[[fp]])
// CHECK-NEXT:       to label %[[g1_resume:.*]] unwind label %[[g2_lpad]]
// CHECK: cleanupret {{.*}} unwind label %[[g2_lpad]]

// CHECK: [[g2_lpad]]
// CHECK: catchpad {{.*}} [ptr null]
// CHECK: catchret
// CHECK: br label %[[trycont]]

// CHECK: [[trycont]]
// CHECK-NEXT: ret i32 1

// CHECK-LABEL: define internal void @"?fin$0@0@nested___except___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer)
// CHECK: call void @g()
// CHECK: unreachable

int nested___except___except(void) {
  int myres = 0;
  __try {
    __try {
      g();
      myres = 16;
    } __except (1) {
      g();
      __leave;  // Refers to the outer __try, not the __except we're in!
      myres = 23;
      return 0;
    }

    myres = 51;
  } __except (1) {
  }
  return 1;
}
// The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define dso_local i32 @nested___except___except()

// CHECK-LABEL: invoke void @g()
// CHECK-NEXT:       to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]

// CHECK: [[g1_lpad]]
// CHECK: catchpad {{.*}} [ptr null]
// CHECK: catchret {{.*}} to label %[[except:[^ ]*]]
// CHECK: [[except]]
// CHECK: invoke void @g()
// CHECK-NEXT:       to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]

// CHECK: [[g2_lpad]]
// CHECK: catchpad {{.*}} [ptr null]
// CHECK: catchret
// CHECK: br label %[[trycont4:[^ ]*]]

// CHECK: [[trycont4]]
// CHECK-NEXT: ret i32 1

// CHECK: [[g2_cont]]
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23

// CHECK: [[g1_cont]]
// CHECK: store i32 16, ptr %myres
// CHECK-NEXT: br label %[[trycont:[^ ]*]]

// CHECK: [[trycont]]
// CHECK-NEXT: store i32 51, ptr %myres
// CHECK-NEXT: br label %[[tryleave]]

// CHECK: [[tryleave]]
// CHECK-NEXT: br label %[[trycont4]]

int nested___finally___except(void) {
  int myres = 0;
  __try {
    __try {
      g();
    } __except (1) {
      g();
      __leave;  // Refers to the outer __try, not the __except!
      myres = 23;
      return 0;
    }

    myres = 51;
  } __finally {
  }
  return 1;
}
// The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define dso_local i32 @nested___finally___except()

// CHECK-LABEL: invoke void @g()
// CHECK-NEXT:       to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]

// CHECK: [[g1_lpad]]
// CHECK: catchpad
// CHECK: catchret
// CHECK: invoke void @g()
// CHECK-NEXT:       to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]

// CHECK: [[g2_cont]]
// CHECK: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: 23

// CHECK: [[g1_cont]]
// CHECK-NEXT: br label %[[trycont:[^ ]*]]

// CHECK: [[trycont]]
// CHECK: store i32 51, ptr %
// CHECK-NEXT: br label %[[tryleave]]

// CHECK: [[tryleave]]
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@nested___finally___except@@"(i8 noundef 0, ptr noundef %[[fp]])
// CHECK-NEXT: ret i32 1

// CHECK: [[g2_lpad]]
// CHECK: cleanuppad
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@nested___finally___except@@"(i8 noundef 1, ptr noundef %[[fp]])
// CHECK: cleanupret {{.*}} unwind to caller

// CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___except@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer)
// CHECK: ret void

int nested___finally___finally(void) {
  int myres = 0;
  __try {
    __try {
      g();
      myres = 16;
    } __finally {
      g();
      __leave;  // Refers to the outer __try, not the __finally we're in!
      myres = 23;
      return 0;
    }

    myres = 51;
  } __finally {
  }
  return 1;
}
// The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define dso_local i32 @nested___finally___finally()

// CHECK: invoke void @g()
// CHECK-NEXT:       to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]

// CHECK: [[g1_cont]]
// CHECK: store i32 16, ptr %[[myres:[^ ]*]],
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: invoke void @"?fin$1@0@nested___finally___finally@@"(i8 noundef 0, ptr noundef %[[fp]])
// CHECK-NEXT:       to label %[[finally_cont:.*]] unwind label %[[g2_lpad:.*]]

// CHECK: [[finally_cont]]
// CHECK: store i32 51, ptr %[[myres]]
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@nested___finally___finally@@"(i8 noundef 0, ptr noundef %[[fp]])
// CHECK-NEXT: ret i32 1

// CHECK: [[g1_lpad]]
// CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad within none []
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: invoke void @"?fin$1@0@nested___finally___finally@@"(i8 noundef 1, ptr noundef %[[fp]])
// CHECK-NEXT:       to label %[[finally_cont2:.*]] unwind label %[[g2_lpad]]
// CHECK: [[finally_cont2]]
// CHECK: cleanupret from %[[padtoken]] unwind label %[[g2_lpad]]

// CHECK: [[g2_lpad]]
// CHECK-NEXT: %[[padtoken:[^ ]*]] = cleanuppad within none []
// CHECK-NEXT: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK-NEXT: call void @"?fin$0@0@nested___finally___finally@@"(i8 noundef 1, ptr noundef %[[fp]])
// CHECK: cleanupret from %[[padtoken]] unwind to caller

// CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer)
// CHECK: ret void

// CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally@@"(i8 noundef %abnormal_termination, ptr noundef %frame_pointer)
// CHECK: call void @g()
// CHECK: unreachable