implemented (disabled by default) sized delete support

gcc 5 and clang++-3.7 support sized deallocation from C++14. We are
taking advantage of that by defining sized versions of operator delete.

This is off by default so that if some existing programs that define own
global operator delete without sized variant are not broken by
tcmalloc's sized delete operator.

There is also risk of breaking exiting code that deletes objects using
wrong class (i.e. base class) without having virtual destructors.
diff --git a/configure.ac b/configure.ac
index 1148c21..321a0f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,16 +323,28 @@
 AM_CONDITIONAL(HAVE_W_NO_UNUSED_RESULT,
 	       test "$perftools_cv_w_no_unused_result" = yes)
 
-AC_CACHE_CHECK([if C++ compiler supports -fsized-deallocation],
-               perftools_cv_sized_deallocation_result,
-               [AC_LANG_PUSH(C++)
-                OLD_CXXFLAGS="$CXXFLAGS"
-                CXXFLAGS="$CXXFLAGS -fsized-deallocation"
-                AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)],
-                                  perftools_cv_sized_deallocation_result=yes,
-                                  perftools_cv_sized_deallocation_result=no)
-                CXXFLAGS="$OLD_CXXFLAGS"
-                AC_LANG_POP(C++)])
+AC_ARG_ENABLE([sized-delete],
+              [AS_HELP_STRING([--enable-sized-delete],
+                              [build sized delete operator])],
+              [enable_sized_delete="$enableval"],
+              [enable_sized_delete="no"])
+AS_IF([test "x$enable_sized_delete" = xyes],
+        [AC_DEFINE([ENABLE_SIZED_DELETE], 1, [Build sized deletion operators])
+         AC_MSG_NOTICE([Will build sized deallocation operators])],
+      [AC_MSG_NOTICE([Will not build sized deallocation operators])])
+
+AS_IF([test "x$enable_sized_delete" = xyes],
+      [AC_CACHE_CHECK([if C++ compiler supports -fsized-deallocation],
+                      perftools_cv_sized_deallocation_result,
+                      [AC_LANG_PUSH(C++)
+                       OLD_CXXFLAGS="$CXXFLAGS"
+                       CXXFLAGS="$CXXFLAGS -fsized-deallocation"
+                       AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)],
+                                         perftools_cv_sized_deallocation_result="$enable_sized_delete",
+                                         perftools_cv_sized_deallocation_result=no)
+                       CXXFLAGS="$OLD_CXXFLAGS"
+                       AC_LANG_POP(C++)])])
+
 AM_CONDITIONAL(HAVE_SIZED_DEALLOCATION,
                test "$perftools_cv_sized_deallocation_result" = yes)
 
diff --git a/src/gperftools/tcmalloc.h.in b/src/gperftools/tcmalloc.h.in
index 0334d3f..0eaa21a 100644
--- a/src/gperftools/tcmalloc.h.in
+++ b/src/gperftools/tcmalloc.h.in
@@ -122,12 +122,14 @@
   PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
                                           const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
                                             const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
   PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
                                                const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
                                                  const std::nothrow_t&) __THROW;
 }
diff --git a/src/libc_override_gcc_and_weak.h b/src/libc_override_gcc_and_weak.h
index 818e43d..5c0def2 100644
--- a/src/libc_override_gcc_and_weak.h
+++ b/src/libc_override_gcc_and_weak.h
@@ -71,6 +71,13 @@
 void operator delete[](void* p, const std::nothrow_t& nt) __THROW
     ALIAS(tc_deletearray_nothrow);
 
+#ifdef ENABLE_SIZED_DELETE
+void operator delete(void *p, size_t size) throw()
+    ALIAS(tc_delete_sized);
+void operator delete[](void *p, size_t size) throw()
+    ALIAS(tc_deletearray_sized);
+#endif
+
 extern "C" {
   void* malloc(size_t size) __THROW               ALIAS(tc_malloc);
   void free(void* ptr) __THROW                    ALIAS(tc_free);
diff --git a/src/libc_override_redefine.h b/src/libc_override_redefine.h
index a1e50f8..4c61116 100644
--- a/src/libc_override_redefine.h
+++ b/src/libc_override_redefine.h
@@ -66,6 +66,12 @@
 void operator delete[](void* ptr, const std::nothrow_t& nt) __THROW {
   return tc_deletearray_nothrow(ptr, nt);
 }
+
+#ifdef ENABLE_SIZED_DELETE
+void operator delete(void* p, size_t s) __THROW  { tc_delete_sized(p, s);     }
+void operator delete[](void* p, size_t s) __THROW{ tc_deletearray_sized(p);   }
+#endif
+
 extern "C" {
   void* malloc(size_t s) __THROW                 { return tc_malloc(s);       }
   void  free(void* p) __THROW                    { tc_free(p);                }
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index f168dfd..d1e7167 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -1595,6 +1595,24 @@
   do_free_with_callback(ptr, &InvalidFree, true, size);
 }
 
+#if defined(__GNUC__) && !defined(WIN32)
+
+extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized(void *p, size_t size) throw()
+  __attribute__((alias("tc_free_sized")));
+extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void *p, size_t size) throw()
+  __attribute__((alias("tc_free_sized")));
+
+#else
+
+extern "C" PERFTOOLS_DLL_DECL void tc_delete_sized(void *p, size_t size) throw() {
+  tc_free_sized(p, size);
+}
+extern "C" PERFTOOLS_DLL_DECL void tc_deletearray_sized(void *p, size_t size) throw() {
+  tc_free_sized(p, size);
+}
+
+#endif
+
 extern "C" PERFTOOLS_DLL_DECL void* tc_calloc(size_t n,
                                               size_t elem_size) __THROW {
   void* result = do_calloc(n, elem_size);
diff --git a/src/windows/gperftools/tcmalloc.h b/src/windows/gperftools/tcmalloc.h
index 5867c7c..9c4753f 100644
--- a/src/windows/gperftools/tcmalloc.h
+++ b/src/windows/gperftools/tcmalloc.h
@@ -112,12 +112,14 @@
   PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
                                           const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
                                             const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
   PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
                                                const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
                                                  const std::nothrow_t&) __THROW;
 }
diff --git a/src/windows/gperftools/tcmalloc.h.in b/src/windows/gperftools/tcmalloc.h.in
index a7ec70f..c963e31 100644
--- a/src/windows/gperftools/tcmalloc.h.in
+++ b/src/windows/gperftools/tcmalloc.h.in
@@ -112,12 +112,14 @@
   PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
                                           const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_delete(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
                                             const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
   PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
                                                const std::nothrow_t&) __THROW;
   PERFTOOLS_DLL_DECL void tc_deletearray(void* p) __THROW;
+  PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) throw();
   PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
                                                  const std::nothrow_t&) __THROW;
 }