smeenai created this revision.
smeenai added reviewers: rjmccall, rnk, rsmith.
Herald added a subscriber: cfe-commits.

The body of a @finally needs to be executed on both exceptional and
non-exceptional paths. On landingpad platforms, this is straightforward:
the @finally body is emitted as a normal (non-exceptional) cleanup, and
then a catch-all is emitted which branches to that cleanup (the cleanup
has code to conditionally re-throw based on a flag which is set by the
catch-all).

Unfortunately, we can't use the same approach for MSVC exceptions, where
the catch-all will be emitted as a catchpad. We can't just branch to the
cleanup from within the catchpad, since we can only exit it via a
catchret, at which point the exception is destroyed and we can't
rethrow. We could potentially emit the finally body inside the catchpad
and have the normal cleanup path somehow branch into it, but that would
require some new IR construct that could branch into a catchpad.

Instead, after discussing it with Reid Kleckner, we decided that
frontend outlining was the best approach, similar to how SEH __finally
works today. We decided to use CapturedStmt rather than CaptureFinder
(which is what __finally uses) since the latter doesn't handle a lot of
cases we care about, e.g. self accesses, property accesses, block
captures, etc. Extending CaptureFinder to handle those additional cases
proved unwieldy, whereas CapturedStmt already took care of all of those.
In theory __finally could also be moved over to CapturedStmt, which
would remove some existing limitations (e.g. the inability to capture
this), although CaptureFinder would still be needed for SEH filters.

The one case supported by @finally but not CapturedStmt (or
CaptureFinder for that matter) is arbitrary control flow out of the
@finally, e.g. having a return statement inside a @finally. We can add
that support as a follow-up, but in practice we've found it to be used
very rarely anyway.


Repository:
  rC Clang

https://reviews.llvm.org/D47564

Files:
  include/clang/AST/Stmt.h
  include/clang/Basic/CapturedStmt.h
  include/clang/Sema/ScopeInfo.h
  lib/CodeGen/CGCXXABI.h
  lib/CodeGen/CGObjCMac.cpp
  lib/CodeGen/MicrosoftCXXABI.cpp
  lib/Parse/ParseObjc.cpp
  test/CodeGenObjC/dllstorage.m
  test/CodeGenObjC/exceptions-msvc.m
  test/SemaObjC/finally-msvc.m

Index: test/SemaObjC/finally-msvc.m
===================================================================
--- /dev/null
+++ test/SemaObjC/finally-msvc.m
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple i686--windows-msvc -fexceptions -fobjc-exceptions -ast-dump %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64--windows-msvc -fexceptions -fobjc-exceptions -ast-dump %s 2>&1 | FileCheck %s
+
+void f() {
+  @try {
+  } @finally {
+  }
+}
+
+// CHECK:      ObjCAtFinallyStmt
+// CHECK-NEXT:   CapturedStmt
+// CHECK-NEXT:     CapturedDecl
+// CHECK-NEXT:       CompoundStmt
+// CHECK-NEXT:       ImplicitParamDecl
Index: test/CodeGenObjC/exceptions-msvc.m
===================================================================
--- /dev/null
+++ test/CodeGenObjC/exceptions-msvc.m
@@ -0,0 +1,69 @@
+// 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
+
+#if __has_feature(objc_arc)
+#define WEAK __weak
+#else
+#define WEAK
+#endif
+
+// CHECK-DAG: $OBJC_EHTYPE_id = comdat any
+// X86-DAG: @OBJC_EHTYPE_id = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [18 x i8] c".PAUobjc_object@@\00" }, comdat
+// X64-DAG: @OBJC_EHTYPE_id = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [19 x i8] c".PEAUobjc_object@@\00" }, comdat
+
+@class I;
+
+// CHECK-DAG: $"OBJC_EHTYPE_$_I" = comdat any
+// X86-DAG: @"OBJC_EHTYPE_$_I" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".PAUI@@\00" }, comdat
+// X64-DAG: @"OBJC_EHTYPE_$_I" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".PEAUI@@\00" }, comdat
+
+@class J;
+
+// CHECK-DAG: $"OBJC_EHTYPE_$_J" = comdat any
+// X86-DAG: @"OBJC_EHTYPE_$_J" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".PAUJ@@\00" }, comdat
+// X64-DAG: @"OBJC_EHTYPE_$_J" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".PEAUJ@@\00" }, comdat
+
+// The EHType shouldn't be exported
+__declspec(dllexport)
+__attribute__((objc_root_class))
+@interface K
+@end
+
+@implementation K
+@end
+
+// CHECK-DAG: $"OBJC_EHTYPE_$_K" = comdat any
+// X86-DAG: @"OBJC_EHTYPE_$_K" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".PAUK@@\00" }, comdat
+// X64-DAG: @"OBJC_EHTYPE_$_K" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".PEAUK@@\00" }, comdat
+
+__attribute__((objc_runtime_name("NotL")))
+@interface L
+@end
+
+// CHECK-DAG: $"OBJC_EHTYPE_$_NotL" = comdat any
+// X86-DAG: @"OBJC_EHTYPE_$_NotL" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".PAUL@@\00" }, comdat
+// X64-DAG: @"OBJC_EHTYPE_$_NotL" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".PEAUL@@\00" }, comdat
+
+@class M;
+
+// CHECK-DAG: $"OBJC_EHTYPE_$_M" = comdat any
+// X86-DAG: @"OBJC_EHTYPE_$_M" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".PAUM@@\00" }, comdat
+// X64-DAG: @"OBJC_EHTYPE_$_M" = linkonce_odr global {{%[^ ]+}} { i8** @"??_7type_info@@6B@", i8* null, [9 x i8] c".PEAUM@@\00" }, comdat
+
+@protocol P;
+
+void f(void);
+
+void g() {
+  @try {
+    f();
+  } @catch (I *) {
+  } @catch (J<P> *) {
+  } @catch (K *) {
+  } @catch (L *) {
+  } @catch (M *WEAK) {
+  } @catch (id) {
+  }
+}
Index: test/CodeGenObjC/dllstorage.m
===================================================================
--- test/CodeGenObjC/dllstorage.m
+++ test/CodeGenObjC/dllstorage.m
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -fdeclspec -fobjc-runtime=ios -fobjc-exceptions -S -emit-llvm -o - %s | FileCheck -check-prefix CHECK-IR %s
-// RUN: %clang_cc1 -triple i686-windows-itanium -fms-extensions -fobjc-runtime=macosx -fdeclspec -fobjc-exceptions -S -emit-llvm -o - %s | FileCheck -check-prefix CHECK-IR %s
+// RUN: %clang_cc1 -triple i686-windows-itanium -fms-extensions -fobjc-runtime=macosx -fdeclspec -fobjc-exceptions -S -emit-llvm -o - %s | FileCheck -check-prefix CHECK-IR -check-prefix CHECK-ITANIUM %s
 // RUN: %clang_cc1 -triple i686-windows-itanium -fms-extensions -fobjc-runtime=objfw -fdeclspec -fobjc-exceptions -S -emit-llvm -o - %s | FileCheck -check-prefix CHECK-FW %s
 
 // CHECK-IR-DAG: @_objc_empty_cache = external dllimport global %struct._objc_cache
