smeenai created this revision.
smeenai added reviewers: DHowett-MSFT, compnerd, majnemer, rjmccall, rnk.

We're implementing funclet-compatible code generation for Obj-C
exceptions when using the MSVC ABI. The idea is that the Obj-C runtime
will wrap Obj-C exceptions inside C++ exceptions, which allows for
interoperability with C++ exceptions (for Obj-C++) and zero-cost
exceptions. This is the approach taken by e.g. WinObjC, and I believe it
to be the best approach for Obj-C exceptions in the MSVC ABI.

This change implements emitting the actual funclet-compatible IR. The
generic exceptions codegen already takes care of emitting the proper
catch dispatch structures, but we need to ensure proper handling of
catch parameters and scoping (emitting the catchret). Finally blocks are
handled quite differently from Itanium; they're expected to be outlined
by the frontend, which limits some control flow possibilities but also
greatly simplifies their codegen. See r334251 for further discussion of
why frontend outlining is used.

Worked on with Saleem Abdulrasool <compn...@compnerd.org>.


Repository:
  rC Clang

https://reviews.llvm.org/D47988

Files:
  lib/CodeGen/CGCXXABI.h
  lib/CodeGen/CGException.cpp
  lib/CodeGen/CGObjCGNU.cpp
  lib/CodeGen/CGObjCMac.cpp
  lib/CodeGen/CGObjCRuntime.cpp
  lib/CodeGen/CGStmt.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/MicrosoftCXXABI.cpp
  test/CodeGenObjC/catch-lexical-block.m
  test/CodeGenObjC/exceptions-msvc.m

Index: test/CodeGenObjC/exceptions-msvc.m
===================================================================
--- test/CodeGenObjC/exceptions-msvc.m
+++ test/CodeGenObjC/exceptions-msvc.m
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 %s
-// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 %s
-// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 %s
-// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 %s
+// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 --enable-var-scope %s
+// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86,ARC --enable-var-scope %s
+// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 --enable-var-scope %s
+// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64,ARC --enable-var-scope %s
 
 #if __has_feature(objc_arc)
 #define WEAK __weak
@@ -67,8 +67,9 @@
 @protocol P;
 
 void f(void);
+void __declspec(nothrow) g(void);
 
