https://gcc.gnu.org/g:1729147f2b1a05ad2a4e8e2d91d1d2ba914039d6

commit r15-5694-g1729147f2b1a05ad2a4e8e2d91d1d2ba914039d6
Author: Jan Hubicka <hubi...@ucw.cz>
Date:   Tue Nov 26 13:52:09 2024 +0100

    improve std::deque::_M_reallocate_map
    
    Looking into reason why we still do throw_bad_alloc in clang binary I 
noticed
    that quite few calls come from deque::_M_reallocate_map.  This patch adds
    unreachable to limit the size of realloc_map.  _M_reallocate_map is called 
only
    if new size is smaller then max_size.  map is an array holding pointers to
    entries of fixed size.
    
    Since rellocation is done by doubling the map size, I think the maximal 
size of
    map allocated is max_size / deque_buf_size rounded up times two.  This 
should
    be also safe for overflows since we have extra bit.
    
    map size is always at least 8. Theoretically this computation may be wrong 
for
    very large T, but in that case callers should never reallocate.
    
    On the testcase I get:
    jh@shroud:~> ~/trunk-install-new4/bin/g++ -O2 dq.C -c ; size -A dq.o | grep 
text
    .text                                              284      0
    .text._ZNSt5dequeIiSaIiEE17_M_reallocate_mapEmb    485      0
    .text.unlikely                                      10      0
    jh@shroud:~> ~/trunk-install-new5/bin/g++ -O2 dq.C -c ; size -A dq.o | grep 
text
    .text                                              284      0
    .text._ZNSt5dequeIiSaIiEE17_M_reallocate_mapEmb    465      0
    .text.unlikely                                      10      0
    
    so this saves about 20 bytes of rellocate_map, which I think is worthwhile.
    Curiously enough gcc14 does:
    
    jh@shroud:~> g++ -O2 dq.C -c ; size -A dq.o | grep text
    .text                 604      0
    .text.unlikely         10      0
    
    which is 145 bytes smaller. Obvoius difference is that _M_reallocate_map 
gets inlined.
    Compiling gcc14 preprocessed file with trunk gives:
    
    jh@shroud:~> g++ -O2 dq.C -S ; size -A dq.o | grep text
    .text                 762      0
    .text.unlikely         10      0
    
    So inlining is due to changes at libstdc++ side, but code size growth is 
due to
    something else.
    
    For clang this reduced number of thris_bad_new_array_length from 121 to 61.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/deque.tcc (std::deque::_M_reallocate_map): Add
            __builtin_unreachable check to declare that maps are not very large.
            * include/bits/stl_deque.h (std::deque::size): Add 
__builtin_unreachable
            to check for maximal size of map.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/tree-ssa/deque-1.C: New test.
            * g++.dg/tree-ssa/deque-2.C: New test.

Diff:
---
 gcc/testsuite/g++.dg/tree-ssa/deque-1.C |  9 +++++++++
 gcc/testsuite/g++.dg/tree-ssa/deque-2.C | 10 ++++++++++
 libstdc++-v3/include/bits/deque.tcc     |  4 ++++
 libstdc++-v3/include/bits/stl_deque.h   |  7 ++++++-
 4 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/gcc/testsuite/g++.dg/tree-ssa/deque-1.C 
b/gcc/testsuite/g++.dg/tree-ssa/deque-1.C
new file mode 100644
index 000000000000..c639ebb1a5f3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/deque-1.C
@@ -0,0 +1,9 @@
+// { dg-do compile } 
+// { dg-options "-O1 -fdump-tree-optimized" }
+#include <deque>
+void
+test(std::deque<int> &q, int v)
+{
+  q.push_back (v);
+}
+// { dg-final { scan-tree-dump-not "throw_bad_alloc" "optimized" } }
diff --git a/gcc/testsuite/g++.dg/tree-ssa/deque-2.C 
b/gcc/testsuite/g++.dg/tree-ssa/deque-2.C
new file mode 100644
index 000000000000..7e268b3f018d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/deque-2.C
@@ -0,0 +1,10 @@
+// { dg-do compile }
+// { dg-options "-O3 -fdump-tree-optimized" }
+#include <deque>
+std::deque<int *>
+test2(std::deque<int *> &q)
+{
+  return q;
+}
+// rethrow is OK, but throw is not.
+// { dg-final { scan-tree-dump-not {[^e]throw} "optimized" } }
diff --git a/libstdc++-v3/include/bits/deque.tcc 
b/libstdc++-v3/include/bits/deque.tcc
index deb010a0ebb5..ee03c917a295 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -956,6 +956,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                                     + std::max(this->_M_impl._M_map_size,
                                                __nodes_to_add) + 2;
 
+         const size_t __bufsz = __deque_buf_size(sizeof(_Tp));
+         if (__new_map_size > ((max_size() + __bufsz - 1) / __bufsz) * 2)
+           __builtin_unreachable();
+
          _Map_pointer __new_map = this->_M_allocate_map(__new_map_size);
          __new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2
                         + (__add_at_front ? __nodes_to_add : 0);
diff --git a/libstdc++-v3/include/bits/stl_deque.h 
b/libstdc++-v3/include/bits/stl_deque.h
index c617933bd81c..dd1212793ded 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -1266,7 +1266,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       _GLIBCXX_NODISCARD
       size_type
       size() const _GLIBCXX_NOEXCEPT
-      { return this->_M_impl._M_finish - this->_M_impl._M_start; }
+      {
+       size_type __sz = this->_M_impl._M_finish - this->_M_impl._M_start;
+       if (__sz > max_size ())
+         __builtin_unreachable ();
+       return __sz;
+      }
 
       /**  Returns the size() of the largest possible %deque.  */
       _GLIBCXX_NODISCARD

Reply via email to