@@ -100,20 +100,20 @@
 @implementation N : I
 @end
 
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_N" = dso_local dllexport global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_N" = dso_local dllexport global %struct._objc_typeinfo
 
 __declspec(dllimport)
 __attribute__((__objc_exception__))
 @interface O : I
 @end
 
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_O" = external dllimport global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_O" = external dllimport global %struct._objc_typeinfo
 
 __attribute__((__objc_exception__))
 @interface P : I
 @end
 
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_P" = external dso_local global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_P" = external dso_local global %struct._objc_typeinfo
 
 @interface Q : M
 @end
@@ -153,9 +153,9 @@
   return 0;
 }
 
-// CHECK-IR-DAG: @OBJC_EHTYPE_id = external dllimport global %struct._objc_typeinfo
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_I" = weak global %struct._objc_typeinfo
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_K" = weak global %struct._objc_typeinfo
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_L" = weak global %struct._objc_typeinfo
-// CHECK-IR-DAG: @"OBJC_EHTYPE_$_M" = weak global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @OBJC_EHTYPE_id = external dllimport global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_I" = weak global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_K" = weak global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_L" = weak global %struct._objc_typeinfo
+// CHECK-ITANIUM-DAG: @"OBJC_EHTYPE_$_M" = weak global %struct._objc_typeinfo
 
Index: lib/Parse/ParseObjc.cpp
===================================================================
--- lib/Parse/ParseObjc.cpp
+++ lib/Parse/ParseObjc.cpp
@@ -2585,13 +2585,28 @@
       ParseScope FinallyScope(this,
                               Scope::DeclScope | Scope::CompoundStmtScope);
 
+      bool ShouldCapture = Actions.getASTContext()
+                               .getTargetInfo()
+                               .getTriple()
+                               .isWindowsMSVCEnvironment();
+      if (ShouldCapture)
+        Actions.ActOnCapturedRegionStart(Tok.getLocation(), getCurScope(),
+                                         CR_ObjCAtFinally, 1);
+
       StmtResult FinallyBody(true);
       if (Tok.is(tok::l_brace))
         FinallyBody = ParseCompoundStatementBody();
       else
         Diag(Tok, diag::err_expected) << tok::l_brace;
-      if (FinallyBody.isInvalid())
+
+      if (FinallyBody.isInvalid()) {
         FinallyBody = Actions.ActOnNullStmt(Tok.getLocation());
+        if (ShouldCapture)
+          Actions.ActOnCapturedRegionError();
+      } else if (ShouldCapture) {
+        FinallyBody = Actions.ActOnCapturedRegionEnd(FinallyBody.get());
+      }
+
       FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc,
                                                    FinallyBody.get());
       catch_or_finally_seen = true;
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -126,6 +126,8 @@
                                                    const VPtrInfo &Info);
 
   llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty) override;