-void g() {
+void generate_ehtypes() {
   @try {
     f();
   } @catch (I *) {
@@ -79,3 +80,393 @@
   } @catch (id) {
   }
 }
+
+void basic_try_catch_1() {
+  @try {
+    f();
+  } @catch (...) {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @basic_try_catch_1(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]]
+// CHECK:       [[DISPATCH]]:
+// CHECK-NEXT:    %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT]]:
+// CHECK-NEXT:    br label %[[EHCONT:[^ ]+]]
+// CHECK:       [[EHCONT]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[CATCH]]:
+// CHECK-NEXT:    %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]]
+// CHECK:       [[CATCHRETDEST]]:
+// CHECK-NEXT:    br label %[[EHCONT]]
+// CHECK-NEXT:  }
+
+void basic_try_catch_2() {
+  @try {
+    f();
+  } @catch (id) {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @basic_try_catch_2(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]]
+// CHECK:       [[DISPATCH]]:
+// CHECK-NEXT:    %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT]]:
+// CHECK-NEXT:    br label %[[EHCONT:[^ ]+]]
+// CHECK:       [[EHCONT]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[CATCH]]:
+// CHECK-NEXT:    %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]]
+// CHECK:       [[CATCHRETDEST]]:
+// CHECK-NEXT:    br label %[[EHCONT]]
+// CHECK-NEXT:  }
+
+void basic_try_catch_3() {
+  @try {
+    f();
+  } @catch (I *e) {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @basic_try_catch_3(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    %e = alloca %[[ETYPE:[^ ]+]]*
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]]
+// CHECK:       [[DISPATCH]]:
+// CHECK-NEXT:    %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT]]:
+// CHECK-NEXT:    br label %[[EHCONT:[^ ]+]]
+// CHECK:       [[EHCONT]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[CATCH]]:
+// CHECK-NEXT:    %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@"OBJC_EHTYPE_$_I"{{.*}}, i32 0, %[[ETYPE]]** %e]
+// ARC-NEXT:      %[[ELOAD:[^ ]+]] = load %[[ETYPE]]*, %[[ETYPE]]** %e
+// ARC-NEXT:      %[[CAST1:[^ ]+]] = bitcast %[[ETYPE]]* %[[ELOAD]] to i8*
+// ARC-NEXT:      call i8* @objc_retain(i8* %[[CAST1]]){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// ARC-NEXT:      bitcast
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// ARC-NEXT:      %[[CAST2:[^ ]+]] = bitcast %[[ETYPE]]** %e to i8**
+// ARC-NEXT:      call void @objc_storeStrong(i8** %[[CAST2]], i8* null){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]]
+// CHECK:       [[CATCHRETDEST]]:
+// CHECK-NEXT:    br label %[[EHCONT]]
+// CHECK-NEXT:  }
+
+void multiple_catch() {
+  @try {
+    f();
+  } @catch (id) {
+    g();
+  } @catch (...) {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @multiple_catch(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]]
+// CHECK:       [[DISPATCH]]:
+// CHECK-NEXT:    %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH1:[^ ]+]], label %[[CATCH2:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT]]:
+// CHECK-NEXT:    br label %[[EHCONT:[^ ]+]]
+// CHECK:       [[EHCONT]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[CATCH1]]:
+// CHECK-NEXT:    %[[CATCHPAD1:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD1]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD1]] to label %[[CATCHRETDEST1:[^ ]+]]
+// CHECK:       [[CATCHRETDEST1]]:
+// CHECK-NEXT:    br label %[[EHCONT]]
+// CHECK:       [[CATCH2]]:
+// CHECK-NEXT:    %[[CATCHPAD2:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD2]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD2]] to label %[[CATCHRETDEST2:[^ ]+]]
+// CHECK:       [[CATCHRETDEST2]]:
+// CHECK-NEXT:    br label %[[EHCONT]]
+// CHECK-NEXT:  }
+
+void basic_try_finally() {
+  @try {
+    f();
+  } @finally {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @basic_try_finally(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    %[[CAPTURE1:[^ ]+]] = alloca
+// CHECK-NEXT:    %[[CAPTURE2:[^ ]+]] = alloca
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]]
+// CHECK:       [[DISPATCH]]:
+// CHECK-NEXT:    %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCHALL:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT]]:
+// CHECK-NEXT:    call void @[[FINALLY_FUNCTION:[^ ]+]]({{.*}} %[[CAPTURE2]])
+// CHECK-NEXT:    ret void
+// CHECK:       [[CATCHALL]]:
+// CHECK-NEXT:    %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @[[FINALLY_FUNCTION]]({{.*}} %[[CAPTURE1]])
+// CHECK-NEXT:    call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+// CHECK-NEXT:    unreachable
+// CHECK-NEXT:  }
+
+// (CHECK-LABEL can't be used with a variable capture)
+// CHECK:      define internal void @[[FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @g()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+void basic_try_catch_finally() {
+  @try {
+    f();
+  } @catch (id) {
+    f();
+  } @catch (...) {
+    g();
+  } @finally {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @basic_try_catch_finally(){{.*}} {
+// CHECK:         invoke void @f()
+// CHECK-NEXT:            to label %[[INVOKECONT1:[^ ]+]] unwind label %[[DISPATCH1:[^ ]+]]
+// CHECK:       [[DISPATCH1]]:
+// CHECK-NEXT:    %[[CATCHSWITCH1:[^ ]+]] = catchswitch within none [label %[[CATCH1:[^ ]+]], label %[[CATCH2:[^ ]+]]] unwind label %[[DISPATCH2:[^ ]+]]
+// CHECK:       [[INVOKECONT1]]:
+// CHECK:         br label %[[CLEANUP:[^ ]+]]
+// CHECK:       [[CLEANUP]]:
+// CHECK-NEXT:    call void @[[FINALLY_FUNCTION:[^ ]+]]
+// CHECK:       [[CATCH1]]:
+// CHECK-NEXT:    %[[CATCHPAD1:[^ ]+]] = catchpad within %[[CATCHSWITCH1]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null]
+// CHECK-NEXT:    invoke void @f() [ "funclet"(token %[[CATCHPAD1]]) ]
+// CHECK-NEXT:            to label %[[INVOKECONT2:[^ ]+]] unwind label %[[DISPATCH2]]
+// CHECK:       [[DISPATCH2]]:
+// CHECK-NEXT:    %[[CATCHSWITCH2:[^ ]+]] = catchswitch within none [label %[[CATCHALL:[^ ]+]]] unwind to caller
+// CHECK:       [[INVOKECONT2]]:
+// CHECK-NEXT:    catchret from %[[CATCHPAD1]] to label %[[CATCHRETDEST1:[^ ]+]]
+// CHECK:       [[CATCHRETDEST1]]:
+// CHECK:         br label %[[CLEANUP]]
+// CHECK:       [[CATCH2]]:
+// CHECK-NEXT:    %[[CATCHPAD2:[^ ]+]] = catchpad within %[[CATCHSWITCH1]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD2]]) ]
+// CHECK-NEXT:    catchret from %[[CATCHPAD2]] to label %[[CATCHRETDEST2:[^ ]+]]
+// CHECK:       [[CATCHRETDEST2]]:
+// CHECK:         br label %cleanup
+// CHECK:       [[CATCHALL]]:
+// CHECK-NEXT:    %[[CATCHPAD3:[^ ]+]] = catchpad within %[[CATCHSWITCH2]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @[[FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[CATCHPAD3]]) ]
+// CHECK-NEXT:    call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[CATCHPAD3]]) ]
+// CHECK-NEXT:    unreachable
+
+// CHECK:      define internal void @[[FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @g()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+void nested_try_catch_finally_1() {
+  f();
+  @try {
+    f();
+    @try {
+      f();
+    } @catch (...) {
+      f();
+    } @finally {
+      f();
+    }
+  } @catch (...) {
+    g();
+  } @finally {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @nested_try_catch_finally_1(){{.*}} {
+// CHECK:         call void @f()
+// CHECK-NEXT:    invoke void @f()
+// CHECK-NEXT:            to label %[[OUTER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH:[^ ]+]]
+// CHECK:       [[OUTER_TRY_INVOKE_CONT]]:
+// CHECK:         invoke void @f()
+// CHECK-NEXT:            to label %[[INNER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_CATCH_DISPATCH:[^ ]+]]
+// CHECK:       [[INNER_CATCH_DISPATCH]]:
+// CHECK-NEXT:    %[[INNER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[INNER_CATCH:[^ ]+]]] unwind label %[[INNER_FINALLY_DISPATCH:[^ ]+]]
+// CHECK:       [[INNER_TRY_INVOKE_CONT]]:
+// CHECK:         br label %[[INNER_CLEANUP:[^ ]+]]
+// CHECK:       [[INNER_CLEANUP]]:
+// CHECK-NEXT:    invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]]
+// CHECK-NEXT:            to label %[[INNER_CLEANUP_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH]]
+// CHECK:       [[OUTER_CATCH_DISPATCH]]:
+// CHECK-NEXT:    %[[OUTER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_CATCH:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH:[^ ]+]]
+// CHECK:       [[OUTER_FINALLY_DISPATCH]]:
+// CHECK-NEXT:    %[[OUTER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_FINALLY:[^ ]+]]] unwind to caller
+// CHECK:       [[INNER_CLEANUP_INVOKE_CONT]]:
+// (the cleanup generates a switch; ignore those labels)
+// CHECK:       {{^[^ ]+}}:
+// CHECK:       {{^[^ ]+}}:
+// CHECK:       [[OUTER_CLEANUP:[^ ]+]]:
+// CHECK-NEXT:    call void @[[OUTER_FINALLY_FUNCTION:[^ ]+]]
+// CHECK:       [[INNER_CATCH]]:
+// CHECK-NEXT:    %[[INNER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    invoke void @f() [ "funclet"(token %[[INNER_CATCH_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %[[INNER_CATCH_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_FINALLY_DISPATCH]]
+// CHECK:       [[INNER_FINALLY_DISPATCH]]:
+// CHECK-NEXT:    %[[INNER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[INNER_FINALLY:[^ ]+]]] unwind label %[[OUTER_CATCH_DISPATCH]]
+// CHECK:       [[INNER_CATCH_INVOKE_CONT]]:
+// CHECK-NEXT:    catchret from %[[INNER_CATCH_CATCHPAD]] to label %[[INNER_CATCH_CATCHRET_DEST:[^ ]+]]
+// CHECK:       [[INNER_CATCH_CATCHRET_DEST]]:
+// CHECK:         br label %[[INNER_CLEANUP]]
+// CHECK:       [[INNER_FINALLY]]:
+// CHECK-NEXT:    %[[INNER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]]{{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %[[INNER_FINALLY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH]]
+// CHECK:       [[INNER_FINALLY_INVOKE_CONT]]:
+// CHECK-NEXT:    invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %unreachable unwind label %[[OUTER_CATCH_DISPATCH]]
+// CHECK:       [[OUTER_CATCH]]:
+// CHECK-NEXT:    %[[OUTER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ]
+// CHECK-NEXT:    catchret from %[[OUTER_CATCH_CATCHPAD]] to label %[[OUTER_CATCH_CATCHRET_DEST:[^ ]+]]
+// CHECK:       [[OUTER_CATCH_CATCHRET_DEST]]:
+// CHECK:         br label %[[OUTER_CLEANUP]]
+// CHECK:       [[OUTER_FINALLY]]:
+// CHECK-NEXT:    %[[OUTER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @[[OUTER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:    call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:    unreachable
+
+// CHECK:      define internal void @[[INNER_FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @f()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+// CHECK:      define internal void @[[OUTER_FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @g()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+void nested_try_catch_finally_2() {
+  @try {
+    f();
+  } @catch (...) {
+    f();
+    @try {
+      f();
+    } @catch (...) {
+      g();
+    } @finally {
+      f();
+    }
+  } @finally {
+    g();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @nested_try_catch_finally_2(){{.*}} {
+// CHECK:         invoke void @f()
+// CHECK-NEXT:            to label %[[OUTER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH:[^ ]+]]
+// CHECK:       [[OUTER_CATCH_DISPATCH]]:
+// CHECK-NEXT:    %[[OUTER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_CATCH:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH:[^ ]+]]
+// CHECK:       [[OUTER_TRY_INVOKE_CONT]]:
+// CHECK:         br label %[[OUTER_CLEANUP:[^ ]+]]
+// CHECK:       [[OUTER_CLEANUP]]:
+// CHECK-NEXT:    call void @[[OUTER_FINALLY_FUNCTION:[^ ]+]]
+// CHECK:       [[OUTER_CATCH]]:
+// CHECK-NEXT:    %[[OUTER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    invoke void @f() [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %[[INNER_TRY:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]]
+// CHECK:       [[INNER_TRY]]:
+// CHECK-NEXT:    invoke void @f() [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %[[INNER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_CATCH_DISPATCH:[^ ]+]]
+// CHECK:       [[INNER_CATCH_DISPATCH]]:
+// CHECK-NEXT:    %[[INNER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within %[[OUTER_CATCH_CATCHPAD]] [label %[[INNER_CATCH:[^ ]+]]] unwind label %[[INNER_FINALLY_DISPATCH:[^ ]+]]
+// CHECK:       [[INNER_FINALLY_DISPATCH]]:
+// CHECK-NEXT:    %[[INNER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within %[[OUTER_CATCH_CATCHPAD]] [label %[[INNER_FINALLY:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH]]
+// CHECK:       [[INNER_TRY_INVOKE_CONT]]:
+// CHECK:         br label %[[INNER_CLEANUP:[^ ]+]]
+// CHECK:       [[INNER_CLEANUP]]:
+// CHECK-NEXT:    invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]]
+// CHECK-NEXT:            to label %[[INNER_CLEANUP_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]]
+// CHECK:       [[OUTER_FINALLY_DISPATCH]]:
+// CHECK-NEXT:    %[[OUTER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_FINALLY:[^ ]+]]] unwind to caller
+// CHECK:       [[INNER_CLEANUP_INVOKE_CONT]]:
+// CHECK:         catchret from %[[OUTER_CATCH_CATCHPAD]] to label %[[OUTER_CATCH_CATCHRET_DEST:[^ ]+]]
+// CHECK:       [[OUTER_CATCH_CATCHRET_DEST]]:
+// CHECK:         br label %[[OUTER_CLEANUP]]
+// CHECK:       [[INNER_CATCH]]:
+// CHECK-NEXT:    %[[INNER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @g(){{.*}} [ "funclet"(token %[[INNER_CATCH_CATCHPAD]]) ]
+// CHECK-NEXT:    catchret from %[[INNER_CATCH_CATCHPAD]] to label %[[INNER_CATCH_CATCHRET_DEST:[^ ]+]]
+// CHECK:       [[INNER_CATCH_CATCHRET_DEST]]:
+// CHECK:         br label %[[INNER_CLEANUP]]
+// CHECK:       [[INNER_FINALLY]]:
+// CHECK-NEXT:    %[[INNER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    invoke void @[[INNER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %[[INNER_FINALLY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]]
+// CHECK:       [[INNER_FINALLY_INVOKE_CONT]]:
+// CHECK-NEXT:    invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:            to label %unreachable unwind label %[[OUTER_FINALLY_DISPATCH]]
+// CHECK:       [[OUTER_FINALLY]]:
+// CHECK-NEXT:    %[[OUTER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null]
+// CHECK-NEXT:    call void @[[OUTER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:    call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ]
+// CHECK-NEXT:    unreachable
+
+// CHECK:      define internal void @[[INNER_FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @f()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+// CHECK:      define internal void @[[OUTER_FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @g()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+
+void empty_try_catch() {
+  @try {
+  } @catch (...) {
+    f();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @empty_try_catch(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    br label %[[EHCONT:[^ ]+]]
+// CHECK:       [[EHCONT]]:
+// CHECK-NEXT:    ret void
+// CHECK-NEXT:  }
+
+void empty_try_finally() {
+  @try {
+  } @finally {
+    f();
+  }
+}
+
+// CHECK-LABEL: define {{.*}}void @empty_try_finally(){{.*}} {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    %[[CAPTURED:[^ ]+]] = alloca
+// CHECK-NEXT:    call void @[[FINALLY_FUNCTION:[^ ]+]]({{.*}} %[[CAPTURED]])
+// CHECK-NEXT:    ret void
+// CHECK-NEXT:  }
+
+// CHECK:      define internal void @[[FINALLY_FUNCTION]]{{.*}} {
+// CHECK:        call void @f()
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
Index: test/CodeGenObjC/catch-lexical-block.m
===================================================================
--- test/CodeGenObjC/catch-lexical-block.m
+++ test/CodeGenObjC/catch-lexical-block.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -debug-info-kind=limited -fobjc-exceptions -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -debug-info-kind=limited -fexceptions -fobjc-exceptions -emit-llvm %s -o - | FileCheck %s
 @interface Foo @end
 void f0() {
   @try {
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -121,6 +121,7 @@
   void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;
 
   void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override;
+  void emitBeginCatch(CodeGenFunction &CGF, const VarDecl *CatchParam) override;
 
   llvm::GlobalVariable *getMSCompleteObjectLocator(const CXXRecordDecl *RD,
                                                    const VPtrInfo &Info);
@@ -863,9 +864,13 @@
 
 void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF,
                                      const CXXCatchStmt *S) {
+  emitBeginCatch(CGF, S->getExceptionDecl());
+}
+
+void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF,
+                                     const VarDecl *CatchParam) {
   // In the MS ABI, the runtime handles the copy, and the catch handler is
   // responsible for destruction.
-  VarDecl *CatchParam = S->getExceptionDecl();
   llvm::BasicBlock *CatchPadBB = CGF.Builder.GetInsertBlock();
   llvm::CatchPadInst *CPI =
       cast<llvm::CatchPadInst>(CatchPadBB->getFirstNonPHI());
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -337,6 +337,10 @@
     ~CGCapturedStmtRAII() { CGF.CapturedStmtInfo = PrevCapturedStmtInfo; }
   };
 
+  /// Map a CapturedStmt to its generated function.
+  llvm::DenseMap<const CapturedStmt *, llvm::Function *>
+      CapturedStmtGeneratedFunctionMap;
+
   /// An abstract representation of regular/ObjC call/message targets.
   class AbstractCallee {
     /// The function declaration of the callee.
@@ -491,6 +495,12 @@
     /// has been saved.
     llvm::AllocaInst *SavedExnVar;
 
+    /// Whether the finally is expected to have been outlined by the frontend.
+    bool IsOutlined;
+
+    /// The outlined finally body.
+    const CapturedStmt *FinallyBody;
+
   public:
     void enter(CodeGenFunction &CGF, const Stmt *Finally,
                llvm::Constant *beginCatchFn, llvm::Constant *endCatchFn,
Index: lib/CodeGen/CGStmt.cpp
===================================================================
--- lib/CodeGen/CGStmt.cpp
+++ lib/CodeGen/CGStmt.cpp
@@ -2251,7 +2251,9 @@
   // Emit the CapturedDecl
   CodeGenFunction CGF(CGM, true);
   CGCapturedStmtRAII CapInfoRAII(CGF, new CGCapturedStmtInfo(S, K));
-  llvm::Function *F = CGF.GenerateCapturedStmtFunction(S);
+  llvm::Function *&F = CapturedStmtGeneratedFunctionMap[&S];
+  if (!F)
+    F = CGF.GenerateCapturedStmtFunction(S);
   delete CGF.CapturedStmtInfo;
 
   // Emit call to the helper function.
Index: lib/CodeGen/CGObjCRuntime.cpp
===================================================================
--- lib/CodeGen/CGObjCRuntime.cpp
+++ lib/CodeGen/CGObjCRuntime.cpp
@@ -14,14 +14,16 @@
 //===----------------------------------------------------------------------===//
 
 #include "CGObjCRuntime.h"
+#include "CGCXXABI.h"
 #include "CGCleanup.h"
 #include "CGRecordLayout.h"
 #include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
 #include "llvm/IR/CallSite.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -119,7 +121,7 @@
     const VarDecl *Variable;
     const Stmt *Body;
     llvm::BasicBlock *Block;
-    llvm::Constant *TypeInfo;
+    CatchTypeInfo TypeInfo;
   };
 
   struct CallObjCEndCatch final : EHScopeStack::Cleanup {
@@ -169,12 +171,12 @@
 
       // @catch(...) always matches.
       if (!CatchDecl) {
-        Handler.TypeInfo = nullptr; // catch-all
+        Handler.TypeInfo = CGF.CGM.getCXXABI().getCatchAllTypeInfo();
         // Don't consider any other catches.
         break;
       }
 
-      Handler.TypeInfo = GetEHType(CatchDecl->getType());
+      Handler.TypeInfo = {GetEHType(CatchDecl->getType()), 0};
     }
 
     EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size());
@@ -195,44 +197,69 @@
   // Emit the handlers.
   for (unsigned I = 0, E = Handlers.size(); I != E; ++I) {
     CatchHandler &Handler = Handlers[I];
+    if (Handler.Block->use_empty()) {
+      delete Handler.Block;
+      continue;
+    }
 
     CGF.EmitBlock(Handler.Block);
-    llvm::Value *RawExn = CGF.getExceptionFromSlot();
-
-    // Enter the catch.
-    llvm::Value *Exn = RawExn;
-    if (beginCatchFn)
-      Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted");
 
     CodeGenFunction::LexicalScope cleanups(CGF, Handler.Body->getSourceRange());
 
-    if (endCatchFn) {
-      // Add a cleanup to leave the catch.
-      bool EndCatchMightThrow = (Handler.Variable == nullptr);
-
-      CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup,
-                                                EndCatchMightThrow,
-                                                endCatchFn);
-    }
+    bool RuntimeCopiesExn = CGF.CGM.getTriple().isWindowsMSVCEnvironment();
+    assert(!(RuntimeCopiesExn && beginCatchFn) &&
+           "Should not have begin-catch function if runtime copies exception");
+    assert(!(RuntimeCopiesExn && endCatchFn) &&
+           "Should not have end-catch function if runtime copies exception");
+
+    if (RuntimeCopiesExn) {
+      SaveAndRestore<llvm::Instruction *> RestoreCurrentFuncletPad(
+          CGF.CurrentFuncletPad);
+      CGF.CGM.getCXXABI().emitBeginCatch(CGF, Handler.Variable);
+      if (Handler.Variable && Handler.Variable->getDeclName())
+        EmitInitOfCatchParam(CGF, nullptr, Handler.Variable);
+
+      CGF.EmitStmt(Handler.Body);
+
+      // Leave any cleanups associated with the catch before we exit the
+      // catchpad funclet scope.
+      cleanups.ForceCleanup();
+    } else {
+      llvm::Value *RawExn = CGF.getExceptionFromSlot();
+
+      // Enter the catch.
+      llvm::Value *Exn = RawExn;
+      if (beginCatchFn)
+        Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted");
+
+      if (endCatchFn) {
+        // Add a cleanup to leave the catch.
+        bool EndCatchMightThrow = (Handler.Variable == nullptr);
+
+        CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup,
+                                                  EndCatchMightThrow,
+                                                  endCatchFn);
+      }
 
-    // Bind the catch parameter if it exists.
-    if (const VarDecl *CatchParam = Handler.Variable) {
-      llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType());
-      llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType);
+      // Bind the catch parameter if it exists.
+      if (const VarDecl *CatchParam = Handler.Variable) {
+        llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType());
+        llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType);
 
-      CGF.EmitAutoVarDecl(*CatchParam);
-      EmitInitOfCatchParam(CGF, CastExn, CatchParam);
-    }
+        CGF.EmitAutoVarDecl(*CatchParam);
+        EmitInitOfCatchParam(CGF, CastExn, CatchParam);
+      }
 
-    CGF.ObjCEHValueStack.push_back(Exn);
-    CGF.EmitStmt(Handler.Body);
-    CGF.ObjCEHValueStack.pop_back();
+      CGF.ObjCEHValueStack.push_back(Exn);
+      CGF.EmitStmt(Handler.Body);
+      CGF.ObjCEHValueStack.pop_back();
 
-    // Leave any cleanups associated with the catch.
-    cleanups.ForceCleanup();
+      // Leave any cleanups associated with the catch.
+      cleanups.ForceCleanup();
+    }
 
     CGF.EmitBranchThroughCleanup(Cont);
-  }  
+  }
 
   // Go back to the try-statement fallthrough.
   CGF.Builder.restoreIP(SavedIP);
@@ -248,21 +275,30 @@
 void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF,
                                          llvm::Value *exn,
                                          const VarDecl *paramDecl) {
-
   Address paramAddr = CGF.GetAddrOfLocalVar(paramDecl);
+  // If exn is nullptr, the exception object has been copied into paramAddr by
+  // the runtime, and we can load it from there (and don't need to store it
+  // back). We perform the loads lazily to avoid emitting the load instruction
+  // unless it's needed.
+  bool RuntimeCopiesExn = (exn == nullptr);
 
   switch (paramDecl->getType().getQualifiers().getObjCLifetime()) {
   case Qualifiers::OCL_Strong:
+    if (RuntimeCopiesExn)
+      exn = CGF.Builder.CreateLoad(paramAddr);
     exn = CGF.EmitARCRetainNonBlock(exn);
     // fallthrough
 
   case Qualifiers::OCL_None:
   case Qualifiers::OCL_ExplicitNone:
   case Qualifiers::OCL_Autoreleasing:
-    CGF.Builder.CreateStore(exn, paramAddr);
+    if (!RuntimeCopiesExn)
+      CGF.Builder.CreateStore(exn, paramAddr);
     return;
 
   case Qualifiers::OCL_Weak:
+    if (RuntimeCopiesExn)
+      exn = CGF.Builder.CreateLoad(paramAddr);
     CGF.EmitARCInitWeak(paramAddr, exn);
     return;
   }
Index: lib/CodeGen/CGObjCMac.cpp
===================================================================
--- lib/CodeGen/CGObjCMac.cpp
+++ lib/CodeGen/CGObjCMac.cpp
@@ -714,12 +714,18 @@
   }
 
   llvm::Constant *getObjCEndCatchFn() {
+    if (CGM.getTriple().isWindowsMSVCEnvironment())
+      return nullptr;
+
     return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.VoidTy, false),
                                      "objc_end_catch");
 
   }
 
   llvm::Constant *getObjCBeginCatchFn() {
+    if (CGM.getTriple().isWindowsMSVCEnvironment())
+      return nullptr;
+
     llvm::Type *params[] = { Int8PtrTy };
     return CGM.CreateRuntimeFunction(llvm::FunctionType::get(Int8PtrTy,
                                                              params, false),
@@ -7496,8 +7502,8 @@
 void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
                                          const ObjCAtTryStmt &S) {
   EmitTryCatchStmt(CGF, S,
-      cast<llvm::Function>(ObjCTypes.getObjCBeginCatchFn()),
-      cast<llvm::Function>(ObjCTypes.getObjCEndCatchFn()),
+      cast_or_null<llvm::Function>(ObjCTypes.getObjCBeginCatchFn()),
+      cast_or_null<llvm::Function>(ObjCTypes.getObjCEndCatchFn()),
       cast<llvm::Function>(ObjCTypes.getExceptionRethrowFn()));
 }
 
Index: lib/CodeGen/CGObjCGNU.cpp
===================================================================
--- lib/CodeGen/CGObjCGNU.cpp
+++ lib/CodeGen/CGObjCGNU.cpp
@@ -813,19 +813,23 @@
       // If we're in ObjC++ mode, then we want to make 
       if (CGM.getLangOpts().CPlusPlus) {
         llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
-        // void *__cxa_begin_catch(void *e)
-        EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy);
-        // void __cxa_end_catch(void)
-        ExitCatchFn.init(&CGM, "__cxa_end_catch", VoidTy);
+        if (!CGM.getTriple().isWindowsMSVCEnvironment()) {
+          // void *__cxa_begin_catch(void *e)
+          EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy);
+          // void __cxa_end_catch(void)
+          ExitCatchFn.init(&CGM, "__cxa_end_catch", VoidTy);
+        }
         // void _Unwind_Resume_or_Rethrow(void*)
         ExceptionReThrowFn.init(&CGM, "_Unwind_Resume_or_Rethrow", VoidTy,
                                 PtrTy);
       } else if (R.getVersion() >= VersionTuple(1, 7)) {
         llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
-        // id objc_begin_catch(void *e)
-        EnterCatchFn.init(&CGM, "objc_begin_catch", IdTy, PtrTy);
-        // void objc_end_catch(void)
-        ExitCatchFn.init(&CGM, "objc_end_catch", VoidTy);
+        if (!CGM.getTriple().isWindowsMSVCEnvironment()) {
+          // id objc_begin_catch(void *e)
+          EnterCatchFn.init(&CGM, "objc_begin_catch", IdTy, PtrTy);
+          // void objc_end_catch(void)
+          ExitCatchFn.init(&CGM, "objc_end_catch", VoidTy);
+        }
         // void _Unwind_Resume_or_Rethrow(void*)
         ExceptionReThrowFn.init(&CGM, "objc_exception_rethrow", VoidTy, PtrTy);
       }
Index: lib/CodeGen/CGException.cpp
===================================================================
--- lib/CodeGen/CGException.cpp
+++ lib/CodeGen/CGException.cpp
@@ -1289,14 +1289,22 @@
     llvm::Value *EndCatchFn;
     llvm::Value *RethrowFn;
     llvm::Value *SavedExnVar;
+    bool IsOutlined;
 
     PerformFinally(const Stmt *Body, llvm::Value *ForEHVar,
-                   llvm::Value *EndCatchFn,
-                   llvm::Value *RethrowFn, llvm::Value *SavedExnVar)
-      : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn),
-        RethrowFn(RethrowFn), SavedExnVar(SavedExnVar) {}
+                   llvm::Value *EndCatchFn, llvm::Value *RethrowFn,
+                   llvm::Value *SavedExnVar, bool IsOutlined)
+        : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn),
+          RethrowFn(RethrowFn), SavedExnVar(SavedExnVar),
+          IsOutlined(IsOutlined) {}
 
     void Emit(CodeGenFunction &CGF, Flags flags) override {
+      if (IsOutlined) {
+        // Just emit the call to the outlined finally.
+        CGF.EmitStmt(Body);
+        return;
+      }
+
       // Enter a cleanup to call the end-catch function if one was provided.
       if (EndCatchFn)
         CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup,
@@ -1345,7 +1353,7 @@
         CGF.PopCleanupBlock();
         CGF.Builder.restoreIP(SavedIP);
       }
-    
+
       // Now make sure we actually have an insertion point or the
       // cleanup gods will hate us.
       CGF.EnsureInsertPoint();
@@ -1365,21 +1373,6 @@
          "begin/end catch functions not paired");
   assert(rethrowFn && "rethrow function is required");
 
