diff mbox

PR libstdc++/80285 optimize std::make_shared for -fno-rtti

Message ID 20170511141910.GA28678@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely May 11, 2017, 2:19 p.m. UTC
std::make_shared didn't support embedding the object within the
shared_ptr bookkeeping structure when -fno-rtti was used. This was
because the trick I use for getting the address of the embedded
object relied on typeid, so without RTTI it just creating the object
separately on the heap.

This removes the alternative code path for -fno-rtti and simply forms
a fake std::type_info& reference using reinterpret_cast. This allows
us to embed the object and get its address without typeid, so we avoid
the second allocation even when -fno-rtti is used.

	PR libstdc++/80285
	* include/bits/shared_ptr_base.h (_Sp_make_shared_tag::_S_ti): Define
	function to get unique fake std::type_info reference.
	(_Sp_counted_ptr_inplace::_M_get_deleter) [!__cpp_rtti]: Compare to
	_S_ti() fake reference.
	(__shared_ptr(_Sp_make_shared_tag, const Alloc&, Args&&...)): Share
	single implementation with or without RTTI enable.
	[!__cpp_rtti]: Pass fake reference to _M_get_deleter.
	* testsuite/20_util/shared_ptr/creation/alloc.cc: Change expected
	allocation and deallocation counts.
	* testsuite/20_util/shared_ptr/creation/single_allocation.cc: New.
	* testsuite/20_util/shared_ptr/creation/single_allocation_no_rtti.cc:
	New.

Tested powerpc64le-linux, committed to trunk.
commit 868bd0876256d0f533614458360e5dfc5545388d
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu May 11 12:34:26 2017 +0100

    PR libstdc++/80285 optimize std::make_shared for -fno-rtti
    
    	PR libstdc++/80285
    	* include/bits/shared_ptr_base.h (_Sp_make_shared_tag::_S_ti): Define
    	function to get unique fake std::type_info reference.
    	(_Sp_counted_ptr_inplace::_M_get_deleter) [!__cpp_rtti]: Compare to
    	_S_ti() fake reference.
    	(__shared_ptr(_Sp_make_shared_tag, const Alloc&, Args&&...)): Share
    	single implementation with or without RTTI enable.
    	[!__cpp_rtti]: Pass fake reference to _M_get_deleter.
    	* testsuite/20_util/shared_ptr/creation/alloc.cc: Change expected
    	allocation and deallocation counts.
    	* testsuite/20_util/shared_ptr/creation/single_allocation.cc: New.
    	* testsuite/20_util/shared_ptr/creation/single_allocation_no_rtti.cc:
    	New.
diff mbox

Patch

diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
index c32cd0f..6918579 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -68,6 +68,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #pragma GCC diagnostic pop
 #endif
 
