Restore r184205 and associated commits (after commit of r185290)

This allows clang to use the backend parameter attribute 'returned' when generating 'this'-returning constructors and destructors in ARM and MSVC C++ ABIs.

llvm-svn: 185291
This commit is contained in:
Stephen Lin 2013-06-30 20:40:16 +00:00
parent 2e551adcd9
commit 9dc6eef755
18 changed files with 263 additions and 276 deletions

View File

@ -97,8 +97,12 @@ public:
return *MangleCtx;
}
/// Returns true if the given instance method is one of the
/// kinds that the ABI says returns 'this'.
/// Returns true if the given constructor or destructor is one of the
/// kinds that the ABI says returns 'this' (only applies when called
/// non-virtually for destructors).
///
/// There currently is no way to indicate if a destructor returns 'this'
/// when called virtually, and code generation does not support the case.
virtual bool HasThisReturn(GlobalDecl GD) const { return false; }
/// Returns true if the given record type should be returned indirectly.
@ -214,10 +218,10 @@ public:
const CXXRecordDecl *BaseClassDecl) = 0;
/// Build the signature of the given constructor variant by adding
/// any required parameters. For convenience, ResTy has been
/// initialized to 'void', and ArgTys has been initialized with the
/// type of 'this' (although this may be changed by the ABI) and
/// will have the formal parameters added to it afterwards.
/// any required parameters. For convenience, ArgTys has been initialized
/// with the type of 'this' and ResTy has been initialized with the type of
/// 'this' if HasThisReturn(GlobalDecl(Ctor, T)) is true or 'void' otherwise
/// (although both may be changed by the ABI).
///
/// If there are ever any ABIs where the implicit parameters are
/// intermixed with the formal parameters, we can address those
@ -231,9 +235,10 @@ public:
const CXXRecordDecl *RD);
/// Build the signature of the given destructor variant by adding
/// any required parameters. For convenience, ResTy has been
/// initialized to 'void' and ArgTys has been initialized with the
/// type of 'this' (although this may be changed by the ABI).
/// any required parameters. For convenience, ArgTys has been initialized
/// with the type of 'this' and ResTy has been initialized with the type of
/// 'this' if HasThisReturn(GlobalDecl(Dtor, T)) is true or 'void' otherwise
/// (although both may be changed by the ABI).
virtual void BuildDestructorSignature(const CXXDestructorDecl *Dtor,
CXXDtorType T,
CanQualType &ResTy,
@ -244,7 +249,8 @@ public:
/// possibly some extra data for constructors and destructors.
///
/// ABIs may also choose to override the return type, which has been
/// initialized with the formal return type of the function.
/// initialized with the type of 'this' if HasThisReturn(CGF.CurGD) is true or
/// the formal return type of the function otherwise.
virtual void BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) = 0;
@ -253,21 +259,20 @@ public:
virtual void EmitInstanceFunctionProlog(CodeGenFunction &CGF) = 0;
/// Emit the constructor call. Return the function that is called.
virtual llvm::Value *EmitConstructorCall(CodeGenFunction &CGF,
virtual void EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D,
CXXCtorType Type, bool ForVirtualBase,
bool Delegating,
CXXCtorType Type,
bool ForVirtualBase, bool Delegating,
llvm::Value *This,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd) = 0;
/// Emit the ABI-specific virtual destructor call.
virtual RValue EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
ReturnValueSlot ReturnValue,
llvm::Value *This) = 0;
virtual void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
llvm::Value *This) = 0;
/// Emit any tables needed to implement virtual inheritance. For Itanium,
/// this emits virtual table tables. For the MSVC++ ABI, this emits virtual

View File

@ -200,7 +200,10 @@ CodeGenTypes::arrangeCXXConstructorDeclaration(const CXXConstructorDecl *D,
CXXCtorType ctorKind) {
SmallVector<CanQualType, 16> argTypes;
argTypes.push_back(GetThisType(Context, D->getParent()));
CanQualType resultType = Context.VoidTy;
GlobalDecl GD(D, ctorKind);
CanQualType resultType =
TheCXXABI.HasThisReturn(GD) ? argTypes.front() : Context.VoidTy;
TheCXXABI.BuildConstructorSignature(D, ctorKind, resultType, argTypes);
@ -225,7 +228,10 @@ CodeGenTypes::arrangeCXXDestructor(const CXXDestructorDecl *D,
CXXDtorType dtorKind) {
SmallVector<CanQualType, 2> argTypes;
argTypes.push_back(GetThisType(Context, D->getParent()));
CanQualType resultType = Context.VoidTy;
GlobalDecl GD(D, dtorKind);
CanQualType resultType =
TheCXXABI.HasThisReturn(GD) ? argTypes.front() : Context.VoidTy;
TheCXXABI.BuildDestructorSignature(D, dtorKind, resultType, argTypes);
@ -1633,18 +1639,6 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
return store;
}
/// Check whether 'this' argument of a callsite matches 'this' of the caller.
static bool checkThisPointer(llvm::Value *ThisArg, llvm::Value *This) {
if (ThisArg == This)
return true;
// Check whether ThisArg is a bitcast of This.
llvm::BitCastInst *Bitcast;
if ((Bitcast = dyn_cast<llvm::BitCastInst>(ThisArg)) &&
Bitcast->getOperand(0) == This)
return true;
return false;
}
void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
bool EmitRetDbgLoc) {
// Functions with no result always return void.
@ -1741,19 +1735,6 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
llvm_unreachable("Invalid ABI kind for return argument");
}
// If this function returns 'this', the last instruction is a CallInst
// that returns 'this', and 'this' argument of the CallInst points to
// the same object as CXXThisValue, use the return value from the CallInst.
// We will not need to keep 'this' alive through the callsite. It also enables
// optimizations in the backend, such as tail call optimization.
if (CalleeWithThisReturn && CGM.getCXXABI().HasThisReturn(CurGD)) {
llvm::BasicBlock *IP = Builder.GetInsertBlock();
llvm::CallInst *Callsite;
if (!IP->empty() && (Callsite = dyn_cast<llvm::CallInst>(&IP->back())) &&
Callsite->getCalledFunction() == CalleeWithThisReturn &&
checkThisPointer(Callsite->getOperand(0), CXXThisValue))
RV = Builder.CreateBitCast(Callsite, RetAI.getCoerceToType());
}
llvm::Instruction *Ret = RV ? Builder.CreateRet(RV) : Builder.CreateRetVoid();
if (!RetDbgLoc.isUnknown())
Ret->setDebugLoc(RetDbgLoc);