-  BeginCatchFn = beginCatchFn;
-
-  // The rethrow function has one of the following two types:
-  //   void (*)()
-  //   void (*)(void*)
-  // In the latter case we need to pass it the exception object.
-  // But we can't use the exception slot because the @finally might
-  // have a landing pad (which would overwrite the exception slot).
-  llvm::FunctionType *rethrowFnTy =
-    cast<llvm::FunctionType>(
-      cast<llvm::PointerType>(rethrowFn->getType())->getElementType());
-  SavedExnVar = nullptr;
-  if (rethrowFnTy->getNumParams())
-    SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn");
-
   // A finally block is a statement which must be executed on any edge
   // out of a given scope.  Unlike a cleanup, the finally block may
   // contain arbitrary control flow leading out of itself.  In
@@ -1392,24 +1385,56 @@
   // The finally block itself is generated in the context of a cleanup
   // which conditionally leaves the catch-all.
 
-  // Jump destination for performing the finally block on an exception
-  // edge.  We'll never actually reach this block, so unreachable is
-  // fine.
-  RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock());
-
-  // Whether the finally block is being executed for EH purposes.
-  ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh");
-  CGF.Builder.CreateFlagStore(false, ForEHVar);
+  // For funclet-based EH (such as Windows MSVC), we cannot branch out of the
+  // catch-all to the cleanup, since the only valid way to leave the catch-all
+  // scope would be via a catchret, which would destroy the exception. Instead,
+  // we expect the frontend to outline the body of the finally, so that it can
+  // be called separately from both the catch-all and the cleanup. This also
+  // means we do not currently support control flow leaving the finally (e.g.
+  // returns and gotos); the frontend will emit errors for that.
+  IsOutlined = CGF.CGM.getTriple().isWindowsMSVCEnvironment();
+  if (IsOutlined) {
+    assert(!beginCatchFn &&
+           "Should not have begin-catch function when @finally is outlined");
+    assert(!endCatchFn &&
+           "Should not have end-catch function when @finally is outlined");
+    FinallyBody = dyn_cast<CapturedStmt>(body);
+    assert(FinallyBody && "Outlined @finally should be a CapturedStmt");
+  } else {
+    BeginCatchFn = beginCatchFn;
+
+    // The rethrow function has one of the following two types:
+    //   void (*)()
+    //   void (*)(void*)
+    // In the latter case we need to pass it the exception object.
+    // But we can't use the exception slot because the @finally might
+    // have a landing pad (which would overwrite the exception slot).
+    llvm::FunctionType *rethrowFnTy =
+      cast<llvm::FunctionType>(
+        cast<llvm::PointerType>(rethrowFn->getType())->getElementType());
+    SavedExnVar = nullptr;
+    if (rethrowFnTy->getNumParams())
+      SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn");
+
+    // Jump destination for performing the finally block on an exception
+    // edge.  We'll never actually reach this block, so unreachable is
+    // fine.
+    RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock());
+
+    // Whether the finally block is being executed for EH purposes.
+    ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh");
+    CGF.Builder.CreateFlagStore(false, ForEHVar);
+  }
 
   // Enter a normal cleanup which will perform the @finally block.
