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
This commit is contained in:
Mike Stump 2009-12-01 03:41:18 +00:00
parent 8f184a31d0
commit 5406614be9
1 changed files with 250 additions and 69 deletions

View File

@ -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<const llvm::Type*> 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<const llvm::Type*> 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<CXXRecordDecl>(ObjectType->getAs<RecordType>()->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<FunctionType>()->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<CXXRecordDecl>(ObjectType->getAs<RecordType>()->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<FunctionType>()->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<CXXRecordDecl>(ThrowType->getAs<RecordType>()->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<FunctionType>()->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<llvm::Value*, 8> ESelArgs;
llvm::Constant *Null = llvm::ConstantPointerNull::get(PtrToInt8Ty);
llvm::SmallVector<llvm::Value*, 8> 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; i<S.getNumHandlers(); ++i) {
const CXXCatchStmt *C = S.getHandler(i);
VarDecl *VD = C->getExceptionDecl();
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; i<S.getNumHandlers(); ++i) {
const CXXCatchStmt *C = S.getHandler(i);
getBeginCatchFn(*this);
getEndCatchFn(*this);
}
VarDecl *CatchParam = C->getExceptionDecl();
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<llvm::Value*, 8> 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