View File

@ -1662,11 +1662,8 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
}
// Non-trivial constructors are handled in an ABI-specific manner.
llvm::Value *Callee = CGM.getCXXABI().EmitConstructorCall(*this, D, Type,
ForVirtualBase, Delegating, This, ArgBeg, ArgEnd);
if (CGM.getCXXABI().HasThisReturn(CurGD) &&
CGM.getCXXABI().HasThisReturn(GlobalDecl(D, Type)))
CalleeWithThisReturn = Callee;
CGM.getCXXABI().EmitConstructorCall(*this, D, Type, ForVirtualBase,
Delegating, This, ArgBeg, ArgEnd);
}
void
@ -1758,9 +1755,6 @@ CodeGenFunction::EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor,
llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(Ctor, CtorType);
EmitCall(CGM.getTypes().arrangeCXXConstructorDeclaration(Ctor, CtorType),
Callee, ReturnValueSlot(), DelegateArgs, Ctor);
if (CGM.getCXXABI().HasThisReturn(CurGD) &&
CGM.getCXXABI().HasThisReturn(GlobalDecl(Ctor, CtorType)))
CalleeWithThisReturn = Callee;
}
namespace {
@ -1827,9 +1821,6 @@ void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD,
EmitCXXMemberCall(DD, SourceLocation(), Callee, ReturnValueSlot(), This,
VTT, getContext().getPointerType(getContext().VoidPtrTy),
0, 0);
if (CGM.getCXXABI().HasThisReturn(CurGD) &&
CGM.getCXXABI().HasThisReturn(GlobalDecl(DD, Type)))
CalleeWithThisReturn = Callee;
}
namespace {

View File

@ -280,16 +280,15 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
// We also don't emit a virtual call if the base expression has a record type
// because then we know what the type is.
bool UseVirtualCall = CanUseVirtualCall && !DevirtualizedMethod;
llvm::Value *Callee;
if (const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(MD)) {
assert(CE->arg_begin() == CE->arg_end() &&
"Destructor shouldn't have explicit parameters");
assert(ReturnValue.isNull() && "Destructor shouldn't have return value");
if (UseVirtualCall) {
assert(CE->arg_begin() == CE->arg_end() &&
"Virtual destructor shouldn't have explicit parameters");
return CGM.getCXXABI().EmitVirtualDestructorCall(*this, Dtor,
Dtor_Complete,
CE->getExprLoc(),
ReturnValue, This);
CGM.getCXXABI().EmitVirtualDestructorCall(*this, Dtor, Dtor_Complete,
CE->getExprLoc(), This);
} else {
if (getLangOpts().AppleKext &&
MD->isVirtual() &&
@ -302,12 +301,16 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
cast<CXXDestructorDecl>(DevirtualizedMethod);
Callee = CGM.GetAddrOfFunction(GlobalDecl(DDtor, Dtor_Complete), Ty);
}
EmitCXXMemberCall(MD, CE->getExprLoc(), Callee, ReturnValue, This,
/*ImplicitParam=*/0, QualType(), 0, 0);
}
} else if (const CXXConstructorDecl *Ctor =
dyn_cast<CXXConstructorDecl>(MD)) {
return RValue::get(0);
}
if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) {
Callee = CGM.GetAddrOfFunction(GlobalDecl(Ctor, Ctor_Complete), Ty);
} else if (UseVirtualCall) {
Callee = BuildVirtualCall(MD, This, Ty);
Callee = BuildVirtualCall(MD, This, Ty);
} else {
if (getLangOpts().AppleKext &&
MD->isVirtual() &&
@ -1413,8 +1416,7 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
// FIXME: Provide a source location here.
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
CGF.CGM.getCXXABI().EmitVirtualDestructorCall(CGF, Dtor, DtorType,
SourceLocation(),
ReturnValueSlot(), Ptr);
SourceLocation(), Ptr);
if (UseGlobalDelete) {
CGF.PopCleanupBlock();

View File

@ -287,8 +287,9 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
GlobalDecl GD, const ThunkInfo &Thunk) {
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
QualType ResultType = FPT->getResultType();
QualType ThisType = MD->getThisType(getContext());
QualType ResultType =
CGM.getCXXABI().HasThisReturn(GD) ? ThisType : FPT->getResultType();
FunctionArgList FunctionArgs;

View File

@ -42,8 +42,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
AutoreleaseResult(false), BlockInfo(0), BlockPointer(0),
LambdaThisCaptureField(0), NormalCleanupDest(0), NextCleanupDestIndex(1),
FirstBlockInfo(0), EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0),
DebugInfo(0), DisableDebugInfo(false), CalleeWithThisReturn(0),
DidCallStackSave(false),
DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false),
IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0),
NumReturnExprs(0), NumSimpleReturnExprs(0),
CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0),
@ -662,8 +661,12 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
QualType ResTy = FD->getResultType();
CurGD = GD;
if (isa<CXXMethodDecl>(FD) && cast<CXXMethodDecl>(FD)->isInstance())
const CXXMethodDecl *MD;
if ((MD = dyn_cast<CXXMethodDecl>(FD)) && MD->isInstance()) {
if (CGM.getCXXABI().HasThisReturn(GD))
ResTy = MD->getThisType(getContext());
CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResTy, Args);
}
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i)
Args.push_back(FD->getParamDecl(i));
@ -672,10 +675,6 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
if (Stmt *Body = FD->getBody()) BodyRange = Body->getSourceRange();
CurEHLocation = BodyRange.getEnd();
// CalleeWithThisReturn keeps track of the last callee inside this function
// that returns 'this'. Before starting the function, we set it to null.
CalleeWithThisReturn = 0;
// Emit the standard function prologue.
StartFunction(GD, ResTy, Fn, FnInfo, Args, BodyRange.getBegin());
@ -727,9 +726,6 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
// Emit the standard function epilogue.
FinishFunction(BodyRange.getEnd());
// CalleeWithThisReturn keeps track of the last callee inside this function
// that returns 'this'. After finishing the function, we set it to null.
CalleeWithThisReturn = 0;
// If we haven't marked the function nothrow through other means, do
// a quick pass now to see if we can.

View File

@ -798,10 +798,6 @@ private:
CGDebugInfo *DebugInfo;
bool DisableDebugInfo;
/// If the current function returns 'this', use the field to keep track of
/// the callee that returns 'this'.
llvm::Value *CalleeWithThisReturn;
/// DidCallStackSave - Whether llvm.stacksave has been called. Used to avoid
/// calling llvm.stacksave for multiple VLAs in the same scope.
bool DidCallStackSave;

View File

@ -722,6 +722,14 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD,
if (!IsIncompleteFunction)
SetLLVMFunctionAttributes(FD, getTypes().arrangeGlobalDeclaration(GD), F);
if (getCXXABI().HasThisReturn(GD)) {
assert(!F->arg_empty() &&
F->arg_begin()->getType()
->canLosslesslyBitCastTo(F->getReturnType()) &&
"unexpected this return");
F->addAttribute(1, llvm::Attribute::Returned);
}
// Only a few attributes are set on declarations; these may later be
// overridden by a definition.

View File

@ -119,20 +119,17 @@ public:
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
llvm::Value *EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D,
CXXCtorType Type, bool ForVirtualBase,
bool Delegating,
void EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D, CXXCtorType Type,
bool ForVirtualBase, bool Delegating,
llvm::Value *This,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd);
RValue EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
ReturnValueSlot ReturnValue,
llvm::Value *This);
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType, SourceLocation CallLoc,
llvm::Value *This);
void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage,
const CXXRecordDecl *RD);
@ -170,21 +167,11 @@ class ARMCXXABI : public ItaniumCXXABI {
public:
ARMCXXABI(CodeGen::CodeGenModule &CGM) : ItaniumCXXABI(CGM, /*ARM*/ true) {}
void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
CXXCtorType T,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys);
void BuildDestructorSignature(const CXXDestructorDecl *Dtor,
CXXDtorType T,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys);
void BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params);
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
bool HasThisReturn(GlobalDecl GD) const {
return (isa<CXXConstructorDecl>(GD.getDecl()) || (
isa<CXXDestructorDecl>(GD.getDecl()) &&
GD.getDtorType() != Dtor_Deleting));
}
void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResTy);
@ -196,15 +183,6 @@ public:
QualType ElementType);
llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, llvm::Value *allocPtr,
CharUnits cookieSize);
/// \brief Returns true if the given instance method is one of the
/// kinds that the ARM ABI says returns 'this'.
bool HasThisReturn(GlobalDecl GD) const {
const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(GD.getDecl());
if (!MD) return false;
return ((isa<CXXDestructorDecl>(MD) && GD.getDtorType() != Dtor_Deleting) ||
(isa<CXXConstructorDecl>(MD)));
}
};
}
@ -759,22 +737,14 @@ void ItaniumCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor,
SmallVectorImpl<CanQualType> &ArgTys) {
ASTContext &Context = getContext();
// 'this' is already there.
// 'this' parameter is already there, as well as 'this' return if
// HasThisReturn(GlobalDecl(Ctor, Type)) is true
// Check if we need to add a VTT parameter (which has type void **).
if (Type == Ctor_Base && Ctor->getParent()->getNumVBases() != 0)
ArgTys.push_back(Context.getPointerType(Context.VoidPtrTy));
}
/// The ARM ABI does the same as the Itanium ABI, but returns 'this'.
void ARMCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor,
CXXCtorType Type,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys) {
ItaniumCXXABI::BuildConstructorSignature(Ctor, Type, ResTy, ArgTys);
ResTy = ArgTys[0];
}
/// The generic ABI passes 'this', plus a VTT if it's destroying a
/// base subobject.
void ItaniumCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor,
@ -783,25 +753,14 @@ void ItaniumCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor,
SmallVectorImpl<CanQualType> &ArgTys) {
ASTContext &Context = getContext();
// 'this' is already there.
// 'this' parameter is already there, as well as 'this' return if
// HasThisReturn(GlobalDecl(Dtor, Type)) is true
// Check if we need to add a VTT parameter (which has type void **).
if (Type == Dtor_Base && Dtor->getParent()->getNumVBases() != 0)
ArgTys.push_back(Context.getPointerType(Context.VoidPtrTy));
}
/// The ARM ABI does the same as the Itanium ABI, but returns 'this'
/// for non-deleting destructors.
void ARMCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor,
CXXDtorType Type,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys) {
ItaniumCXXABI::BuildDestructorSignature(Dtor, Type, ResTy, ArgTys);
if (Type != Dtor_Deleting)
ResTy = ArgTys[0];
}
void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) {
@ -825,16 +784,6 @@ void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
}
}
void ARMCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) {
ItaniumCXXABI::BuildInstanceFunctionParams(CGF, ResTy, Params);
// Return 'this' from certain constructors and destructors.
if (HasThisReturn(CGF.CurGD))
ResTy = Params[0]->getType();
}
void ItaniumCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
/// Initialize the 'this' slot.
EmitThisParam(CGF);
@ -845,21 +794,23 @@ void ItaniumCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
= CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(getVTTDecl(CGF)),
"vtt");
}
}
void ARMCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
ItaniumCXXABI::EmitInstanceFunctionProlog(CGF);
/// Initialize the return slot to 'this' at the start of the
/// function.
/// If this is a function that the ABI specifies returns 'this', initialize
/// the return slot to 'this' at the start of the function.
///
/// Unlike the setting of return types, this is done within the ABI
/// implementation instead of by clients of CGCXXABI because:
/// 1) getThisValue is currently protected
/// 2) in theory, an ABI could implement 'this' returns some other way;
/// HasThisReturn only specifies a contract, not the implementation
if (HasThisReturn(CGF.CurGD))
CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue);
}
llvm::Value *ItaniumCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
void ItaniumCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D,
CXXCtorType Type, bool ForVirtualBase,
bool Delegating,
CXXCtorType Type,
bool ForVirtualBase, bool Delegating,
llvm::Value *This,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd) {
@ -869,17 +820,15 @@ llvm::Value *ItaniumCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
llvm::Value *Callee = CGM.GetAddrOfCXXConstructor(D, Type);
// FIXME: Provide a source location here.
CGF.EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This,
VTT, VTTTy, ArgBeg, ArgEnd);
return Callee;
CGF.EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(),
This, VTT, VTTTy, ArgBeg, ArgEnd);
}
RValue ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
ReturnValueSlot ReturnValue,
llvm::Value *This) {
void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
llvm::Value *This) {
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
const CGFunctionInfo *FInfo
@ -887,8 +836,8 @@ RValue ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
llvm::Value *Callee = CGF.BuildVirtualCall(Dtor, DtorType, This, Ty);
return CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValue, This,
/*ImplicitParam=*/0, QualType(), 0, 0);
CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValueSlot(), This,
/*ImplicitParam=*/0, QualType(), 0, 0);
}
void ItaniumCXXABI::EmitVirtualInheritanceTables(

View File

@ -30,6 +30,8 @@ class MicrosoftCXXABI : public CGCXXABI {
public:
MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {}
bool HasThisReturn(GlobalDecl GD) const;
bool isReturnTypeIndirect(const CXXRecordDecl *RD) const {
// Structures that are not C++03 PODs are always indirect.
return !RD->isPOD();
@ -74,20 +76,17 @@ public:
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
llvm::Value *EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D,
CXXCtorType Type, bool ForVirtualBase,
bool Delegating,
void EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D, CXXCtorType Type,
bool ForVirtualBase, bool Delegating,
llvm::Value *This,
CallExpr::const_arg_iterator ArgBeg,
CallExpr::const_arg_iterator ArgEnd);
RValue EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
ReturnValueSlot ReturnValue,
llvm::Value *This);
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType, SourceLocation CallLoc,
llvm::Value *This);
void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage,
const CXXRecordDecl *RD);
@ -130,7 +129,6 @@ public:
llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize);
static bool needThisReturn(GlobalDecl GD);
private:
llvm::Constant *getZeroInt() {
@ -314,19 +312,15 @@ MicrosoftCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF,
return CGF.Builder.CreateNSWAdd(VBPtrOffset, VBPtrToNewBase);
}
bool MicrosoftCXXABI::needThisReturn(GlobalDecl GD) {
const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
return isa<CXXConstructorDecl>(MD);
bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
return isa<CXXConstructorDecl>(GD.getDecl());
}
void MicrosoftCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor,
CXXCtorType Type,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys) {
// 'this' is already in place
// Ctor returns this ptr
ResTy = ArgTys[0];
// 'this' parameter and 'this' return are already in place
const CXXRecordDecl *Class = Ctor->getParent();
if (Class->getNumVBases()) {
@ -384,6 +378,7 @@ void MicrosoftCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys) {
// 'this' is already in place
// TODO: 'for base' flag
if (Type == Dtor_Deleting) {
@ -404,9 +399,6 @@ void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) {
BuildThisParam(CGF, Params);
if (needThisReturn(CGF.CurGD)) {
ResTy = Params[0]->getType();
}
ASTContext &Context = getContext();
const CXXMethodDecl *MD = cast<CXXMethodDecl>(CGF.CurGD.getDecl());
@ -431,9 +423,17 @@ void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
EmitThisParam(CGF);
if (needThisReturn(CGF.CurGD)) {
/// If this is a function that the ABI specifies returns 'this', initialize
/// the return slot to 'this' at the start of the function.
///
/// Unlike the setting of return types, this is done within the ABI
/// implementation instead of by clients of CGCXXABI because:
/// 1) getThisValue is currently protected
/// 2) in theory, an ABI could implement 'this' returns some other way;
/// HasThisReturn only specifies a contract, not the implementation
if (HasThisReturn(CGF.CurGD))
CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue);
}
const CXXMethodDecl *MD = cast<CXXMethodDecl>(CGF.CurGD.getDecl());
if (isa<CXXConstructorDecl>(MD) && MD->getParent()->getNumVBases()) {
@ -455,9 +455,10 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
}
}
llvm::Value *MicrosoftCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
void MicrosoftCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
const CXXConstructorDecl *D,
CXXCtorType Type, bool ForVirtualBase,
CXXCtorType Type,
bool ForVirtualBase,
bool Delegating,
llvm::Value *This,
CallExpr::const_arg_iterator ArgBeg,
@ -474,17 +475,14 @@ llvm::Value *MicrosoftCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
// FIXME: Provide a source location here.
CGF.EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This,
ImplicitParam, ImplicitParamTy,
ArgBeg, ArgEnd);
return Callee;
ImplicitParam, ImplicitParamTy, ArgBeg, ArgEnd);
}
RValue MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
ReturnValueSlot ReturnValue,
llvm::Value *This) {
void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
SourceLocation CallLoc,
llvm::Value *This) {
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
// We have only one destructor in the vftable but can get both behaviors
@ -499,8 +497,8 @@ RValue MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
= llvm::ConstantInt::get(llvm::IntegerType::getInt1Ty(CGF.getLLVMContext()),
DtorType == Dtor_Deleting);
return CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValue, This,
ImplicitParam, Context.BoolTy, 0, 0);
CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValueSlot(), This,
ImplicitParam, Context.BoolTy, 0, 0);
}
const VBTableVector &

View File

@ -52,19 +52,19 @@ namespace test1 {
a.bar();
}
// CHECK: define linkonce_odr [[A]]* @_ZN5test11AC1Ei([[A]]* %this, i32 %i) unnamed_addr
// CHECK: define linkonce_odr [[A]]* @_ZN5test11AC1Ei([[A]]* returned %this, i32 %i) unnamed_addr
// CHECK: [[THIS:%.*]] = alloca [[A]]*, align 4
// CHECK: store [[A]]* {{.*}}, [[A]]** [[THIS]]
// CHECK: [[THIS1:%.*]] = load [[A]]** [[THIS]]
// CHECK: [[THIS2:%.*]] = call [[A]]* @_ZN5test11AC2Ei(
// CHECK: ret [[A]]* [[THIS2]]
// CHECK: {{%.*}} = call [[A]]* @_ZN5test11AC2Ei(
// CHECK: ret [[A]]* [[THIS1]]
// CHECK: define linkonce_odr [[A]]* @_ZN5test11AD1Ev([[A]]* %this) unnamed_addr
// CHECK: define linkonce_odr [[A]]* @_ZN5test11AD1Ev([[A]]* returned %this) unnamed_addr
// CHECK: [[THIS:%.*]] = alloca [[A]]*, align 4
// CHECK: store [[A]]* {{.*}}, [[A]]** [[THIS]]
// CHECK: [[THIS1:%.*]] = load [[A]]** [[THIS]]
// CHECK: [[THIS2:%.*]] = call [[A]]* @_ZN5test11AD2Ev(
// CHECK: ret [[A]]* [[THIS2]]
// CHECK: {{%.*}} = call [[A]]* @_ZN5test11AD2Ev(
// CHECK: ret [[A]]* [[THIS1]]
}
// Awkward virtual cases.

