forked from OSchip/llvm-project
MS ABI: Properly call global delete when invoking virtual destructors
Summary: The Itanium ABI approach of using offset-to-top isn't possible with the MS ABI, it doesn't have that kind of information lying around. Instead, we do the following: - Call the virtual deleting destructor with the "don't delete the object flag" set. The virtual deleting destructor will return a pointer to 'this' adjusted to the most derived class. - Call the global delete using the adjusted 'this' pointer. Reviewers: rnk Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D5996 llvm-svn: 220993
This commit is contained in:
parent
6e21338abb
commit
0c0b6d9ac6
|
@ -93,6 +93,8 @@ public:
|
|||
/// when called virtually, and code generation does not support the case.
|
||||
virtual bool HasThisReturn(GlobalDecl GD) const { return false; }
|
||||
|
||||
virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; }
|
||||
|
||||
/// If the C++ ABI requires the given type be returned in a particular way,
|
||||
/// this method sets RetAI and returns true.
|
||||
virtual bool classifyReturnType(CGFunctionInfo &FI) const = 0;
|
||||
|
@ -207,14 +209,11 @@ protected:
|
|||
CharUnits getMemberPointerPathAdjustment(const APValue &MP);
|
||||
|
||||
public:
|
||||
/// Adjust the given non-null pointer to an object of polymorphic
|
||||
/// type to point to the complete object.
|
||||
///
|
||||
/// The IR type of the result should be a pointer but is otherwise
|
||||
/// irrelevant.
|
||||
virtual llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
|
||||
llvm::Value *ptr,
|
||||
QualType type) = 0;
|
||||
virtual void emitVirtualObjectDelete(CodeGenFunction &CGF,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *Ptr, QualType ElementType,
|
||||
bool UseGlobalDelete,
|
||||
const CXXDestructorDecl *Dtor) = 0;
|
||||
|
||||
virtual llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty) = 0;
|
||||
|
||||
|
@ -359,11 +358,10 @@ public:
|
|||
llvm::Type *Ty) = 0;
|
||||
|
||||
/// Emit the ABI-specific virtual destructor call.
|
||||
virtual void EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType,
|
||||
llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) = 0;
|
||||
virtual llvm::Value *
|
||||
EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType, llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) = 0;
|
||||
|
||||
virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
|
||||
GlobalDecl GD,
|
||||
|
|
|
@ -214,8 +214,11 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD,
|
|||
(MD->isVariadic() ? RequiredArgs(argTypes.size()) : RequiredArgs::All);
|
||||
|
||||
FunctionType::ExtInfo extInfo = FTP->getExtInfo();
|
||||
CanQualType resultType =
|
||||
TheCXXABI.HasThisReturn(GD) ? argTypes.front() : Context.VoidTy;
|
||||
CanQualType resultType = TheCXXABI.HasThisReturn(GD)
|
||||
? argTypes.front()
|
||||
: TheCXXABI.hasMostDerivedReturn(GD)
|
||||
? CGM.getContext().VoidPtrTy
|
||||
: Context.VoidTy;
|
||||
return arrangeLLVMFunctionInfo(resultType, true, argTypes, extInfo, required);
|
||||
}
|
||||
|
||||
|
@ -233,8 +236,11 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
|
|||
CanQual<FunctionProtoType> FPT = GetFormalType(D);
|
||||
RequiredArgs Required = RequiredArgs::forPrototypePlus(FPT, 1 + ExtraArgs);
|
||||
GlobalDecl GD(D, CtorKind);
|
||||
CanQualType ResultType =
|
||||
TheCXXABI.HasThisReturn(GD) ? ArgTypes.front() : Context.VoidTy;
|
||||
CanQualType ResultType = TheCXXABI.HasThisReturn(GD)
|
||||
? ArgTypes.front()
|
||||
: TheCXXABI.hasMostDerivedReturn(GD)
|
||||
? CGM.getContext().VoidPtrTy
|
||||
: Context.VoidTy;
|
||||
|
||||
FunctionType::ExtInfo Info = FPT->getExtInfo();
|
||||
return arrangeLLVMFunctionInfo(ResultType, true, ArgTypes, Info, Required);
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
using namespace clang;
|
||||
using namespace CodeGen;
|
||||
|
||||
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
|
||||
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
|
||||
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
|
||||
const CallExpr *CE) {
|
||||
static RequiredArgs commonEmitCXXMemberOrOperatorCall(
|
||||
CodeGenFunction &CGF, const CXXMethodDecl *MD, llvm::Value *Callee,
|
||||
ReturnValueSlot ReturnValue, llvm::Value *This, llvm::Value *ImplicitParam,
|
||||
QualType ImplicitParamTy, const CallExpr *CE, CallArgList &Args) {
|
||||
assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
|
||||
isa<CXXOperatorCallExpr>(CE));
|
||||
assert(MD->isInstance() &&
|
||||
|
@ -39,14 +39,13 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
|
|||
SourceLocation CallLoc;
|
||||
if (CE)
|
||||
CallLoc = CE->getExprLoc();
|
||||
EmitTypeCheck(isa<CXXConstructorDecl>(MD) ? TCK_ConstructorCall
|
||||
: TCK_MemberCall,
|
||||
CallLoc, This, getContext().getRecordType(MD->getParent()));
|
||||
|
||||
CallArgList Args;
|
||||
CGF.EmitTypeCheck(
|
||||
isa<CXXConstructorDecl>(MD) ? CodeGenFunction::TCK_ConstructorCall
|
||||
: CodeGenFunction::TCK_MemberCall,
|
||||
CallLoc, This, CGF.getContext().getRecordType(MD->getParent()));
|
||||
|
||||
// Push the this ptr.
|
||||
Args.add(RValue::get(This), MD->getThisType(getContext()));
|
||||
Args.add(RValue::get(This), MD->getThisType(CGF.getContext()));
|
||||
|
||||
// If there is an implicit parameter (e.g. VTT), emit it.
|
||||
if (ImplicitParam) {
|
||||
|
@ -60,18 +59,40 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
|
|||
if (CE) {
|
||||
// Special case: skip first argument of CXXOperatorCall (it is "this").
|
||||
unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
|
||||
EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(),
|
||||
CE->getDirectCallee());
|
||||
CGF.EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(),
|
||||
CE->getDirectCallee());
|
||||
} else {
|
||||
assert(
|
||||
FPT->getNumParams() == 0 &&
|
||||
"No CallExpr specified for function with non-zero number of arguments");
|
||||
}
|
||||
return required;
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
|
||||
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
|
||||
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
|
||||
const CallExpr *CE) {
|
||||
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
|
||||
CallArgList Args;
|
||||
RequiredArgs required = commonEmitCXXMemberOrOperatorCall(
|
||||
*this, MD, Callee, ReturnValue, This, ImplicitParam, ImplicitParamTy, CE,
|
||||
Args);
|
||||
return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
|
||||
Callee, ReturnValue, Args, MD);
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitCXXStructorCall(
|
||||
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
|
||||
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
|
||||
const CallExpr *CE, StructorType Type) {
|
||||
CallArgList Args;
|
||||
commonEmitCXXMemberOrOperatorCall(*this, MD, Callee, ReturnValue, This,
|
||||
ImplicitParam, ImplicitParamTy, CE, Args);
|
||||
return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(MD, Type),
|
||||
Callee, ReturnValue, Args, MD);
|
||||
}
|
||||
|
||||
static CXXRecordDecl *getCXXRecord(const Expr *E) {
|
||||
QualType T = E->getType();
|
||||
if (const PointerType *PTy = T->getAs<PointerType>())
|
||||
|
@ -1389,6 +1410,14 @@ namespace {
|
|||
};
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenFunction::pushCallObjectDeleteCleanup(const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *CompletePtr,
|
||||
QualType ElementType) {
|
||||
EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup, CompletePtr,
|
||||
OperatorDelete, ElementType);
|
||||
}
|
||||
|
||||
/// Emit the code for deleting a single object.
|
||||
static void EmitObjectDelete(CodeGenFunction &CGF,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
|
@ -1404,30 +1433,8 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
|
|||
Dtor = RD->getDestructor();
|
||||
|
||||
if (Dtor->isVirtual()) {
|
||||
if (UseGlobalDelete) {
|
||||
// If we're supposed to call the global delete, make sure we do so
|
||||
// even if the destructor throws.
|
||||
|
||||
// Derive the complete-object pointer, which is what we need
|
||||
// to pass to the deallocation function.
|
||||
llvm::Value *completePtr =
|
||||
CGF.CGM.getCXXABI().adjustToCompleteObject(CGF, Ptr, ElementType);
|
||||
|
||||
CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
|
||||
completePtr, OperatorDelete,
|
||||
ElementType);
|
||||
}
|
||||
|
||||
// FIXME: Provide a source location here even though there's no
|
||||
// CXXMemberCallExpr for dtor call.
|
||||
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
|
||||
CGF.CGM.getCXXABI().EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr,
|
||||
nullptr);
|
||||
|
||||
if (UseGlobalDelete) {
|
||||
CGF.PopCleanupBlock();
|
||||
}
|
||||
|
||||
CGF.CGM.getCXXABI().emitVirtualObjectDelete(
|
||||
CGF, OperatorDelete, Ptr, ElementType, UseGlobalDelete, Dtor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,8 +200,11 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD,
|
|||
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
|
||||
QualType ThisType = MD->getThisType(getContext());
|
||||
const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
|
||||
QualType ResultType =
|
||||
CGM.getCXXABI().HasThisReturn(GD) ? ThisType : FPT->getReturnType();
|
||||
QualType ResultType = CGM.getCXXABI().HasThisReturn(GD)
|
||||
? ThisType
|
||||
: CGM.getCXXABI().hasMostDerivedReturn(GD)
|
||||
? CGM.getContext().VoidPtrTy
|
||||
: FPT->getReturnType();
|
||||
FunctionArgList FunctionArgs;
|
||||
|
||||
// Create the implicit 'this' parameter declaration.
|
||||
|
@ -278,8 +281,11 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Value *Callee,
|
|||
#endif
|
||||
|
||||
// Determine whether we have a return value slot to use.
|
||||
QualType ResultType =
|
||||
CGM.getCXXABI().HasThisReturn(CurGD) ? ThisType : FPT->getReturnType();
|
||||
QualType ResultType = CGM.getCXXABI().HasThisReturn(CurGD)
|
||||
? ThisType
|
||||
: CGM.getCXXABI().hasMostDerivedReturn(CurGD)
|
||||
? CGM.getContext().VoidPtrTy
|
||||
: FPT->getReturnType();
|
||||
ReturnValueSlot Slot;
|
||||
if (!ResultType->isVoidType() &&
|
||||
CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&
|
||||
|
|
|
@ -822,6 +822,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
|
|||
if (MD && MD->isInstance()) {
|
||||
if (CGM.getCXXABI().HasThisReturn(GD))
|
||||
ResTy = MD->getThisType(getContext());
|
||||
else if (CGM.getCXXABI().hasMostDerivedReturn(GD))
|
||||
ResTy = CGM.getContext().VoidPtrTy;
|
||||
CGM.getCXXABI().buildThisParam(*this, Args);
|
||||
}
|
||||
|
||||
|
|
|
@ -1136,6 +1136,9 @@ public:
|
|||
void pushLifetimeExtendedDestroy(CleanupKind kind, llvm::Value *addr,
|
||||
QualType type, Destroyer *destroyer,
|
||||
bool useEHCleanupForArray);
|
||||
void pushCallObjectDeleteCleanup(const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *CompletePtr,
|
||||
QualType ElementType);
|
||||
void pushStackRestore(CleanupKind kind, llvm::Value *SPMem);
|
||||
void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,
|
||||
bool useEHCleanupForArray);
|
||||
|
@ -2319,6 +2322,11 @@ public:
|
|||
ReturnValueSlot ReturnValue, llvm::Value *This,
|
||||
llvm::Value *ImplicitParam,
|
||||
QualType ImplicitParamTy, const CallExpr *E);
|
||||
RValue EmitCXXStructorCall(const CXXMethodDecl *MD, llvm::Value *Callee,
|
||||
ReturnValueSlot ReturnValue, llvm::Value *This,
|
||||
llvm::Value *ImplicitParam,
|
||||
QualType ImplicitParamTy, const CallExpr *E,
|
||||
StructorType Type);
|
||||
RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E,
|
||||
ReturnValueSlot ReturnValue);
|
||||
RValue EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E,
|
||||
|
|
|
@ -106,8 +106,11 @@ public:
|
|||
llvm::Value *Addr,
|
||||
const MemberPointerType *MPT) override;
|
||||
|
||||
llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF, llvm::Value *ptr,
|
||||
QualType type) override;
|
||||
void emitVirtualObjectDelete(CodeGenFunction &CGF,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *Ptr, QualType ElementType,
|
||||
bool UseGlobalDelete,
|
||||
const CXXDestructorDecl *Dtor) override;
|
||||
|
||||
void EmitFundamentalRTTIDescriptor(QualType Type);
|
||||
void EmitFundamentalRTTIDescriptors();
|
||||
|
@ -187,10 +190,11 @@ public:
|
|||
llvm::Value *This,
|
||||
llvm::Type *Ty) override;
|
||||
|
||||
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType, llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) override;
|
||||
llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType,
|
||||
llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) override;
|
||||
|
||||
void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
|
||||
|
||||
|
@ -847,21 +851,38 @@ bool ItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
|
|||
|
||||
/// The Itanium ABI always places an offset to the complete object
|
||||
/// at entry -2 in the vtable.
|
||||
llvm::Value *ItaniumCXXABI::adjustToCompleteObject(CodeGenFunction &CGF,
|
||||
llvm::Value *ptr,
|
||||
QualType type) {
|
||||
// Grab the vtable pointer as an intptr_t*.
|
||||
llvm::Value *vtable = CGF.GetVTablePtr(ptr, CGF.IntPtrTy->getPointerTo());
|
||||
void ItaniumCXXABI::emitVirtualObjectDelete(
|
||||
CodeGenFunction &CGF, const FunctionDecl *OperatorDelete, llvm::Value *Ptr,
|
||||
QualType ElementType, bool UseGlobalDelete, const CXXDestructorDecl *Dtor) {
|
||||
if (UseGlobalDelete) {
|
||||
// Derive the complete-object pointer, which is what we need
|
||||
// to pass to the deallocation function.
|
||||
|
||||
// Track back to entry -2 and pull out the offset there.
|
||||
llvm::Value *offsetPtr =
|
||||
CGF.Builder.CreateConstInBoundsGEP1_64(vtable, -2, "complete-offset.ptr");
|
||||
llvm::LoadInst *offset = CGF.Builder.CreateLoad(offsetPtr);
|
||||
offset->setAlignment(CGF.PointerAlignInBytes);
|
||||
// Grab the vtable pointer as an intptr_t*.
|
||||
llvm::Value *VTable = CGF.GetVTablePtr(Ptr, CGF.IntPtrTy->getPointerTo());
|
||||
|
||||
// Apply the offset.
|
||||
ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy);
|
||||
return CGF.Builder.CreateInBoundsGEP(ptr, offset);
|
||||
// Track back to entry -2 and pull out the offset there.
|
||||
llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64(
|
||||
VTable, -2, "complete-offset.ptr");
|
||||
llvm::LoadInst *Offset = CGF.Builder.CreateLoad(OffsetPtr);
|
||||
Offset->setAlignment(CGF.PointerAlignInBytes);
|
||||
|
||||
// Apply the offset.
|
||||
llvm::Value *CompletePtr = CGF.Builder.CreateBitCast(Ptr, CGF.Int8PtrTy);
|
||||
CompletePtr = CGF.Builder.CreateInBoundsGEP(CompletePtr, Offset);
|
||||
|
||||
// If we're supposed to call the global delete, make sure we do so
|
||||
// even if the destructor throws.
|
||||
CGF.pushCallObjectDeleteCleanup(OperatorDelete, CompletePtr, ElementType);
|
||||
}
|
||||
|
||||
// FIXME: Provide a source location here even though there's no
|
||||
// CXXMemberCallExpr for dtor call.
|
||||
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
|
||||
EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr);
|
||||
|
||||
if (UseGlobalDelete)
|
||||
CGF.PopCleanupBlock();
|
||||
}
|
||||
|
||||
static llvm::Constant *getItaniumDynamicCastFn(CodeGenFunction &CGF) {
|
||||
|
@ -1333,11 +1354,9 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
|
|||
return CGF.Builder.CreateLoad(VFuncPtr);
|
||||
}
|
||||
|
||||
void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType,
|
||||
llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) {
|
||||
llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
|
||||
CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
|
||||
llvm::Value *This, const CXXMemberCallExpr *CE) {
|
||||
assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
|
||||
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
|
||||
|
||||
|
@ -1349,6 +1368,7 @@ void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
|||
|
||||
CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This,
|
||||
/*ImplicitParam=*/nullptr, QualType(), CE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ItaniumCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
CompleteObjectLocatorType(nullptr) {}
|
||||
|
||||
bool HasThisReturn(GlobalDecl GD) const override;
|
||||
bool hasMostDerivedReturn(GlobalDecl GD) const override;
|
||||
|
||||
bool classifyReturnType(CGFunctionInfo &FI) const override;
|
||||
|
||||
|
@ -65,9 +66,11 @@ public:
|
|||
StringRef GetPureVirtualCallName() override { return "_purecall"; }
|
||||
StringRef GetDeletedVirtualCallName() override { return "_purecall"; }
|
||||
|
||||
llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
|
||||
llvm::Value *ptr,
|
||||
QualType type) override;
|
||||
void emitVirtualObjectDelete(CodeGenFunction &CGF,
|
||||
const FunctionDecl *OperatorDelete,
|
||||
llvm::Value *Ptr, QualType ElementType,
|
||||
bool UseGlobalDelete,
|
||||
const CXXDestructorDecl *Dtor) override;
|
||||
|
||||
llvm::GlobalVariable *getMSCompleteObjectLocator(const CXXRecordDecl *RD,
|
||||
const VPtrInfo *Info);
|
||||
|
@ -211,10 +214,11 @@ public:
|
|||
llvm::Value *This,
|
||||
llvm::Type *Ty) override;
|
||||
|
||||
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType, llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) override;
|
||||
llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType,
|
||||
llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) override;
|
||||
|
||||
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
|
||||
CallArgList &CallArgs) override {
|
||||
|
@ -641,11 +645,16 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
|
|||
llvm_unreachable("invalid enum");
|
||||
}
|
||||
|
||||
llvm::Value *MicrosoftCXXABI::adjustToCompleteObject(CodeGenFunction &CGF,
|
||||
llvm::Value *ptr,
|
||||
QualType type) {
|
||||
// FIXME: implement
|
||||
return ptr;
|
||||
void MicrosoftCXXABI::emitVirtualObjectDelete(
|
||||
CodeGenFunction &CGF, const FunctionDecl *OperatorDelete, llvm::Value *Ptr,
|
||||
QualType ElementType, bool UseGlobalDelete, const CXXDestructorDecl *Dtor) {
|
||||
// FIXME: Provide a source location here even though there's no
|
||||
// CXXMemberCallExpr for dtor call.
|
||||
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
|
||||
llvm::Value *MDThis =
|
||||
EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr);
|
||||
if (UseGlobalDelete)
|
||||
CGF.EmitDeleteCall(OperatorDelete, MDThis, ElementType);
|
||||
}
|
||||
|
||||
/// \brief Gets the offset to the virtual base that contains the vfptr for
|
||||
|
@ -795,6 +804,15 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
|
|||
return isa<CXXConstructorDecl>(GD.getDecl());
|
||||
}
|
||||
|
||||
static bool isDeletingDtor(GlobalDecl GD) {
|
||||
return isa<CXXDestructorDecl>(GD.getDecl()) &&
|
||||
GD.getDtorType() == Dtor_Deleting;
|
||||
}
|
||||
|
||||
bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
|
||||
return isDeletingDtor(GD);
|
||||
}
|
||||
|
||||
bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
|
||||
const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl();
|
||||
if (!RD)
|
||||
|
@ -1059,14 +1077,6 @@ llvm::Value *MicrosoftCXXABI::adjustThisArgumentForVirtualFunctionCall(
|
|||
return This;
|
||||
}
|
||||
|
||||
static bool IsDeletingDtor(GlobalDecl GD) {
|
||||
const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
|
||||
if (isa<CXXDestructorDecl>(MD)) {
|
||||
return GD.getDtorType() == Dtor_Deleting;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::addImplicitStructorParams(CodeGenFunction &CGF,
|
||||
QualType &ResTy,
|
||||
FunctionArgList &Params) {
|
||||
|
@ -1087,7 +1097,7 @@ void MicrosoftCXXABI::addImplicitStructorParams(CodeGenFunction &CGF,
|
|||
else
|
||||
Params.push_back(IsMostDerived);
|
||||
getStructorImplicitParamDecl(CGF) = IsMostDerived;
|
||||
} else if (IsDeletingDtor(CGF.CurGD)) {
|
||||
} else if (isDeletingDtor(CGF.CurGD)) {
|
||||
ImplicitParamDecl *ShouldDelete
|
||||
= ImplicitParamDecl::Create(Context, nullptr,
|
||||
CGF.CurGD.getDecl()->getLocation(),
|
||||
|
@ -1133,6 +1143,9 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
|
|||
/// HasThisReturn only specifies a contract, not the implementation
|
||||
if (HasThisReturn(CGF.CurGD))
|
||||
CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue);
|
||||
else if (hasMostDerivedReturn(CGF.CurGD))
|
||||
CGF.Builder.CreateStore(CGF.EmitCastToVoidPtr(getThisValue(CGF)),
|
||||
CGF.ReturnValue);
|
||||
|
||||
const CXXMethodDecl *MD = cast<CXXMethodDecl>(CGF.CurGD.getDecl());
|
||||
if (isa<CXXConstructorDecl>(MD) && MD->getParent()->getNumVBases()) {
|
||||
|
@ -1144,7 +1157,7 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
|
|||
"is_most_derived");
|
||||
}
|
||||
|
||||
if (IsDeletingDtor(CGF.CurGD)) {
|
||||
if (isDeletingDtor(CGF.CurGD)) {
|
||||
assert(getStructorImplicitParamDecl(CGF) &&
|
||||
"no implicit parameter for a deleting destructor?");
|
||||
getStructorImplicitParamValue(CGF)
|
||||
|
@ -1192,9 +1205,10 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
|
|||
This, false);
|
||||
}
|
||||
|
||||
CGF.EmitCXXMemberOrOperatorCall(DD, Callee, ReturnValueSlot(), This,
|
||||
/*ImplicitParam=*/nullptr,
|
||||
/*ImplicitParamTy=*/QualType(), nullptr);
|
||||
CGF.EmitCXXStructorCall(DD, Callee, ReturnValueSlot(), This,
|
||||
/*ImplicitParam=*/nullptr,
|
||||
/*ImplicitParamTy=*/QualType(), nullptr,
|
||||
getFromDtorType(Type));
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
|
||||
|
@ -1402,11 +1416,9 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
|
|||
return Builder.CreateLoad(VFuncPtr);
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
const CXXDestructorDecl *Dtor,
|
||||
CXXDtorType DtorType,
|
||||
llvm::Value *This,
|
||||
const CXXMemberCallExpr *CE) {
|
||||
llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
|
||||
CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
|
||||
llvm::Value *This, const CXXMemberCallExpr *CE) {
|
||||
assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
|
||||
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
|
||||
|
||||
|
@ -1424,8 +1436,10 @@ void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
|||
DtorType == Dtor_Deleting);
|
||||
|
||||
This = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true);
|
||||
CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This,
|
||||
ImplicitParam, Context.IntTy, CE);
|
||||
RValue RV = CGF.EmitCXXStructorCall(Dtor, Callee, ReturnValueSlot(), This,
|
||||
ImplicitParam, Context.IntTy, CE,
|
||||
StructorType::Deleting);
|
||||
return RV.getScalarVal();
|
||||
}
|
||||
|
||||
const VBTableGlobals &
|
||||
|
|
|
@ -534,7 +534,7 @@ struct __declspec(dllexport) Y {
|
|||
|
||||
struct __declspec(dllexport) Z { virtual ~Z() {} };
|
||||
// The scalar deleting dtor does not get exported:
|
||||
// M32-DAG: define linkonce_odr x86_thiscallcc void @"\01??_GZ@@UAEPAXI@Z"
|
||||
// M32-DAG: define linkonce_odr x86_thiscallcc i8* @"\01??_GZ@@UAEPAXI@Z"
|
||||
|
||||
|
||||
// The user-defined dtor does get exported:
|
||||
|
|
|
@ -46,8 +46,9 @@ B::B() {
|
|||
|
||||
struct C {
|
||||
virtual ~C() {
|
||||
// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %should_call_delete)
|
||||
// DTORS: define linkonce_odr x86_thiscallcc i8* @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %should_call_delete)
|
||||
// DTORS: store i32 %should_call_delete, i32* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 4
|
||||
// DTORS: store i8* %{{.*}}, i8** %[[RETVAL:[0-9a-z._]+]]
|
||||
// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32* %[[SHOULD_DELETE_VAR]]
|
||||
// DTORS: call x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]])
|
||||
// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0
|
||||
|
@ -59,7 +60,8 @@ struct C {
|
|||
// DTORS-NEXT: br label %[[CONTINUE_LABEL]]
|
||||
//
|
||||
// DTORS: [[CONTINUE_LABEL]]
|
||||
// DTORS-NEXT: ret void
|
||||
// DTORS-NEXT: %[[RET:.*]] = load i8** %[[RETVAL]]
|
||||
// DTORS-NEXT: ret i8* %[[RET]]
|
||||
|
||||
// Check that we do the mangling correctly on x64.
|
||||
// DTORS-X64: @"\01??_GC@basic@@UEAAPEAXI@Z"
|
||||
|
@ -81,11 +83,11 @@ void call_complete_dtor(C *obj_ptr) {
|
|||
// CHECK: define void @"\01?call_complete_dtor@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
|
||||
obj_ptr->~C();
|
||||
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
|
||||
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
||||
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
||||
// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
|
||||
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
||||
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
||||
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
|
@ -96,11 +98,27 @@ void call_deleting_dtor(C *obj_ptr) {
|
|||
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
|
||||
|
||||
// CHECK: [[DELETE_NOTNULL]]
|
||||
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
||||
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
||||
// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
|
||||
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
||||
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
||||
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
void call_deleting_dtor_and_global_delete(C *obj_ptr) {
|
||||
// CHECK: define void @"\01?call_deleting_dtor_and_global_delete@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
|
||||
::delete obj_ptr;
|
||||
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
|
||||
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
|
||||
|
||||
// CHECK: [[DELETE_NOTNULL]]
|
||||
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
||||
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
||||
// CHECK-NEXT: %[[CALL:.*]] = call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
|
||||
// CHECK-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[CALL]])
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
|
@ -153,13 +171,13 @@ C::~C() {
|
|||
void foo() {
|
||||
C c;
|
||||
}
|
||||
// DTORS2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
|
||||
// DTORS2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
|
||||
// DTORS2: (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete)
|
||||
// Do an adjustment from B* to C*.
|
||||
// DTORS2: getelementptr i8* %{{.*}}, i32 -4
|
||||
// DTORS2: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"*
|
||||
// DTORS2: call x86_thiscallcc void @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
|
||||
// DTORS2: ret void
|
||||
// DTORS2: %[[CALL:.*]] = call x86_thiscallcc i8* @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
|
||||
// DTORS2: ret i8* %[[CALL]]
|
||||
|
||||
}
|
||||
|
||||
|
@ -436,7 +454,7 @@ struct A {
|
|||
void *getA() {
|
||||
return (void*)new A();
|
||||
}
|
||||
// CHECK: define internal x86_thiscallcc void @"\01??_GA@?A@@UAEPAXI@Z"
|
||||
// CHECK: define internal x86_thiscallcc i8* @"\01??_GA@?A@@UAEPAXI@Z"
|
||||
// CHECK: (%"struct.(anonymous namespace)::A"* %this, i32 %should_call_delete)
|
||||
// CHECK: define internal x86_thiscallcc void @"\01??1A@?A@@UAE@XZ"
|
||||
// CHECK: (%"struct.(anonymous namespace)::A"* %this)
|
||||
|
|
|
@ -61,10 +61,10 @@ struct C : A, B {
|
|||
|
||||
C::C() {} // Emits vftable and forces thunk generation.
|
||||
|
||||
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
|
||||
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
|
||||
// CODEGEN: getelementptr i8* {{.*}}, i32 -4
|
||||
// FIXME: should actually call _EC, not _GC.
|
||||
// CODEGEN: call x86_thiscallcc void @"\01??_GC@@UAEPAXI@Z"
|
||||
// CODEGEN: call x86_thiscallcc i8* @"\01??_GC@@UAEPAXI@Z"
|
||||
// CODEGEN: ret
|
||||
|
||||
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C*
|
||||
|
|
|
@ -96,7 +96,7 @@ B::~B() {
|
|||
// CHECK2: call x86_thiscallcc void @"\01??1VBase@@UAE@XZ"(%struct.VBase* %[[VBASE]])
|
||||
// CHECK2: ret
|
||||
|
||||
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_GB@@UAEPAXI@Z"
|
||||
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_GB@@UAEPAXI@Z"
|
||||
// CHECK2: %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* {{.*}} to i8*
|
||||
// CHECK2: %[[THIS_i8:.*]] = getelementptr inbounds i8* %[[THIS_PARAM_i8:.*]], i32 -8
|
||||
// CHECK2: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
|
||||
|
@ -184,10 +184,10 @@ void delete_B(B *obj) {
|
|||
// CHECK: %[[VBOFFSET32:.*]] = load i32* %[[VBENTRY]]
|
||||
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
||||
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to void (%struct.B*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load void (%struct.B*, i32)*** %[[VFPTR]]
|
||||
// CHECK: %[[VFUN:.*]] = getelementptr inbounds void (%struct.B*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN_VALUE:.*]] = load void (%struct.B*, i32)** %[[VFUN]]
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (%struct.B*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load i8* (%struct.B*, i32)*** %[[VFPTR]]
|
||||
// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (%struct.B*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN_VALUE:.*]] = load i8* (%struct.B*, i32)** %[[VFUN]]
|
||||
//
|
||||
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
|
||||
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 0
|
||||
|
@ -199,7 +199,7 @@ void delete_B(B *obj) {
|
|||
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
||||
// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.B*
|
||||
//
|
||||
// CHECK: call x86_thiscallcc void %[[VFUN_VALUE]](%struct.B* %[[VBASE]], i32 1)
|
||||
// CHECK: call x86_thiscallcc i8* %[[VFUN_VALUE]](%struct.B* %[[VBASE]], i32 1)
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
|
@ -407,11 +407,11 @@ void destroy(C *obj) {
|
|||
// CHECK-LABEL: define void @"\01?destroy@test4@@YAXPAUC@1@@Z"(%"struct.test4::C"* %obj)
|
||||
|
||||
delete obj;
|
||||
// CHECK: %[[VPTR:.*]] = bitcast %"struct.test4::C"* %[[OBJ:.*]] to void (%"struct.test4::C"*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load void (%"struct.test4::C"*, i32)*** %[[VPTR]]
|
||||
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds void (%"struct.test4::C"*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN:.*]] = load void (%"struct.test4::C"*, i32)** %[[VFTENTRY]]
|
||||
// CHECK: call x86_thiscallcc void %[[VFUN]](%"struct.test4::C"* %[[OBJ]], i32 1)
|
||||
// CHECK: %[[VPTR:.*]] = bitcast %"struct.test4::C"* %[[OBJ:.*]] to i8* (%"struct.test4::C"*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::C"*, i32)*** %[[VPTR]]
|
||||
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::C"*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::C"*, i32)** %[[VFTENTRY]]
|
||||
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::C"* %[[OBJ]], i32 1)
|
||||
// CHECK: ret
|
||||
}
|
||||
|
||||
|
@ -442,15 +442,15 @@ void destroy(E *obj) {
|
|||
// CHECK-NOT: getelementptr
|
||||
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ:.*]] to i8*
|
||||
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 4
|
||||
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to void (%"struct.test4::E"*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load void (%"struct.test4::E"*, i32)*** %[[VPTR]]
|
||||
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds void (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN:.*]] = load void (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
|
||||
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (%"struct.test4::E"*, i32)***
|
||||
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::E"*, i32)*** %[[VPTR]]
|
||||
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
|
||||
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
|
||||
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ]] to i8*
|
||||
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 4
|
||||
// FIXME: in fact, the call should take i8* and the bitcast is redundant.
|
||||
// CHECK: %[[B_as_E:.*]] = bitcast i8* %[[B_i8]] to %"struct.test4::E"*
|
||||
// CHECK: call x86_thiscallcc void %[[VFUN]](%"struct.test4::E"* %[[B_as_E]], i32 1)
|
||||
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::E"* %[[B_as_E]], i32 1)
|
||||
delete obj;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue