Thanks, Dmitri.
Updated the patch, but no documents and no tests yet. I'll add them soon...
http://llvm-reviews.chandlerc.com/D298
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D298?vs=696&id=699#toc
Files:
docs/LanguageExtensions.rst
include/clang/AST/DeclCXX.h
include/clang/AST/ExprCXX.h
include/clang/Basic/LangOptions.def
include/clang/Driver/Options.td
include/clang/Sema/Sema.h
lib/AST/ExprCXX.cpp
lib/CodeGen/CGCXXABI.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CodeGenFunction.h
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
test/CodeGenCXX/intercept-allocation.cpp
test/Driver/fintercept-allocation.cpp
test/SemaCXX/intercept-new-delete.cpp
Index: docs/LanguageExtensions.rst
===================================================================
--- docs/LanguageExtensions.rst
+++ docs/LanguageExtensions.rst
@@ -1597,6 +1597,11 @@
to specify that address safety instrumentation (e.g. AddressSanitizer) should
not be applied to that function.
+Allocation Interceptor
+----------------------
+
+(to be written)
+
Thread-Safety Annotation Checking
=================================
Index: include/clang/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -2222,13 +2222,16 @@
FunctionDecl *OperatorDelete;
+ // Points to the interceptor function for the allocation function.
+ FunctionDecl *Interceptor;
+
CXXDestructorDecl(CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo,
QualType T, TypeSourceInfo *TInfo,
bool isInline, bool isImplicitlyDeclared)
: CXXMethodDecl(CXXDestructor, RD, StartLoc, NameInfo, T, TInfo, false,
SC_None, isInline, /*isConstexpr=*/false, SourceLocation()),
- ImplicitlyDefined(false), OperatorDelete(0) {
+ ImplicitlyDefined(false), OperatorDelete(0), Interceptor(0) {
setImplicit(isImplicitlyDeclared);
}
@@ -2263,6 +2266,8 @@
void setOperatorDelete(FunctionDecl *OD) { OperatorDelete = OD; }
const FunctionDecl *getOperatorDelete() const { return OperatorDelete; }
+ void setInterceptor(FunctionDecl *I) { Interceptor = I; }
+ const FunctionDecl *getInterceptor() const { return Interceptor; }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -1470,6 +1470,8 @@
/// \brief Points to the deallocation function used in case of error. May be
/// null.
FunctionDecl *OperatorDelete;
+ /// \brief Points to the interceptor function for the allocation function.
+ FunctionDecl *Interceptor;
/// \brief The allocated type-source information, as written in the source.
TypeSourceInfo *AllocatedTypeInfo;
@@ -1497,6 +1499,8 @@
// In storage, we distinguish between "none, and no initializer expr", and
// "none, but an implicit initializer expr".
unsigned StoredInitializationStyle : 2;
+ // Does the delete interceptor require a size_t argument?
+ bool DeleteInterceptorWantsSize : 1;
friend class ASTStmtReader;
friend class ASTStmtWriter;
@@ -1545,6 +1549,9 @@
FunctionDecl *getOperatorDelete() const { return OperatorDelete; }
void setOperatorDelete(FunctionDecl *D) { OperatorDelete = D; }
+ FunctionDecl *getInterceptor() const { return Interceptor; }
+ void setInterceptor(FunctionDecl *D) { Interceptor = D; }
+
bool isArray() const { return Array; }
Expr *getArraySize() {
return Array ? cast<Expr>(SubExprs[0]) : 0;
@@ -1602,6 +1609,15 @@
return UsualArrayDeleteWantsSize;
}
+ /// Answers whether the delete interceptor expects the size of the
+ /// allocation as a parameter.
+ bool doesDeleteInterceptorWantSize() const {
+ return DeleteInterceptorWantsSize;
+ }
+ void setDeleteInterceptorWantsSize(bool F) {
+ DeleteInterceptorWantsSize = F;
+ }
+
typedef ExprIterator arg_iterator;
typedef ConstExprIterator const_arg_iterator;
@@ -1654,6 +1670,8 @@
class CXXDeleteExpr : public Expr {
// Points to the operator delete overload that is used. Could be a member.
FunctionDecl *OperatorDelete;
+ /// Points to the interceptor function for the deallocation function.
+ FunctionDecl *Interceptor;
// The pointer expression to be deleted.
Stmt *Argument;
// Location of the expression.
@@ -1669,17 +1687,20 @@
// Does the usual deallocation function for the element type require
// a size_t argument?
bool UsualArrayDeleteWantsSize : 1;
+ // Does the delete interceptor require a size_t argument?
+ bool DeleteInterceptorWantsSize : 1;
public:
CXXDeleteExpr(QualType ty, bool globalDelete, bool arrayForm,
bool arrayFormAsWritten, bool usualArrayDeleteWantsSize,
FunctionDecl *operatorDelete, Expr *arg, SourceLocation loc)
: Expr(CXXDeleteExprClass, ty, VK_RValue, OK_Ordinary, false, false,
arg->isInstantiationDependent(),
arg->containsUnexpandedParameterPack()),
- OperatorDelete(operatorDelete), Argument(arg), Loc(loc),
+ OperatorDelete(operatorDelete), Interceptor(0), Argument(arg), Loc(loc),
GlobalDelete(globalDelete),
ArrayForm(arrayForm), ArrayFormAsWritten(arrayFormAsWritten),
- UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) { }
+ UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize),
+ DeleteInterceptorWantsSize(false) { }
explicit CXXDeleteExpr(EmptyShell Shell)
: Expr(CXXDeleteExprClass, Shell), OperatorDelete(0), Argument(0) { }
@@ -1695,7 +1716,22 @@
return UsualArrayDeleteWantsSize;
}
+ /// Answers whether the delete interceptor expects the size of the
+ /// allocation as a parameter.
+ bool doesDeleteInterceptorWantSize() const {
+ return DeleteInterceptorWantsSize;
+ }
+ void setDeleteInterceptorWantsSize(bool F) {
+ DeleteInterceptorWantsSize = F;
+ }
+
FunctionDecl *getOperatorDelete() const { return OperatorDelete; }
+ FunctionDecl *getInterceptor() const {
+ return Interceptor;
+ }
+ void setInterceptor(FunctionDecl *D) {
+ Interceptor = D;
+ }
Expr *getArgument() { return cast<Expr>(Argument); }
const Expr *getArgument() const { return cast<Expr>(Argument); }
Index: include/clang/Basic/LangOptions.def
===================================================================
--- include/clang/Basic/LangOptions.def
+++ include/clang/Basic/LangOptions.def
@@ -131,6 +131,8 @@
BENIGN_LANGOPT(DebuggerCastResultToId, 1, 0, "for 'po' in the debugger, cast the result to id if it is of unknown type")
BENIGN_LANGOPT(DebuggerObjCLiteral , 1, 0, "debugger Objective-C literals and subscripting support")
+BENIGN_LANGOPT(InterceptAllocation , 1, 0, "intercepting (de)allocation")
+
BENIGN_LANGOPT(SpellChecking , 1, 1, "spell-checking")
LANGOPT(SinglePrecisionConstants , 1, 0, "treating double-precision floating point constants as single precision constants")
LANGOPT(FastRelaxedMath , 1, 0, "OpenCL fast relaxed math")
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -299,6 +299,9 @@
def fno_address_sanitizer : Flag<["-"], "fno-address-sanitizer">, Group<f_Group>;
def fthread_sanitizer : Flag<["-"], "fthread-sanitizer">, Group<f_Group>;
def fno_thread_sanitizer : Flag<["-"], "fno-thread-sanitizer">, Group<f_Group>;
+def fintercept_allocation : Flag<["-"], "fintercept-allocation">, Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Intercept (de)allocation">;
+def fno_intercept_allocation : Flag<["-"], "fno-intercept-allocation">, Group<f_Group>, Flags<[CC1Option]>;
def fasm : Flag<["-"], "fasm">, Group<f_Group>;
def fasm_blocks : Flag<["-"], "fasm-blocks">, Group<f_Group>, Flags<[CC1Option]>;
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -3328,6 +3328,8 @@
NamespaceDecl *getStdNamespace() const;
NamespaceDecl *getOrCreateStdNamespace();
+ RecordDecl *getOrCreateCXXTypeInfoDecl();
+
CXXRecordDecl *getStdBadAlloc() const;
/// \brief Tests whether Ty is an instance of std::initializer_list and, if
@@ -3857,6 +3859,17 @@
DeclarationName Name, FunctionDecl* &Operator,
bool Diagnose = true);
+ bool ShouldInterceptAllocation();
+
+ /// Declares a single implicit allocation interceptor if it doesn't already
+ /// exist.
+ void DeclareAllocationInterceptor(DeclarationName Name, bool UseSize);
+
+ /// Looks up a specified allocation interpreter declaration.
+ FunctionDecl *LookupAllocationInterceptor(const char *InterceptName,
+ SourceLocation StartLoc,
+ bool UseSize);
+
/// ActOnCXXDelete - Parsed a C++ 'delete' expression
ExprResult ActOnCXXDelete(SourceLocation StartLoc,
bool UseGlobal, bool ArrayForm,
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -92,9 +92,11 @@
ty->isInstantiationDependentType(),
ty->containsUnexpandedParameterPack()),
SubExprs(0), OperatorNew(operatorNew), OperatorDelete(operatorDelete),
+ Interceptor(0),
AllocatedTypeInfo(allocatedTypeInfo), TypeIdParens(typeIdParens),
Range(Range), DirectInitRange(directInitRange),
- GlobalNew(globalNew), UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize) {
+ GlobalNew(globalNew), UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize),
+ DeleteInterceptorWantsSize(false) {
assert((initializer != 0 || initializationStyle == NoInit) &&
"Only NoInit can have no initializer.");
StoredInitializationStyle = initializer ? initializationStyle + 1 : 0;
Index: lib/CodeGen/CGCXXABI.cpp
===================================================================
--- lib/CodeGen/CGCXXABI.cpp
+++ lib/CodeGen/CGCXXABI.cpp
@@ -167,18 +167,20 @@
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())
+ // If the class's usual deallocation function or the deallocation
+ // interceptor takes two arguments, it needs a cookie.
+ if (expr->doesUsualArrayDeleteWantSize() ||
+ expr->doesDeleteInterceptorWantSize())
return true;
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())
+ // If the class's usual deallocation function or the deallocation
+ // interceptor takes two arguments, it needs a cookie.
+ if (expr->doesUsualArrayDeleteWantSize() ||
+ expr->doesDeleteInterceptorWantSize())
return true;
return expr->getAllocatedType().isDestructedType();
Index: lib/CodeGen/CGClass.cpp
===================================================================
--- lib/CodeGen/CGClass.cpp
+++ lib/CodeGen/CGClass.cpp
@@ -966,7 +966,8 @@
void Emit(CodeGenFunction &CGF, Flags flags) {
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
const CXXRecordDecl *ClassDecl = Dtor->getParent();
- CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
+ CGF.EmitDeleteCall(Dtor->getOperatorDelete(), Dtor->getInterceptor(),
+ CGF.LoadCXXThis(),
CGF.getContext().getTagDeclType(ClassDecl));
}
};
Index: lib/CodeGen/CGExprCXX.cpp
===================================================================
--- lib/CodeGen/CGExprCXX.cpp
+++ lib/CodeGen/CGExprCXX.cpp
@@ -542,7 +542,8 @@
// No cookie is required if the operator new[] being used is the
// reserved placement operator new[].
- if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
+ if (!E->doesDeleteInterceptorWantSize() &&
+ E->getOperatorNew()->isReservedGlobalPlacementOperator())
return CharUnits::Zero();
return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
@@ -1153,6 +1154,50 @@
CGF.initFullExprCleanup();
}
+bool CodeGenFunction::ShouldInterceptAllocation(
+ const FunctionDecl *Interceptor) {
+ return getContext().getLangOpts().InterceptAllocation &&
+ getContext().getLangOpts().RTTI && Interceptor;
+}
+
+RValue CodeGenFunction::EmitInterceptAllocation(
+ RValue PtrRV,
+ const FunctionDecl *Interceptor,
+ llvm::Value *Size,
+ QualType Type,
+ bool DoesDeleteInterceptorWantsSize) {
+ if (!ShouldInterceptAllocation(Interceptor))
+ return PtrRV;
+
+ const FunctionProtoType *InterceptorType =
+ Interceptor->getType()->castAs<FunctionProtoType>();
+
+ // Second Argument: const std::type_info&
+ QualType TypeOfTypeInfo = InterceptorType->getArgType(1);
+ const IdentifierInfo *identifier = TypeOfTypeInfo.getBaseTypeIdentifier();
+ if (!identifier || strcmp(identifier->getNameStart(), "type_info") != 0)
+ return PtrRV;
+ llvm::Value *TypeInfo = Builder.CreateBitCast(
+ CGM.GetAddrOfRTTIDescriptor(Type.getUnqualifiedType()),
+ ConvertType(TypeOfTypeInfo));
+
+ // Third Argument: size_t
+ QualType TypeOfSizeT;
+ if (DoesDeleteInterceptorWantsSize)
+ TypeOfSizeT = InterceptorType->getArgType(2);
+
+ // Emit
+ CallArgList InterceptorArgs;
+ InterceptorArgs.add(PtrRV, getContext().VoidPtrTy);
+ InterceptorArgs.add(RValue::get(TypeInfo), TypeOfTypeInfo);
+ if (DoesDeleteInterceptorWantsSize)
+ InterceptorArgs.add(RValue::get(Size), TypeOfSizeT);
+ return EmitCall(CGM.getTypes().arrangeFreeFunctionCall(InterceptorArgs,
+ InterceptorType),
+ CGM.GetAddrOfFunction(Interceptor), ReturnValueSlot(),
+ InterceptorArgs, Interceptor);
+}
+
llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
// The element type being allocated.
QualType allocType = getContext().getBaseElementType(E->getAllocatedType());
@@ -1227,6 +1272,11 @@
allocatorArgs, allocator);
}
+ if (ShouldInterceptAllocation(E->getInterceptor())) {
+ RV = EmitInterceptAllocation(RV, E->getInterceptor(),
+ allocSize, allocType, true);
+ }
+
// Emit a null check on the allocation result if the allocation
// function is allowed to return null (because it has a non-throwing
// exception spec; for this part, we inline
@@ -1317,30 +1367,50 @@
}
void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
+ const FunctionDecl *Interceptor,
llvm::Value *Ptr,
QualType DeleteTy) {
assert(DeleteFD->getOverloadedOperator() == OO_Delete);
const FunctionProtoType *DeleteFTy =
DeleteFD->getType()->getAs<FunctionProtoType>();
+ const FunctionProtoType *InterceptorFTy;
CallArgList DeleteArgs;
- // Check if we need to pass the size to the delete operator.
+ bool ShouldIntercept = ShouldInterceptAllocation(Interceptor);
+ bool DoesDeleteInterceptorWantsSize = false;
+ if (ShouldIntercept) {
+ InterceptorFTy = Interceptor->getType()->getAs<FunctionProtoType>();
+ DoesDeleteInterceptorWantsSize = (InterceptorFTy->getNumArgs() == 3);
+ }
+
+ // Check if we need to pass the size to the delete operator or the
+ // allocation interceptor.
llvm::Value *Size = 0;
QualType SizeTy;
- if (DeleteFTy->getNumArgs() == 2) {
- SizeTy = DeleteFTy->getArgType(1);
+ if (DeleteFTy->getNumArgs() == 2 || DoesDeleteInterceptorWantsSize) {
+ if (DeleteFTy->getNumArgs() == 2) {
+ SizeTy = DeleteFTy->getArgType(1);
+ } else {
+ SizeTy = InterceptorFTy->getArgType(2);
+ }
CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy);
Size = llvm::ConstantInt::get(ConvertType(SizeTy),
DeleteTypeSize.getQuantity());
}
QualType ArgTy = DeleteFTy->getArgType(0);
llvm::Value *DeletePtr = Builder.CreateBitCast(Ptr, ConvertType(ArgTy));
- DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
- if (Size)
+ RValue RV = RValue::get(DeletePtr);
+ if (ShouldIntercept) {
+ RV = EmitInterceptAllocation(RV, Interceptor, Size, DeleteTy,
+ DoesDeleteInterceptorWantsSize);
+ }
+ DeleteArgs.add(RV, ArgTy);
+
+ if (DeleteFTy->getNumArgs() == 2)
DeleteArgs.add(RValue::get(Size), SizeTy);
// Emit the call to delete.
@@ -1354,22 +1424,26 @@
struct CallObjectDelete : EHScopeStack::Cleanup {
llvm::Value *Ptr;
const FunctionDecl *OperatorDelete;
+ const FunctionDecl *Interceptor;
QualType ElementType;
CallObjectDelete(llvm::Value *Ptr,
const FunctionDecl *OperatorDelete,
+ const FunctionDecl *Interceptor,
QualType ElementType)
- : Ptr(Ptr), OperatorDelete(OperatorDelete), ElementType(ElementType) {}
+ : Ptr(Ptr), OperatorDelete(OperatorDelete), Interceptor(Interceptor),
+ ElementType(ElementType) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
- CGF.EmitDeleteCall(OperatorDelete, Ptr, ElementType);
+ CGF.EmitDeleteCall(OperatorDelete, Interceptor, Ptr, ElementType);
}
};
}
/// Emit the code for deleting a single object.
static void EmitObjectDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
+ const FunctionDecl *Interceptor,
llvm::Value *Ptr,
QualType ElementType,
bool UseGlobalDelete) {
@@ -1393,7 +1467,7 @@
CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
completePtr, OperatorDelete,
- ElementType);
+ Interceptor, ElementType);
}
llvm::Type *Ty =
@@ -1420,8 +1494,8 @@
// Make sure that we call delete even if the dtor throws.
// This doesn't have to a conditional cleanup because we're going
// to pop it off in a second.
- CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
- Ptr, OperatorDelete, ElementType);
+ CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup, Ptr, OperatorDelete,
+ Interceptor, ElementType);
if (Dtor)
CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete,
@@ -1457,54 +1531,77 @@
struct CallArrayDelete : EHScopeStack::Cleanup {
llvm::Value *Ptr;
const FunctionDecl *OperatorDelete;
+ const FunctionDecl *Interceptor;
llvm::Value *NumElements;
QualType ElementType;
CharUnits CookieSize;
CallArrayDelete(llvm::Value *Ptr,
const FunctionDecl *OperatorDelete,
+ const FunctionDecl *Interceptor,
llvm::Value *NumElements,
QualType ElementType,
CharUnits CookieSize)
- : Ptr(Ptr), OperatorDelete(OperatorDelete), NumElements(NumElements),
- ElementType(ElementType), CookieSize(CookieSize) {}
+ : Ptr(Ptr), OperatorDelete(OperatorDelete), Interceptor(Interceptor),
+ NumElements(NumElements), ElementType(ElementType),
+ CookieSize(CookieSize) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
const FunctionProtoType *DeleteFTy =
OperatorDelete->getType()->getAs<FunctionProtoType>();
assert(DeleteFTy->getNumArgs() == 1 || DeleteFTy->getNumArgs() == 2);
+ const FunctionProtoType *InterceptorFTy;
CallArgList Args;
-
- // Pass the pointer as the first argument.
- QualType VoidPtrTy = DeleteFTy->getArgType(0);
- llvm::Value *DeletePtr
- = CGF.Builder.CreateBitCast(Ptr, CGF.ConvertType(VoidPtrTy));
- Args.add(RValue::get(DeletePtr), VoidPtrTy);
- // Pass the original requested size as the second argument.
- if (DeleteFTy->getNumArgs() == 2) {
- QualType size_t = DeleteFTy->getArgType(1);
+ bool ShouldIntercept = CGF.ShouldInterceptAllocation(Interceptor);
+ bool DoesDeleteInterceptorWantsSize = false;
+ if (ShouldIntercept) {
+ InterceptorFTy = Interceptor->getType()->getAs<FunctionProtoType>();
+ DoesDeleteInterceptorWantsSize = (InterceptorFTy->getNumArgs() == 3);
+ }
+
+ llvm::Value *Size = 0;
+ QualType size_t;
+ if (DeleteFTy->getNumArgs() == 2 || DoesDeleteInterceptorWantsSize) {
+ if (DeleteFTy->getNumArgs() == 2) {
+ size_t = DeleteFTy->getArgType(1);
+ } else {
+ size_t = InterceptorFTy->getArgType(2);
+ }
llvm::IntegerType *SizeTy
= cast<llvm::IntegerType>(CGF.ConvertType(size_t));
CharUnits ElementTypeSize =
CGF.CGM.getContext().getTypeSizeInChars(ElementType);
// The size of an element, multiplied by the number of elements.
- llvm::Value *Size
- = llvm::ConstantInt::get(SizeTy, ElementTypeSize.getQuantity());
+ Size = llvm::ConstantInt::get(SizeTy, ElementTypeSize.getQuantity());
Size = CGF.Builder.CreateMul(Size, NumElements);
// Plus the size of the cookie if applicable.
if (!CookieSize.isZero()) {
llvm::Value *CookieSizeV
= llvm::ConstantInt::get(SizeTy, CookieSize.getQuantity());
Size = CGF.Builder.CreateAdd(Size, CookieSizeV);
}
+ }
- Args.add(RValue::get(Size), size_t);
+ // Pass the pointer as the first argument.
+ QualType VoidPtrTy = DeleteFTy->getArgType(0);
+ llvm::Value *DeletePtr
+ = CGF.Builder.CreateBitCast(Ptr, CGF.ConvertType(VoidPtrTy));
+
+ RValue RV = RValue::get(DeletePtr);
+ if (ShouldIntercept) {
+ RV = CGF.EmitInterceptAllocation(RV, Interceptor, Size, ElementType,
+ DoesDeleteInterceptorWantsSize);
}
+ Args.add(RV, VoidPtrTy);
+
+ // Pass the original requested size as the second argument.
+ if (DeleteFTy->getNumArgs() == 2)
+ Args.add(RValue::get(Size), size_t);
// Emit the call to delete.
CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Args, DeleteFTy),
@@ -1529,10 +1626,11 @@
// Make sure that we call delete even if one of the dtors throws.
const FunctionDecl *operatorDelete = E->getOperatorDelete();
+ const FunctionDecl *interceptor = E->getInterceptor();
CGF.EHStack.pushCleanup<CallArrayDelete>(NormalAndEHCleanup,
allocatedPtr, operatorDelete,
- numElements, elementType,
- cookieSize);
+ interceptor, numElements,
+ elementType, cookieSize);
// Destroy the elements.
if (QualType::DestructionKind dtorKind = elementType.isDestructedType()) {
@@ -1596,8 +1694,8 @@
if (E->isArrayForm()) {
EmitArrayDelete(*this, E, Ptr, DeleteTy);
} else {
- EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy,
- E->isGlobalDelete());
+ EmitObjectDelete(*this, E->getOperatorDelete(), E->getInterceptor(),
+ Ptr, DeleteTy, E->isGlobalDelete());
}
EmitBlock(DeleteEnd);
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -407,6 +407,14 @@
(void) Obj;
}
+ /// Push a lazily-created cleanup on the stack.
+ template <class T, class A0, class A1, class A2, class A3, class A4, class A5>
+ void pushCleanup(CleanupKind Kind, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+ void *Buffer = pushCleanup(Kind, sizeof(T));
+ Cleanup *Obj = new(Buffer) T(a0, a1, a2, a3, a4, a5);
+ (void) Obj;
+ }
+
// Feel free to add more variants of the following:
/// Push a cleanup with non-constant storage requirements on the
@@ -1837,11 +1845,18 @@
void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType,
llvm::Value *Ptr);
+ bool ShouldInterceptAllocation(const FunctionDecl *Intercept);
+
+ RValue EmitInterceptAllocation(RValue PtrRV, const FunctionDecl *Interceptor,
+ llvm::Value *Size, QualType Type,
+ bool DoesDeleteInterceptorWantsSize);
+
llvm::Value *EmitCXXNewExpr(const CXXNewExpr *E);
void EmitCXXDeleteExpr(const CXXDeleteExpr *E);
- void EmitDeleteCall(const FunctionDecl *DeleteFD, llvm::Value *Ptr,
- QualType DeleteTy);
+ void EmitDeleteCall(const FunctionDecl *DeleteFD,
+ const FunctionDecl *Interceptor,
+ llvm::Value *Ptr, QualType DeleteTy);
llvm::Value* EmitCXXTypeidExpr(const CXXTypeidExpr *E);
llvm::Value *EmitDynamicCast(llvm::Value *V, const CXXDynamicCastExpr *DCE);
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -2493,6 +2493,15 @@
if (getToolChain().SupportsProfiling())
Args.AddLastArg(CmdArgs, options::OPT_pg);
+ if (Args.hasFlag(options::OPT_fintercept_allocation,
+ options::OPT_fno_intercept_allocation, false)) {
+ CmdArgs.push_back("-fintercept-allocation");
+ // <typeinfo> is required since -fintercept-allocation depends on
+ // std::type_info.
+ CmdArgs.push_back("-include");
+ CmdArgs.push_back("typeinfo");
+ }
+
// -flax-vector-conversions is default.
if (!Args.hasFlag(options::OPT_flax_vector_conversions,
options::OPT_fno_lax_vector_conversions))
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1239,6 +1239,7 @@
Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support);
Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id);
Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal);
+ Opts.InterceptAllocation = Args.hasArg(OPT_fintercept_allocation);
Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack);
Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name);
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -5670,6 +5670,15 @@
MarkFunctionReferenced(Loc, OperatorDelete);
Destructor->setOperatorDelete(OperatorDelete);
+
+ if (ShouldInterceptAllocation()) {
+ bool UseSize = false;
+ FunctionDecl *Fn = LookupAllocationInterceptor("__intercept_delete__",
+ Loc, UseSize);
+ if (Fn) {
+ Destructor->setInterceptor(Fn);
+ }
+ }
}
return false;
@@ -6173,6 +6182,26 @@
return getStdNamespace();
}
+RecordDecl *Sema::getOrCreateCXXTypeInfoDecl() {
+ if (CXXTypeInfoDecl)
+ return CXXTypeInfoDecl;
+
+ IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info");
+ LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName);
+ NamespaceDecl *StdNamespace = getStdNamespace();
+ if (!StdNamespace)
+ return NULL;
+ LookupQualifiedName(R, StdNamespace);
+ CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
+ // Microsoft's typeinfo doesn't have type_info in std but in the global
+ // namespace if _HAS_EXCEPTIONS is defined to 0. See PR13153.
+ if (!CXXTypeInfoDecl && LangOpts.MicrosoftMode) {
+ LookupQualifiedName(R, Context.getTranslationUnitDecl());
+ CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
+ }
+ return CXXTypeInfoDecl;
+}
+
bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
assert(getLangOpts().CPlusPlus &&
"Looking for std::initializer_list outside of C++.");
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -371,26 +371,15 @@
if (!getStdNamespace())
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
- if (!CXXTypeInfoDecl) {
- IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info");
- LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName);
- LookupQualifiedName(R, getStdNamespace());
- CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
- // Microsoft's typeinfo doesn't have type_info in std but in the global
- // namespace if _HAS_EXCEPTIONS is defined to 0. See PR13153.
- if (!CXXTypeInfoDecl && LangOpts.MicrosoftMode) {
- LookupQualifiedName(R, Context.getTranslationUnitDecl());
- CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
- }
- if (!CXXTypeInfoDecl)
- return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
- }
+ RecordDecl *TypeInfoDecl = getOrCreateCXXTypeInfoDecl();
+ if (!TypeInfoDecl)
+ return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
if (!getLangOpts().RTTI) {
return ExprError(Diag(OpLoc, diag::err_no_typeid_with_fno_rtti));
}
- QualType TypeInfoType = Context.getTypeDeclType(CXXTypeInfoDecl);
+ QualType TypeInfoType = Context.getTypeDeclType(TypeInfoDecl);
if (isType) {
// The operand is a type; handle it as such.
@@ -1397,14 +1386,28 @@
}
}
- return Owned(new (Context) CXXNewExpr(Context, UseGlobal, OperatorNew,
+ CXXNewExpr *Result =
+ new (Context) CXXNewExpr(Context, UseGlobal, OperatorNew,
OperatorDelete,
UsualArrayDeleteWantsSize,
llvm::makeArrayRef(PlaceArgs, NumPlaceArgs),
TypeIdParens,
ArraySize, initStyle, Initializer,
ResultType, AllocTypeInfo,
- Range, DirectInitRange));
+ Range, DirectInitRange);
+
+ if (ShouldInterceptAllocation()) {
+ bool UseSize = false;
+ // new should always use size_t.
+ FunctionDecl *Fn = LookupAllocationInterceptor("__intercept_new__",
+ StartLoc, true);
+ if (Fn) {
+ Result->setInterceptor(Fn);
+ Result->setDeleteInterceptorWantsSize(UseSize);
+ }
+ }
+
+ return Owned(Result);
}
/// \brief Checks that a type is suitable as the allocated type
@@ -2005,6 +2008,153 @@
return false;
}
+void Sema::DeclareAllocationInterceptor(DeclarationName Name, bool UseSize) {
+ DeclContext *GlobalCtx = Context.getTranslationUnitDecl();
+
+ QualType VoidPtrType = Context.getPointerType(Context.VoidTy);
+ QualType SizeType = Context.getSizeType();
+
+ RecordDecl *TypeInfoDecl = getOrCreateCXXTypeInfoDecl();
+ QualType TypeInfoType = Context.getTypeDeclType(TypeInfoDecl);
+ QualType ConstRefTypeInfoType = Context.getLValueReferenceType(
+ TypeInfoType.withConst());
+
+ unsigned NumParams = UseSize ? 3 : 2;
+ // Check if this function is already declared.
+ {
+ DeclContext::lookup_result R = GlobalCtx->lookup(Name);
+ for (DeclContext::lookup_iterator Interceptor = R.begin(),
+ InterceptorEnd = R.end();
+ Interceptor != InterceptorEnd; ++Interceptor) {
+ // Only look at non-template functions, as it is the predefined,
+ // non-templated allocation function we are trying to declare here.
+ if (FunctionDecl *Func = dyn_cast<FunctionDecl>(*Interceptor)) {
+ if (Func->getNumParams() != NumParams)
+ continue;
+ QualType ParamType1 = Context.getCanonicalType(
+ Func->getParamDecl(0)->getType().getUnqualifiedType());
+ QualType ParamType2 = Context.getCanonicalType(
+ Func->getParamDecl(1)->getType().getUnqualifiedType());
+ if (ParamType1 == VoidPtrType && ParamType2 == ConstRefTypeInfoType)
+ return;
+ if (UseSize) {
+ QualType ParamType3 = Context.getCanonicalType(
+ Func->getParamDecl(2)->getType().getUnqualifiedType());
+ if (ParamType3 == SizeType)
+ return;
+ }
+ }
+ }
+ }
+
+ QualType ArgumentArray[3] = { VoidPtrType, ConstRefTypeInfoType, SizeType };
+
+ FunctionProtoType::ExtProtoInfo EPI;
+ QualType FnType;
+ FnType = Context.getFunctionType(VoidPtrType, ArgumentArray, NumParams, EPI);
+ FunctionDecl *Interceptor =
+ FunctionDecl::Create(Context, GlobalCtx, SourceLocation(),
+ SourceLocation(), Name,
+ FnType, /*TInfo=*/0, SC_None,
+ SC_None, false, true);
+ Interceptor->setImplicit();
+
+ SmallVector<ParmVarDecl*, 3> Params;
+ ParmVarDecl *Param1 = ParmVarDecl::Create(Context, Interceptor,
+ SourceLocation(), SourceLocation(),
+ 0, VoidPtrType,
+ /*TInfo=*/0, SC_None, SC_None, 0);
+ Params.push_back(Param1);
+ ParmVarDecl *Param2 = ParmVarDecl::Create(Context, Interceptor,
+ SourceLocation(), SourceLocation(),
+ 0, ConstRefTypeInfoType,
+ /*TInfo=*/0, SC_None, SC_None, 0);
+ Params.push_back(Param2);
+ if (UseSize) {
+ ParmVarDecl *Param3 = ParmVarDecl::Create(Context, Interceptor,
+ SourceLocation(),
+ SourceLocation(),
+ 0, SizeType,
+ /*TInfo=*/0, SC_None, SC_None, 0);
+ Params.push_back(Param3);
+ }
+ Interceptor->setParams(Params);
+
+ // FIXME: Also add this declaration to the IdentifierResolver, but
+ // make sure it is at the end of the chain to coincide with the
+ // global scope.
+ Context.getTranslationUnitDecl()->addDecl(Interceptor);
+}
+
+bool Sema::ShouldInterceptAllocation() {
+ return getLangOpts().InterceptAllocation && getLangOpts().RTTI;
+}
+
+FunctionDecl* Sema::LookupAllocationInterceptor(const char *InterceptorName,
+ SourceLocation StartLoc,
+ bool UseSize) {
+ IdentifierInfo *InterceptorInfo =
+ &PP.getIdentifierTable().get(InterceptorName);
+ DeclareAllocationInterceptor(
+ DeclarationName(InterceptorInfo), UseSize);
+ LookupResult R(*this, InterceptorInfo, SourceLocation(), LookupOrdinaryName);
+ LookupQualifiedName(R, Context.getTranslationUnitDecl());
+
+ RecordDecl* TypeInfoDecl = getOrCreateCXXTypeInfoDecl();
+ if (!R.empty() && !R.isAmbiguous() && TypeInfoDecl) {
+ R.suppressDiagnostics();
+
+ QualType TypeInfoType = Context.getTypeDeclType(TypeInfoDecl);
+ TypeSourceInfo* TemporaryTypeSourceInfo =
+ Context.getTrivialTypeSourceInfo(Context.VoidPtrTy);
+ CXXTypeidExpr* TemporaryTypeidExpr =
+ new (Context) CXXTypeidExpr(TypeInfoType.withConst(),
+ TemporaryTypeSourceInfo,
+ SourceRange(StartLoc, StartLoc));
+
+ OverloadCandidateSet Candidates(StartLoc);
+ for (LookupResult::iterator Interceptor = R.begin(),
+ InterceptorEnd = R.end();
+ Interceptor != InterceptorEnd; ++Interceptor) {
+ // Even member operator new/delete are implicitly treated as
+ // static, so don't use AddMemberCandidate.
+ NamedDecl *D = (*Interceptor)->getUnderlyingDecl();
+ FunctionDecl *Fn = cast<FunctionDecl>(D);
+
+ CXXNullPtrLiteralExpr Null(Context.VoidPtrTy, SourceLocation());
+ Expr* InterceptorArgs[3];
+ InterceptorArgs[0] = &Null;
+ InterceptorArgs[1] = TemporaryTypeidExpr;
+ if (UseSize) {
+ IntegerLiteral Size(Context, llvm::APInt::getNullValue(
+ Context.getTargetInfo().getPointerWidth(0)),
+ Context.getSizeType(), SourceLocation());
+ InterceptorArgs[2] = &Size;
+ AddOverloadCandidate(Fn, Interceptor.getPair(),
+ llvm::makeArrayRef(InterceptorArgs, 3), Candidates,
+ /*SuppressUserConversions=*/false);
+ } else {
+ AddOverloadCandidate(Fn, Interceptor.getPair(),
+ llvm::makeArrayRef(InterceptorArgs, 2), Candidates,
+ /*SuppressUserConversions=*/false);
+ }
+ }
+
+ // Do the resolution.
+ OverloadCandidateSet::iterator Best;
+ switch (Candidates.BestViableFunction(*this, StartLoc, Best)) {
+ case OR_Success: {
+ FunctionDecl* Fn = Best->Function;
+ MarkFunctionReferenced(StartLoc, Fn);
+ return Fn;
+ }
+ default:
+ ;
+ }
+ }
+ return NULL;
+}
+
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
/// or
@@ -2207,10 +2357,23 @@
}
- return Owned(new (Context) CXXDeleteExpr(Context.VoidTy, UseGlobal, ArrayForm,
- ArrayFormAsWritten,
- UsualArrayDeleteWantsSize,
- OperatorDelete, Ex.take(), StartLoc));
+ CXXDeleteExpr *Result =
+ new (Context) CXXDeleteExpr(Context.VoidTy, UseGlobal, ArrayForm,
+ ArrayFormAsWritten,
+ UsualArrayDeleteWantsSize,
+ OperatorDelete, Ex.take(), StartLoc);
+
+ if (ShouldInterceptAllocation()) {
+ bool UseSize = false;
+ FunctionDecl *Fn = LookupAllocationInterceptor("__intercept_delete__",
+ StartLoc, UseSize);
+ if (Fn) {
+ Result->setInterceptor(Fn);
+ Result->setDeleteInterceptorWantsSize(UseSize);
+ }
+ }
+
+ return Owned(Result);
}
/// \brief Check the use of the given variable as a C++ condition in an if,
Index: test/CodeGenCXX/intercept-allocation.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/intercept-allocation.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -fintercept-allocation -triple "i686-unknown-unknown" -emit-llvm -x c %s -o - -O3 | FileCheck %s
+// RUN: %clang_cc1 -fintercept-allocation -triple "x86_64-unknown-unknown" -emit-llvm -x c %s -o - -O3 | FileCheck %s
+// RUN: %clang_cc1 -fintercept-allocation -triple "x86_64-mingw32" -emit-llvm -x c %s -o - -O3 | FileCheck %s
+
+// (to be written)
+
+void* __intercept_new__(
+ void* address, const std::type_info& type, std::size_t size) {
+ return address;
+}
+
+void* __intercept_delete__(
+ void* address, const std::type_info& type, std::size_t size) {
+ return address;
+}
+
+void* __intercept_delete__(
+ void* address, const std::type_info& type) {
+ return address;
+}
+
+void test_int() {
+ // CHECK: @test_addcs
+ // CHECK: %{{.+}} = {{.*}} call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %x, i16 %y)
+ // CHECK: %{{.+}} = extractvalue { i16, i1 } %{{.+}}, 1
+ // CHECK: %{{.+}} = extractvalue { i16, i1 } %{{.+}}, 0
+ // CHECK: %{{.+}} = {{.*}} call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %{{.+}}, i16 %carryin)
+ // CHECK: %{{.+}} = extractvalue { i16, i1 } %{{.+}}, 1
+ // CHECK: %{{.+}} = extractvalue { i16, i1 } %{{.+}}, 0
+ // CHECK: %{{.+}} = or i1 %{{.+}}, %{{.+}}
+ // CHECK: %{{.+}} = zext i1 %{{.+}} to i16
+ // CHECK: store i16 %{{.+}}, i16* %z, align 2
+
+ int *i;
+ i = new int(3);
+ delete i;
+}
+
+void test_string() {
+ char *s;
+ s = new char[12];
+ delete[] s;
+}
+
+struct Klass {
+ int x;
+ Klass() { x = 1; }
+ ~Klass() {}
+};
+
+void test_class() {
+ Klass *k;
+ k = new Klass();
+ delete k;
+}
+
+struct VirtualKlass {
+ char a;
+ VirtualKlass() { a = 3; }
+ virtual ~VirtualKlass() {}
+};
+
+void test_class_virtual_destructor() {
+ VirtualKlass *v;
+ v = new VirtualKlass();
+ delete v;
+}
Index: test/Driver/fintercept-allocation.cpp
===================================================================
--- /dev/null
+++ test/Driver/fintercept-allocation.cpp
@@ -0,0 +1,11 @@
+// Check that we pass -fintercept-allocation to frontend.
+
+// (to be written)
+
+//
+// RUN: %clang -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RETAIN
+// RUN: %clang -c %s -fretain-comments-from-system-headers -### 2>&1 | FileCheck %s --check-prefix=CHECK-RETAIN
+//
+// CHECK-RETAIN: -fintercept-allocation
+//
+// CHECK-NO-RETAIN-NOT: -fintercept-allocation
Index: test/SemaCXX/intercept-new-delete.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/intercept-new-delete.cpp
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 -fintercept-allocation -verify %s
+
+// (to be written)
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits