Refactor the C++ ABI code a little bit to take advantage of

what I'm going to treat as basically universal properties of
array-cookie code.  Implement MS array cookies on top of that.
Based on a patch by Timur Iskhodzhanov!

llvm-svn: 155886
This commit is contained in:
John McCall 2012-05-01 05:23:51 +00:00
parent 0eacda5f69
commit b91cd6687c
7 changed files with 249 additions and 166 deletions

View File

@ -145,6 +145,13 @@ void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
}
CharUnits CGCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
if (!requiresArrayCookie(expr))
return CharUnits::Zero();
return getArrayCookieSizeImpl(expr->getAllocatedType());
}
CharUnits CGCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// BOGUS
return CharUnits::Zero();
}
@ -158,16 +165,53 @@ llvm::Value *CGCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
return 0;
}
void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr, QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize) {
ErrorUnsupportedABI(CGF, "array cookie reading");
bool CGCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
QualType elementType) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
// This should be enough to avoid assertions.
NumElements = 0;
AllocPtr = llvm::Constant::getNullValue(CGF.Builder.getInt8PtrTy());
CookieSize = CharUnits::Zero();
return elementType.isDestructedType();
}
bool CGCXXABI::requiresArrayCookie(const CXXNewExpr *expr) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
return expr->getAllocatedType().isDestructedType();
}
void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *ptr,
const CXXDeleteExpr *expr, QualType eltTy,
llvm::Value *&numElements,
llvm::Value *&allocPtr, CharUnits &cookieSize) {
// Derive a char* in the same address space as the pointer.
unsigned AS = cast<llvm::PointerType>(ptr->getType())->getAddressSpace();
llvm::Type *charPtrTy = CGF.Int8Ty->getPointerTo(AS);
ptr = CGF.Builder.CreateBitCast(ptr, charPtrTy);
// If we don't need an array cookie, bail out early.
if (!requiresArrayCookie(expr, eltTy)) {
allocPtr = ptr;
numElements = 0;
cookieSize = CharUnits::Zero();
return;
}
cookieSize = getArrayCookieSizeImpl(eltTy);
allocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(ptr,
-cookieSize.getQuantity());
numElements = readArrayCookieImpl(CGF, allocPtr, cookieSize);
}
llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *ptr,
CharUnits cookieSize) {
ErrorUnsupportedABI(CGF, "reading a new[] cookie");
return llvm::ConstantInt::get(CGF.SizeTy, 0);
}
void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,

View File

@ -71,6 +71,9 @@ protected:
ASTContext &getContext() const { return CGM.getContext(); }
virtual bool requiresArrayCookie(const CXXDeleteExpr *E, QualType eltType);
virtual bool requiresArrayCookie(const CXXNewExpr *E);
public:
virtual ~CGCXXABI();
@ -209,7 +212,8 @@ public:
/// \param NewPtr - a char* which is the presumed-non-null
/// return value of the allocation function
/// \param NumElements - the computed number of elements,
/// potentially collapsed from the multidimensional array case
/// potentially collapsed from the multidimensional array case;
/// always a size_t
/// \param ElementType - the base element allocated type,
/// i.e. the allocated type after stripping all array types
virtual llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
@ -236,6 +240,25 @@ public:
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
protected:
/// Returns the extra size required in order to store the array
/// cookie for the given type. Assumes that an array cookie is
/// required.
virtual CharUnits getArrayCookieSizeImpl(QualType elementType);
/// Reads the array cookie for an allocation which is known to have one.
/// This is called by the standard implementation of ReadArrayCookie.
///
/// \param ptr - a pointer to the allocation made for an array, as a char*
/// \param cookieSize - the computed cookie size of an array
/// Other parameters are as above.
/// \return a size_t
virtual llvm::Value *readArrayCookieImpl(CodeGenFunction &IGF,
llvm::Value *ptr,
CharUnits cookieSize);
public:
/*************************** Static local guards ****************************/
/// Emits the guarded initializer and destructor setup for the given

View File

@ -138,6 +138,7 @@ namespace CodeGen {
union {
unsigned char PointerAlignInBytes;
unsigned char PointerSizeInBytes;
unsigned char SizeSizeInBytes; // sizeof(size_t)
};
};

View File

