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

Reply via email to