From 5406614be90f38e62797e9dd8220f0bf568425a3 Mon Sep 17 00:00:00 2001 From: Mike Stump Date: Tue, 1 Dec 2009 03:41:18 +0000 Subject: [PATCH] Much work on try/catch statement. WIP. Highlights include: Fix __cxa_begin_catch so it now returns a value. Added getUnwindResumeOrRethrowFn helper to build up calls to _Unwind_Resume_or_Rethrow. Broke out object copying code into CopyObject from EmitCXXThrowExpr. Built up another version of CopyObject that can copy from memory for use in the catch parameter code. RTTI generation for type matching for catch. Code to check for the type match for catch. Code to generate the catch parameter, WIP, need make sure references and pointers and copy ctors work. llvm-svn: 90205 --- clang/lib/CodeGen/CGException.cpp | 319 +++++++++++++++++++++++------- 1 file changed, 250 insertions(+), 69 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index cf84b3fcd247..420e275becd1 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -55,14 +55,13 @@ static llvm::Constant *getReThrowFn(CodeGenFunction &CGF) { } static llvm::Constant *getBeginCatchFn(CodeGenFunction &CGF) { - // void __cxa_begin_catch (); + // void* __cxa_begin_catch (); const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); std::vector Args(1, Int8PtrTy); const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), Args, - false); + llvm::FunctionType::get(Int8PtrTy, Args, false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_begin_catch"); } @@ -76,6 +75,108 @@ static llvm::Constant *getEndCatchFn(CodeGenFunction &CGF) { return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_end_catch"); } +// FIXME: Eventually this will all go into the backend. Set from the target for +// now. +static int using_sjlj_exceptions = 0; + +static llvm::Constant *getUnwindResumeOrRethrowFn(CodeGenFunction &CGF) { + const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + std::vector Args(1, Int8PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), Args, + false); + + if (using_sjlj_exceptions) + return CGF.CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume"); + return CGF.CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume_or_Rethrow"); +} + +// CopyObject - Utility to copy an object. Calls copy constructor as necessary. +// N is casted to the right type. +static void CopyObject(CodeGenFunction &CGF, const Expr *E, llvm::Value *N) { + QualType ObjectType = E->getType(); + + // Store the throw exception in the exception object. + if (!CGF.hasAggregateLLVMType(ObjectType)) { + llvm::Value *Value = CGF.EmitScalarExpr(E); + const llvm::Type *ValuePtrTy = Value->getType()->getPointerTo(0); + + CGF.Builder.CreateStore(Value, CGF.Builder.CreateBitCast(N, ValuePtrTy)); + } else { + const llvm::Type *Ty = CGF.ConvertType(ObjectType)->getPointerTo(0); + const CXXRecordDecl *RD; + RD = cast(ObjectType->getAs()->getDecl()); + llvm::Value *This = CGF.Builder.CreateBitCast(N, Ty); + if (RD->hasTrivialCopyConstructor()) { + CGF.EmitAggExpr(E, This, false); + } else if (CXXConstructorDecl *CopyCtor + = RD->getCopyConstructor(CGF.getContext(), 0)) { + // FIXME: region management + llvm::Value *Src = CGF.EmitLValue(E).getAddress(); + + // Stolen from EmitClassAggrMemberwiseCopy + llvm::Value *Callee = CGF.CGM.GetAddrOfCXXConstructor(CopyCtor, + Ctor_Complete); + CallArgList CallArgs; + CallArgs.push_back(std::make_pair(RValue::get(This), + CopyCtor->getThisType(CGF.getContext()))); + + // Push the Src ptr. + CallArgs.push_back(std::make_pair(RValue::get(Src), + CopyCtor->getParamDecl(0)->getType())); + QualType ResultType = + CopyCtor->getType()->getAs()->getResultType(); + CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + Callee, CallArgs, CopyCtor); + // FIXME: region management + } else + CGF.ErrorUnsupported(E, "uncopyable object"); + } +} + +// CopyObject - Utility to copy an object. Calls copy constructor as necessary. +// N is casted to the right type. +static void CopyObject(CodeGenFunction &CGF, QualType ObjectType, + llvm::Value *E, llvm::Value *N) { + // Store the throw exception in the exception object. + if (!CGF.hasAggregateLLVMType(ObjectType)) { + llvm::Value *Value = E; + const llvm::Type *ValuePtrTy = Value->getType()->getPointerTo(0); + + CGF.Builder.CreateStore(Value, CGF.Builder.CreateBitCast(N, ValuePtrTy)); + } else { + const llvm::Type *Ty = CGF.ConvertType(ObjectType)->getPointerTo(0); + const CXXRecordDecl *RD; + RD = cast(ObjectType->getAs()->getDecl()); + llvm::Value *This = CGF.Builder.CreateBitCast(N, Ty); + if (RD->hasTrivialCopyConstructor()) { + CGF.EmitAggregateCopy(This, E, ObjectType); + } else if (CXXConstructorDecl *CopyCtor + = RD->getCopyConstructor(CGF.getContext(), 0)) { + // FIXME: region management + llvm::Value *Src = E; + + // Stolen from EmitClassAggrMemberwiseCopy + llvm::Value *Callee = CGF.CGM.GetAddrOfCXXConstructor(CopyCtor, + Ctor_Complete); + CallArgList CallArgs; + CallArgs.push_back(std::make_pair(RValue::get(This), + CopyCtor->getThisType(CGF.getContext()))); + + // Push the Src ptr. + CallArgs.push_back(std::make_pair(RValue::get(Src), + CopyCtor->getParamDecl(0)->getType())); + QualType ResultType = + CopyCtor->getType()->getAs()->getResultType(); + CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(ResultType, CallArgs), + Callee, CallArgs, CopyCtor); + // FIXME: region management + } else + llvm::llvm_unreachable("uncopyable object"); + } +} + void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { if (!E->getSubExpr()) { Builder.CreateCall(getReThrowFn(*this))->setDoesNotReturn(); @@ -103,42 +204,7 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { llvm::ConstantInt::get(SizeTy, TypeSize), "exception"); - // Store the throw exception in the exception object. - if (!hasAggregateLLVMType(ThrowType)) { - llvm::Value *Value = EmitScalarExpr(E->getSubExpr()); - const llvm::Type *ValuePtrTy = Value->getType()->getPointerTo(0); - - Builder.CreateStore(Value, Builder.CreateBitCast(ExceptionPtr, ValuePtrTy)); - } else { - const llvm::Type *Ty = ConvertType(ThrowType)->getPointerTo(0); - const CXXRecordDecl *RD; - RD = cast(ThrowType->getAs()->getDecl()); - llvm::Value *This = Builder.CreateBitCast(ExceptionPtr, Ty); - if (RD->hasTrivialCopyConstructor()) { - EmitAggExpr(E->getSubExpr(), This, false); - } else if (CXXConstructorDecl *CopyCtor - = RD->getCopyConstructor(getContext(), 0)) { - // FIXME: region management - llvm::Value *Src = EmitLValue(E->getSubExpr()).getAddress(); - - // Stolen from EmitClassAggrMemberwiseCopy - llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(CopyCtor, - Ctor_Complete); - CallArgList CallArgs; - CallArgs.push_back(std::make_pair(RValue::get(This), - CopyCtor->getThisType(getContext()))); - - // Push the Src ptr. - CallArgs.push_back(std::make_pair(RValue::get(Src), - CopyCtor->getParamDecl(0)->getType())); - QualType ResultType = - CopyCtor->getType()->getAs()->getResultType(); - EmitCall(CGM.getTypes().getFunctionInfo(ResultType, CallArgs), - Callee, CallArgs, CopyCtor); - // FIXME: region management - } else - ErrorUnsupported(E, "throw expression with copy ctor"); - } + CopyObject(*this, E->getSubExpr(), ExceptionPtr); // Now throw the exception. const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); @@ -155,13 +221,16 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { } void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { - // FIXME: We need to do more here. +#if 1 EmitStmt(S.getTryBlock()); - getBeginCatchFn(*this); - getEndCatchFn(*this); - -#if 0 - // WIP. Can't enable until the basic structure is correct. + if (0) { + getBeginCatchFn(*this); + getEndCatchFn(*this); + getUnwindResumeOrRethrowFn(*this); + CopyObject(*this, QualType(), 0, 0); + } +#else + // FIXME: The below is still just a sketch of the code we need. // Pointer to the personality function llvm::Constant *Personality = CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getInt32Ty @@ -170,15 +239,18 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { "__gxx_personality_v0"); Personality = llvm::ConstantExpr::getBitCast(Personality, PtrToInt8Ty); - llvm::BasicBlock *TryBlock = createBasicBlock("try"); llvm::BasicBlock *PrevLandingPad = getInvokeDest(); llvm::BasicBlock *TryHandler = createBasicBlock("try.handler"); - llvm::BasicBlock *CatchInCatch = createBasicBlock("catch.rethrow"); +#if 0 llvm::BasicBlock *FinallyBlock = createBasicBlock("finally"); +#endif + llvm::BasicBlock *FinallyRethrow = createBasicBlock("finally.throw"); llvm::BasicBlock *FinallyEnd = createBasicBlock("finally.end"); +#if 0 // Push an EH context entry, used for handling rethrows. PushCleanupBlock(FinallyBlock); +#endif // Emit the statements in the try {} block setInvokeDest(TryHandler); @@ -196,58 +268,167 @@ void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { Int8Ty = llvm::Type::getInt8Ty(VMContext); // C string type. Used in lots of places. PtrToInt8Ty = llvm::PointerType::getUnqual(Int8Ty); - llvm::Constant *NULLPtr = llvm::ConstantPointerNull::get(PtrToInt8Ty); - llvm::SmallVector ESelArgs; + llvm::Constant *Null = llvm::ConstantPointerNull::get(PtrToInt8Ty); + llvm::SmallVector SelectorArgs; llvm::Value *llvm_eh_exception = CGM.getIntrinsic(llvm::Intrinsic::eh_exception); llvm::Value *llvm_eh_selector = CGM.getIntrinsic(llvm::Intrinsic::eh_selector); + llvm::Value *llvm_eh_typeid_for = + CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for); // Exception object llvm::Value *Exc = Builder.CreateCall(llvm_eh_exception, "exc"); + llvm::Value *RethrowPtr = CreateTempAlloca(Exc->getType(), "_rethrow"); - ESelArgs.push_back(Exc); - ESelArgs.push_back(Personality); + SelectorArgs.push_back(Exc); + SelectorArgs.push_back(Personality); + bool HasCatchAll = false; for (unsigned i = 0; igetExceptionDecl(); - if (VD) { -#if 0 - // FIXME: Handle type matching. - llvm::Value *EHType = 0; - ESelArgs.push_back(EHType); -#endif + VarDecl *CatchParam = C->getExceptionDecl(); + if (CatchParam) { + llvm::Value *EHType = CGM.GenerateRtti(C->getCaughtType().getNonReferenceType()); + SelectorArgs.push_back(EHType); } else { // null indicates catch all - ESelArgs.push_back(NULLPtr); + SelectorArgs.push_back(Null); + HasCatchAll = true; } } - // Find which handler was matched. - llvm::Value *ESelector - = Builder.CreateCall(llvm_eh_selector, ESelArgs.begin(), ESelArgs.end(), - "selector"); + // We use a cleanup unless there was already a catch all. + if (!HasCatchAll) { + SelectorArgs.push_back(Null); + } + // Find which handler was matched. + llvm::Value *Selector + = Builder.CreateCall(llvm_eh_selector, SelectorArgs.begin(), + SelectorArgs.end(), "selector"); for (unsigned i = 0; igetExceptionDecl(); + Stmt *CatchBody = C->getHandlerBlock(); + + llvm::BasicBlock *Next = 0; + + if (SelectorArgs[i+2] != Null) { + llvm::BasicBlock *Match = createBasicBlock("match"); + Next = createBasicBlock("catch.next"); + const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); + llvm::Value *Id + = Builder.CreateCall(llvm_eh_typeid_for, + Builder.CreateBitCast(SelectorArgs[i+2], + Int8PtrTy)); + Builder.CreateCondBr(Builder.CreateICmpEQ(Selector, Id), + Match, Next); + EmitBlock(Match); + } + + llvm::BasicBlock *MatchEnd = createBasicBlock("match.end"); + llvm::BasicBlock *MatchHandler = createBasicBlock("match.handler"); + + PushCleanupBlock(MatchEnd); + setInvokeDest(MatchHandler); + + llvm::Value *ExcObject = Builder.CreateCall(getBeginCatchFn(*this), Exc); + + // Bind the catch parameter if it exists. + if (CatchParam) { + QualType CatchType = CatchParam->getType().getNonReferenceType(); + if (!CatchType.getTypePtr()->isPointerType()) + CatchType = getContext().getPointerType(CatchType); + ExcObject = + Builder.CreateBitCast(ExcObject, ConvertType(CatchType)); + // CatchParam is a ParmVarDecl because of the grammar + // construction used to handle this, but for codegen purposes + // we treat this as a local decl. + EmitLocalBlockVarDecl(*CatchParam); +#if 0 + // FIXME: objects with ctors, references + Builder.CreateStore(ExcObject, GetAddrOfLocalVar(CatchParam)); +#else + CopyObject(*this, CatchParam->getType().getNonReferenceType(), + ExcObject, GetAddrOfLocalVar(CatchParam)); +#endif + } + + EmitStmt(CatchBody); + EmitBranchThroughCleanup(FinallyEnd); + + EmitBlock(MatchHandler); + + llvm::Value *Exc = Builder.CreateCall(llvm_eh_exception, "exc"); + // We are required to emit this call to satisfy LLVM, even + // though we don't use the result. + llvm::SmallVector Args; + Args.push_back(Exc); + Args.push_back(Personality); + Args.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), + 0)); + Builder.CreateCall(llvm_eh_selector, Args.begin(), Args.end()); + Builder.CreateStore(Exc, RethrowPtr); + EmitBranchThroughCleanup(FinallyRethrow); + + CodeGenFunction::CleanupBlockInfo Info = PopCleanupBlock(); + + EmitBlock(MatchEnd); + + // Unfortunately, we also have to generate another EH frame here + // in case this throws. + llvm::BasicBlock *MatchEndHandler = + createBasicBlock("match.end.handler"); + llvm::BasicBlock *Cont = createBasicBlock("myinvoke.cont"); + Builder.CreateInvoke(getEndCatchFn(*this), + Cont, MatchEndHandler, + Args.begin(), Args.begin()); + + EmitBlock(Cont); + if (Info.SwitchBlock) + EmitBlock(Info.SwitchBlock); + if (Info.EndBlock) + EmitBlock(Info.EndBlock); + + EmitBlock(MatchEndHandler); + Exc = Builder.CreateCall(llvm_eh_exception, "exc"); + // We are required to emit this call to satisfy LLVM, even + // though we don't use the result. + Args.clear(); + Args.push_back(Exc); + Args.push_back(Personality); + Args.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), + 0)); + Builder.CreateCall(llvm_eh_selector, Args.begin(), Args.end()); + Builder.CreateStore(Exc, RethrowPtr); + EmitBranchThroughCleanup(FinallyRethrow); + + if (Next) + EmitBlock(Next); + } + if (!HasCatchAll) + EmitBranchThroughCleanup(FinallyRethrow); CodeGenFunction::CleanupBlockInfo Info = PopCleanupBlock(); setInvokeDest(PrevLandingPad); +#if 0 EmitBlock(FinallyBlock); -#if 0 + if (Info.SwitchBlock) + EmitBlock(Info.SwitchBlock); + if (Info.EndBlock) + EmitBlock(Info.EndBlock); + // Branch around the rethrow code. EmitBranch(FinallyEnd); +#endif EmitBlock(FinallyRethrow); - Builder.CreateCall(RethrowFn, Builder.CreateLoad(RethrowPtr)); + Builder.CreateCall(getUnwindResumeOrRethrowFn(*this), + Builder.CreateLoad(RethrowPtr)); Builder.CreateUnreachable(); -#endif EmitBlock(FinallyEnd); #endif