@ -48,10 +48,6 @@ protected:
return PtrDiffTy;
}
bool NeedsArrayCookie(const CXXNewExpr *expr);
bool NeedsArrayCookie(const CXXDeleteExpr *expr,
QualType elementType);
public:
ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) :
CGCXXABI(CGM), PtrDiffTy(0), IsARM(IsARM) { }
@ -111,16 +107,15 @@ public:
void EmitInstanceFunctionProlog(CodeGenFunction &CGF);
CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
CharUnits getArrayCookieSizeImpl(QualType elementType);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize);
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
llvm::GlobalVariable *DeclPtr, bool PerformInit);
@ -148,16 +143,14 @@ public:
void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResTy);
CharUnits GetArrayCookieSize(const CXXNewExpr *expr);
CharUnits getArrayCookieSizeImpl(QualType elementType);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, llvm::Value *allocPtr,
CharUnits cookieSize);
private:
/// \brief Returns true if the given instance method is one of the
@ -796,54 +789,11 @@ void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
/************************** Array allocation cookies **************************/
bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
// Automatic Reference Counting:
// We need an array cookie for pointers with strong or weak lifetime.
QualType AllocatedType = expr->getAllocatedType();
if (getContext().getLangOpts().ObjCAutoRefCount &&
AllocatedType->isObjCLifetimeType()) {
switch (AllocatedType.getObjCLifetime()) {
case Qualifiers::OCL_None:
case Qualifiers::OCL_ExplicitNone:
case Qualifiers::OCL_Autoreleasing:
return false;
case Qualifiers::OCL_Strong:
case Qualifiers::OCL_Weak:
return true;
}
}
// Otherwise, if the class has a non-trivial destructor, it always
// needs a cookie.
const CXXRecordDecl *record =
AllocatedType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
return (record && !record->hasTrivialDestructor());
}
bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr,
QualType elementType) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (expr->doesUsualArrayDeleteWantSize())
return true;
return elementType.isDestructedType();
}
CharUnits ItaniumCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
if (!NeedsArrayCookie(expr))
return CharUnits::Zero();
// Padding is the maximum of sizeof(size_t) and alignof(elementType)
ASTContext &Ctx = getContext();
return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()),
Ctx.getTypeAlignInChars(expr->getAllocatedType()));
CharUnits ItaniumCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// The array cookie is a size_t; pad that up to the element alignment.
// The cookie is actually right-justified in that space.
return std::max(CharUnits::fromQuantity(CGM.SizeSizeInBytes),
CGM.getContext().getTypeAlignInChars(elementType));
}
llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
@ -851,7 +801,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
assert(NeedsArrayCookie(expr));
assert(requiresArrayCookie(expr));
unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace();
@ -862,6 +812,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
// The size of the cookie.
CharUnits CookieSize =
std::max(SizeSize, Ctx.getTypeAlignInChars(ElementType));
assert(CookieSize == getArrayCookieSizeImpl(ElementType));
// Compute an offset to the cookie.
llvm::Value *CookiePtr = NewPtr;
@ -882,53 +833,25 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
CookieSize.getQuantity());
}
void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr,
CharUnits &CookieSize) {
// Derive a char* in the same address space as the pointer.
unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace();
llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
llvm::Value *ItaniumCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize) {
// The element size is right-justified in the cookie.
llvm::Value *numElementsPtr = allocPtr;
CharUnits numElementsOffset =
cookieSize - CharUnits::fromQuantity(CGF.SizeSizeInBytes);
if (!numElementsOffset.isZero())
numElementsPtr =
CGF.Builder.CreateConstInBoundsGEP1_64(numElementsPtr,
numElementsOffset.getQuantity());
// If we don't need an array cookie, bail out early.
if (!NeedsArrayCookie(expr, ElementType)) {
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
NumElements = 0;
CookieSize = CharUnits::Zero();
return;
}
QualType SizeTy = getContext().getSizeType();
CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy);
llvm::Type *SizeLTy = CGF.ConvertType(SizeTy);
CookieSize
= std::max(SizeSize, getContext().getTypeAlignInChars(ElementType));
CharUnits NumElementsOffset = CookieSize - SizeSize;
// Compute the allocated pointer.
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
-CookieSize.getQuantity());
llvm::Value *NumElementsPtr = AllocPtr;
if (!NumElementsOffset.isZero())
NumElementsPtr =
CGF.Builder.CreateConstInBoundsGEP1_64(NumElementsPtr,
NumElementsOffset.getQuantity());
NumElementsPtr =
CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS));
NumElements = CGF.Builder.CreateLoad(NumElementsPtr);
unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
numElementsPtr =
CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS));
return CGF.Builder.CreateLoad(numElementsPtr);
}
CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
if (!NeedsArrayCookie(expr))
return CharUnits::Zero();
CharUnits ARMCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// On ARM, the cookie is always:
// struct array_cookie {
// std::size_t element_size; // element_size != 0
@ -936,7 +859,7 @@ CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) {
// };
// TODO: what should we do if the allocated type actually wants
// greater alignment?
return getContext().getTypeSizeInChars(getContext().getSizeType()) * 2;
return CharUnits::fromQuantity(2 * CGM.SizeSizeInBytes);
}
llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
@ -944,7 +867,7 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType) {
assert(NeedsArrayCookie(expr));
assert(requiresArrayCookie(expr));
// NewPtr is a char*.
@ -975,44 +898,18 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
CookieSize.getQuantity());
}
void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF,
llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType,
llvm::Value *&NumElements,
llvm::Value *&AllocPtr,
CharUnits &CookieSize) {
// Derive a char* in the same address space as the pointer.
unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace();
llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS);
llvm::Value *ARMCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize) {
// The number of elements is at offset sizeof(size_t) relative to
// the allocated pointer.
llvm::Value *numElementsPtr
= CGF.Builder.CreateConstInBoundsGEP1_64(allocPtr, CGF.SizeSizeInBytes);
// If we don't need an array cookie, bail out early.
if (!NeedsArrayCookie(expr, ElementType)) {
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
NumElements = 0;
CookieSize = CharUnits::Zero();
return;
}
QualType SizeTy = getContext().getSizeType();
CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy);
llvm::Type *SizeLTy = CGF.ConvertType(SizeTy);
// The cookie size is always 2 * sizeof(size_t).
CookieSize = 2 * SizeSize;
// The allocated pointer is the input ptr, minus that amount.
AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy);
AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
-CookieSize.getQuantity());
// The number of elements is at offset sizeof(size_t) relative to that.
llvm::Value *NumElementsPtr
= CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr,
SizeSize.getQuantity());
NumElementsPtr =
CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS));
NumElements = CGF.Builder.CreateLoad(NumElementsPtr);
unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
numElementsPtr =
CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS));
return CGF.Builder.CreateLoad(numElementsPtr);
}
/*********************** Static local initialization **************************/

View File

@ -78,17 +78,77 @@ public:
// delete[] p;
// }
// Whereas it prints "104" and "104" if you give A a destructor.
void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr,
const CXXDeleteExpr *expr,
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize) {
CGF.CGM.ErrorUnsupported(expr, "don't know how to handle array cookies "
"in the Microsoft C++ ABI");
}
bool requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType);
bool requiresArrayCookie(const CXXNewExpr *expr);
CharUnits getArrayCookieSizeImpl(QualType type);
llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *NewPtr,
llvm::Value *NumElements,
const CXXNewExpr *expr,
QualType ElementType);
llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize);
};
}
bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr,
QualType elementType) {
// Microsoft seems to completely ignore the possibility of a
// two-argument usual deallocation function.
return elementType.isDestructedType();
}
bool MicrosoftCXXABI::requiresArrayCookie(const CXXNewExpr *expr) {
// Microsoft seems to completely ignore the possibility of a
// two-argument usual deallocation function.
return expr->getAllocatedType().isDestructedType();
}
CharUnits MicrosoftCXXABI::getArrayCookieSizeImpl(QualType type) {
// The array cookie is always a size_t; we then pad that out to the
// alignment of the element type.
ASTContext &Ctx = getContext();
return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()),
Ctx.getTypeAlignInChars(type));
}
llvm::Value *MicrosoftCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
llvm::Value *allocPtr,
CharUnits cookieSize) {
unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace();
llvm::Value *numElementsPtr =
CGF.Builder.CreateBitCast(allocPtr, CGF.SizeTy->getPointerTo(AS));
return CGF.Builder.CreateLoad(numElementsPtr);
}
llvm::Value* MicrosoftCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
llvm::Value *newPtr,
llvm::Value *numElements,
const CXXNewExpr *expr,
QualType elementType) {
assert(requiresArrayCookie(expr));
// The size of the cookie.
CharUnits cookieSize = getArrayCookieSizeImpl(elementType);
// Compute an offset to the cookie.
llvm::Value *cookiePtr = newPtr;
// Write the number of elements into the appropriate slot.
unsigned AS = cast<llvm::PointerType>(newPtr->getType())->getAddressSpace();
llvm::Value *numElementsPtr
= CGF.Builder.CreateBitCast(cookiePtr, CGF.SizeTy->getPointerTo(AS));
CGF.Builder.CreateStore(numElements, numElementsPtr);
// Finally, compute a pointer to the actual data buffer by skipping
// over the cookie completely.
return CGF.Builder.CreateConstInBoundsGEP1_64(newPtr,
cookieSize.getQuantity());
}
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}

