Hi, 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. Regtested x86_64-linux, OK? libstdc++-v3/ChangeLog: * include/bits/deque.tcc: Compute maximal size of alloc_map. gcc/testsuite/ChangeLog: * g++.dg/tree-ssa/deque.C: New test. diff --git a/gcc/testsuite/g++.dg/tree-ssa/deque.C b/gcc/testsuite/g++.dg/tree-ssa/deque.C new file mode 100644 index 00000000000..c79de9b2161 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/deque.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/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc index deb010a0ebb..653354f90a7 100644 --- a/libstdc++-v3/include/bits/deque.tcc +++ b/libstdc++-v3/include/bits/deque.tcc @@ -955,6 +955,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER size_type __new_map_size = this->_M_impl._M_map_size + std::max(this->_M_impl._M_map_size, __nodes_to_add) + 2; + size_type __max = __deque_buf_size(sizeof(_Tp)); + if (__new_map_size > ((max_size() + __deque_buf_size(sizeof(_Tp)) - 1) + / __deque_buf_size(sizeof(_Tp))) * 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