[cfe-commits] r108944 - in /cfe/trunk: lib/CodeGen/CGException.cpp lib/CodeGen/TargetInfo.h test/CodeGenCXX/eh.cpp

John McCall rjmccall at apple.com
Tue Jul 20 15:17:55 PDT 2010


Author: rjmccall
Date: Tue Jul 20 17:17:55 2010
New Revision: 108944

URL: http://llvm.org/viewvc/llvm-project?rev=108944&view=rev
Log:
Fix the IR generation for catching pointers by references.
Fixes <rdar://problem/8212123>.


Modified:
    cfe/trunk/lib/CodeGen/CGException.cpp
    cfe/trunk/lib/CodeGen/TargetInfo.h
    cfe/trunk/test/CodeGenCXX/eh.cpp

Modified: cfe/trunk/lib/CodeGen/CGException.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=108944&r1=108943&r2=108944&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGException.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGException.cpp Tue Jul 20 17:17:55 2010
@@ -18,6 +18,7 @@
 
 #include "CodeGenFunction.h"
 #include "CGException.h"
+#include "TargetInfo.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -1099,11 +1100,57 @@
   // If we're catching by reference, we can just cast the object
   // pointer to the appropriate pointer.
   if (isa<ReferenceType>(CatchType)) {
-    bool EndCatchMightThrow = cast<ReferenceType>(CatchType)->getPointeeType()
-      ->isRecordType();
+    QualType CaughtType = cast<ReferenceType>(CatchType)->getPointeeType();
+    bool EndCatchMightThrow = CaughtType->isRecordType();
 
     // __cxa_begin_catch returns the adjusted object pointer.
     llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, EndCatchMightThrow);
+
+    // We have no way to tell the personality function that we're
+    // catching by reference, so if we're catching a pointer,
+    // __cxa_begin_catch will actually return that pointer by value.
+    if (const PointerType *PT = dyn_cast<PointerType>(CaughtType)) {
+      QualType PointeeType = PT->getPointeeType();
+
+      // When catching by reference, generally we should just ignore
+      // this by-value pointer and use the exception object instead.
+      if (!PointeeType->isRecordType()) {
+
+        // Exn points to the struct _Unwind_Exception header, which
+        // we have to skip past in order to reach the exception data.
+        unsigned HeaderSize =
+          CGF.CGM.getTargetCodeGenInfo().getSizeOfUnwindException();
+        AdjustedExn = CGF.Builder.CreateConstGEP1_32(Exn, HeaderSize);
+
+      // However, if we're catching a pointer-to-record type that won't
+      // work, because the personality function might have adjusted
+      // the pointer.  There's actually no way for us to fully satisfy
+      // the language/ABI contract here:  we can't use Exn because it
+      // might have the wrong adjustment, but we can't use the by-value
+      // pointer because it's off by a level of abstraction.
+      //
+      // The current solution is to dump the adjusted pointer into an
+      // alloca, which breaks language semantics (because changing the
+      // pointer doesn't change the exception) but at least works.
+      // The better solution would be to filter out non-exact matches
+      // and rethrow them, but this is tricky because the rethrow
+      // really needs to be catchable by other sites at this landing
+      // pad.  The best solution is to fix the personality function.
+      } else {
+        // Pull the pointer for the reference type off.
+        const llvm::Type *PtrTy =
+          cast<llvm::PointerType>(LLVMCatchTy)->getElementType();
+
+        // Create the temporary and write the adjusted pointer into it.
+        llvm::Value *ExnPtrTmp = CGF.CreateTempAlloca(PtrTy, "exn.byref.tmp");
+        llvm::Value *Casted = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy);
+        CGF.Builder.CreateStore(Casted, ExnPtrTmp);
+
+        // Bind the reference to the temporary.
+        AdjustedExn = ExnPtrTmp;
+      }
+    }
+
     llvm::Value *ExnCast =
       CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref");
     CGF.Builder.CreateStore(ExnCast, ParamAddr);

Modified: cfe/trunk/lib/CodeGen/TargetInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.h?rev=108944&r1=108943&r2=108944&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/TargetInfo.h (original)
+++ cfe/trunk/lib/CodeGen/TargetInfo.h Tue Jul 20 17:17:55 2010
@@ -47,6 +47,16 @@
     virtual void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
                                      CodeGen::CodeGenModule &M) const { }
 
+    /// Determines the size of struct _Unwind_Exception on this platform,
+    /// in 8-bit units.  The Itanium ABI defines this as:
+    ///   struct _Unwind_Exception {
+    ///     uint64 exception_class;
+    ///     _Unwind_Exception_Cleanup_Fn exception_cleanup;
+    ///     uint64 private_1;
+    ///     uint64 private_2;
+    ///   };
+    unsigned getSizeOfUnwindException() const { return 32; }
+
     /// Controls whether __builtin_extend_pointer should sign-extend
     /// pointers to uint64_t or zero-extend them (the default).  Has
     /// no effect for targets:

Modified: cfe/trunk/test/CodeGenCXX/eh.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/eh.cpp?rev=108944&r1=108943&r2=108944&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/eh.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/eh.cpp Tue Jul 20 17:17:55 2010
@@ -249,3 +249,44 @@
     // CHECK: call void @_ZN6test101AD1Ev(
   }
 }
+
+// __cxa_begin_catch returns pointers by value, even when catching by reference
+// <rdar://problem/8212123>
+namespace test11 {
+  void opaque();
+
+  // CHECK: define void @_ZN6test113fooEv()
+  void foo() {
+    try {
+      // CHECK:      invoke void @_ZN6test116opaqueEv()
+      opaque();
+    } catch (int**&p) {
+      // CHECK:      [[EXN:%.*]] = load i8**
+      // CHECK-NEXT: call i8* @__cxa_begin_catch(i8* [[EXN]]) nounwind
+      // CHECK-NEXT: [[ADJ1:%.*]] = getelementptr i8* [[EXN]], i32 32
+      // CHECK-NEXT: [[ADJ2:%.*]] = bitcast i8* [[ADJ1]] to i32***
+      // CHECK-NEXT: store i32*** [[ADJ2]], i32**** [[P:%.*]]
+      // CHECK-NEXT: call void @__cxa_end_catch() nounwind
+    }
+  }
+
+  struct A {};
+
+  // CHECK: define void @_ZN6test113barEv()
+  void bar() {
+    try {
+      // CHECK:      [[EXNSLOT:%.*]] = alloca i8*
+      // CHECK-NEXT: [[P:%.*]] = alloca [[A:%.*]]**,
+      // CHECK-NEXT: [[TMP:%.*]] = alloca [[A]]*
+      // CHECK-NEXT: invoke void @_ZN6test116opaqueEv()
+      opaque();
+    } catch (A*&p) {
+      // CHECK:      [[EXN:%.*]] = load i8** [[EXNSLOT]]
+      // CHECK-NEXT: [[ADJ1:%.*]] = call i8* @__cxa_begin_catch(i8* [[EXN]]) nounwind
+      // CHECK-NEXT: [[ADJ2:%.*]] = bitcast i8* [[ADJ1]] to [[A]]*
+      // CHECK-NEXT: store [[A]]* [[ADJ2]], [[A]]** [[TMP]]
+      // CHECK-NEXT: store [[A]]** [[TMP]], [[A]]*** [[P]]
+      // CHECK-NEXT: call void @__cxa_end_catch() nounwind
+    }
+  }
+}





More information about the cfe-commits mailing list