-  CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body,
-                                          ForEHVar, endCatchFn,
-                                          rethrowFn, SavedExnVar);
+  CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body, ForEHVar,
+                                          endCatchFn, rethrowFn, SavedExnVar,
+                                          IsOutlined);
 
   // Enter a catch-all scope.
   llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall");
   EHCatchScope *catchScope = CGF.EHStack.pushCatch(1);
-  catchScope->setCatchAllHandler(0, catchBB);
+  catchScope->setHandler(0, CGF.CGM.getCXXABI().getCatchAllTypeInfo(), catchBB);
 }
 
 void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) {
@@ -1426,25 +1451,33 @@
     CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP();
     CGF.EmitBlock(catchBB);
 
-    llvm::Value *exn = nullptr;
+    if (IsOutlined) {
+      SaveAndRestore<llvm::Instruction *> RestoreCurrentFuncletPad(
+          CGF.CurrentFuncletPad);
+      CGF.CurrentFuncletPad = catchBB->getFirstNonPHI();
+      CGF.EmitStmt(FinallyBody);
+      CGF.CGM.getCXXABI().emitRethrow(CGF, /*IsNoReturn=*/true);
+    } else {
+      llvm::Value *exn = nullptr;
 
-    // If there's a begin-catch function, call it.
-    if (BeginCatchFn) {
-      exn = CGF.getExceptionFromSlot();
-      CGF.EmitNounwindRuntimeCall(BeginCatchFn, exn);
-    }
+      // If there's a begin-catch function, call it.
+      if (BeginCatchFn) {
+        exn = CGF.getExceptionFromSlot();
+        CGF.EmitNounwindRuntimeCall(BeginCatchFn, exn);
+      }
 
-    // If we need to remember the exception pointer to rethrow later, do so.
-    if (SavedExnVar) {
-      if (!exn) exn = CGF.getExceptionFromSlot();
-      CGF.Builder.CreateAlignedStore(exn, SavedExnVar, CGF.getPointerAlign());
-    }
+      // If we need to remember the exception pointer to rethrow later, do so.
+      if (SavedExnVar) {
+        if (!exn) exn = CGF.getExceptionFromSlot();
+        CGF.Builder.CreateAlignedStore(exn, SavedExnVar, CGF.getPointerAlign());
+      }
 
-    // Tell the cleanups in the finally block that we're do this for EH.
-    CGF.Builder.CreateFlagStore(true, ForEHVar);
+      // Tell the cleanups in the finally block that we're do this for EH.
+      CGF.Builder.CreateFlagStore(true, ForEHVar);
 
-    // Thread a jump through the finally cleanup.
-    CGF.EmitBranchThroughCleanup(RethrowDest);
+      // Thread a jump through the finally cleanup.
+      CGF.EmitBranchThroughCleanup(RethrowDest);
+    }
 
     CGF.Builder.restoreIP(savedIP);
   }
Index: lib/CodeGen/CGCXXABI.h
===================================================================
--- lib/CodeGen/CGCXXABI.h
+++ lib/CodeGen/CGCXXABI.h
@@ -243,6 +243,9 @@
   virtual bool canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const = 0;
 
   virtual void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) = 0;
+  virtual void emitBeginCatch(CodeGenFunction &CGF, const VarDecl *CatchParam) {
+    llvm_unreachable("Only needed for the Microsoft ABI");
+  }
 
   virtual llvm::CallInst *
   emitTerminateForUnexpectedException(CodeGenFunction &CGF,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to