From 204b075fcc47c3f2aa7276dfba9b42eb25840b53 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 20 Jul 2010 22:17:55 +0000 Subject: [PATCH] Fix the IR generation for catching pointers by references. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108944 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGException.cpp | 51 +++++++++++++++++++++++++++++++++++-- lib/CodeGen/TargetInfo.h | 10 ++++++++ test/CodeGenCXX/eh.cpp | 41 +++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 615bf5802f..4649b90156 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -18,6 +18,7 @@ #include "CodeGenFunction.h" #include "CGException.h" +#include "TargetInfo.h" using namespace clang; using namespace CodeGen; @@ -1099,11 +1100,57 @@ static void InitCatchParam(CodeGenFunction &CGF, // If we're catching by reference, we can just cast the object // pointer to the appropriate pointer. if (isa(CatchType)) { - bool EndCatchMightThrow = cast(CatchType)->getPointeeType() - ->isRecordType(); + QualType CaughtType = cast(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(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(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); diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index f0a78243ff..9d4cf16103 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/lib/CodeGen/TargetInfo.h @@ -47,6 +47,16 @@ namespace clang { 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: diff --git a/test/CodeGenCXX/eh.cpp b/test/CodeGenCXX/eh.cpp index 6d79c3e17b..1cf3d12389 100644 --- a/test/CodeGenCXX/eh.cpp +++ b/test/CodeGenCXX/eh.cpp @@ -249,3 +249,44 @@ namespace test10 { // CHECK: call void @_ZN6test101AD1Ev( } } + +// __cxa_begin_catch returns pointers by value, even when catching by reference +// +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 + } + } +} -- 2.40.0