+#if !__cpp_rtti
+  class type_info;
+#endif
+
  /**
    *  @brief  Exception possibly thrown by @c shared_ptr.
    *  @ingroup exceptions
@@ -498,7 +502,23 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // helpers for make_shared / allocate_shared
 
-  struct _Sp_make_shared_tag { };
+  struct _Sp_make_shared_tag
+  {
+#if !__cpp_rtti
+  private:
+    template<typename _Tp, _Lock_policy _Lp>
+      friend class __shared_ptr;
+    template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
+      friend class _Sp_counted_ptr_inplace;
+
+    static const type_info&
+    _S_ti() noexcept
+    {
+      static constexpr _Sp_make_shared_tag __tag;
+      return reinterpret_cast<const type_info&>(__tag);
+    }
+#endif
+  };
 
   template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
     class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp>
@@ -551,8 +571,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
 #if __cpp_rtti
 	if (__ti == typeid(_Sp_make_shared_tag))
-	  return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
+#else
+	if (&__ti == &_Sp_make_shared_tag::_S_ti())
 #endif
+	  return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
 	return nullptr;
       }
 
@@ -1295,7 +1317,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	owner_before(__weak_ptr<_Tp1, _Lp> const& __rhs) const
 	{ return _M_refcount._M_less(__rhs._M_refcount); }
 
-#if __cpp_rtti
     protected:
       // This constructor is non-standard, it is used by allocate_shared.
       template<typename _Alloc, typename... _Args>
@@ -1306,43 +1327,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // _M_ptr needs to point to the newly constructed object.
 	  // This relies on _Sp_counted_ptr_inplace::_M_get_deleter.
+#if __cpp_rtti
 	  void* __p = _M_refcount._M_get_deleter(typeid(__tag));
+#else
+	  void* __p = _M_refcount._M_get_deleter(_Sp_make_shared_tag::_S_ti());
+#endif
 	  _M_ptr = static_cast<_Tp*>(__p);
 	  _M_enable_shared_from_this_with(_M_ptr);
 	}
-#else
-      template<typename _Alloc>
-        struct _Deleter
-        {
-          void operator()(typename _Alloc::value_type* __ptr)
-          {
-	    __allocated_ptr<_Alloc> __guard{ _M_alloc, __ptr };
-	    allocator_traits<_Alloc>::destroy(_M_alloc, __guard.get());
-          }
-          _Alloc _M_alloc;
-        };
-
-      template<typename _Alloc, typename... _Args>
-	__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
-		     _Args&&... __args)
-	: _M_ptr(), _M_refcount()
-	{
-	  typedef typename allocator_traits<_Alloc>::template
-	    rebind_traits<typename std::remove_cv<_Tp>::type> __traits;
-	  _Deleter<typename __traits::allocator_type> __del = { __a };
-	  auto __guard = std::__allocate_guarded(__del._M_alloc);
-	  auto __ptr = __guard.get();
-	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
-	  // 2070. allocate_shared should use allocator_traits<A>::construct
-	  __traits::construct(__del._M_alloc, __ptr,
-			      std::forward<_Args>(__args)...);
-	  __guard = nullptr;
-	  __shared_count<_Lp> __count(__ptr, __del, __del._M_alloc);
-	  _M_refcount._M_swap(__count);
-	  _M_ptr = __ptr;
-	  _M_enable_shared_from_this_with(_M_ptr);
-	}
-#endif
 
       template<typename _Tp1, _Lock_policy _Lp1, typename _Alloc,
 	       typename... _Args>
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/alloc.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/alloc.cc
index b17387f..7e53e41 100644
--- a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/alloc.cc
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/alloc.cc
@@ -63,7 +63,7 @@  test01()
     VERIFY( p1.get() != 0 );
     VERIFY( p1.use_count() == 1 );
     VERIFY( A::ctor_count == 1 );
-    VERIFY( tracker_allocator_counter::get_allocation_count() > 0 );
+    VERIFY( tracker_allocator_counter::get_allocation_count() > sizeof(A) );
   }
   VERIFY( A::ctor_count == A::dtor_count );
   VERIFY( tracker_allocator_counter::get_allocation_count()
@@ -79,12 +79,12 @@  test02()
   
   p1 = std::allocate_shared<A>(tracker_allocator<A>(), 1);
   VERIFY( A::ctor_count == 1 );
-  VERIFY( tracker_allocator_counter::get_allocation_count() > 0 );
+  VERIFY( tracker_allocator_counter::get_allocation_count() > sizeof(A) );
 
   p1 = std::allocate_shared<A>(tracker_allocator<A>(), 1, 2.0);
   VERIFY( A::ctor_count == 2 );
   VERIFY( A::dtor_count == 1 );
-  VERIFY( tracker_allocator_counter::get_deallocation_count() > 0 );
+  VERIFY( tracker_allocator_counter::get_deallocation_count() > sizeof(A) );
 
   p1 = std::allocate_shared<A>(tracker_allocator<A>(), 1, 2.0, '3');
   VERIFY( A::ctor_count == 3 );
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation.cc
new file mode 100644
index 0000000..51b6b1b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation.cc
@@ -0,0 +1,55 @@ 
+// Copyright (C) 2017 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+template<typename T>
+struct Alloc : std::allocator<T>
+{
+  template<typename U>
+    struct rebind { using other = Alloc<U>; };
+
+  Alloc() = default;
+
+  template<typename U>
+    Alloc(const Alloc<U>&) { }
+
+  T* allocate(std::size_t n)
+  {
+    ++counter;
+    return std::allocator<T>::allocate(n);
+  }
+};
+
+
+void
+test01()
+{
+  std::allocate_shared<int>(Alloc<int>());
+  VERIFY( counter == 1 );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation_no_rtti.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation_no_rtti.cc
new file mode 100644
index 0000000..ba94f3c
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/single_allocation_no_rtti.cc
@@ -0,0 +1,56 @@ 
+// Copyright (C) 2017 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-fno-rtti" }
+// { dg-do run { target c++11 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+template<typename T>
+struct Alloc : std::allocator<T>
+{
+  template<typename U>
+    struct rebind { using other = Alloc<U>; };
+
+  Alloc() = default;
+
+  template<typename U>
+    Alloc(const Alloc<U>&) { }
+
+  T* allocate(std::size_t n)
+  {
+    ++counter;
+    return std::allocator<T>::allocate(n);
+  }
+};
+
+
+void
+test01()
+{
+  std::allocate_shared<int>(Alloc<int>());
+  VERIFY( counter == 1 );
+}
+
+int
+main()
+{
+  test01();
+}