View File

@ -1,60 +1,119 @@
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-apple-ios3.0 -target-abi apcs-gnu | FileCheck %s
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=i686-unknown-linux | FileCheck --check-prefix=CHECKGEN %s
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-apple-ios3.0 -target-abi apcs-gnu | FileCheck --check-prefix=CHECKARM %s
//RUN: %clang_cc1 %s -emit-llvm -o - -DPR12784_WORKAROUND -triple=x86_64-pc-win32 -cxx-abi microsoft | FileCheck --check-prefix=CHECKMS %s
// For constructors/desctructors that return 'this', if there exists a callsite
// that returns 'this' and is immediately before the return instruction, make
// sure we are using the return value from the callsite.
// rdar://12818789
// FIXME: Add checks to ensure that Microsoft destructors do not return 'this'
// once PR12784 is resolved
// CHECK: define linkonce_odr [[A:%.*]] @_ZN11ObjectCacheC1Ev([[A]] %this) unnamed_addr
// CHECK: [[THIS1:%.*]] = call [[A]] @_ZN11ObjectCacheC2Ev(
// CHECK-NEXT: ret [[A]] [[THIS1]]
// Make sure we attach the 'returned' attribute to the 'this' parameter of
// constructors and destructors which return this (and only these cases)
// CHECK: define linkonce_odr [[A:%.*]] @_ZN5TimerI11ObjectCacheEC1EPS0_MS0_FvPS1_E([[A]] %this
// CHECK: [[THIS1:%.*]] = call [[A]] @_ZN5TimerI11ObjectCacheEC2EPS0_MS0_FvPS1_E(
// CHECK-NEXT: ret [[A]] [[THIS1]]
// CHECK: define linkonce_odr [[A:%.*]] @_ZN5TimerI11ObjectCacheED1Ev([[A]] %this) unnamed_addr
// CHECK: [[THIS1:%.*]] = call [[A]] @_ZN5TimerI11ObjectCacheED2Ev(
// CHECK-NEXT: ret [[A]] [[THIS1]]
// CHECK: define linkonce_odr [[A:%.*]] @_ZN5TimerI11ObjectCacheED2Ev([[A]] %this) unnamed_addr
// CHECK: [[THIS1:%.*]] = call [[B:%.*]] @_ZN9TimerBaseD2Ev(
// CHECK-NEXT: [[THIS2:%.*]] = bitcast [[B]] [[THIS1]] to [[A]]
// CHECK-NEXT: ret [[A]] [[THIS2]]
class TimerBase {
class A {
public:
TimerBase();
virtual ~TimerBase();
};
template <typename TimerFiredClass> class Timer : public TimerBase {
public:
typedef void (TimerFiredClass::*TimerFiredFunction)(Timer*);
Timer(TimerFiredClass* o, TimerFiredFunction f)
: m_object(o), m_function(f) { }
A();
~A();
private:
virtual void fired() { (m_object->*m_function)(this); }
TimerFiredClass* m_object;
TimerFiredFunction m_function;
int x_;
};
class ObjectCache {
class B : public A {
public:
explicit ObjectCache();
~ObjectCache();
B(int *i);
~B();
private:
Timer<ObjectCache> m_notificationPostTimer;
int *i_;
};
inline ObjectCache::ObjectCache() : m_notificationPostTimer(this, 0) { }
inline ObjectCache::~ObjectCache() { }
B::B(int *i) : i_(i) { }
#ifndef PR12784_WORKAROUND
B::~B() { }
#endif
ObjectCache *test() {
ObjectCache *dd = new ObjectCache();
return dd;
// CHECKGEN: define void @_ZN1BC1EPi(%class.B* %this, i32* %i)
// CHECKGEN: define void @_ZN1BC2EPi(%class.B* %this, i32* %i)
// CHECKGEN: define void @_ZN1BD1Ev(%class.B* %this)
// CHECKGEN: define void @_ZN1BD2Ev(%class.B* %this)
// CHECKARM: define %class.B* @_ZN1BC1EPi(%class.B* returned %this, i32* %i)
// CHECKARM: define %class.B* @_ZN1BC2EPi(%class.B* returned %this, i32* %i)
// CHECKARM: define %class.B* @_ZN1BD1Ev(%class.B* returned %this)
// CHECKARM: define %class.B* @_ZN1BD2Ev(%class.B* returned %this)
// CHECKMS: define %class.B* @"\01??0B@@QEAA@PEAH@Z"(%class.B* returned %this, i32* %i)
class C : public A, public B {
public:
C(int *i, char *c);
virtual ~C();
private:
char *c_;
};
C::C(int *i, char *c) : B(i), c_(c) { }
#ifndef PR12784_WORKAROUND
C::~C() { }
#endif
// CHECKGEN: define void @_ZN1CC1EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN: define void @_ZN1CC2EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKGEN: define void @_ZN1CD1Ev(%class.C* %this)
// CHECKGEN: define void @_ZN1CD2Ev(%class.C* %this)
// CHECKARM: define %class.C* @_ZN1CC1EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM: define %class.C* @_ZN1CC2EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKARM: define %class.C* @_ZN1CD1Ev(%class.C* returned %this)
// CHECKARM: define %class.C* @_ZN1CD2Ev(%class.C* returned %this)
// CHECKMS: define %class.C* @"\01??0C@@QEAA@PEAHPEAD@Z"(%class.C* returned %this, i32* %i, i8* %c)
class D : public virtual A {
public:
D();
~D();
};
#ifndef PR12784_WORKAROUND
D::D() { }
D::~D() { }
#endif
// CHECKGEN: define void @_ZN1DC1Ev(%class.D* %this)
// CHECKGEN: define void @_ZN1DC2Ev(%class.D* %this, i8** %vtt)
// CHECKGEN: define void @_ZN1DD1Ev(%class.D* %this)
// CHECKGEN: define void @_ZN1DD2Ev(%class.D* %this, i8** %vtt)
// CHECKARM: define %class.D* @_ZN1DC1Ev(%class.D* returned %this)
// CHECKARM: define %class.D* @_ZN1DC2Ev(%class.D* returned %this, i8** %vtt)
// CHECKARM: define %class.D* @_ZN1DD1Ev(%class.D* returned %this)
// CHECKARM: define %class.D* @_ZN1DD2Ev(%class.D* returned %this, i8** %vtt)
class E {
public:
E();
virtual ~E();
};
E* gete();
void test_destructor() {
const E& e1 = E();
E* e2 = gete();
e2->~E();
}
// CHECKARM: define void @_Z15test_destructorv()
// Verify that virtual calls to destructors are not marked with a 'returned'
// this parameter at the call site...
// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)**
// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)** [[VFN]]
// CHECKARM: call %class.E* [[THUNK]](%class.E* %
// ...but static calls create declarations with 'returned' this
// CHECKARM: {{%.*}} = call %class.E* @_ZN1ED1Ev(%class.E* %
// CHECKARM: declare %class.E* @_ZN1ED1Ev(%class.E* returned)

View File

@ -21,7 +21,7 @@ namespace no_elide_base {
Derived(const Other &O);
};
// CHECK: define {{.*}} @_ZN13no_elide_base7DerivedC1ERKNS_5OtherE(%"struct.no_elide_base::Derived"* %this, %"struct.no_elide_base::Other"* %O) unnamed_addr
// CHECK: define {{.*}} @_ZN13no_elide_base7DerivedC1ERKNS_5OtherE(%"struct.no_elide_base::Derived"* returned %this, %"struct.no_elide_base::Other"* %O) unnamed_addr
Derived::Derived(const Other &O)
// CHECK: call {{.*}} @_ZNK13no_elide_base5OthercvNS_4BaseEEv
// CHECK: call {{.*}} @_ZN13no_elide_base4BaseC2ERKS0_

View File

@ -3,5 +3,5 @@
struct A { virtual void a(); };
A x(A& y) { return y; }
// CHECK: define linkonce_odr {{.*}} @_ZN1AC1ERKS_(%struct.A* %this, %struct.A*) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN1AC1ERKS_(%struct.A* {{.*}}%this, %struct.A*) unnamed_addr
// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1A, i64 0, i64 2)

View File

@ -5,6 +5,7 @@ struct B { A<int> x; };
void a() {
B b;
}
// CHECK: call {{.*}} @_ZN1BC1Ev
// CHECK: define linkonce_odr {{.*}} @_ZN1BC1Ev(%struct.B* %this) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN1BC1Ev(%struct.B* {{.*}}%this) unnamed_addr
// CHECK: call {{.*}} @_ZN1AIiEC1Ev

View File

@ -82,7 +82,7 @@ namespace test7 {
X(U*, typename int_c<(meta<T>::value + meta<U>::value)>::type *) { }
};
// CHECK: define weak_odr {{.*}} @_ZN5test71XIiEC1IdEEPT_PNS_5int_cIXplL_ZNS_4metaIiE5valueEEsr4metaIS3_EE5valueEE4typeE(%"struct.test7::X"* %this, double*, float*) unnamed_addr
// CHECK: define weak_odr {{.*}} @_ZN5test71XIiEC1IdEEPT_PNS_5int_cIXplL_ZNS_4metaIiE5valueEEsr4metaIS3_EE5valueEE4typeE(
template X<int>::X(double*, float*);
}
@ -101,7 +101,7 @@ namespace test8 {
template<typename T>
void f(int_c<meta<T>::type::value>) { }
// CHECK: define weak_odr void @_ZN5test81fIiEEvNS_5int_cIXsr4metaIT_E4typeE5valueEEE
// CHECK: define weak_odr void @_ZN5test81fIiEEvNS_5int_cIXsr4metaIT_E4typeE5valueEEE(
template void f<int>(int_c<sizeof(int)>);
}

View File

@ -15,7 +15,7 @@ class A {
void no_constructor_destructor_infinite_recursion() {
A a;
// CHECK: define linkonce_odr x86_thiscallcc %"class.basic::A"* @"\01??0A@basic@@QAE@XZ"(%"class.basic::A"* %this)
// CHECK: define linkonce_odr x86_thiscallcc %"class.basic::A"* @"\01??0A@basic@@QAE@XZ"(%"class.basic::A"* returned %this)
// CHECK: [[THIS_ADDR:%[.0-9A-Z_a-z]+]] = alloca %"class.basic::A"*, align 4
// CHECK-NEXT: store %"class.basic::A"* %this, %"class.basic::A"** [[THIS_ADDR]], align 4
// CHECK-NEXT: [[T1:%[.0-9A-Z_a-z]+]] = load %"class.basic::A"** [[THIS_ADDR]]
@ -34,7 +34,7 @@ struct B {
// Tests that we can define constructors outside the class (PR12784).
B::B() {
// CHECK: define x86_thiscallcc %"struct.basic::B"* @"\01??0B@basic@@QAE@XZ"(%"struct.basic::B"* %this)
// CHECK: define x86_thiscallcc %"struct.basic::B"* @"\01??0B@basic@@QAE@XZ"(%"struct.basic::B"* returned %this)
// CHECK: ret
}
@ -136,7 +136,7 @@ struct B : A {
};
B::B() {
// CHECK: define x86_thiscallcc %"struct.constructors::B"* @"\01??0B@constructors@@QAE@XZ"(%"struct.constructors::B"* %this)
// CHECK: define x86_thiscallcc %"struct.constructors::B"* @"\01??0B@constructors@@QAE@XZ"(%"struct.constructors::B"* returned %this)
// CHECK: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}})
// CHECK: ret
}
@ -146,7 +146,7 @@ struct C : virtual A {
};
C::C() {
// CHECK: define x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* %this, i32 %is_most_derived)
// CHECK: define x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* returned %this, i32 %is_most_derived)
// TODO: make sure this works in the Release build too;
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
@ -179,7 +179,7 @@ struct D : C {
};
D::D() {
// CHECK: define x86_thiscallcc %"struct.constructors::D"* @"\01??0D@constructors@@QAE@XZ"(%"struct.constructors::D"* %this, i32 %is_most_derived) unnamed_addr
// CHECK: define x86_thiscallcc %"struct.constructors::D"* @"\01??0D@constructors@@QAE@XZ"(%"struct.constructors::D"* returned %this, i32 %is_most_derived) unnamed_addr
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
// CHECK: %[[SHOULD_CALL_VBASE_CTORS:.*]] = icmp ne i32 %[[IS_MOST_DERIVED_VAL]], 0
@ -204,7 +204,7 @@ struct E : virtual C {
};
E::E() {
// CHECK: define x86_thiscallcc %"struct.constructors::E"* @"\01??0E@constructors@@QAE@XZ"(%"struct.constructors::E"* %this, i32 %is_most_derived) unnamed_addr
// CHECK: define x86_thiscallcc %"struct.constructors::E"* @"\01??0E@constructors@@QAE@XZ"(%"struct.constructors::E"* returned %this, i32 %is_most_derived) unnamed_addr
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
// CHECK: %[[SHOULD_CALL_VBASE_CTORS:.*]] = icmp ne i32 %[[IS_MOST_DERIVED_VAL]], 0

View File

@ -18,34 +18,34 @@ int main() {
// basic_iostream's complete dtor calls its base dtor, then its
// virtual base's dtor.
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED1Ev(%struct.basic_iostream* %this) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED1Ev(%struct.basic_iostream* {{.*}}%this) unnamed_addr
// CHECK: call {{.*}} @_ZN14basic_iostreamIcED2Ev
// CHECK: call {{.*}} @_ZN9basic_iosD2Ev
// basic_iostream's base dtor calls its non-virtual base dtor.
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED2Ev(%struct.basic_iostream* %this, i8** %vtt) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED2Ev(%struct.basic_iostream* {{.*}}%this, i8** %vtt) unnamed_addr
// CHECK: call {{.*}} @_ZN13basic_istreamIcED2Ev
// CHECK: }
// basic_iostream's deleting dtor calls its complete dtor, then
// operator delete().
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* %this) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* {{.*}}%this) unnamed_addr
// CHECK: call {{.*}} @_ZN14basic_iostreamIcED1Ev
// CHECK: call {{.*}} @_ZdlPv
// basic_istream's complete dtor calls the base dtor,
// then its virtual base's base dtor.
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED1Ev(%struct.basic_istream* %this) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED1Ev(%struct.basic_istream* {{.*}}%this) unnamed_addr
// CHECK: call {{.*}} @_ZN13basic_istreamIcED2Ev
// CHECK: call {{.*}} @_ZN9basic_iosD2Ev
// basic_istream's deleting dtor calls the complete dtor, then
// operator delete().
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED0Ev(%struct.basic_istream* %this) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED0Ev(%struct.basic_istream* {{.*}}%this) unnamed_addr
// CHECK: call {{.*}} @_ZN13basic_istreamIcED1Ev
// CHECK: call {{.*}} @_ZdlPv
// basic_istream's base dtor is a no-op.
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED2Ev(%struct.basic_istream* %this, i8** %vtt) unnamed_addr
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED2Ev(%struct.basic_istream* {{.*}}%this, i8** %vtt) unnamed_addr
// CHECK-NOT: call
// CHECK: }