+  llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty,
+                                          StringRef MangledName) override;
   CatchTypeInfo
   getAddrOfCXXCatchHandlerType(QualType Ty, QualType CatchHandlerType) override;
 
@@ -3773,7 +3775,11 @@
     llvm::raw_svector_ostream Out(MangledName);
     getMangleContext().mangleCXXRTTI(Type, Out);
   }
+  return getAddrOfRTTIDescriptor(Type, MangledName);
+}
 
+llvm::Constant *
+MicrosoftCXXABI::getAddrOfRTTIDescriptor(QualType Type, StringRef MangledName) {
   // Check to see if we've already declared this TypeDescriptor.
   if (llvm::GlobalVariable *GV = CGM.getModule().getNamedGlobal(MangledName))
     return llvm::ConstantExpr::getBitCast(GV, CGM.Int8PtrTy);
Index: lib/CodeGen/CGObjCMac.cpp
===================================================================
--- lib/CodeGen/CGObjCMac.cpp
+++ lib/CodeGen/CGObjCMac.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CGBlocks.h"
+#include "CGCXXABI.h"
 #include "CGCleanup.h"
 #include "CGObjCRuntime.h"
 #include "CGRecordLayout.h"
@@ -7456,6 +7457,10 @@
 CGObjCNonFragileABIMac::GetEHType(QualType T) {
   // There's a particular fixed type info for 'id'.
   if (T->isObjCIdType() || T->isObjCQualifiedIdType()) {
+    if (CGM.getTriple().isWindowsMSVCEnvironment())
+      return CGM.getCXXABI().getAddrOfRTTIDescriptor(
+          T.getLocalUnqualifiedType(), "OBJC_EHTYPE_id");
+
     auto *IDEHType = CGM.getModule().getGlobalVariable("OBJC_EHTYPE_id");
     if (!IDEHType) {
       IDEHType =
@@ -7475,7 +7480,15 @@
   const ObjCInterfaceType *IT = PT->getInterfaceType();
   assert(IT && "Invalid @catch type.");
 
-  return GetInterfaceEHType(IT->getDecl(), NotForDefinition);
+  const ObjCInterfaceDecl *ID = IT->getDecl();
+  if (CGM.getTriple().isWindowsMSVCEnvironment()) {
+    const ASTContext &Ctx = CGM.getContext();
+    return CGM.getCXXABI().getAddrOfRTTIDescriptor(
+        Ctx.getObjCObjectPointerType(IT->stripObjCKindOfTypeAndQuals(Ctx)),
+        ("OBJC_EHTYPE_$_" + ID->getObjCRuntimeNameAsString()).str());
+  }
+
+  return GetInterfaceEHType(ID, NotForDefinition);
 }
 
 void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
Index: lib/CodeGen/CGCXXABI.h
===================================================================
--- lib/CodeGen/CGCXXABI.h
+++ lib/CodeGen/CGCXXABI.h
@@ -249,6 +249,10 @@
                                       llvm::Value *Exn);
 
   virtual llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty) = 0;
+  virtual llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty,
+                                                  StringRef MangledName) {
+    llvm_unreachable("Only needed for the Microsoft ABI");
+  }
   virtual CatchTypeInfo
   getAddrOfCXXCatchHandlerType(QualType Ty, QualType CatchHandlerType) = 0;
   virtual CatchTypeInfo getCatchAllTypeInfo();
Index: include/clang/Sema/ScopeInfo.h
===================================================================
--- include/clang/Sema/ScopeInfo.h
+++ include/clang/Sema/ScopeInfo.h
@@ -748,6 +748,8 @@
     switch (CapRegionKind) {
     case CR_Default:
       return "default captured statement";
+    case CR_ObjCAtFinally:
+      return "Objective-C @finally statement";
     case CR_OpenMP:
       return "OpenMP region";
     }
Index: include/clang/Basic/CapturedStmt.h
===================================================================
--- include/clang/Basic/CapturedStmt.h
+++ include/clang/Basic/CapturedStmt.h
@@ -16,6 +16,7 @@
 /// The different kinds of captured statement.
 enum CapturedRegionKind {
   CR_Default,
+  CR_ObjCAtFinally,
   CR_OpenMP
 };
 
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -2133,7 +2133,7 @@
 
   /// The pointer part is the implicit the outlined function and the 
   /// int part is the captured region kind, 'CR_Default' etc.
-  llvm::PointerIntPair<CapturedDecl *, 1, CapturedRegionKind> CapDeclAndKind;
+  llvm::PointerIntPair<CapturedDecl *, 2, CapturedRegionKind> CapDeclAndKind;
 
   /// The record for captured variables, a RecordDecl or CXXRecordDecl.
   RecordDecl *TheRecordDecl = nullptr;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to