View File

@ -113,7 +113,6 @@ void operator_new_delete() {
char *array = new char[42];
// CHECK: @"\01??_U@YAPAXI@Z"
// FIXME: enable once PR12333 is fixed
// delete [] array;
// Should be: @"\01??_V@YAXPAX@Z"
delete [] array;
// CHECK: @"\01??_V@YAXPAX@Z"
}

View File

@ -0,0 +1,59 @@
// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
struct ClassWithoutDtor {
char x;
};
void check_array_no_cookies() {
// CHECK: define void @"\01?check_array_no_cookies@@YAXXZ"() nounwind
// CHECK: call noalias i8* @"\01??_U@YAPAXI@Z"(i32 42)
ClassWithoutDtor *array = new ClassWithoutDtor[42];
// CHECK: call void @"\01??_V@YAXPAX@Z"(
delete [] array;
}
struct ClassWithDtor {
char x;
~ClassWithDtor() {}
};
void check_array_cookies_simple() {
// CHECK: define {{.*}} @"\01?check_array_cookies_simple@@YAXXZ"()
ClassWithDtor *array = new ClassWithDtor[42];
// CHECK: [[ALLOCATED:%.*]] = call noalias i8* @"\01??_U@YAPAXI@Z"(i32 46)
// 46 = 42 + size of cookie (4)
// CHECK: [[COOKIE:%.*]] = bitcast i8* [[ALLOCATED]] to i32*
// CHECK: store i32 42, i32* [[COOKIE]]
// CHECK: [[ARRAY:%.*]] = getelementptr inbounds i8* [[ALLOCATED]], i{{[0-9]+}} 4
// CHECK: bitcast i8* [[ARRAY]] to %struct.ClassWithDtor*
delete [] array;
// CHECK: [[ARRAY_AS_CHAR:%.*]] = bitcast %struct.ClassWithDtor* %3 to i8*
// CHECK: getelementptr inbounds i8* [[ARRAY_AS_CHAR]], i{{[0-9]+}} -4
}
struct __attribute__((aligned(8))) ClassWithAlignment {
// FIXME: replace __attribute__((aligned(8))) with __declspec(align(8)) once
// http://llvm.org/bugs/show_bug.cgi?id=12631 is fixed.
int *x, *y;
~ClassWithAlignment() {}
};
void check_array_cookies_aligned() {
// CHECK: define {{.*}} @"\01?check_array_cookies_aligned@@YAXXZ"()
ClassWithAlignment *array = new ClassWithAlignment[42];
// CHECK: [[ALLOCATED:%.*]] = call noalias i8* @"\01??_U@YAPAXI@Z"(i32 344)
// 344 = 42*8 + size of cookie (8, due to aligment)
// CHECK: [[COOKIE:%.*]] = bitcast i8* [[ALLOCATED]] to i32*
// CHECK: store i32 42, i32* [[COOKIE]]
// CHECK: [[ARRAY:%.*]] = getelementptr inbounds i8* [[ALLOCATED]], i{{[0-9]+}} 8
// CHECK: bitcast i8* [[ARRAY]] to %struct.ClassWithAlignment*
delete [] array;
// CHECK: [[ARRAY_AS_CHAR:%.*]] = bitcast %struct.ClassWithAlignment* %3 to i8*
// CHECK: getelementptr inbounds i8* [[ARRAY_AS_CHAR]], i{{[0-9]+}} -8
}