Re: [PATCH] Fix gnu-versioned-namespace build

2019-12-11 Thread François Dumont

On 12/11/19 9:44 PM, Jonathan Wakely wrote:

On 11/12/19 21:23 +0100, François Dumont wrote:

On 12/11/19 12:16 PM, Jonathan Wakely wrote:

On 11/12/19 08:29 +0100, François Dumont wrote:

I plan to commit this tomorrow.

Note that rather than just adding the missing 
_GLIBCXX_[BEGIN,END]_VERSION_NAMESPACE I also move anonymous 
namespace usage outside std namespace. Let me know if it was 
intentional.


It was intentional, why move it?


I just don't get the intention so I proposed to move it. But there 
are indeed other usages of this pattern in other src files.


Note that in src/c++11/debug.cc I am using anonymous namespace at 
global scope, is that wrong ?


It's certainly more fragile, so arguably it's wrong, yes.

Consider:

// some libc function in a system header we don't control:
extern "C" void __foo();

// libstdc++ code in a .cc file:
namespace
{
 void foo() { }
}
namespace std
{
 void bar() { foo(); }
}

This fails to compile, because the name foo is ambiguous in the global
scope. We don't control the libc headers, so we don't know all the
names they might declare at global scope.

If you don't put the unnamed namespace at global scope, the problem
simply doesn't exist:

namespace std
{
 namespace
 {
   void foo() { }
 }

 void bar() { foo(); }
}

Now it doesn't matter what names libc puts in the global scope,
because we're never looking for foo in the global scope.

It's obviously better to add our declarations to our own namespace
that we control, not to the global namespace (and an unnamed namespace
at global scope effectively adds the names to the global namespace).



Adding the BEGIN/END_VERSION macros is unnecessary. Those namespaces
are inline, so std::random_device already refers to
std::__8::random_device when the original declaration was in the
versioned namespace.


Ok. I must confess I wasn't clear about this but looking at other src 
files, at least in src/c++11, was showing that it is done almost 
always this way, I guess we could cleanup those files.




The only fix needed here seems to be qualifying std::isdigit (and
strictly-speaking we should also include  to declare that).


Like in attached patch ?


Did you attach the wrong patch?



Indeed, here is the correct one.


diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc
index 10fbe1dc4c4..04edc582b69 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -41,6 +41,7 @@
 
 #include 
 #include 
+#include  // For std::isdigit.
 
 #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H
 # include 
@@ -286,7 +287,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _M_mt.seed(seed);
 #else
 // Convert old default token "mt19937" or numeric seed tokens to "default".
-if (token == "mt19937" || isdigit((unsigned char)token[0]))
+if (token == "mt19937" || std::isdigit((unsigned char)token[0]))
   _M_init("default");
 else
   _M_init(token);


[PATCH] Fix gnu-versioned-namespace tr1 declaration

2019-12-11 Thread François Dumont

On 12/11/19 12:22 PM, Jonathan Wakely wrote:

On 11/12/19 11:16 +, Jonathan Wakely wrote:

On 11/12/19 08:29 +0100, François Dumont wrote:

I plan to commit this tomorrow.

Note that rather than just adding the missing 
_GLIBCXX_[BEGIN,END]_VERSION_NAMESPACE I also move anonymous 
namespace usage outside std namespace. Let me know if it was 
intentional.


It was intentional, why move it?

Adding the BEGIN/END_VERSION macros is unnecessary. Those namespaces
are inline, so std::random_device already refers to
std::__8::random_device when the original declaration was in the
versioned namespace.

The only fix needed here seems to be qualifying std::isdigit (and
strictly-speaking we should also include  to declare that).


I was curious why that qualification is needed. Th problem is that
 is being indirectly included by some other header, and so is
, so the declarations visible are ::isdigit(int) and
std::__8::isdigit(CharT, const locale&). Even after including
 we still can't call it unqualified, because  doesn't
use the versioned namespace:


Yes, this is the other patch I wanted to propose. Make sure that tr1 
namespace is always defined consistently with the version namespace.


For instance 17_intro/headers/c++2011/parallel_mode.cc is failing at the 
moment with:


/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/tr1/gamma.tcc:292: 
error: reference to 'tr1' is ambiguous
In file included from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/tr1/random:45,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/random_number.h:36,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/partition.h:38,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/quicksort.h:36,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/sort.h:48,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/algo.h:45,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/algorithm:37,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/algorithm:80,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/stdc++.h:65,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/extc++.h:32,
 from 
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/17_intro/headers/c++2011/parallel_mode.cc:24:
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/tr1/type_traits:40: 
note: candidates are: 'namespace std::__8::tr1 { }'
In file included from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/types.h:37,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/parallel.h:38,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/base.h:40,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/parallel/algobase.h:40,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_algobase.h:2071,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:39,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/ios:40,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/istream:38,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/sstream:38,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/complex:45,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/ccomplex:39,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/stdc++.h:54,
 from 
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/extc++.h:32,
 from 
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/17_intro/headers/c++2011/parallel_mode.cc:24:
/home/fdt/dev/gcc/build_versioned_ns/x86_64-pc-linux-gnu/libstdc++-v3/include/tr1/cstdint:61: 
note: 'namespace std::tr1 { }'


Tested under Linux x86_64 normal and versioned namespace.

Ok to commit ?

François


diff --git a/libstdc++-v3/include/tr1/cctype b/libstdc++-v3/include/tr1/cctype
index ce994066188..b35cd04f0db 10

Re: [PATCH] Fix gnu-versioned-namespace build

2019-12-11 Thread François Dumont

On 12/11/19 12:16 PM, Jonathan Wakely wrote:

On 11/12/19 08:29 +0100, François Dumont wrote:

I plan to commit this tomorrow.

Note that rather than just adding the missing 
_GLIBCXX_[BEGIN,END]_VERSION_NAMESPACE I also move anonymous 
namespace usage outside std namespace. Let me know if it was 
intentional.


It was intentional, why move it?


I just don't get the intention so I proposed to move it. But there are 
indeed other usages of this pattern in other src files.


Note that in src/c++11/debug.cc I am using anonymous namespace at global 
scope, is that wrong ?




Adding the BEGIN/END_VERSION macros is unnecessary. Those namespaces
are inline, so std::random_device already refers to
std::__8::random_device when the original declaration was in the
versioned namespace.


Ok. I must confess I wasn't clear about this but looking at other src 
files, at least in src/c++11, was showing that it is done almost always 
this way, I guess we could cleanup those files.




The only fix needed here seems to be qualifying std::isdigit (and
strictly-speaking we should also include  to declare that).


Like in attached patch ?

I am including it unconditionnaly with other C wrapping headers like 
, is that fine ?


At least it builds fine.




    * src/c++11/random.cc: Add _GLIBCXX_BEGIN_NAMESPACE_VERSION and
    _GLIBCXX_END_NAMESPACE_VERSION. Move anonymous namespace outside std
    namespace.

Tested under Linux x86_64 normal/debug/versioned namespace modes.

There are still tests failing in versioned namespace, more patches to 
come.


François



diff --git a/libstdc++-v3/src/c++11/random.cc 
b/libstdc++-v3/src/c++11/random.cc

index 10fbe1dc4c4..d4ebc9556ab 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -73,8 +73,6 @@
# define USE_MT19937 1
#endif

-namespace std _GLIBCXX_VISIBILITY(default)
-{
namespace
{
#if USE_RDRAND
@@ -124,6 +122,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
#endif
}

+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
  void
  random_device::_M_init(const std::string& token)
  {
@@ -286,7 +288,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
    _M_mt.seed(seed);
#else
    // Convert old default token "mt19937" or numeric seed tokens to 
"default".

-    if (token == "mt19937" || isdigit((unsigned char)token[0]))
+    if (token == "mt19937" || std::isdigit((unsigned char)token[0]))
  _M_init("default");
    else
  _M_init(token);
@@ -407,5 +409,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
    0x9d2c5680UL, 15,
    0xefc6UL, 18, 1812433253UL>;
#endif // USE_MT19937
-}
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
#endif // _GLIBCXX_USE_C99_STDINT_TR1


.



diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc
index 10fbe1dc4c4..d4ebc9556ab 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -73,8 +73,6 @@
 # define USE_MT19937 1
 #endif
 
-namespace std _GLIBCXX_VISIBILITY(default)
-{
 namespace
 {
 #if USE_RDRAND
@@ -124,6 +122,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
 #endif
 }
 
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
   void
   random_device::_M_init(const std::string& token)
   {
@@ -286,7 +288,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _M_mt.seed(seed);
 #else
 // Convert old default token "mt19937" or numeric seed tokens to "default".
-if (token == "mt19937" || isdigit((unsigned char)token[0]))
+if (token == "mt19937" || std::isdigit((unsigned char)token[0]))
   _M_init("default");
 else
   _M_init(token);
@@ -407,5 +409,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 0x9d2c5680UL, 15,
 0xefc6UL, 18, 1812433253UL>;
 #endif // USE_MT19937
-}
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
 #endif // _GLIBCXX_USE_C99_STDINT_TR1



[PATCH] Fix gnu-versioned-namespace build

2019-12-10 Thread François Dumont

I plan to commit this tomorrow.

Note that rather than just adding the missing 
_GLIBCXX_[BEGIN,END]_VERSION_NAMESPACE I also move anonymous namespace 
usage outside std namespace. Let me know if it was intentional.


    * src/c++11/random.cc: Add _GLIBCXX_BEGIN_NAMESPACE_VERSION and
    _GLIBCXX_END_NAMESPACE_VERSION. Move anonymous namespace outside std
    namespace.

Tested under Linux x86_64 normal/debug/versioned namespace modes.

There are still tests failing in versioned namespace, more patches to come.

François

diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc
index 10fbe1dc4c4..d4ebc9556ab 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -73,8 +73,6 @@
 # define USE_MT19937 1
 #endif
 
-namespace std _GLIBCXX_VISIBILITY(default)
-{
 namespace
 {
 #if USE_RDRAND
@@ -124,6 +122,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
 #endif
 }
 
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
   void
   random_device::_M_init(const std::string& token)
   {
@@ -286,7 +288,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _M_mt.seed(seed);
 #else
 // Convert old default token "mt19937" or numeric seed tokens to "default".
-if (token == "mt19937" || isdigit((unsigned char)token[0]))
+if (token == "mt19937" || std::isdigit((unsigned char)token[0]))
   _M_init("default");
 else
   _M_init(token);
@@ -407,5 +409,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 0x9d2c5680UL, 15,
 0xefc6UL, 18, 1812433253UL>;
 #endif // USE_MT19937
-}
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
 #endif // _GLIBCXX_USE_C99_STDINT_TR1


Re: [PATCH][Hashtable 0/6] Code review

2019-12-09 Thread François Dumont

This patch also require an update of the printers.py file.

Here is an updated version.

François

On 11/17/19 9:42 PM, François Dumont wrote:

This is the begining of a patch series for _Hashtable

Initial patch to clarify code. I was tired to see true/false or 
true_type/false_type without knowing what was true/false.


I also made code more consistent by chosing to specialize methods 
through usage of __unique_keys_t/__multi_keys_t rather than calling 
them _M_[multi]_XXX.



    * include/bits/hashtable_policy.h (__detail::__unique_keys_t): New.
    (__detail::__multi_keys_t): New.
    (__detail::__constant_iterators_t): New.
    (__detail::__mutable_iterators_t): New.
    (__detail::__hash_cached_t): New.
    (__detail::__hash_not_cached_t): New.
    (_Hash_node<>): Change _Cache_hash_code template parameter from 
bool to

    typename. Adapt partial specializations.
    (_Node_iterator_base<>): Likewise.
    (operator==(const _Node_iterator_base<>&,const 
_Node_iterator_base<>&)):

    Adapt.
    (operator!=(const _Node_iterator_base<>&,const 
_Node_iterator_base<>&)):

    Adapt.
    (_Node_iterator<>): Change __constant_iterators and __cache template
    parameters from bool to typename.
    (_Node_const_iterator<>): Likewise.
    (_Map_base<>): Change _Unique_keys template parameter from bool to
    typename. Adapt partial specializations.
    (_Insert<>): Change _Constant_iterators template parameter from 
bool to

    typename. Adapt partial specializations.
    (_Local_iterator_base<>): Change __cache_hash_code template parameter
    from bool to typename. Adapt partial specialization.
    (_Hash_code_base<>): Likewise.
    (operator==(const _Local_iterator_base<>&,
    const _Local_iterator_base<>&)): Adapt.
    (operator!=(const _Local_iterator_base<>&,
    const _Local_iterator_base<>&)):
    Adapt.
    (_Local_iterator<>): Change __constant_iterators and __cache template
    parameters from bool to typename.
    (_Local_const_iterator<>): Likewise.
    (_Hashtable_base<>): Adapt.
    (_Equal_hash_code<>): Adapt.
    (_Equality<>): Adapt.
    * include/bits/hashtable.h (_Hashtable<>): Replace occurences of
    true_type/false_type by respoectively __unique_type_t/__multi_type_t.
    (_M_insert_unique_node(const key_type&, size_t, __hash_code,
    __node_type*, size_t)): Replace by...
    (_M_insert_node(__unique_keys_t, size_t, __hash_code, __node_type*,
    size_t)): ...this.
    (_M_insert_muti_node(__node_type*, const key_type&, __hash_code,
    __node_type*)): Replace by...
    (_M_insert_node(__multi_keys_t, __node_type*, __hash_code,
    __node_type*)): ...this.
    (_M_reinsert_node(node_type&&)): Replace by...
    (_M_reinsert_node(node_type&&, __unique_keys_t)): ...this.
    (_M_reinsert_node(const_iterator, node_type&&, __unique_keys_t)): 
New,

    forward to latter.
    (_M_reinsert_node_multi(const_iterator, node_type&&)): Replace by...
    (_M_reinsert_node(const_iterator, node_type&&, __multi_keys_t)):
    ...this.
    (_M_reinsert_node(node_type&&, __multi_keys_t)): New, forward to 
latter.

    (_M_reinsert_node(node_type&&)): New, use latters.
    (_M_reinsert_node(const_iterator, node_type&&)): Likewise.
    (_M_merge_unique(_Compatible_Hashtable&)): Replace by...
    (_M_merge(__unique_keys_t, _Compatible_Hashtable&)): ...this.
    (_M_merge_multi(_Compatible_Hashtable&)): Replace by...
    (_M_merge(__multi_keys_t, _Compatible_Hashtable&)): ...this.
    (_M_merge(_Compatible_Hashtable&)): New, use latters.
    * include/bits/unordered_map.h
    (unordered_map<>::insert(const_iterator, node_type&&)): Adapt.
    (unordered_map<>::merge(unordered_map<>&)): Adapt.
(unordered_map<>::merge(unordered_multimap<>&)): Adapt.
    (unordered_multimap<>::insert(node_type&&)): Adapt.
    (unordered_multimap<>::insert(const_iterator, node_type&&)): Adapt.
(unordered_multimap<>::merge(unordered_multimap<>&)): Adapt.
(unordered_multimap<>::merge(unordered_map<>&)): Adapt.
    * include/bits/unordered_set.h
    (unordered_set<>::insert(const_iterator, node_type&&)): Adapt.
    (unordered_set<>::merge(unordered_set<>&)): Adapt.
(unordered_set<>::merge(unordered_multiset<>&)): Adapt.
    (unordered_multiset<>::insert(node_type&&)): Adapt.
    (unordered_multiset<>::insert(const_iterator, node_type&&)): Adapt.
(unordered_multiset<>::merge(unordered_multiset<>&)): Adapt.
(unordered_multiset<>::merge(unordered_set<>&)): Adapt.

Tested under Linux x86_64.

François



diff --git a/libstdc++-v3/include/bits/hashtable.h b/l

[PATCH] Extend std::copy_n optimization

2019-12-09 Thread François Dumont

Last patch of my series following this one:

https://gcc.gnu.org/ml/libstdc++/2019-12/msg00028.html

This time I work on std::copy_n/std::copy overloads for 
istreambuf_iterator so that it works also for deque iterators and 
transparently in _GLIBCXX_DEBUG mode.



    * include/bits/stl_algo.h (__copy_n_a): Move to ...
    * include/bits/stl_algobase.h (__copy_n_a): ...here. Add __strict
    parameter.
    (__niter_base(const _Safe_iterator<_Ite, _Seq,
    random_access_iterator_tag>&)): New declaration.
    (__copy_move_a2(istreambuf_iterator<>, istreambuf_iterator<>,
    _Deque_iterator<>)): New declaration.
    (__copy_n_a(istreambuf_iterator<>, _Size, _Deque_iterator<>, bool)):
    New declaration.
    * include/bits/deque.tcc
    (__copy_move_a2(istreambuf_iterator<>, istreambuf_iterator<>,
    _Deque_iterator<>)): Add definition.
    (__copy_n_a(istreambuf_iterator<>, _Size, _Deque_iterator<>, bool)):
    Add definition.
    * include/bits/streambuf_iterator.h
    (__copy_n_a(istreambuf_iterator<>, _Size, _CharT*, bool)): Adapt
    definition.
    * include/debug/safe_iterator.tcc (__niter_base): Add definition.
    * testsuite/25_algorithms/copy/streambuf_iterators/char/4.cc (test03):
    New.
    * testsuite/25_algorithms/copy/streambuf_iterators/char/debug/
    deque_neg.cc: New.
    * testsuite/25_algorithms/copy_n/debug/istreambuf_ite_deque_neg.cc:
    New.
    * testsuite/25_algorithms/copy_n/istreambuf_iterator/2.cc: New.
    * testsuite/25_algorithms/copy_n/istreambuf_iterator/deque.cc: New.

Tested under Linux x86_64 normal and debug modes.

François

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index ef32d2d19dd..009b696e7c4 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -1065,6 +1065,57 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
   return __result;
 }
 
+#if __cplusplus >= 201103L
+  template
+__enable_if_t<__is_char<_CharT>::__value,
+		  _GLIBCXX_STD_C::_Deque_iterator<_CharT, _CharT&, _CharT*>>
+__copy_move_a2(
+	istreambuf_iterator<_CharT, char_traits<_CharT> > __first,
+	istreambuf_iterator<_CharT, char_traits<_CharT> > __last,
+	_GLIBCXX_STD_C::_Deque_iterator<_CharT, _CharT&, _CharT*> __result)
+{
+  if (__first == __last)
+	return __result;
+
+  for (;;)
+	{
+	  const auto __len = __result._M_last - __result._M_cur;
+	  const auto __nb
+	= std::__copy_n_a(__first, __len, __result._M_cur, false)
+	- __result._M_cur;
+	  __result += __nb;
+
+	  if (__nb != __len)
+	break;
+	}
+
+  return __result;
+}
+
+  template
+__enable_if_t<__is_char<_CharT>::__value,
+		  _GLIBCXX_STD_C::_Deque_iterator<_CharT, _CharT&, _CharT*>>
+__copy_n_a(
+  istreambuf_iterator<_CharT, char_traits<_CharT>> __it, _Size __size,
+  _GLIBCXX_STD_C::_Deque_iterator<_CharT, _CharT&, _CharT*> __result,
+  bool __strict)
+{
+  if (__size == 0)
+	return __result;
+
+  do
+	{
+	  const auto __len = std::min<_Size>(__result._M_last - __result._M_cur,
+	 __size);
+	  std::__copy_n_a(__it, __len, __result._M_cur, __strict);
+	  __result += __len;
+	  __size -= __len;
+	}
+  while (__size != 0);
+  return __result;
+}
+#endif
+
   template
 _OI
diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index 769c27a02b6..4f2a6bbdbbf 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -775,31 +775,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   return __result;
 }
 
-  template
-_GLIBCXX20_CONSTEXPR
-_OutputIterator
-__copy_n_a(_InputIterator __first, _Size __n, _OutputIterator __result)
-{
-  if (__n > 0)
-	{
-	  while (true)
-	{
-	  *__result = *__first;
-	  ++__result;
-	  if (--__n > 0)
-		++__first;
-	  else
-		break;
-	}
-	}
-  return __result;
-}
- 
-  template
-__enable_if_t<__is_char<_CharT>::__value, _CharT*>
-__copy_n_a(istreambuf_iterator<_CharT, char_traits<_CharT>>,
-	   _Size, _CharT*);
-
   template
 _GLIBCXX20_CONSTEXPR
 _OutputIterator
@@ -808,7 +783,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 {
   return std::__niter_wrap(__result,
 			   __copy_n_a(__first, __n,
-	  std::__niter_base(__result)));
+	  std::__niter_base(__result), true));
 }
 
   template::value)
 { return __it; }
 
+  template
+_Ite
+__niter_base(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq,
+		 std::random_access_iterator_tag>&);
+
   // Reverse the __niter_base transformation to get a
   // __normal_iterator back again (this assumes that __normal_iterator
   // is only used to wrap random access iterators, like pointers).
@@ -484,6 +489,13 @@ namespace __detail
 	}
 };
 
+_GLIBCXX_BEGIN_NAMESPACE_CONTAINER
+
+  template
+struct _Deque_iterator;
+
+_GLIBCXX_END_NAMESPACE_CONTAINER
+
   // Helpers for streambuf iterators (either istream or ostream).
   // NB: avoid 

[PATCH] Extend std::lexicographical_compare optimizations

2019-12-09 Thread François Dumont

Following:

https://gcc.gnu.org/ml/libstdc++/2019-12/msg00028.html

I've done the same kind of work on std::lexicographical_compare algo.

I had to make the internal lexicographical_compare functions return int 
rather than bool cause with bool you can't use a chunck based approach 
unless you double the number of comparison (once a < b and and another b 
< a).


    * include/bits/stl_algobase.h
    (__lexicographical_compare_impl): Return int.
    (__lexicographical_compare::__lc): Likewise.
    (__lexicographical_compare_aux1(_II1, _II1, _II2, _II2)): New.
    (__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
    _II2, _II2)): New.
    (__lexicographical_compare_aux1(_II1, _II1,
    _Deque_iterator<>, _Deque_iterator<>)): New.
    (__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
    _Deque_iterator<>, _Deque_iterator<>)): New.
    (__lexicographical_compare_aux): Adapt, call later.
    (__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
    _II2, _II2)): New.
    (__lexicographical_compare_aux(_II1, _II1,
    _Safe_iterator<>, _Safe_iterator<>)): New.
    (__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
    _Safe_iterator<>, _Safe_iterator<>)): New.
    (std::lexicographical_compare): Adapt, call later.
    * include/bits/deque.tcc (__lex_cmp_dit): New.
    (__lexicographical_compare_aux1): Add definitions.
    * include/debug/safe_iterator.tcc (__lexicographical_compare_aux): New.
    * testsuite/25_algorithms/lexicographical_compare/1.cc (test6, test7):
    New.
    * testsuite/25_algorithms/lexicographical_compare/deque_iterators/1.cc:
    New.

Tested under Linux x86_64 normal and debug modes.

François

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index ae5366d6208..ef32d2d19dd 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -1210,6 +1210,98 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
   return true;
 }
 
+  template
+int
+__lex_cmp_dit(
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __first1,
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __last1,
+	_II __first2, _II __last2)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> _Iter;
+  typedef typename _Iter::difference_type difference_type;
+
+  if (__first1._M_node != __last1._M_node)
+	{
+	  difference_type __len = __last2 - __first2;
+	  difference_type __flen
+	= std::min(__len, __first1._M_last - __first1._M_cur);
+	  if (int __ret = std::__lexicographical_compare_aux1(
+	  __first1._M_cur, __first1._M_last, __first2, __first2 + __flen))
+	return __ret;
+
+	  __first2 += __flen;
+	  __len -= __flen;
+	  __flen = std::min(__len, _Iter::_S_buffer_size());
+	  for (typename _Iter::_Map_pointer __node = __first1._M_node + 1;
+	   __node != __last1._M_node;
+	   __first2 += __flen, __len -= __flen,
+	   __flen = std::min(__len, _Iter::_S_buffer_size()),
+	   ++__node)
+	if (int __ret = std::__lexicographical_compare_aux1(
+		  *__node, *__node + _Iter::_S_buffer_size(),
+		  __first2, __first2 + __flen))
+	  return __ret;
+
+	  return std::__lexicographical_compare_aux1(
+	__last1._M_first, __last1._M_cur, __first2, __last2);
+	}
+
+  return std::__lexicographical_compare_aux1(
+	  __first1._M_cur, __last1._M_cur, __first2, __last2);
+}
+
+  template
+typename __gnu_cxx::__enable_if<
+  __is_random_access_iter<_II2>::__value, int>::__type
+__lexicographical_compare_aux1(
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
+		_II2 __first2, _II2 __last2)
+{ return std::__lex_cmp_dit(__first1, __last1, __first2, __last2); }
+
+  template
+int
+__lexicographical_compare_aux1(
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
+{ return std::__lex_cmp_dit(__first1, __last1, __first2, __last2); }
+
+  template
+typename __gnu_cxx::__enable_if<
+  __is_random_access_iter<_II1>::__value, int>::__type
+__lexicographical_compare_aux1(
+		_II1 __first1, _II1 __last1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> _Iter;
+  typedef typename _Iter::difference_type difference_type;
+
+  difference_type __len = __last1 - __first1;
+  while (__len > 0)
+	{
+	  const difference_type __flen = __first2._M_node == __last2._M_node
+	? __last2._M_cur - __first2._M_cur
+	: __first2._M_last - __first2._M_cur;
+	  const difference_type __clen = std::min(__len, __flen);
+	  if (int __ret = 

Re: copy/copy_backward/fill/fill_n/equal rework

2019-12-09 Thread François Dumont
After completing this work and running more tests I realized that the 
declaration of algos was still not ideal.


So here is another version where algos are not re-declare in 
stl_deque.h, I rather include stl_algobase.h in deque.tcc. The problem 
was spotted but another patch I am going to submit afterward.


Note that this patch is based after this one:

https://gcc.gnu.org/ml/libstdc++/2019-10/msg00072.html

François

On 9/25/19 6:44 AM, François Dumont wrote:

Ping ?

On 9/9/19 8:34 PM, François Dumont wrote:

Hi

    This patch improves stl_algobase.h 
copy/copy_backward/fill/fill_n/equal implementations. The 
improvements are:


- activation of algo specialization for __gnu_debug::_Safe_iterator 
(w/o _GLIBCXX_DEBUG mode)


- activation of algo specialization for _Deque_iterator even if mixed 
with another kind of iterator.


- activation of algo specializations __copy_move_a2 for something 
else than pointers. For example this code:


std::vector v { 'a', 'b',  };

ostreambuf_iterator out(std::cout);

std::copy(v.begin(), v.end(), out);

is not calling the specialization __copy_move_a2(const char*, const 
char*, ostreambuf_iterator<>);


It also fix a _GLIBCXX_DEBUG issue where the __niter_base 
specialization was wrongly removing the _Safe_iterator<> layer. The 
testsuite/25_algorithms/copy/debug/1_neg.cc test case was failing on 
a debug assertion because _after_ the copy we were trying to 
increment the vector iterator after past-the-end. Of course the 
problem is the _after_, Debug mode should detect this _before_ it 
takes place which it does now.


Note that std::fill_n is now making use of std::fill for some 
optimizations dealing with random access iterators.


Performances are very good:

Before:

copy_backward_deque_iterators.cc    deque 2 deque 1084r 1084u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector 3373r 3372u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque 3316r 3316u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 3610r 
3609u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 3552r 
3552u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 10528r 10528u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2161r 2162u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         752r 
751u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector       3300r 
3299u    0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque       3144r 
3140u    0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      3340r 
3338u    1s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      3132r 
3132u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     10013r 
10012u    0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2274r 
2275u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque       8676r 
8675u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs vector      5870r 
5870u    0s 0mem    0pf
equal_deque_iterators.cc     vector vs deque      3163r 
3163u    0s 0mem    0pf
equal_deque_iterators.cc     int deque vs char vector     5845r 
5845u    0s 0mem    0pf
equal_deque_iterators.cc     char vector vs int deque     3307r 
3307u    0s 0mem    0pf


After:

copy_backward_deque_iterators.cc    deque 2 deque  697r  697u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector  219r  218u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque  453r  453u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 1914r 
1915u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 2112r 
2111u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 7770r 7771u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2194r 2193u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         505r 
504u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector        221r 
221u    0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque        398r 
397u    0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      1770r 
1767u    0s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      1995r 
1993u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     7650r 
7641u    2s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2270r 
2270u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque        769r 
768u    0s 0mem    0pf
equal_deque_iterators.cc     deq

Re: [PATCH] Enhance _GLIBCXX_DEBUG constexpr support

2019-12-09 Thread François Dumont
After completing execution of all tests I had to fix implementation of 
__check_singular to get only 1 return statement.




On 12/2/19 8:31 PM, François Dumont wrote:

Hi

    Here is a patch to enhance constexpr support in _GLIBCXX_DEBUG. I 
work on std::lower_bound/upper_bound to find out if Debug mode is well 
prepared. I'll continue on other algos later.


    I initially hope that I could count on the compiler for the 
valid_range check. But for lower_bound/upper_bound there is no 
constexpr dedicated implementation like for copy/copy_backward so it 
implies changing the existing implementation. So I tried to change the 
while(__len > 0) into a while(__len != 0) and it gave:


constexpr_valid_range_neg.cc:35:20: error: non-constant condition for 
static assertion

   35 | static_assert(test1()); // { dg-error "" }
  |   ~^~
In file included from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/algorithm:61,

 from constexpr_valid_range_neg.cc:22:
constexpr_valid_range_neg.cc:35:20:   in ‘constexpr’ expansion of 
‘test1()’
constexpr_valid_range_neg.cc:30:38:   in ‘constexpr’ expansion of 
‘std::lower_bound(ca0.std::array::end(), 
ca0.std::array::begin(), 6)’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1484:32: 
in ‘constexpr’ expansion of ‘std::__lower_bound__gnu_cxx::__ops::_Iter_less_val>(__first, __last, (* & __val), 
__gnu_cxx::__ops::__iter_less_val())’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1444:7: 
error: ‘constexpr’ loop iteration count exceeds limit of 262144 (use 
‘-fconstexpr-loop-limit=’ to increase the limit)

 1444 |   while (__len != 0)
  |   ^

    It seems ok but it isn't. The compiler had to loop 262144 times to 
eventually give this status which is not even clear about the fact 
that begin/end has been inverted. It is a quite heavy operation for a 
limited result.


    So this patch rather enable _GLIBCXX_DEBUG valid_range check which 
gives:


constexpr_valid_range_neg.cc:35:20: error: non-constant condition for 
static assertion

   35 | static_assert(test1()); // { dg-error "" }
  |   ~^~
In file included from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/debug/debug.h:90,
 from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:69,
 from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/algorithm:61,

 from constexpr_valid_range_neg.cc:22:
constexpr_valid_range_neg.cc:35:20:   in ‘constexpr’ expansion of 
‘test1()’
constexpr_valid_range_neg.cc:30:38:   in ‘constexpr’ expansion of 
‘std::lower_bound(ca0.std::__debug::array12>::end(), ca0.std::__debug::array::begin(), 6)’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1482:7: 
error: inline assembly is not a constant expression
 1482 |   __glibcxx_requires_partitioned_lower(__first, __last, 
__val);

  |   ^~~~
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1482:7: 
note: only unevaluated inline assembly is allowed in a ‘constexpr’ 
function in C++2a


This time it is done in no time. Of course you can see that the asm 
trick to generate a non-constant condition is not so nice.


We just don't see the asm call parameter but showing 
__glibcxx_requires_partitioned_lower is not so bad. For this reason 
the tests in this patch are not checking for any failure message. 
We'll see how to adapt when we have the necessary front end help to 
generate this compilation error.


Of course I could have a nicer compilation error by directly calling 
__glibcxx_requires_valid_range in the algo and stop doing it within 
__glibcxx_requires_partitioned_lower. Do you want to expose all macros 
this way ?



    * include/debug/formatter.h (__check_singular): Add C++11 constexpr
    qualification.
    * include/debug/helper_functions.h (__check_singular): Likewise. Skip
    check if constant evaluated.
    (__valid_range): Remove check skip if constant evaluated.
    * include/debug/macros.h 
[_GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED]

    (_GLIBCXX_DEBUG_VERIFY_COND_AT): Define.
    * testsuite/25_algorithms/lower_bound/constexpr.cc (test): Add 
checks on

    lower_bound results.
    * testsuite/25_algorithms/upper_bound/constexpr.cc (test): Likewise.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_partitioned_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_partitioned_pred_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_valid_range_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/partitioned_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/partitioned_pred_neg.cc:
    New.
    * testsuite/25_algorithms/upper_bound/debug/
    constexpr_partitioned_neg.cc: New.
    * testsuite/25_algorithms/upper_bound/debug/
    constexpr_partitioned_pred_neg.cc: New.
    * testsuit

[PATCH] Fix _GLIBCXX_DEBUG tests static_assert lines

2019-12-09 Thread François Dumont

When applying:

2019-11-26  François Dumont  

    * include/debug/array (array<>::fill): Add C++20 constexpr.
    (array<>::swap): Likewise.

I forgot to adapt some line numbers.

Committed as trivial.

François

diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
index 3c60a435491..a4b199c23e3 100644
--- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
@@ -27,6 +27,6 @@ int n1 = std::get<1>(a);
 int n2 = std::get<1>(std::move(a));
 int n3 = std::get<1>(ca);
 
-// { dg-error "static assertion failed" "" { target *-*-* } 294 }
-// { dg-error "static assertion failed" "" { target *-*-* } 303 }
-// { dg-error "static assertion failed" "" { target *-*-* } 311 }
+// { dg-error "static assertion failed" "" { target *-*-* } 295 }
+// { dg-error "static assertion failed" "" { target *-*-* } 304 }
+// { dg-error "static assertion failed" "" { target *-*-* } 312 }
diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
index a6b44eb57fe..59e728c4a37 100644
--- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
@@ -22,4 +22,4 @@
 
 typedef std::tuple_element<1, std::array>::type type;
 
-// { dg-error "static assertion failed" "" { target *-*-* } 376 }
+// { dg-error "static assertion failed" "" { target *-*-* } 377 }


[PATCH] Enhance _GLIBCXX_DEBUG constexpr support

2019-12-02 Thread François Dumont

Hi

    Here is a patch to enhance constexpr support in _GLIBCXX_DEBUG. I 
work on std::lower_bound/upper_bound to find out if Debug mode is well 
prepared. I'll continue on other algos later.


    I initially hope that I could count on the compiler for the 
valid_range check. But for lower_bound/upper_bound there is no constexpr 
dedicated implementation like for copy/copy_backward so it implies 
changing the existing implementation. So I tried to change the 
while(__len > 0) into a while(__len != 0) and it gave:


constexpr_valid_range_neg.cc:35:20: error: non-constant condition for 
static assertion

   35 | static_assert(test1()); // { dg-error "" }
  |   ~^~
In file included from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/algorithm:61,

 from constexpr_valid_range_neg.cc:22:
constexpr_valid_range_neg.cc:35:20:   in ‘constexpr’ expansion of ‘test1()’
constexpr_valid_range_neg.cc:30:38:   in ‘constexpr’ expansion of 
‘std::lower_bound(ca0.std::array::end(), 
ca0.std::array::begin(), 6)’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1484:32: 
in ‘constexpr’ expansion of ‘std::__lower_bound__gnu_cxx::__ops::_Iter_less_val>(__first, __last, (* & __val), 
__gnu_cxx::__ops::__iter_less_val())’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1444:7: 
error: ‘constexpr’ loop iteration count exceeds limit of 262144 (use 
‘-fconstexpr-loop-limit=’ to increase the limit)

 1444 |   while (__len != 0)
  |   ^

    It seems ok but it isn't. The compiler had to loop 262144 times to 
eventually give this status which is not even clear about the fact that 
begin/end has been inverted. It is a quite heavy operation for a limited 
result.


    So this patch rather enable _GLIBCXX_DEBUG valid_range check which 
gives:


constexpr_valid_range_neg.cc:35:20: error: non-constant condition for 
static assertion

   35 | static_assert(test1()); // { dg-error "" }
  |   ~^~
In file included from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/debug/debug.h:90,
 from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:69,
 from 
/home/fdt/dev/gcc/install/include/c++/10.0.0/algorithm:61,

 from constexpr_valid_range_neg.cc:22:
constexpr_valid_range_neg.cc:35:20:   in ‘constexpr’ expansion of ‘test1()’
constexpr_valid_range_neg.cc:30:38:   in ‘constexpr’ expansion of 
‘std::lower_bound(ca0.std::__debug::array12>::end(), ca0.std::__debug::array::begin(), 6)’
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1482:7: 
error: inline assembly is not a constant expression

 1482 |   __glibcxx_requires_partitioned_lower(__first, __last, __val);
  |   ^~~~
/home/fdt/dev/gcc/install/include/c++/10.0.0/bits/stl_algobase.h:1482:7: 
note: only unevaluated inline assembly is allowed in a ‘constexpr’ 
function in C++2a


This time it is done in no time. Of course you can see that the asm 
trick to generate a non-constant condition is not so nice.


We just don't see the asm call parameter but showing 
__glibcxx_requires_partitioned_lower is not so bad. For this reason the 
tests in this patch are not checking for any failure message. We'll see 
how to adapt when we have the necessary front end help to generate this 
compilation error.


Of course I could have a nicer compilation error by directly calling 
__glibcxx_requires_valid_range in the algo and stop doing it within 
__glibcxx_requires_partitioned_lower. Do you want to expose all macros 
this way ?



    * include/debug/formatter.h (__check_singular): Add C++11 constexpr
    qualification.
    * include/debug/helper_functions.h (__check_singular): Likewise. Skip
    check if constant evaluated.
    (__valid_range): Remove check skip if constant evaluated.
    * include/debug/macros.h [_GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED]
    (_GLIBCXX_DEBUG_VERIFY_COND_AT): Define.
    * testsuite/25_algorithms/lower_bound/constexpr.cc (test): Add 
checks on

    lower_bound results.
    * testsuite/25_algorithms/upper_bound/constexpr.cc (test): Likewise.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_partitioned_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_partitioned_pred_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/
    constexpr_valid_range_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/partitioned_neg.cc: New.
    * testsuite/25_algorithms/lower_bound/debug/partitioned_pred_neg.cc:
    New.
    * testsuite/25_algorithms/upper_bound/debug/
    constexpr_partitioned_neg.cc: New.
    * testsuite/25_algorithms/upper_bound/debug/
    constexpr_partitioned_pred_neg.cc: New.
    * testsuite/25_algorithms/upper_bound/debug/
    constexpr_valid_range_neg.cc: New.
    * testsuite/25_algorithms/upper_bound/debug/partitioned_neg.cc: New.
    * 

Re: [PATCH][_GLIBCXX_DEBUG] Improve valid_range check

2019-11-26 Thread François Dumont

On 11/26/19 10:52 PM, Jonathan Wakely wrote:

On 26/11/19 20:07 +0100, François Dumont wrote:

Sure, I am about to do so.

However I wasn't sure about this syntax before the commit so I had 
run the new 2_neg.cc with:


make CXXFLAGS=-std=c++98 check-debug

and it worked fine and still is !


The testsuite doesn't use CXXFLAGS.


Did you mean CPPFLAGS ?

I do see the option added to the compiler call in libstdc++.log file. 
And I used it to build for instance pretty-printers tests in 
_GLIBCXX_DEBUG mode with success.




I also try -std=gnu++98 and made sure that pch had been updated by 
re-building libstdc++ first. I just can't get the expected 
compilation error.


Maybe I need to rebuild the whole stuff to get an error...


No, you need to pass the right flags so they are used by the
testsuite. This will do it:

make -C testsuite/  check-debug 
debug_flags=unix/-D_GLIBCXX_DEBUG/-std=gnu++98/-Wsystem-headers

I'll have to keep your mail to remember it !


But since it only shows up with -Wsystem-headers, there's no point
trying to test for it.



Ok, makes sens.

Thanks



Re: [PATCH][_GLIBCXX_DEBUG] Improve valid_range check

2019-11-26 Thread François Dumont

On 11/26/19 9:49 PM, Stephan Bergmann wrote:

On 26/11/2019 20:07, François Dumont wrote:
However I wasn't sure about this syntax before the commit so I had 
run the new 2_neg.cc with:


make CXXFLAGS=-std=c++98 check-debug

and it worked fine and still is !

I also try -std=gnu++98 and made sure that pch had been updated by 
re-building libstdc++ first. I just can't get the expected 
compilation error.


Maybe I need to rebuild the whole stuff to get an error...


I saw the error with recent Clang and -std=gnu++98.  Recent GCC indeed 
seems to give at most a warning.




Ok, thanks for the feedback, gcc might be more permissive then.

Whatever I committed the fix an hour ago.



Re: [PATCH][_GLIBCXX_DEBUG] Improve valid_range check

2019-11-26 Thread François Dumont

On 11/26/19 1:21 PM, Jonathan Wakely wrote:

On 26/11/19 12:33 +0100, Stephan Bergmann wrote:

On 22/11/2019 18:59, Jonathan Wakely wrote:

On 22/11/19 18:38 +0100, François Dumont wrote:
    I noticed that we are not checking that iterators are not 
singular in valid_range. Moreover __check_singular signature for 
pointers is not intercepting all kind of pointers in terms of 
qualification.


    I'd like to commit it next week but considering we are in stage 
3 I need proper acceptance.


    * include/debug/functions.h: Remove  include.
    (__check_singular_aux, __check_singular): Move...
    * include/debug/helper_functions.h:
    (__check_singular_aux, __check_singular): ...here.
    (__valid_range_aux): Adapt to use latter.
    * testsuite/25_algorithms/copy/debug/2_neg.cc: New.

Tested under Linux x86_64 normal and debug modes.


OK for trunk, thanks.


The curly braces...

diff --git a/libstdc++-v3/include/debug/helper_functions.h 
b/libstdc++-v3/include/debug/helper_functions.h

index c3e7478f649..5a858754875 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h

[...]

@@ -138,14 +156,23 @@ namespace __gnu_debug
    inline bool
    __valid_range_aux(_InputIterator __first, _InputIterator __last,
  std::input_iterator_tag)
-    { return true; }
+    {
+  if (__first != __last)
+    return !__check_singular(__first) && !__check_singular(__last);
+
+  return true;
+    }
  template
    _GLIBCXX_CONSTEXPR
    inline bool
    __valid_range_aux(_InputIterator __first, _InputIterator __last,
  std::random_access_iterator_tag)
-    { return __first <= __last; }
+    {
+  return
+    __valid_range_aux(__first, __last, std::input_iterator_tag{})


...^^^ here...


+    && __first <= __last;
+    }
  /** We have iterators, so figure out what kind of iterators they are
   *  to see if we can check the range ahead of time.
@@ -167,6 +194,9 @@ namespace __gnu_debug
  typename _Distance_traits<_InputIterator>::__type& 
__dist,

  std::__false_type)
    {
+  if (!__valid_range_aux(__first, __last, 
std::input_iterator_tag{}))


...and ^^^ here are not allowed pre C++11.  Replacing those with

 std::input_iterator_tag()

should fix it.


Indeed. We should also have tests that use "-std=gnu++98
-D_GLIBCXX_DEBUG" so they'd catch this.

François, can you take care of the fix please?




Sure, I am about to do so.

However I wasn't sure about this syntax before the commit so I had run 
the new 2_neg.cc with:


make CXXFLAGS=-std=c++98 check-debug

and it worked fine and still is !

I also try -std=gnu++98 and made sure that pch had been updated by 
re-building libstdc++ first. I just can't get the expected compilation 
error.


Maybe I need to rebuild the whole stuff to get an error...

Sorry



Re: [PATCH, libstdc++] More

2019-11-25 Thread François Dumont
I have a patch for the same location so here is my remark that might 
make my patch useless.


Maybe you can even merge it with yours Ed, here it is:

https://gcc.gnu.org/ml/libstdc++/2019-10/msg00072.html

On 11/25/19 10:15 PM, Jonathan Wakely wrote:

On 15/11/19 22:17 -0500, Ed Smith-Rowland via libstdc++ wrote:

Index: include/bits/stl_algobase.h
===
--- include/bits/stl_algobase.h    (revision 278318)
+++ include/bits/stl_algobase.h    (working copy)
@@ -107,6 +107,50 @@
    }

  /*
+   * A constexpr wrapper for __builtin_memmove.


This should say __builtin_memcpy.


+   * @param __num The number of elements of type _Tp (not bytes).
+   */
+  template
+    _GLIBCXX14_CONSTEXPR
+    inline void*
+    __memcpy(_Tp* __dst, const _Tp* __src, size_t __num)
+    {
+#ifdef __cpp_lib_is_constant_evaluated
+  if (std::is_constant_evaluated())
+    {
+  for(; __num > 0; --__num)


for (; __num != 0; --__num)

and make __num prtdiff_t.

would be better here as this way the compiler is able to report bad 
usages rather than silently do nothing.




+    *__dst++ = *__src++;
+  return __dst;
+    }
+  else
+#endif
+    return __builtin_memcpy(__dst, __src, sizeof(_Tp) * __num);
+  return __dst;
+    }
+
+  /*
+   * A constexpr wrapper for __builtin_memmove.


And this should say __builtin_memset.


+   * @param __num The number of elements of type _Tp (not bytes).
+   */
+  template
+    _GLIBCXX14_CONSTEXPR
+    inline void*
+    __memset(_Tp* __dst, _Tp __a, size_t __num)
+    {
+#ifdef __cpp_lib_is_constant_evaluated
+  if (std::is_constant_evaluated())
+    {
+  for(; __num > 0; --__num)
+    *__dst = __a;
+  return __dst;
+    }
+  else
+#endif
+    return __builtin_memset(__dst, __a, sizeof(_Tp) * __num);
+  return __dst;
+    }


OK for trunk with those two comment fixes. Thanks.







[PATCH] C++20 P1032 for __debug::array

2019-11-25 Thread François Dumont

I plan to commit this tomorrow.

But I really wonder if we couldn't just add some __glibcxx_checks_xxx in 
std/array ?


    * include/debug/array (array<>::fill): Add C++20 constexpr.
    (array<>::swap): Likewise.

François

diff --git a/libstdc++-v3/include/debug/array b/libstdc++-v3/include/debug/array
index 5566a087f9a..4e0fac8daac 100644
--- a/libstdc++-v3/include/debug/array
+++ b/libstdc++-v3/include/debug/array
@@ -80,11 +80,11 @@ namespace __debug
   // No explicit construct/copy/destroy for aggregate type.
 
   // DR 776.
-  void
+  _GLIBCXX20_CONSTEXPR void
   fill(const value_type& __u)
   { std::fill_n(begin(), size(), __u); }
 
-  void
+  _GLIBCXX20_CONSTEXPR void
   swap(array& __other)
   noexcept(_AT_Type::_Is_nothrow_swappable::value)
   { std::swap_ranges(begin(), end(), __other.begin()); }
@@ -282,6 +282,7 @@ namespace __debug
 #endif
 
   template
+_GLIBCXX20_CONSTEXPR
 inline void
 swap(array<_Tp, _Nm>& __one, array<_Tp, _Nm>& __two)
 noexcept(noexcept(__one.swap(__two)))


[PATCH] Add safe iterator iterator_concept

2019-11-25 Thread François Dumont

I plan to commit this patch tomorrow.

    * include/debug/safe_iterator.h
[__cpp_lib_concepts](_Safe_iterator<>::iterator_concept): Define for
    C++20.

François

diff --git a/libstdc++-v3/include/debug/safe_iterator.h b/libstdc++-v3/include/debug/safe_iterator.h
index 685256551d9..abf575186c7 100644
--- a/libstdc++-v3/include/debug/safe_iterator.h
+++ b/libstdc++-v3/include/debug/safe_iterator.h
@@ -141,6 +141,10 @@ namespace __gnu_debug
   typedef typename _Traits::reference		reference;
   typedef typename _Traits::pointer			pointer;
 
+#if __cplusplus > 201703L && __cpp_lib_concepts
+  using iterator_concept = std::__detail::__iter_concept<_Iterator>;
+#endif
+
   /// @post the iterator is singular and unattached
   _Safe_iterator() _GLIBCXX_NOEXCEPT : _Iter_base() { }
 


[PATCH][_GLIBCXX_DEBUG] Improve valid_range check

2019-11-22 Thread François Dumont

Hi

    I noticed that we are not checking that iterators are not singular 
in valid_range. Moreover __check_singular signature for pointers is not 
intercepting all kind of pointers in terms of qualification.


    I'd like to commit it next week but considering we are in stage 3 I 
need proper acceptance.


    * include/debug/functions.h: Remove  include.
    (__check_singular_aux, __check_singular): Move...
    * include/debug/helper_functions.h:
    (__check_singular_aux, __check_singular): ...here.
    (__valid_range_aux): Adapt to use latter.
    * testsuite/25_algorithms/copy/debug/2_neg.cc: New.

Tested under Linux x86_64 normal and debug modes.

François

diff --git a/libstdc++-v3/include/debug/functions.h b/libstdc++-v3/include/debug/functions.h
index 8c385b87244..12df745b573 100644
--- a/libstdc++-v3/include/debug/functions.h
+++ b/libstdc++-v3/include/debug/functions.h
@@ -29,7 +29,6 @@
 #ifndef _GLIBCXX_DEBUG_FUNCTIONS_H
 #define _GLIBCXX_DEBUG_FUNCTIONS_H 1
 
-#include 		// for __addressof
 #include 	// for less
 
 #if __cplusplus >= 201103L
@@ -49,23 +48,6 @@ namespace __gnu_debug
   template
 struct _Is_contiguous_sequence : std::__false_type { };
 
-  // An arbitrary iterator pointer is not singular.
-  inline bool
-  __check_singular_aux(const void*) { return false; }
-
-  // We may have an iterator that derives from _Safe_iterator_base but isn't
-  // a _Safe_iterator.
-  template
-inline bool
-__check_singular(const _Iterator& __x)
-{ return __check_singular_aux(std::__addressof(__x)); }
-
-  /** Non-NULL pointers are nonsingular. */
-  template
-inline bool
-__check_singular(const _Tp* __ptr)
-{ return __ptr == 0; }
-
   /* Checks that [first, last) is a valid range, and then returns
* __first. This routine is useful when we can't use a separate
* assertion statement because, e.g., we are in a constructor.
diff --git a/libstdc++-v3/include/debug/helper_functions.h b/libstdc++-v3/include/debug/helper_functions.h
index c3e7478f649..5a858754875 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h
@@ -29,6 +29,7 @@
 #ifndef _GLIBCXX_DEBUG_HELPER_FUNCTIONS_H
 #define _GLIBCXX_DEBUG_HELPER_FUNCTIONS_H 1
 
+#include // for __addressof
 #include 	// for iterator_traits,
 		// categories and _Iter_base
 #include 		// for __is_integer
@@ -112,6 +113,23 @@ namespace __gnu_debug
 __get_distance(_Iterator __lhs, _Iterator __rhs)
 { return __get_distance(__lhs, __rhs, std::__iterator_category(__lhs)); }
 
+  // An arbitrary iterator pointer is not singular.
+  inline bool
+  __check_singular_aux(const void*) { return false; }
+
+  // We may have an iterator that derives from _Safe_iterator_base but isn't
+  // a _Safe_iterator.
+  template
+inline bool
+__check_singular(_Iterator const& __x)
+{ return __check_singular_aux(std::__addressof(__x)); }
+
+  /** Non-NULL pointers are nonsingular. */
+  template
+inline bool
+__check_singular(_Tp* const& __ptr)
+{ return __ptr == 0; }
+
   /** We say that integral types for a valid range, and defer to other
*  routines to realize what to do with integral types instead of
*  iterators.
@@ -138,14 +156,23 @@ namespace __gnu_debug
 inline bool
 __valid_range_aux(_InputIterator __first, _InputIterator __last,
 		  std::input_iterator_tag)
-{ return true; }
+{
+  if (__first != __last)
+	return !__check_singular(__first) && !__check_singular(__last);
+
+  return true;
+}
 
   template
 _GLIBCXX_CONSTEXPR
 inline bool
 __valid_range_aux(_InputIterator __first, _InputIterator __last,
 		  std::random_access_iterator_tag)
-{ return __first <= __last; }
+{
+  return
+	__valid_range_aux(__first, __last, std::input_iterator_tag{})
+	&& __first <= __last;
+}
 
   /** We have iterators, so figure out what kind of iterators they are
*  to see if we can check the range ahead of time.
@@ -167,6 +194,9 @@ namespace __gnu_debug
 		  typename _Distance_traits<_InputIterator>::__type& __dist,
 		  std::__false_type)
 {
+  if (!__valid_range_aux(__first, __last, std::input_iterator_tag{}))
+	return false;
+
   __dist = __get_distance(__first, __last);
   switch (__dist.second)
 	{
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/debug/2_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy/debug/2_neg.cc
new file mode 100644
index 000..8bbf873de96
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/debug/2_neg.cc
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 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 

Re: [PATCH][Hashtable 5/6] Remove H1/H2 template parameters

2019-11-18 Thread François Dumont

On 11/17/19 11:21 PM, Ville Voutilainen wrote:

On Sun, 17 Nov 2019 at 23:15, François Dumont  wrote:

H1 used to be a reference to the user Hash, now _Hashtable and unordered
types agree on the same Hash type which is more intuitive.

I also chose to not support anymore a stateful ranged hash functor. We
only use _Mod_range_hashing and _Mask_range_hashing.

Thanks to this simplification _M_bucket_index can also be simplified.

Do we know whether there are existing users that this breaks?


That's the problem with this kind of extension, we don't know.

However we never get any report neither about potential users. And I 
remember that I fixed some years ago a bug that users would have 
reported much sooner if there have been users. But it was some years ago.



  Also, is
this ABI-compatible
for our unordered containers?


IMHO, yes it is.

In hashtable_policy.h _H1 was the user hash which I renamed in _Hash, 
the same template name that for unordered containers.


_H2 was always _Mod_range_hashing and previous _Hash always 
_Default_ranged_hash, both stateless types. Only _H1 and _H2 were stored 
in _Hash_code_base for instance. _H1 is still as _Hash and _H2 was just 
empty and moreover stored last so removing it has no impact.


François



[PATCH][Hashtable 6/6] PR 68303 small size optimization

2019-11-17 Thread François Dumont

This is an implementation of PR 68303.

I try to use this idea as much as possible to avoid computation of hash 
codes.


Note that tests are not showing any gain. I guess hash computation must 
be quite bad to get a benefit from it. So I am only activating it when 
hash code is not cached and/or when computation is not fast.


    PR libstdc++/68303
    * include/bits/hashtable_policy.h
    (_Small_size_threshold<_Hash>): New.
    (_Hashtable_traits<>): Add _Small_size_threshold std::size_t template
    parameter, default to 0.
    (_Hashtable_traits<>::__small_size_threshold): New.
    (_Hash_code_base<>::_M_hash_code(const __node_type*)): New.
    (_Equal_helper<>::_S_node_equals): New.
    * include/bits/hashtable.h:
    (__small_size_threshold_default<>): New template alias.
    (_Hashtable<>::find): Add linear lookup when size is lower or equal to
    _Small_size_threshold.
    (_Hashtable<>::_M_emplace<_Args>(true_type, _Args&&...)): Add linear
    lookup when size is lower or equal to _Small_size_threshold.
    (_Hashtable<>::_M_insert<>(_Arg&&, const _NodeGenerator&, true_type,
    size_type)): Likewise.
    (_Hashtable<>::_M_compute_hash_code(const_iterator, const key_type&)):
    New.
    (_Hashtable<>::_M_emplace<_Args>(false_type, _Args&&...)): Use latter.
    (_Hashtable<>::_M_insert(const_iterator, _Arg&&, const _NodeGenerator&,
    false_type)): Likewise.
    (_Hashtable<>::_M_find_before_node(const key_type&)): New.
    (_Hashtable<>::_M_erase(true_type, const key_type&)): Use latter if 
size

    is lower or equal to _Small_size_threshold.
    (_Hashtable<>::_M_erase(false_type, const key_type&)): Likewise.
    * include/bits/unordered_map.h (__umaps_traits): Adapt using small size
    threshold set to 20.
    (__ummap_traits): Likewise.
    * include/bits/unordered_set.h (__uset_traits, __ummset_traits): 
Likewise.

    * src/c++11/hashtable_c++0x.cc: Add  include.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index 9dadc62e328..460f25affe4 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -48,6 +48,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		   // Mandatory to have erase not throwing.
 		   __is_nothrow_invocable>>;
 
+  template
+using __small_size_threshold_default
+  = typename conditional<__cache,
+		// No small size optimization if hash code is cached...
+		integral_constant,
+		_Small_size_threshold<_Hash>>::type;
   /**
*  Primary class template _Hashtable.
*
@@ -743,6 +749,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __node_base*
   _M_find_before_node(size_type, const key_type&, __hash_code) const;
 
+  __node_base*
+  _M_find_before_node(const key_type&);
+
   __node_type*
   _M_find_node(size_type __bkt, const key_type& __key,
 		   __hash_code __c) const
@@ -766,6 +775,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __node_base*
   _M_get_previous_node(size_type __bkt, __node_base* __n);
 
+  pair
+  _M_compute_hash_code(const_iterator __hint, const key_type& __k) const;
+
   // Insert node __n with hash code __code, in bucket __bkt if no
   // rehash (assumes no element with same key already present).
   // Takes ownership of __n if insertion succeeds, throws otherwise.
@@ -1490,6 +1502,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 find(const key_type& __k)
 -> iterator
 {
+  if (size() <= __traits_type::__small_size_threshold::value)
+	{
+	  for (auto __it = begin(); __it != end(); ++__it)
+	if (this->_M_key_equals(__k, __it._M_cur))
+	  return __it;
+	  return end();
+	}
+
   __hash_code __code = this->_M_hash_code(__k);
   std::size_t __bkt = _M_bucket_index(__code);
   return iterator(_M_find_node(__bkt, __k, __code));
@@ -1504,6 +1524,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 find(const key_type& __k) const
 -> const_iterator
 {
+  if (size() <= __traits_type::__small_size_threshold::value)
+	{
+	  for (auto __it = begin(); __it != end(); ++__it)
+	if (this->_M_key_equals(__k, __it._M_cur))
+	  return __it;
+	  return end();
+	}
+
   __hash_code __code = this->_M_hash_code(__k);
   std::size_t __bkt = _M_bucket_index(__code);
   return const_iterator(_M_find_node(__bkt, __k, __code));
@@ -1619,6 +1647,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   return nullptr;
 }
 
+  // Find the node before the one whose key compares equal to k.
+  // Return nullptr if no node is found.
+  template
+auto
+_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+	   _Hash, _RehashPolicy, _Traits>::
+_M_find_before_node(const key_type& __k)
+-> __node_base*
+{
+  __node_base* __prev_p = &_M_before_begin;
+  if (!__prev_p->_M_nxt)
+	return nullptr;
+
+  for (__node_type* __p = static_cast<__node_type*>(__prev_p->_M_nxt);
+	   __p != nullptr;
+	   __p = __p->_M_next())
+	{
+	  if 

[PATCH][Hashtable 5/6] Remove H1/H2 template parameters

2019-11-17 Thread François Dumont
H1 used to be a reference to the user Hash, now _Hashtable and unordered 
types agree on the same Hash type which is more intuitive.


I also chose to not support anymore a stateful ranged hash functor. We 
only use _Mod_range_hashing and _Mask_range_hashing.


Thanks to this simplification _M_bucket_index can also be simplified.

    * include/bits/hashtable_policy.h (_Hashtable<>): Remove _H1 and _H2
    template parameters.
    (_Hastable_base<>): Likewise.
    (_Default_ranged_hash): Remove.
    (_Prime_rehash_policy::__ranged_hash): New.
    (_Power2_rehash_policy::__ranged_hash): New.
    (_Map_base<>): Remove _H1 and _H2 template parameters.
    (_Insert_base<>): Likewise.
    (_Insert<>): Likewise.
    (_Rehash_base<>): Likewise.
    (_Local_iterator_base<>): Remove _H1 and _H2 template parameters 
and add

    _RangedHash.
    (_Hash_code_base<>): Likewise.
    (_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
    __hash_not_cached_t>): Remove.
    (_Hash_code_base<>::_M_bucket_index(const _Key&, __hash_code, size_t)):
    Replace by...
    (_Hash_code_base<>::_M_bucket_index(__hash_code, size_t)): ...this.
    (_Local_iterator<>): Remove _H1 and _H2 template parameters.
    (_Local_const_iterator<>): Likewise.
    (_Equality<>): Likewise.
    * include/bits/hashtable.h (_Hashtable<>): Remove _H1 and _H2 template
    parameters.
    * include/bits/node_handle.h: Adapt.
    * include/bits/unordered_map.h: Adapt.
    * include/bits/unordered_set.h: Adapt.
    * testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt.
    * testsuite/23_containers/unordered_set/hash_policy/71181.cc: Adapt.
    * testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
    Adapt.
    * testsuite/23_containers/unordered_set/hash_policy/rehash.cc: Adapt.
    * testsuite/23_containers/unordered_set/insert/hash_policy.cc: Adapt.
    * testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
    Adapt.
    * testsuite/performance/23_containers/insert/54075.cc: Adapt.
    * testsuite/performance/23_containers/insert_erase/41975.cc: Adapt.
    * testsuite/performance/23_containers/insert_erase/
    unordered_small_size.cc: Adapt.

Tested under Linux x86_64.

François


diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ad07a36eb83..d09c851e8a4 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -69,31 +69,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*  and returns a bool-like value that is true if the two objects
*  are considered equal.
*
-   *  @tparam _H1  The hash function. A unary function object with
+   *  @tparam _Hash  The hash function. A unary function object with
*  argument type _Key and result type size_t. Return values should
*  be distributed over the entire range [0, numeric_limits:::max()].
*
-   *  @tparam _H2  The range-hashing function (in the terminology of
-   *  Tavori and Dreizin).  A binary function object whose argument
-   *  types and result type are all size_t.  Given arguments r and N,
-   *  the return value is in the range [0, N).
-   *
-   *  @tparam _Hash  The ranged hash function (Tavori and Dreizin). A
-   *  binary function whose argument types are _Key and size_t and
-   *  whose result type is size_t.  Given arguments k and N, the
-   *  return value is in the range [0, N).  Default: hash(k, N) =
-   *  h2(h1(k), N).  If _Hash is anything other than the default, _H1
-   *  and _H2 are ignored.
-   *
-   *  @tparam _RehashPolicy  Policy class with three members, all of
-   *  which govern the bucket count. _M_next_bkt(n) returns a bucket
-   *  count no smaller than n.  _M_bkt_for_elements(n) returns a
-   *  bucket count appropriate for an element count of n.
-   *  _M_need_rehash(n_bkt, n_elt, n_ins) determines whether, if the
-   *  current bucket count is n_bkt and the current element count is
-   *  n_elt, we need to increase the bucket count.  If so, returns
-   *  make_pair(true, n), where n is the new bucket count.  If not,
-   *  returns make_pair(false, )
+   *  @tparam _RehashPolicy  Policy class with three members, all of which
+   *  govern the bucket count. _M_next_bkt(n) returns a bucket count no smaller
+   *  than n. _M_bkt_for_elements(n) returns a bucket count appropriate for an
+   *  element count of n. _M_need_rehash(n_bkt, n_elt, n_ins) determines
+   *  whether, if the current bucket count is n_bkt and the current element
+   *  count is n_elt, we need to increase the bucket count for n_ins insertions.
+   *  If so, returns make_pair(true, n), where n is the new bucket count. If
+   *  not, returns make_pair(false, )
*
*  @tparam _Traits  Compile-time class with three boolean
*  std::integral_constant members:  __cache_hash_code, __constant_iterators,
@@ -168,19 +155,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
   template
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
 class 

[PATCH][Hashtable 4/6] Clean local_iterator implementation

2019-11-17 Thread François Dumont
Simplify local_iterator implementation. It makes local_iterator and 
iterator comparable which is used in debug containers.


    * include/bits/hashtable_policy.h (_Node_iterator_base()): New.
    (operator==(const _Node_iterator_base&, const _Node_iterator_base&)):
    Make hidden friend.
    (operator!=(const _Node_iterator_base&, const _Node_iterator_base&)):
    Make hidden friend.
    (_Local_iterator_base<>): Inherits _Node_iterator_base.
    (_Local_iterator_base<>::_M_cur): Remove.
    (_Local_iterator_base<>::_M_curr()): Remove.
    (operator==(const _Local_iterator_base&, const _Local_iterator_base&)):
    Remove.
    (operator!=(const _Local_iterator_base&, const _Local_iterator_base&)):
    Remove.
    * include/debug/unordered_map (unordered_map<>::_M_invalidate): Adapt.
    (unordered_multimap<>::_M_invalidate): Adapt.
    * include/debug/unordered_set (unordered_set<>::_M_invalidate): Adapt.
    (unordered_multiset<>::_M_invalidate): Adapt.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index f330f7f811b..5cc943b3d22 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -301,27 +301,24 @@ namespace __detail
 
   __node_type*  _M_cur;
 
+  _Node_iterator_base() = default;
   _Node_iterator_base(__node_type* __p) noexcept
   : _M_cur(__p) { }
 
   void
   _M_incr() noexcept
   { _M_cur = _M_cur->_M_next(); }
-};
 
-  template
-inline bool
-operator==(const _Node_iterator_base<_Value, _Cache_hash_code>& __x,
-	   const _Node_iterator_base<_Value, _Cache_hash_code >& __y)
-noexcept
-{ return __x._M_cur == __y._M_cur; }
+  friend bool
+  operator==(const _Node_iterator_base& __x, const _Node_iterator_base& __y)
+  noexcept
+  { return __x._M_cur == __y._M_cur; }
 
-  template
-inline bool
-operator!=(const _Node_iterator_base<_Value, _Cache_hash_code>& __x,
-	   const _Node_iterator_base<_Value, _Cache_hash_code>& __y)
-noexcept
-{ return __x._M_cur != __y._M_cur; }
+  friend bool
+  operator!=(const _Node_iterator_base& __x, const _Node_iterator_base& __y)
+  noexcept
+  { return __x._M_cur != __y._M_cur; }
+};
 
   /// Node iterators, used to iterate through all the hashtable.
   template::type;
 
   _Node_iterator() noexcept
-  : __base_type(0) { }
+  : __base_type(nullptr) { }
 
   explicit
   _Node_iterator(__node_type* __p) noexcept
@@ -394,7 +391,7 @@ namespace __detail
   typedef const _Value&reference;
 
   _Node_const_iterator() noexcept
-  : __base_type(0) { }
+  : __base_type(nullptr) { }
 
   explicit
   _Node_const_iterator(__node_type* __p) noexcept
@@ -1426,9 +1423,11 @@ namespace __detail
 struct _Local_iterator_base<_Key, _Value, _ExtractKey,
 _H1, _H2, _Hash, __hash_cached_t>
 : private _Hashtable_ebo_helper<0, _H2>
+, _Node_iterator_base<_Value, __hash_cached_t>
 {
 protected:
   using __base_type = _Hashtable_ebo_helper<0, _H2>;
+  using __base_node_iter = _Node_iterator_base<_Value, __hash_cached_t>;
   using __hash_code_base = _Hash_code_base<_Key, _Value, _ExtractKey,
 	   _H1, _H2, _Hash,
 	   __hash_cached_t>;
@@ -1437,31 +1436,27 @@ namespace __detail
   _Local_iterator_base(const __hash_code_base& __base,
 			   _Hash_node<_Value, __hash_cached_t>* __p,
 			   std::size_t __bkt, std::size_t __bkt_count)
-  : __base_type(__base._M_h2()),
-	_M_cur(__p), _M_bucket(__bkt), _M_bucket_count(__bkt_count) { }
+  : __base_type(__base._M_h2()), __base_node_iter(__p)
+  , _M_bucket(__bkt), _M_bucket_count(__bkt_count) { }
 
   void
   _M_incr()
   {
-	_M_cur = _M_cur->_M_next();
-	if (_M_cur)
+	__base_node_iter::_M_incr();
+	if (this->_M_cur)
 	  {
 	std::size_t __bkt
-	  = __base_type::_M_get()(_M_cur->_M_hash_code,
-	   _M_bucket_count);
+	  = __base_type::_M_get()(this->_M_cur->_M_hash_code,
+  _M_bucket_count);
 	if (__bkt != _M_bucket)
-	  _M_cur = nullptr;
+	  this->_M_cur = nullptr;
 	  }
   }
 
-  _Hash_node<_Value, __hash_cached_t>*  _M_cur;
   std::size_t _M_bucket;
   std::size_t _M_bucket_count;
 
 public:
-  const void*
-  _M_curr() const { return _M_cur; }  // for equality ops
-
   std::size_t
   _M_get_bucket() const { return _M_bucket; }  // for debug mode
 };
@@ -1510,18 +1505,20 @@ namespace __detail
 struct _Local_iterator_base<_Key, _Value, _ExtractKey,
 _H1, _H2, _Hash, __hash_not_cached_t>
 : __hash_code_for_local_iter<_Key, _Value, _ExtractKey, _H1, _H2, _Hash>
+, _Node_iterator_base<_Value, __hash_not_cached_t>
 {
 protected:
   using __hash_code_base = _Hash_code_base<_Key, _Value, _ExtractKey,
 	   _H1, _H2, _Hash,
 	   __hash_not_cached_t>;
+  

[PATCH][Hashtable 3/6] Fix noexcept qualifications

2019-11-17 Thread François Dumont
This patch adds noexcept qualification on allocator aware constructors 
and fix the one on the default constructor.


    * include/bits/hashtable.h
    (_Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a, true_type)):
    Add noexcept qualification.
    (_Hashtable(_Hashtable&&)): Fix noexcept qualification.
    (_Hashtable(_Hashtable&&, const allocator_type&)): Add noexcept
    qualification.
    * include/bits/unordered_map.h
    (unordered_map(unordered_map&&, const allocator_type&)): Add noexcept
    qualification.
    (unordered_multimap(unordered_multimap&&, const allocator_type&)): Add
    noexcept qualification.
    * include/bits/unordered_set.h
    (unordered_set(unordered_set&&, const allocator_type&)): Add noexcept
    qualification.
    (unordered_multiset(unordered_multiset&&, const allocator_type&)): Add
    noexcept qualification.
    * testsuite/23_containers/unordered_map/allocator/default_init.cc:
    New.
    * testsuite/23_containers/unordered_map/cons/
    noexcept_default_construct.cc: New.
    * testsuite/23_containers/unordered_map/cons/
    noexcept_move_construct.cc: New.
    * testsuite/23_containers/unordered_map/modifiers/move_assign.cc:
    New.
    * testsuite/23_containers/unordered_multimap/cons/
    noexcept_default_construct.cc: New.
    * testsuite/23_containers/unordered_multimap/cons/
    noexcept_move_construct.cc: New.
    * testsuite/23_containers/unordered_multiset/cons/
    noexcept_default_construct.cc: New.
    * testsuite/23_containers/unordered_multiset/cons/
    noexcept_move_construct.cc: New.
    * testsuite/23_containers/unordered_set/allocator/default_init.cc: New.
    * testsuite/23_containers/unordered_set/cons/
    noexcept_default_construct.cc: New.
    * 
testsuite/23_containers/unordered_set/cons/noexcept_move_construct.cc:

    New.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index 5f785d4904d..ad07a36eb83 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -463,6 +463,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__hashtable_alloc(__node_alloc_type(__a))
   { }
 
+  _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a, true_type)
+	noexcept(std::is_nothrow_copy_constructible<_H1>::value &&
+		 std::is_nothrow_copy_constructible<_Equal>::value)
+  : __hashtable_base(__ht),
+	__map_base(__ht),
+	__rehash_base(__ht),
+	__hashtable_alloc(std::move(__a)),
+	_M_buckets(__ht._M_buckets),
+	_M_bucket_count(__ht._M_bucket_count),
+	_M_before_begin(__ht._M_before_begin._M_nxt),
+	_M_element_count(__ht._M_element_count),
+	_M_rehash_policy(__ht._M_rehash_policy)
+  {
+	// Update, if necessary, buckets if __ht is using its single bucket.
+	if (__ht._M_uses_single_bucket())
+	  {
+	_M_buckets = &_M_single_bucket;
+	_M_single_bucket = __ht._M_single_bucket;
+	  }
+
+	// Fix bucket containing the _M_before_begin pointer that can't be
+	// moved.
+	_M_update_bbegin();
+
+	__ht._M_reset();
+  }
+
+  _Hashtable(_Hashtable&&, __node_alloc_type&&, false_type);
+
   template
 	_Hashtable(_InputIterator __first, _InputIterator __last,
 		   size_type __bkt_count_hint,
@@ -489,11 +518,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   _Hashtable(const _Hashtable&);
 
-  _Hashtable(_Hashtable&&) noexcept;
+  _Hashtable(_Hashtable&& __ht)
+	noexcept( noexcept(
+	  _Hashtable(std::declval<_Hashtable&&>(),
+	std::declval<__node_alloc_type&&>(), std::declval())) )
+  : _Hashtable(std::move(__ht), std::move(__ht._M_node_allocator()),
+		   true_type{})
+  { }
 
   _Hashtable(const _Hashtable&, const allocator_type&);
 
-  _Hashtable(_Hashtable&&, const allocator_type&);
+  _Hashtable(_Hashtable&& __ht, const allocator_type& __a)
+	noexcept( noexcept(
+	  _Hashtable(std::declval<_Hashtable&&>(),
+	std::declval<__node_alloc_type&&>(),
+	std::declval())) )
+  : _Hashtable(std::move(__ht), __node_alloc_type(__a),
+		   typename __node_alloc_traits::is_always_equal{})
+  { }
 
   // Use delegating constructors.
   template
@@ -1368,36 +1410,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _M_assign(__ht, __alloc_node_gen);
 }
 
-  template
-_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	   _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-_Hashtable(_Hashtable&& __ht) noexcept
-: __hashtable_base(__ht),
-  __map_base(__ht),
-  __rehash_base(__ht),
-  __hashtable_alloc(std::move(__ht._M_base_alloc())),
-  _M_buckets(__ht._M_buckets),
-  _M_bucket_count(__ht._M_bucket_count),
-  _M_before_begin(__ht._M_before_begin._M_nxt),
-  _M_element_count(__ht._M_element_count),
-  _M_rehash_policy(__ht._M_rehash_policy)
-{
-  // Update, if necessary, buckets if __ht is using its single bucket.
-  if (__ht._M_uses_single_bucket())
-	{
-	  _M_buckets = &_M_single_bucket;
-	  _M_single_bucket = 

[PATCH][Hashtable 2/6] Avoid over-sizing container

2019-11-17 Thread François Dumont
This patch avoids over-sizing of the container by rather considering the 
bucket count hint or potential reservation.


It concerns only the non-multi containers.

    * include/bits/hashtable.h
    (_Hashtable<>(_InputIterator, _InputIterator, size_t, const _H1&,
    const _H2&, const _Hash&, const _Equal&, const _ExtractKey&,
    const allocator_type&, __unique_keys_t)): New.
    (_Hashtable<>(_InputIterator, _InputIterator, size_t, const _H1&,
    const _H2&, const _Hash&, const _Equal&, const _ExtractKey&,
    const allocator_type&, __multi_keys_t)): New.
    (_Hashtable<>(_InputIterator, _InputIterator, size_t, const _H1&,
    const _H2&, const _Hash&, const _Equal&, const _ExtractKey&,
    const allocator_type&)): Delegate to latters.
    (operator=(initializer_list)): Rehash if too small.
    (_M_insert(_Arg&&, const _NodeGenerator&, __unique_keys_t)): Remove
    size_t len parameter.
    * include/bits/hashtable_policy.h (_Insert_base<>::_M_insert_range):
    Do not try to get input range distance.
    * testsuite/23_containers/unordered_set/cons/bucket_hint.cc: New.
    * testsuite/23_containers/unordered_set/modifiers/insert.cc: New.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index a685c20376f..5f785d4904d 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -463,17 +463,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__hashtable_alloc(__node_alloc_type(__a))
   { }
 
-public:
-  // Constructor, destructor, assignment, swap
-  _Hashtable() = default;
-  _Hashtable(size_type __bkt_count_hint,
+  template
+	_Hashtable(_InputIterator __first, _InputIterator __last,
+		   size_type __bkt_count_hint,
 		   const _H1&, const _H2&, const _Hash&,
 		   const _Equal&, const _ExtractKey&,
-		 const allocator_type&);
+		   const allocator_type&,
+		   __unique_keys_t);
 
   template
 	_Hashtable(_InputIterator __first, _InputIterator __last,
 		   size_type __bkt_count_hint,
+		   const _H1&, const _H2&, const _Hash&,
+		   const _Equal&, const _ExtractKey&,
+		   const allocator_type&,
+		   __multi_keys_t);
+
+public:
+  // Constructor, destructor, assignment, swap
+  _Hashtable() = default;
+  _Hashtable(size_type __bkt_count_hint,
 		 const _H1&, const _H2&, const _Hash&,
 		 const _Equal&, const _ExtractKey&,
 		 const allocator_type&);
@@ -487,6 +496,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Hashtable(_Hashtable&&, const allocator_type&);
 
   // Use delegating constructors.
+  template
+	_Hashtable(_InputIterator __first, _InputIterator __last,
+		   size_type __bkt_count_hint,
+		   const _H1& __h1, const _H2& __h2, const _Hash& __h,
+		   const _Equal& __eq, const _ExtractKey& __exk,
+		   const allocator_type& __a)
+	: _Hashtable(__first, __last, __bkt_count_hint,
+		 __h1, __h2, __h, __eq, __exk, __a, __unique_keys{})
+	{ }
+
   explicit
   _Hashtable(const allocator_type& __a)
   : __hashtable_alloc(__node_alloc_type(__a))
@@ -543,6 +562,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__reuse_or_alloc_node_gen_t __roan(_M_begin(), *this);
 	_M_before_begin._M_nxt = nullptr;
 	clear();
+
+	// We consider that all elements of __l are going to be inserted.
+	auto __l_bkt_count = _M_rehash_policy._M_bkt_for_elements(__l.size());
+
+	// Do not shrink to keep potential user reservation.
+	if (_M_bucket_count < __l_bkt_count)
+	  rehash(__l_bkt_count);
+
 	this->_M_insert_range(__l.begin(), __l.end(), __roan, __unique_keys{});
 	return *this;
   }
@@ -763,7 +790,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template
 	std::pair
-	_M_insert(_Arg&&, const _NodeGenerator&, __unique_keys_t, size_type = 1);
+	_M_insert(_Arg&&, const _NodeGenerator&, __unique_keys_t);
 
   template
 	iterator
@@ -1062,7 +1089,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		 size_type __bkt_count_hint,
 		 const _H1& __h1, const _H2& __h2, const _Hash& __h,
 		 const _Equal& __eq, const _ExtractKey& __exk,
-		 const allocator_type& __a)
+		 const allocator_type& __a, __unique_keys_t)
+  : _Hashtable(__bkt_count_hint, __h1, __h2, __h, __eq, __exk, __a)
+  {
+	for (; __f != __l; ++__f)
+	  this->insert(*__f);
+  }
+
+  template
+template
+  _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+  _Hashtable(_InputIterator __f, _InputIterator __l,
+		 size_type __bkt_count_hint,
+		 const _H1& __h1, const _H2& __h2, const _Hash& __h,
+		 const _Equal& __eq, const _ExtractKey& __exk,
+		 const allocator_type& __a, __multi_keys_t)
   : _Hashtable(__h1, __h2, __h, __eq, __exk, __a)
   {
 	auto __nb_elems = __detail::__distance_fw(__f, __l);
@@ -1830,7 +1875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
   _M_insert(_Arg&& __v, const _NodeGenerator& __node_gen,

[PATCH][Hashtable 1/6] Code simplification/optimization

2019-11-17 Thread François Dumont

This patch simplifies a number of implementations.

It tries as much as possible to avoid computing hash code. This is 
especially true for the erase implementation in case of multi keys.



    * include/bits/hashtable_policy.h (_Map_base<>::at): Use
    _Hashtable<>::find.
(_Hashtable_base<>::_Equal_hash_code<>::_S_node_equals):New.
    (_Hashtable_base<>::_M_node_equals): New, use latter.
    * include/bits/hashtable.h (_Hashtable<>::_M_update_bbegin): New.
    (_Hashtable<>::_M_assign): Use latter.
    (_Hashtable<>::_M_move_assign): Likewise.
    (_Hashtable<>(_Hashtable<>&&)): Likewise.
    (_Hashtable<>(_Hashtable<>&&, const allocator_type&)): Likewise.
    (_Hashtable<>::swap): Likewise.
    (_Hashtable<>::find): Build iterator directly from _M_find_node result.
    (_Hashtable<>::count): Use _Hashtable<>::find.
    (_Hashtable<>::equal_range): Likewise.
    (_Hashtable<>::_M_erase(false_type, const key_type&)): Use
    _M_node_equals.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ef71c090f3b..b8cfdde2f31 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -378,6 +378,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // numerous checks in the code to avoid 0 modulus.
   __bucket_type		_M_single_bucket	= nullptr;
 
+  void
+  _M_update_bbegin()
+  {
+	if (_M_begin())
+	  _M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  }
+
+  void
+  _M_update_bbegin(__node_type* __n)
+  {
+	_M_before_begin._M_nxt = __n;
+	_M_update_bbegin();
+  }
+
   bool
   _M_uses_single_bucket(__bucket_type* __bkts) const
   { return __builtin_expect(__bkts == &_M_single_bucket, false); }
@@ -674,7 +688,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   std::pair
   equal_range(const key_type& __k) const;
 
-protected:
+private:
   // Bucket index computation helpers.
   size_type
   _M_bucket_index(__node_type* __n) const noexcept
@@ -1196,8 +1210,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  = __node_gen(__fwd_value(std::forward<_Ht>(__ht),
    __ht_n->_M_v()));
 	this->_M_copy_code(__this_n, __ht_n);
-	_M_before_begin._M_nxt = __this_n;
-	_M_buckets[_M_bucket_index(__this_n)] = &_M_before_begin;
+	_M_update_bbegin(__this_n);
 
 	// Then deal with other nodes.
 	__node_base* __prev_n = __this_n;
@@ -1259,15 +1272,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_buckets = &_M_single_bucket;
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
+
   _M_bucket_count = __ht._M_bucket_count;
   _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
   _M_element_count = __ht._M_element_count;
   std::__alloc_on_move(this->_M_node_allocator(), __ht._M_node_allocator());
 
-  // Fix buckets containing the _M_before_begin pointers that can't be
-  // moved.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be moved.
+  _M_update_bbegin();
   __ht._M_reset();
 }
 
@@ -1335,10 +1347,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
 
-  // Update, if necessary, bucket pointing to before begin that hasn't
-  // moved.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be moved.
+  _M_update_bbegin();
 
   __ht._M_reset();
 }
@@ -1389,11 +1399,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  else
 	_M_buckets = __ht._M_buckets;
 
-	  _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
-	  // Update, if necessary, bucket pointing to before begin that hasn't
+	  // Fix bucket containing the _M_before_begin pointer that can't be
 	  // moved.
-	  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+	  _M_update_bbegin(__ht._M_begin());
+
 	  __ht._M_reset();
 	}
   else
@@ -1457,14 +1466,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   std::swap(_M_element_count, __x._M_element_count);
   std::swap(_M_single_bucket, __x._M_single_bucket);
 
-  // Fix buckets containing the _M_before_begin pointers that can't be
-  // swapped.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
-
-  if (__x._M_begin())
-	__x._M_buckets[__x._M_bucket_index(__x._M_begin())]
-	  = &__x._M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be swap.
+  _M_update_bbegin();
+  __x._M_update_bbegin();
 }
 
   template_M_hash_code(__k);
   std::size_t __bkt = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__bkt, __k, __code);
-  return __p ? iterator(__p) : end();
+  return iterator(_M_find_node(__bkt, __k, __code));
 }
 
   template_M_hash_code(__k);
   std::size_t 

[PATCH][Hashtable 0/6] Code review

2019-11-17 Thread François Dumont

This is the begining of a patch series for _Hashtable

Initial patch to clarify code. I was tired to see true/false or 
true_type/false_type without knowing what was true/false.


I also made code more consistent by chosing to specialize methods 
through usage of __unique_keys_t/__multi_keys_t rather than calling them 
_M_[multi]_XXX.



    * include/bits/hashtable_policy.h (__detail::__unique_keys_t): New.
    (__detail::__multi_keys_t): New.
    (__detail::__constant_iterators_t): New.
    (__detail::__mutable_iterators_t): New.
    (__detail::__hash_cached_t): New.
    (__detail::__hash_not_cached_t): New.
    (_Hash_node<>): Change _Cache_hash_code template parameter from bool to
    typename. Adapt partial specializations.
    (_Node_iterator_base<>): Likewise.
    (operator==(const _Node_iterator_base<>&,const 
_Node_iterator_base<>&)):

    Adapt.
    (operator!=(const _Node_iterator_base<>&,const 
_Node_iterator_base<>&)):

    Adapt.
    (_Node_iterator<>): Change __constant_iterators and __cache template
    parameters from bool to typename.
    (_Node_const_iterator<>): Likewise.
    (_Map_base<>): Change _Unique_keys template parameter from bool to
    typename. Adapt partial specializations.
    (_Insert<>): Change _Constant_iterators template parameter from bool to
    typename. Adapt partial specializations.
    (_Local_iterator_base<>): Change __cache_hash_code template parameter
    from bool to typename. Adapt partial specialization.
    (_Hash_code_base<>): Likewise.
    (operator==(const _Local_iterator_base<>&,
    const _Local_iterator_base<>&)): Adapt.
    (operator!=(const _Local_iterator_base<>&,
    const _Local_iterator_base<>&)):
    Adapt.
    (_Local_iterator<>): Change __constant_iterators and __cache template
    parameters from bool to typename.
    (_Local_const_iterator<>): Likewise.
    (_Hashtable_base<>): Adapt.
    (_Equal_hash_code<>): Adapt.
    (_Equality<>): Adapt.
    * include/bits/hashtable.h (_Hashtable<>): Replace occurences of
    true_type/false_type by respoectively __unique_type_t/__multi_type_t.
    (_M_insert_unique_node(const key_type&, size_t, __hash_code,
    __node_type*, size_t)): Replace by...
    (_M_insert_node(__unique_keys_t, size_t, __hash_code, __node_type*,
    size_t)): ...this.
    (_M_insert_muti_node(__node_type*, const key_type&, __hash_code,
    __node_type*)): Replace by...
    (_M_insert_node(__multi_keys_t, __node_type*, __hash_code,
    __node_type*)): ...this.
    (_M_reinsert_node(node_type&&)): Replace by...
    (_M_reinsert_node(node_type&&, __unique_keys_t)): ...this.
    (_M_reinsert_node(const_iterator, node_type&&, __unique_keys_t)): New,
    forward to latter.
    (_M_reinsert_node_multi(const_iterator, node_type&&)): Replace by...
    (_M_reinsert_node(const_iterator, node_type&&, __multi_keys_t)):
    ...this.
    (_M_reinsert_node(node_type&&, __multi_keys_t)): New, forward to 
latter.

    (_M_reinsert_node(node_type&&)): New, use latters.
    (_M_reinsert_node(const_iterator, node_type&&)): Likewise.
    (_M_merge_unique(_Compatible_Hashtable&)): Replace by...
    (_M_merge(__unique_keys_t, _Compatible_Hashtable&)): ...this.
    (_M_merge_multi(_Compatible_Hashtable&)): Replace by...
    (_M_merge(__multi_keys_t, _Compatible_Hashtable&)): ...this.
    (_M_merge(_Compatible_Hashtable&)): New, use latters.
    * include/bits/unordered_map.h
    (unordered_map<>::insert(const_iterator, node_type&&)): Adapt.
    (unordered_map<>::merge(unordered_map<>&)): Adapt.
(unordered_map<>::merge(unordered_multimap<>&)): Adapt.
    (unordered_multimap<>::insert(node_type&&)): Adapt.
    (unordered_multimap<>::insert(const_iterator, node_type&&)): Adapt.
(unordered_multimap<>::merge(unordered_multimap<>&)): Adapt.
(unordered_multimap<>::merge(unordered_map<>&)): Adapt.
    * include/bits/unordered_set.h
    (unordered_set<>::insert(const_iterator, node_type&&)): Adapt.
    (unordered_set<>::merge(unordered_set<>&)): Adapt.
(unordered_set<>::merge(unordered_multiset<>&)): Adapt.
    (unordered_multiset<>::insert(node_type&&)): Adapt.
    (unordered_multiset<>::insert(const_iterator, node_type&&)): Adapt.
(unordered_multiset<>::merge(unordered_multiset<>&)): Adapt.
(unordered_multiset<>::merge(unordered_set<>&)): Adapt.

Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index c2b2219d471..ef71c090f3b 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -184,7 +184,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   private __detail::_Hashtable_alloc<
 	__alloc_rebind<_Alloc,
 		   __detail::_Hash_node<_Value,
-	_Traits::__hash_cached::value>>>
+	typename _Traits::__hash_cached>>>
 {
   static_assert(is_same::type, _Value>::value,
 	  "unordered container must have a non-const, non-volatile value_type");
@@ -195,7 +195,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   using __traits_type = 

[PATCH] PR libstdc++/92124 on hashtable

2019-11-07 Thread François Dumont
From what I understood from recent fix the unordered containers need to 
be updated the same way.


I hope you'll appreciate the usage of rvalue forwarding. Containers node 
values are moved as soon as _M_assign is called with a rvalue reference 
to the source container.


Additionnaly this patch removes usages of lambdas in _Hashtable.

If you confirm it I'll check for the same on _Rb_tree.

    * include/bits/hashtable.h (_Hashtable<>::__alloc_node_gen_t): New
    template alias.
    (_Hashtable<>::__mv_if_value_type_mv_noexcept): New.
    (_Hashtable<>::__fwd_value): New.
    (_Hashtable<>::_M_assign_elements<>): Remove _NodeGenerator template
    parameter.
    (_Hashtable<>::_M_assign<>): Add _Ht template parameter.
    (_Hashtable<>::operator=(const _Hashtable<>&)): Adapt.
    (_Hashtable<>::_M_move_assign): Adapt.
    (_Hashtable<>::_Hashtable(const _Hashtable&)): Adapt.
    (_Hashtable<>::_Hashtable(const _Hashtable&, const allocator_type&)):
    Adapt.
    (_Hashtable<>::_Hashtable(_Hashtable&&, const allocator_type&)):
    Adapt.
    * testsuite/23_containers/unordered_set/92124.cc: New.

Tested under Linux x86_64.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab579a7059e..c2b2219d471 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -255,6 +255,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   using __reuse_or_alloc_node_gen_t =
 	__detail::_ReuseOrAllocNode<__node_alloc_type>;
+  using __alloc_node_gen_t =
+	__detail::_AllocNode<__node_alloc_type>;
 
   // Simple RAII type for managing a node containing an element
   struct _Scoped_node
@@ -280,6 +282,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__node_type* _M_node;
   };
 
+  template
+	static constexpr
+	typename conditional<__move_if_noexcept_cond::value,
+			 const _Tp&, _Tp&&>::type
+	__mv_if_value_type_mv_noexcept(_Tp& __x) noexcept
+	{ return std::move(__x); }
+
+  template
+	static constexpr
+	typename conditional::value,
+			 value_type&&, const value_type&>::type
+	__fwd_value(_Ht&&, value_type& __val) noexcept
+	{ return std::move(__val); }
+
   // Metaprogramming for picking apart hash caching.
   template
 	using __if_hash_cached = __or_<__not_<__hash_cached>, _Cond>;
@@ -406,13 +422,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Assign *this using another _Hashtable instance. Either elements
   // are copy or move depends on the _NodeGenerator.
-  template
+  template
 	void
-	_M_assign_elements(_Ht&&, const _NodeGenerator&);
+	_M_assign_elements(_Ht&&);
 
-  template
+  template
 	void
-	_M_assign(const _Hashtable&, const _NodeGenerator&);
+	_M_assign(_Ht&&, const _NodeGenerator&);
 
   void
   _M_move_assign(_Hashtable&&, true_type);
@@ -1051,11 +1067,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_bucket_count = __ht._M_bucket_count;
 	  _M_element_count = __ht._M_element_count;
 	  _M_rehash_policy = __ht._M_rehash_policy;
+	  __alloc_node_gen_t __alloc_node_gen(*this);
 	  __try
 		{
-		  _M_assign(__ht,
-			[this](const __node_type* __n)
-			{ return this->_M_allocate_node(__n->_M_v()); });
+		  _M_assign(__ht, __alloc_node_gen);
 		}
 	  __catch(...)
 		{
@@ -1070,9 +1085,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 
   // Reuse allocated buckets and nodes.
-  _M_assign_elements(__ht,
-	[](const __reuse_or_alloc_node_gen_t& __roan, const __node_type* __n)
-	{ return __roan(__n->_M_v()); });
+  _M_assign_elements(__ht);
   return *this;
 }
 
@@ -1080,11 +1093,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
 	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
 	   typename _Traits>
-template
+template
   void
   _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-  _M_assign_elements(_Ht&& __ht, const _NodeGenerator& __node_gen)
+  _M_assign_elements(_Ht&& __ht)
   {
 	__bucket_type* __former_buckets = nullptr;
 	std::size_t __former_bucket_count = _M_bucket_count;
@@ -1107,9 +1120,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_rehash_policy = __ht._M_rehash_policy;
 	__reuse_or_alloc_node_gen_t __roan(_M_begin(), *this);
 	_M_before_begin._M_nxt = nullptr;
-	_M_assign(__ht,
-		  [&__node_gen, &__roan](__node_type* __n)
-		  { return __node_gen(__roan, __n); });
+	_M_assign(std::forward<_Ht>(__ht), __roan);
 	if (__former_buckets)
 	  _M_deallocate_buckets(__former_buckets, __former_bucket_count);
 	  }
@@ -1133,11 +1144,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
 	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
 	   typename _Traits>
-template
+template
   void
   _Hashtable<_Key, _Value, _Alloc, _ExtractKey, 

Re: [PATCH] Help compiler detect invalid code

2019-10-16 Thread François Dumont

Here is a version with __detail::__copy and __detail::__copy_backward.

I prefered to introduce the __detail namespace cause __copy is quite a 
common name so putting it in __detail namespace will I hope clarify that 
it is for internal usage only.


I even hesitated to put more details into this namespace, maybe for 
another patch later.


    * include/bits/stl_algobase.h (__memmove): Replace by...
    (__detail::__copy) ...that. Return void, loop as long as __n != 0.
    (__copy_move<_IsMove, true, 
std::random_access_iterator_tag>::__copy_m):

    Adapt to use latter.
    (__detail::__copy_backward): New.
    (__copy_move_backward<_IsMove, true,
    std::random_access_iterator_tag>::__copy_m): Adapt to use latter.
    (__copy_move_backward_a): Remove std::is_constant_evaluated block.
    * testsuite/25_algorithms/copy/constexpr.cc (test): Add check on copied
    values.
    * testsuite/25_algorithms/copy_backward/constexpr.cc (test): Likewise
    and rename in test1.
    (test2): New.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.
    * testsuite/25_algorithms/equal/constexpr_neg.cc: New.
    * testsuite/25_algorithms/move/constexpr.cc: New.
    * testsuite/25_algorithms/move/constexpr_neg.cc: New.

François

On 10/10/19 10:03 PM, Jonathan Wakely wrote:

On 01/10/19 22:05 +0200, François Dumont wrote:

On 9/27/19 1:24 PM, Jonathan Wakely wrote:

On 20/09/19 07:08 +0200, François Dumont wrote:
I already realized that previous patch will be too controversial to 
be accepted.


In this new version I just implement a real memmove in __memmove so


A real memmove doesn't just work backwards, it needs to detect any
overlaps and work forwards *or* backwards, as needed.
ok, good to know, I understand now why using __builtin_memcopy didn't 
show any performance enhancement when I tested it !


I think your change will break this case:

#include 

constexpr int f(int i, int j, int k)
{
 int arr[5] = { 0, 0, i, j, k };
 std::move(arr+2, arr+5, arr);
 return arr[0] + arr[1] + arr[2];
}

static_assert( f(1, 2, 3) == 6 );

This is valid because std::move only requires that the result iterator
is not in the input range, but it's OK for the two ranges to overlap.

I haven't tested it, but I think with your change the array will end
up containing {3, 2, 3, 2, 3} instead of {1, 2, 3, 2, 3}.

Indeed, I've added a std::move constexpr test in this new proposal 
which demonstrate that.


C++ Standard clearly states that [copy|move]_backward is done 
backward. So in this new proposal I propose to add a __memcopy used 
in copy/move and keep __memmove for *_backward algos. Both are using 
__builtin_memmove as before.


Then they *really* need better names now (__memmove was already a bad
name, but now it's terrible). If the difference is that one goes
forwards and one goes backwards, the names should reflect that.

I'll review it properly tomorrow.




diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc++-v3/include/bits/stl_algobase.h
index 98d324827ed..dc6b3d3fc76 100644
--- a/libstdc++-v3/include/bits/stl_algobase.h
+++ b/libstdc++-v3/include/bits/stl_algobase.h
@@ -77,19 +77,22 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
+namespace __detail
+{
   /*
* A constexpr wrapper for __builtin_memmove.
+   * When constant-evaluated performs a forward copy.
* @param __num The number of elements of type _Tp (not bytes).
*/
   template
 _GLIBCXX14_CONSTEXPR
-inline void*
-__memmove(_Tp* __dst, const _Tp* __src, size_t __num)
+inline void
+__copy(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
 {
 #ifdef __cpp_lib_is_constant_evaluated
   if (std::is_constant_evaluated())
 	{
-	  for(; __num > 0; --__num)
+	  for (; __num != 0; --__num)
 	{
 	  if constexpr (_IsMove)
 		*__dst = std::move(*__src);
@@ -98,13 +101,40 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  ++__src;
 	  ++__dst;
 	}
-	  return __dst;
 	}
   else
 #endif
-	return __builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
-  return __dst;
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
+}
+
+  /*
+   * A constexpr wrapper for __builtin_memmove.
+   * When constant-evaluated performs a backward copy.
+   * @param __num The number of elements of type _Tp (not bytes).
+   */
+  template
+_GLIBCXX14_CONSTEXPR
+inline void
+__copy_backward(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
+{
+#ifdef __cpp_lib_is_constant_evaluated
+  if (std::is_constant_evaluated())
+	{
+	  __dst += __num;
+	  __src += __num;
+	  for (; __num != 0; --__num)
+	{
+	  if constexpr (_IsMove)
+		*--__dst = std::move(*--__src);
+	  else
+		*--__dst = *--__src;
+	}
+	}
+  else
+#endif
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
 }
+} // namespace __detail
 
   /*
* A constexpr wrapper for __builtin_memcmp.
@

[PATCH] Clarify constness and state

2019-10-15 Thread François Dumont

    * src/c++11/debug.cc (print_field): Replace constness_names 
    entry with . Replace state_names  entry 
with

    .

Committed as trivial.

François

Index: src/c++11/debug.cc
===
--- src/c++11/debug.cc	(révision 277048)
+++ src/c++11/debug.cc	(copie de travail)
@@ -721,7 +721,7 @@
 	static const char*
 	  constness_names[_Error_formatter::__last_constness] =
 	  {
-		"",
+		"",
 		"constant",
 		"mutable"
 	  };
@@ -732,7 +732,7 @@
 	static const char*
 	  state_names[_Error_formatter::__last_state] =
 	  {
-		"",
+		"",
 		"singular",
 		"dereferenceable (start-of-sequence)",
 		"dereferenceable",


[PATCH] ostreambuf_iterator std::advance overload

2019-10-15 Thread François Dumont

Hi

    I completed this overload before noticing that the Standard do not 
expect anything when 'advancing' an output iterator.


    But as I've done it consistenly with the istreambuf_iterator here 
it is with samples about how to use it.


    Let me know if acceptable.

François

diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h
index 17dd3972d45..76ba96d634a 100644
--- a/libstdc++-v3/include/bits/streambuf_iterator.h
+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
@@ -242,6 +242,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	copy(istreambuf_iterator<_CharT2>, istreambuf_iterator<_CharT2>,
 	 ostreambuf_iterator<_CharT2>);
 
+  template
+	friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
+	   void>::__type
+	advance(ostreambuf_iterator<_CharT2>&, _Distance);
+
 private:
   streambuf_type*	_M_sbuf;
   bool		_M_failed;
@@ -453,6 +458,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 }
 
+  template
+typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value,
+void>::__type
+advance(ostreambuf_iterator<_CharT>& __i, _Distance __n)
+{
+  if (__n == 0)
+	return;
+
+  __glibcxx_assert(__n > 0);
+  __glibcxx_requires_cond(!__i.failed(),
+			  _M_message(__gnu_debug::__msg_advance_oob)
+			  ._M_iterator(__i)
+			  ._M_integer(__n));
+
+  typedef basic_streambuf<_CharT> __streambuf_t;
+  typedef typename __streambuf_t::pos_type __pos_t;
+  __pos_t __cur_pos
+	= __i._M_sbuf->pubseekoff(0, ios_base::cur, ios_base::out);
+  __pos_t __new_pos =
+	__i._M_sbuf->pubseekoff(__n, ios_base::cur, ios_base::out);
+
+  if (__new_pos - __cur_pos != __n)
+	{
+	  __i._M_failed = true;
+	  __glibcxx_requires_cond(!__i.failed(),
+  _M_message(__gnu_debug::__msg_advance_oob)
+  ._M_iterator(__i)
+  ._M_integer(__n));
+	}
+}
+
 // @} group iterators
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1.cc b/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1.cc
new file mode 100644
index 000..15cf0b27863
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019 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
+// .
+
+#include 
+#include 
+#include 
+
+#include 
+
+void test01()
+{
+  using namespace std;
+
+  const char data1[] = "Drei Phantasien nach Friedrich Holderlin";
+  string str1(data1);
+  str1[17] = 'i';
+
+  ostringstream oss1(str1);
+  ostreambuf_iterator beg1(oss1);
+
+  std::advance(beg1, 17);
+  *beg1 = 'a';
+
+  VERIFY( !beg1.failed() );
+  VERIFY( oss1.str() == data1 );
+  str1 = oss1.str();
+
+  // -1 for the trailing '\0'
+  // -1 for the beg1 assignment.
+  std::advance(beg1, sizeof(data1) - 17 - 1 - 1);
+  *beg1 = '.';
+
+  str1 += '.';
+  VERIFY( oss1.str() == str1 );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1_neg.cc b/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1_neg.cc
new file mode 100644
index 000..9d1e4a94742
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/advance/ostreambuf_iterator/char/1_neg.cc
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 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
+// .
+
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include 
+#include 
+#include 
+
+void test01()
+{
+  using namespace std;
+
+  const char 

Re: [PATCH] Implement std::advance for istreambuf_iterator using pubseekoff

2019-10-15 Thread François Dumont

Here is an update to set _M_sbuf to null if the user advance too much.

Note that in this case the streambuf remains un-modified which is 
different from the current implementation. I think it is another 
enhancement.


I also change the Debug assertion message for something more dedicated 
to std::advance algo.


François

On 10/14/19 10:12 PM, François Dumont wrote:
The same way I proposed to review std::copy overload for 
istreambuf_iterator we can implement std::advance using pubseekoff.


It is both a cleaner implementation and avoids yet another friend 
declaration.



    * include/std/streambuf
    (advance(istreambuf_iterator<>&, _Distance)): Remove friend 
declaration.
    * include/bits/streambuf_iterator.h (__copy_move_a2): Re-implement 
using

    streambuf pubseekoff.

Tested under Linux x86_64.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h
index 134b3486b9a..17dd3972d45 100644
--- a/libstdc++-v3/include/bits/streambuf_iterator.h
+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
@@ -431,37 +431,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   __glibcxx_assert(__n > 0);
   __glibcxx_requires_cond(!__i._M_at_eof(),
-			  _M_message(__gnu_debug::__msg_inc_istreambuf)
-			  ._M_iterator(__i));
-
-  typedef istreambuf_iterator<_CharT>		   __is_iterator_type;
-  typedef typename __is_iterator_type::traits_type	   traits_type;
-  typedef typename __is_iterator_type::streambuf_type  streambuf_type;
-  typedef typename traits_type::int_type		   int_type;
-  const int_type __eof = traits_type::eof();
-
-  streambuf_type* __sb = __i._M_sbuf;
-  while (__n > 0)
-	{
-	  streamsize __size = __sb->egptr() - __sb->gptr();
-	  if (__size > __n)
+			  _M_message(__gnu_debug::__msg_advance_oob)
+			  ._M_iterator(__i)
+			  ._M_integer(__n));
+
+  typedef basic_streambuf<_CharT> __streambuf_t;
+  typedef typename __streambuf_t::pos_type __pos_t;
+  __pos_t __cur_pos
+	= __i._M_sbuf->pubseekoff(0, ios_base::cur, ios_base::in);
+  __pos_t __new_pos
+	= __i._M_sbuf->pubseekoff(__n, ios_base::cur, ios_base::in);
+  __i._M_c = char_traits<_CharT>::eof();
+
+  if (__new_pos - __cur_pos != __n)
 	{
-	  __sb->__safe_gbump(__n);
-	  break;
-	}
-
-	  __sb->__safe_gbump(__size);
-	  __n -= __size;
-	  if (traits_type::eq_int_type(__sb->underflow(), __eof))
-	{
-	  __glibcxx_requires_cond(__n == 0,
-_M_message(__gnu_debug::__msg_inc_istreambuf)
-._M_iterator(__i));
-	  break;
-	}
+	  __i._M_sbuf = 0;
+	  __glibcxx_requires_cond(!__i._M_at_eof(),
+  _M_message(__gnu_debug::__msg_advance_oob)
+  ._M_iterator(__i)
+  ._M_integer(__n));
 	}
-
-  __i._M_c = __eof;
 }
 
 // @} group iterators
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index 3442f19bd78..ef03da39bc2 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -155,11 +155,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 find(istreambuf_iterator<_CharT2>, istreambuf_iterator<_CharT2>,
 	 const _CharT2&);
 
-  template
-friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
-	   void>::__type
-advance(istreambuf_iterator<_CharT2>&, _Distance);
-
   template
 friend basic_istream<_CharT2, _Traits2>&
 operator>>(basic_istream<_CharT2, _Traits2>&, _CharT2*);
diff --git a/libstdc++-v3/testsuite/25_algorithms/advance/istreambuf_iterators/char/3.cc b/libstdc++-v3/testsuite/25_algorithms/advance/istreambuf_iterators/char/3.cc
new file mode 100644
index 000..8763e7fa78e
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/advance/istreambuf_iterators/char/3.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2017-2019 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/>.
+
+// Debug mode would detect the invalid std::advance call.
+// { dg-require-normal-mode "" }
+
+#include 
+#include 
+#include 
+
+#include 
+
+void test01()
+{
+  using namespace std;
+
+  typedef istreambuf_iterator in_iterat

[PATCH] Implement std::advance for istreambuf_iterator using pubseekoff

2019-10-14 Thread François Dumont
The same way I proposed to review std::copy overload for 
istreambuf_iterator we can implement std::advance using pubseekoff.


It is both a cleaner implementation and avoids yet another friend 
declaration.



    * include/std/streambuf
    (advance(istreambuf_iterator<>&, _Distance)): Remove friend 
declaration.
    * include/bits/streambuf_iterator.h (__copy_move_a2): Re-implement 
using

    streambuf pubseekoff.

Tested under Linux x86_64.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h
index 134b3486b9a..afe5c95f021 100644
--- a/libstdc++-v3/include/bits/streambuf_iterator.h
+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
@@ -434,34 +434,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			  _M_message(__gnu_debug::__msg_inc_istreambuf)
 			  ._M_iterator(__i));
 
-  typedef istreambuf_iterator<_CharT>		   __is_iterator_type;
-  typedef typename __is_iterator_type::traits_type	   traits_type;
-  typedef typename __is_iterator_type::streambuf_type  streambuf_type;
-  typedef typename traits_type::int_type		   int_type;
-  const int_type __eof = traits_type::eof();
-
-  streambuf_type* __sb = __i._M_sbuf;
-  while (__n > 0)
-	{
-	  streamsize __size = __sb->egptr() - __sb->gptr();
-	  if (__size > __n)
-	{
-	  __sb->__safe_gbump(__n);
-	  break;
-	}
-
-	  __sb->__safe_gbump(__size);
-	  __n -= __size;
-	  if (traits_type::eq_int_type(__sb->underflow(), __eof))
-	{
-	  __glibcxx_requires_cond(__n == 0,
-_M_message(__gnu_debug::__msg_inc_istreambuf)
-._M_iterator(__i));
-	  break;
-	}
-	}
+#ifdef _GLIBCXX_DEBUG
+  typedef basic_streambuf<_CharT> __streambuf_t;
+  typedef typename __streambuf_t::pos_type __pos_type;
+  __pos_type __cur_pos
+	= __i._M_sbuf->pubseekoff(0, ios_base::cur, ios_base::in);
+  __pos_type __new_pos =
+#endif
+  __i._M_sbuf->pubseekoff(__n, ios_base::cur, ios_base::in);
+  __i._M_c = char_traits<_CharT>::eof();
 
-  __i._M_c = __eof;
+  __glibcxx_requires_cond(__new_pos - __cur_pos == __n,
+			  _M_message(__gnu_debug::__msg_inc_istreambuf)
+			  ._M_iterator(__i));
 }
 
 // @} group iterators
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index 3442f19bd78..ef03da39bc2 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -155,11 +155,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 find(istreambuf_iterator<_CharT2>, istreambuf_iterator<_CharT2>,
 	 const _CharT2&);
 
-  template
-friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
-	   void>::__type
-advance(istreambuf_iterator<_CharT2>&, _Distance);
-
   template
 friend basic_istream<_CharT2, _Traits2>&
 operator>>(basic_istream<_CharT2, _Traits2>&, _CharT2*);


Re: Add std::copy_n overload for istreambuf_iterator

2019-10-10 Thread François Dumont
I think this build has been done between the 2 commits I used to apply 
this patch (because of a problem between the chair and the keyboard). 
Now it should be fine.


But it shows that it changed the behavior of std::copy_n as soon as the 
new specialization is being used.


Without this change the result is "12234" whereas with the 
specialization it is "12345". So in addition to being a nice perf 
enhancement this patch was also a partial fix for PR 81857.


Let me know Jonathan if there is something to do about it.

François

On 10/9/19 10:18 PM, Christophe Lyon wrote:



On Fri, 4 Oct 2019 at 07:01, François Dumont <mailto:frs.dum...@gmail.com>> wrote:


On 9/27/19 1:00 PM, Jonathan Wakely wrote:
    > On 19/07/19 23:37 +0200, François Dumont wrote:
>> It sounds reasonable to overload std::copy_n for
istreambuf_iterator.
> I wonder whether it's worth doing:
>
> #if __cplusplus >= 201703L
>    if constexpr (is_same_v<_OutputIterator, _CharT*>)
>  return __result + __it._M_sbuf->sgetn(__result, __n);
>    else
>  {
> #endif
>  ...
> #if __cplusplus >= 201703L
>  }
> #endif
>
> We could extend that to also work for basic_string<_CharT>::iterator
> and vector<_CharT>::iterator too if we wanted.
>
> I'm not sure if it will perform any better than the code below (it's
> approximately equivalent) but it should result in smaller
binaries, as we
> wouldn't be instantiating the code below when outputting to a
pointer
> or contiguous iterator.
>
> We don't need to do that now, it can be a separate patch later
(if we
> do it at all).

Very good remark, I hadn't check streambuf to find out if there were
better to do. For me it is the streambuf method to target for an
std::copy_n overload.

So here is a new proposal much simpler. I see no reason to enable it
only for char types, is there ?

Once the other patch on copy/copy_backward... algos is in I'll
provide
what necessary to benefit from the same optimization for std::deque
iterators and in Debug mode.

>
>> +#endif
>
> Because the matching #if is more than 40 lines away, please add a
> comment noting the condition that this corresponds to, i.e.
>
> #endif // C++11
Ok, done even if there is no 40 lines anymore. And also added it in
stl_algo.h.
>
>> +
>>   template
>>     typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value,
>> istreambuf_iterator<_CharT> >::__type
>> diff --git a/libstdc++-v3/include/std/streambuf
>> b/libstdc++-v3/include/std/streambuf
>> index d9ca981d704..4f62ebf4d95 100644
>> --- a/libstdc++-v3/include/std/streambuf
>> +++ b/libstdc++-v3/include/std/streambuf
>> @@ -155,6 +155,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> __copy_move_a2(istreambuf_iterator<_CharT2>,
>>    istreambuf_iterator<_CharT2>, _CharT2*);
>>
>> +#if __cplusplus >= 201103L
>> +  template> _OutputIterator>
>> +    friend typename enable_if<__is_char<_CharT2>::__value,
>> +  _OutputIterator>::type
>> +    copy_n(istreambuf_iterator<_CharT2>, _Size, _OutputIterator);
>> +#endif
>> +
>>   template
>>     friend typename
>> __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
>> istreambuf_iterator<_CharT2> >::__type
>> diff --git
>>
a/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>>
b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>> new file mode 100644
>> index 000..ebd769cf7c0
>> --- /dev/null
>> +++
b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>> @@ -0,0 +1,59 @@
>> +// Copyright (C) 2019 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 L

Re: Add std::copy_n overload for istreambuf_iterator

2019-10-09 Thread François Dumont

On 10/9/19 10:18 PM, Christophe Lyon wrote:



On Fri, 4 Oct 2019 at 07:01, François Dumont <mailto:frs.dum...@gmail.com>> wrote:


On 9/27/19 1:00 PM, Jonathan Wakely wrote:
> On 19/07/19 23:37 +0200, François Dumont wrote:
>> It sounds reasonable to overload std::copy_n for
istreambuf_iterator.
> I wonder whether it's worth doing:
>
> #if __cplusplus >= 201703L
>    if constexpr (is_same_v<_OutputIterator, _CharT*>)
>  return __result + __it._M_sbuf->sgetn(__result, __n);
>    else
>  {
> #endif
>  ...
> #if __cplusplus >= 201703L
>  }
> #endif
>
> We could extend that to also work for basic_string<_CharT>::iterator
> and vector<_CharT>::iterator too if we wanted.
>
> I'm not sure if it will perform any better than the code below (it's
> approximately equivalent) but it should result in smaller
binaries, as we
> wouldn't be instantiating the code below when outputting to a
pointer
> or contiguous iterator.
>
> We don't need to do that now, it can be a separate patch later
(if we
> do it at all).

Very good remark, I hadn't check streambuf to find out if there were
better to do. For me it is the streambuf method to target for an
std::copy_n overload.

So here is a new proposal much simpler. I see no reason to enable it
only for char types, is there ?

Once the other patch on copy/copy_backward... algos is in I'll
provide
what necessary to benefit from the same optimization for std::deque
iterators and in Debug mode.

>
>> +#endif
>
> Because the matching #if is more than 40 lines away, please add a
> comment noting the condition that this corresponds to, i.e.
>
> #endif // C++11
Ok, done even if there is no 40 lines anymore. And also added it in
stl_algo.h.
>
>> +
>>   template
>>     typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value,
>> istreambuf_iterator<_CharT> >::__type
>> diff --git a/libstdc++-v3/include/std/streambuf
>> b/libstdc++-v3/include/std/streambuf
>> index d9ca981d704..4f62ebf4d95 100644
>> --- a/libstdc++-v3/include/std/streambuf
>> +++ b/libstdc++-v3/include/std/streambuf
>> @@ -155,6 +155,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> __copy_move_a2(istreambuf_iterator<_CharT2>,
>>    istreambuf_iterator<_CharT2>, _CharT2*);
>>
>> +#if __cplusplus >= 201103L
>> +  template> _OutputIterator>
>> +    friend typename enable_if<__is_char<_CharT2>::__value,
>> +  _OutputIterator>::type
>> +    copy_n(istreambuf_iterator<_CharT2>, _Size, _OutputIterator);
>> +#endif
>> +
>>   template
>>     friend typename
>> __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
>> istreambuf_iterator<_CharT2> >::__type
>> diff --git
>>
a/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>>
b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>> new file mode 100644
>> index 000..ebd769cf7c0
>> --- /dev/null
>> +++
b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
>> @@ -0,0 +1,59 @@
>> +// Copyright (C) 2019 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 
>> +#include 
>> +#include 
>> +
>> +#include 
>> +
>> +void test01()
>> +{
>> +  std::stringstream ss("12

Re: copy/copy_backward/fill/fill_n/equal rework

2019-10-08 Thread François Dumont
Following recently committed patches some changes that couldn't be 
committed are now part of this patch.


Moreover testing istreambuf_iterator std::copy changes I realized that 
this specialization was broken because order of function declarations in 
stl_algobase.h was wrong. I'll check if I can find a way to confirm that 
a given overload is indeed being called.


So here is this patch again.

François

On 9/27/19 11:14 PM, François Dumont wrote:

On 9/27/19 2:28 PM, Jonathan Wakely wrote:

On 09/09/19 20:34 +0200, François Dumont wrote:

Hi

    This patch improves stl_algobase.h 
copy/copy_backward/fill/fill_n/equal implementations. The 
improvements are:


- activation of algo specialization for __gnu_debug::_Safe_iterator 
(w/o _GLIBCXX_DEBUG mode)


- activation of algo specialization for _Deque_iterator even if 
mixed with another kind of iterator.


- activation of algo specializations __copy_move_a2 for something 
else than pointers. For example this code:


std::vector v { 'a', 'b',  };

ostreambuf_iterator out(std::cout);

std::copy(v.begin(), v.end(), out);

is not calling the specialization __copy_move_a2(const char*, const 
char*, ostreambuf_iterator<>);


It also fix a _GLIBCXX_DEBUG issue where the __niter_base 
specialization was wrongly removing the _Safe_iterator<> layer. The 
testsuite/25_algorithms/copy/debug/1_neg.cc test case was failing on 
a debug assertion because _after_ the copy we were trying to 
increment the vector iterator after past-the-end. Of course the 
problem is the _after_, Debug mode should detect this _before_ it 
takes place which it does now.


Note that std::fill_n is now making use of std::fill for some 
optimizations dealing with random access iterators.


Performances are very good:


This looks good, but I'm unable to apply the patch:


error: patch failed: libstdc++-v3/include/bits/deque.tcc:967
error: libstdc++-v3/include/bits/deque.tcc: patch does not apply
error: patch failed: libstdc++-v3/include/bits/stl_algobase.h:499
error: libstdc++-v3/include/bits/stl_algobase.h: patch does not apply

Could you regenerate the patch (against a clean master tree) and
resend? Thanks.


Here it is, thanks.



diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index 3f77b4f079c..ab996ca52fa 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -967,155 +967,247 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
 }
 
+_GLIBCXX_END_NAMESPACE_CONTAINER
+
   // Overload for deque::iterators, exploiting the "segmented-iterator
   // optimization".
-  template
+  template
 void
-fill(const _Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
-	 const _Deque_iterator<_Tp, _Tp&, _Tp*>& __last, const _Tp& __value)
+__fill_a1(const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
+	  const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __last,
+	  const _VTp& __value)
 {
-  typedef typename _Deque_iterator<_Tp, _Tp&, _Tp*>::_Self _Self;
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> _Iter;
+  if (__first._M_node != __last._M_node)
+	{
+	  std::__fill_a1(__first._M_cur, __first._M_last, __value);
 
-  for (typename _Self::_Map_pointer __node = __first._M_node + 1;
-   __node < __last._M_node; ++__node)
-	std::fill(*__node, *__node + _Self::_S_buffer_size(), __value);
+	  for (typename _Iter::_Map_pointer __node = __first._M_node + 1;
+	   __node < __last._M_node; ++__node)
+	std::__fill_a1(*__node, *__node + _Iter::_S_buffer_size(), __value);
+
+	  std::__fill_a1(__last._M_first, __last._M_cur, __value);
+	}
+  else
+	std::__fill_a1(__first._M_cur, __last._M_cur, __value);
+}
 
+  template
+_OI
+__copy_move_dit(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __last,
+		_OI __result)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> _Iter;
   if (__first._M_node != __last._M_node)
 	{
-	  std::fill(__first._M_cur, __first._M_last, __value);
-	  std::fill(__last._M_first, __last._M_cur, __value);
+	  __result
+	= std::__copy_move_a1<_IsMove>(__first._M_cur, __first._M_last,
+	   __result);
+
+	  for (typename _Iter::_Map_pointer __node = __first._M_node + 1;
+	   __node != __last._M_node; ++__node)
+	__result
+	  = std::__copy_move_a1<_IsMove>(*__node,
+	 *__node + _Iter::_S_buffer_size(),
+	 __result);
+
+	  return std::__copy_move_a1<_IsMove>(__last._M_first, __last._M_cur,
+	  __result);
 	}
-  else
-	std::fill(__first._M_cur, __last._M_cur, __value);
+
+  return std::__copy_move_a1<_IsMove>(__first._M_cur, __last._M_cur,
+	  __result);

[PATCH] Review std::copy istreambuf_iterator specialization

2019-10-08 Thread François Dumont

Hi

    Following what has been done for std::copy_n I think we could 
simplify the __copy_move_a2 overload to also use sgetn. Code is simpler 
and we avoid a friend declaration.


    Tested under Linux x86_64.


    * include/std/streambuf (__copy_move_a2): Remove friend declaration.
    * include/bits/streambuf_iterator.h (__copy_move_a2): Re-implement 
using

    streambuf in_avail and sgetn.

    Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h
index e3e8736e768..134b3486b9a 100644
--- a/libstdc++-v3/include/bits/streambuf_iterator.h
+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
@@ -345,31 +345,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		   istreambuf_iterator<_CharT> __last, _CharT* __result)
 {
   typedef istreambuf_iterator<_CharT>		   __is_iterator_type;
-  typedef typename __is_iterator_type::traits_type	   traits_type;
   typedef typename __is_iterator_type::streambuf_type  streambuf_type;
-  typedef typename traits_type::int_type		   int_type;
 
   if (__first._M_sbuf && !__last._M_sbuf)
 	{
 	  streambuf_type* __sb = __first._M_sbuf;
-	  int_type __c = __sb->sgetc();
-	  while (!traits_type::eq_int_type(__c, traits_type::eof()))
+	  std::streamsize __avail = __sb->in_avail();
+	  while (__avail > 0)
 	{
-	  const streamsize __n = __sb->egptr() - __sb->gptr();
-	  if (__n > 1)
-		{
-		  traits_type::copy(__result, __sb->gptr(), __n);
-		  __sb->__safe_gbump(__n);
-		  __result += __n;
-		  __c = __sb->underflow();
-		}
-	  else
-		{
-		  *__result++ = traits_type::to_char_type(__c);
-		  __c = __sb->snextc();
-		}
+	  __result += __sb->sgetn(__result, __avail);
+	  __avail = __sb->in_avail();
 	}
 	}
+
   return __result;
 }
 
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index d9ca981d704..3442f19bd78 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -149,12 +149,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   friend streamsize
   __copy_streambufs_eof<>(basic_streambuf*, basic_streambuf*, bool&);
 
-  template
-friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
-	   _CharT2*>::__type
-__copy_move_a2(istreambuf_iterator<_CharT2>,
-		   istreambuf_iterator<_CharT2>, _CharT2*);
-
   template
 friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
   istreambuf_iterator<_CharT2> >::__type


Re: [PATCH] Improve _Safe_iterator _M_distance_to

2019-10-04 Thread François Dumont

I eventually committed the attach patch.

The usage of __dp_sign_max_size will come later.

François

On 9/27/19 1:45 PM, Jonathan Wakely wrote:

On 16/09/19 22:31 +0200, François Dumont wrote:
    Here is the patch to improve _Safe_iterator<>::_M_get_distance_to 
implementation.


I introduced a new _Distance_precision  __sp_sign_max_size for 
occasions where we can't find out the exact size of a range but still 
get the max size of it. Thanks to this the performance tests are much 
better when dealing with list iterators:


Normal mode:

copy_backward_deque_iterators.cc    deque 2 list 5616r 5616u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 1586r 1586u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     5495r 
5495u    0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2400r 
2400u    0s 0mem    0pf


Debug mode:

copy_backward_deque_iterators.cc    deque 2 list 5789r 5785u 
1s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 1656r 1655u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     5792r 
5793u    0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2636r 
2636u    0s 0mem    0pf


Tested under Linux x86_64.

I'll commit once other patches are in.


    * include/debug/forward_list
(_Sequence_traits<__debug::forward_list<>>::_S_size): Returns __dp_sign
    distance when not empty.
    * include/debug/list
    (_Sequence_traits<__debug::list<>>::_S_size): Likewise.
    * include/debug/helper_functions.h (__dp_sign_max_size): New
    _Distance_precision enum entry.
    * include/debug/safe_iterator.h
    (__copy_move_a(_II, _II, const _Safe_iterator<>&)): Check for output
    iterator _M_can_advance as soon as input range distance precision is
    strictly higher than __dp_size.
    (__copy_move_a(const _Safe_iterator<>&, const _Safe_iterator<>&,
    const _Safe_iterator<>&)): Likewise.
    (__copy_move_backward_a(_II, _II, const _Safe_iterator<>&)): 
Likewise.

    (__copy_move_backward_a(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(_II, _II, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.


The patch looks OK, but the safe_iterator.tcc changes are not listed
in the changelog, and those are the most complex part of the patch.

Please add a changelog entry for the _M_get_distance_to changes. OK
for trunk after that, thanks.





diff --git a/libstdc++-v3/include/debug/forward_list b/libstdc++-v3/include/debug/forward_list
index e30b09e..f1756ddec9d 100644
--- a/libstdc++-v3/include/debug/forward_list
+++ b/libstdc++-v3/include/debug/forward_list
@@ -911,7 +911,7 @@ namespace __gnu_debug
   _S_size(const std::__debug::forward_list<_Tp, _Alloc>& __seq)
   {
 	return __seq.empty()
-	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_equality);
+	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_sign);
   }
 };
 
diff --git a/libstdc++-v3/include/debug/helper_functions.h b/libstdc++-v3/include/debug/helper_functions.h
index 475fdda1d7b..5a920bb9a6f 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h
@@ -50,10 +50,11 @@ namespace __gnu_debug
*/
   enum _Distance_precision
 {
-  __dp_none,	// Not even an iterator type
-  __dp_equality,	//< Can compare iterator equality, only
-  __dp_sign,	//< Can determine equality and ordering
-  __dp_exact	//< Can determine distance precisely
+  __dp_none,		// Not even an iterator type
+  __dp_equality,		//< Can compare iterator equality, only
+  __dp_sign,		//< Can determine equality and ordering
+  __dp_sign_max_size,	//< __dp_sign and gives max range size
+  __dp_exact		//< Can determine distance precisely
 };
 
   template= 0;
 	}
diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list
index 5eb9a6094e3..140546a633e 100644
--- a/libstdc++-v3/include/debug/list
+++ b/libstdc++-v3/include/debug/list
@@ -916,7 +916,7 @@ namespace __gnu_debug
   _S_size(const std::__debug::list<_Tp, _Alloc>& __seq)
   {
 	return __seq.empty()
-	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_equality);
+	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_sign);
   }
 };
 #endif
diff --git a/libstdc++-v3/include/debug/safe_iterator.tcc b/libstdc++-v3/include/debug/safe_iterator.tcc
index 581c51c9607..1750bc473d2 100644
--- a/libstdc++-v3/include/debug/safe_iterator.tcc
+++ b/libstdc++-v3/include/debug/safe_iterator.tcc
@@ -113,18 +113,22 @@ namespace __gnu_d

Re: Add std::copy_n overload for istreambuf_iterator

2019-10-03 Thread François Dumont

On 9/27/19 1:00 PM, Jonathan Wakely wrote:

On 19/07/19 23:37 +0200, François Dumont wrote:

It sounds reasonable to overload std::copy_n for istreambuf_iterator.

I wonder whether it's worth doing:

#if __cplusplus >= 201703L
   if constexpr (is_same_v<_OutputIterator, _CharT*>)
 return __result + __it._M_sbuf->sgetn(__result, __n);
   else
 {
#endif
 ...
#if __cplusplus >= 201703L
 }
#endif

We could extend that to also work for basic_string<_CharT>::iterator
and vector<_CharT>::iterator too if we wanted.

I'm not sure if it will perform any better than the code below (it's
approximately equivalent) but it should result in smaller binaries, as we
wouldn't be instantiating the code below when outputting to a pointer
or contiguous iterator.

We don't need to do that now, it can be a separate patch later (if we
do it at all).


Very good remark, I hadn't check streambuf to find out if there were 
better to do. For me it is the streambuf method to target for an 
std::copy_n overload.


So here is a new proposal much simpler. I see no reason to enable it 
only for char types, is there ?


Once the other patch on copy/copy_backward... algos is in I'll provide 
what necessary to benefit from the same optimization for std::deque 
iterators and in Debug mode.





+#endif


Because the matching #if is more than 40 lines away, please add a
comment noting the condition that this corresponds to, i.e.

#endif // C++11
Ok, done even if there is no 40 lines anymore. And also added it in 
stl_algo.h.



+
  template
    typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value,
  istreambuf_iterator<_CharT> >::__type
diff --git a/libstdc++-v3/include/std/streambuf 
b/libstdc++-v3/include/std/streambuf

index d9ca981d704..4f62ebf4d95 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -155,6 +155,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    __copy_move_a2(istreambuf_iterator<_CharT2>,
   istreambuf_iterator<_CharT2>, _CharT2*);

+#if __cplusplus >= 201103L
+  template_OutputIterator>

+    friend typename enable_if<__is_char<_CharT2>::__value,
+  _OutputIterator>::type
+    copy_n(istreambuf_iterator<_CharT2>, _Size, _OutputIterator);
+#endif
+
  template
    friend typename 
__gnu_cxx::__enable_if<__is_char<_CharT2>::__value,

  istreambuf_iterator<_CharT2> >::__type
diff --git 
a/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc 
b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc

new file mode 100644
index 000..ebd769cf7c0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
@@ -0,0 +1,59 @@
+// Copyright (C) 2019 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 
+#include 
+#include 
+
+#include 
+
+void test01()
+{
+  std::stringstream ss("12345");
+
+  std::string ostr(5, '0');
+  typedef std::istreambuf_iterator istrb_ite;
+  std::copy_n(istrb_ite(ss), 0, ostr.begin());
+  VERIFY( ostr.front() == '0' );


I'd like to see a check here that the value returned from copy_n is
equal to ostr.begin().


+
+  std::copy_n(istrb_ite(ss), 2, ostr.begin());
+  VERIFY( ostr == "12000" );


And equal to ostr.begin() + 2.


+
+  std::copy_n(istrb_ite(ss), 3, ostr.begin() + 2);
+  VERIFY( ostr == "12345" );


And equal to ostr.begin() + 5 here.

Done.



+}
+
+void test02()
+{
+  std::stringstream ss("12345");
+
+  std::string ostr(5, '0');
+  typedef std::istreambuf_iterator istrb_ite;
+
+  istrb_ite ibfit(ss);
+  std::copy_n(ibfit, 3, std::copy_n(ibfit, 2, ostr.begin()));
+  VERIFY( ostr == "12345" );
+}



Ideally I'd also like to see tests where the input buffer is larger
than the size being read, e.g. read 5 chars from "123456" and verify
we don't read the '6'.

In test01 I am doing something like that.


Also, these tests don't exercise the code path that causes an
underflow. It would be good to use an ifstream to read from one of the
files in the testsuite/data directory, and read a large amount of data
(more th

[PATCH] Add std::__iterator_category_t

2019-10-03 Thread François Dumont

Hi

    May I add this convenient function ? I'll also use it in coming 
patches.


    Note that I removed a template parameter in __is_random_access_iter 
in C++11.



    * include/bits/stl_iterator_base_types.h (__iterator_category_t): 
Define

    for C++11.
    (__is_random_access_iter): Adapt to use latter.
    (_RequireInputIte): Likewise and use __enable_if_t.

    Tested under Linux x86_64.

François

diff --git a/libstdc++-v3/include/bits/stl_iterator_base_types.h b/libstdc++-v3/include/bits/stl_iterator_base_types.h
index 8135f4857fc..d12ac3a20ea 100644
--- a/libstdc++-v3/include/bits/stl_iterator_base_types.h
+++ b/libstdc++-v3/include/bits/stl_iterator_base_types.h
@@ -208,14 +208,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   //@}
 
 #if __cplusplus >= 201103L
+  template
+using __iterator_category_t
+  = typename iterator_traits<_Iter>::iterator_category;
+
   template
-using _RequireInputIter = typename
-  enable_if::iterator_category,
-			   input_iterator_tag>::value>::type;
+using _RequireInputIter =
+  __enable_if_t,
+   input_iterator_tag>::value>;
 
-  template,
-	   typename _Cat = typename _Traits::iterator_category>
+  template>
 struct __is_random_access_iter
   : is_base_of
 {


[PATCH] Add std::copy_n debug checks

2019-10-03 Thread François Dumont

Hi

    We are missing obvious debug checks in std::copy_n. Moreover I'll 
need them when I'll remove the Debug layer in a coming patch.


    Tested under Linux x86_64.


    * include/bits/stl_algo.h (copy_n): Add 
__glibcxx_requires_can_increment

    debug checks.
    * testsuite/25_algorithms/copy_n/debug/1_neg.cc: New.
    * testsuite/25_algorithms/copy_n/debug/2_neg.cc: New.

    I'll commit this day or this week end if not told otherwise.

François

diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index c1003077176..078efc028cc 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -816,6 +816,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
   __glibcxx_function_requires(_OutputIteratorConcept<_OutputIterator,
 	typename iterator_traits<_InputIterator>::value_type>)
+  __glibcxx_requires_can_increment(__first, __n);
+  __glibcxx_requires_can_increment(__result, __n);
 
   return std::__copy_n(__first, __n, __result,
 			   std::__iterator_category(__first));
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/1_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/1_neg.cc
new file mode 100644
index 000..3e0e0299e1d
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/1_neg.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2019 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
+// .
+
+// { dg-do run { target c++11 xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include 
+#include 
+
+void
+test01()
+{
+  std::vector v1(3, 1);
+  std::vector v2(5, 0);
+
+  std::copy_n(v1.begin(), 5, v2.begin());
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/2_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/2_neg.cc
new file mode 100644
index 000..ebc7cb5ea4c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/debug/2_neg.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2019 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
+// .
+
+// { dg-do run { target c++11 xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include 
+#include 
+
+void
+test01()
+{
+  std::vector v1(5, 1);
+  std::vector v2(3, 0);
+
+  std::copy_n(v1.begin(), 5, v2.begin());
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}


Re: [PATCH] Help compiler detect invalid code

2019-10-01 Thread François Dumont

On 9/27/19 1:24 PM, Jonathan Wakely wrote:

On 20/09/19 07:08 +0200, François Dumont wrote:
I already realized that previous patch will be too controversial to 
be accepted.


In this new version I just implement a real memmove in __memmove so 


A real memmove doesn't just work backwards, it needs to detect any
overlaps and work forwards *or* backwards, as needed.
ok, good to know, I understand now why using __builtin_memcopy didn't 
show any performance enhancement when I tested it !


I think your change will break this case:

#include 

constexpr int f(int i, int j, int k)
{
 int arr[5] = { 0, 0, i, j, k };
 std::move(arr+2, arr+5, arr);
 return arr[0] + arr[1] + arr[2];
}

static_assert( f(1, 2, 3) == 6 );

This is valid because std::move only requires that the result iterator
is not in the input range, but it's OK for the two ranges to overlap.

I haven't tested it, but I think with your change the array will end
up containing {3, 2, 3, 2, 3} instead of {1, 2, 3, 2, 3}.

Indeed, I've added a std::move constexpr test in this new proposal which 
demonstrate that.


C++ Standard clearly states that [copy|move]_backward is done backward. 
So in this new proposal I propose to add a __memcopy used in copy/move 
and keep __memmove for *_backward algos. Both are using 
__builtin_memmove as before.



    * include/bits/stl_algobase.h (__memmove): Return void, loop as long as
    __n != 0.
    (__memcopy): New.
    (__copy_move<_IsMove, true, 
std::random_access_iterator_tag>::__copy_m):

    Adapt to use latter.
    (__copy_move_backward_a): Remove std::is_constant_evaluated block.
    * testsuite/25_algorithms/copy/constexpr.cc (test): Add check on copied
    values.
    * testsuite/25_algorithms/copy_backward/constexpr.cc (test): Likewise
    and rename in test1.
    (test2): New.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.
    * testsuite/25_algorithms/equal/constexpr_neg.cc: New.
    * testsuite/25_algorithms/move/constexpr.cc: New.
    * testsuite/25_algorithms/move/constexpr_neg.cc: New.

Tested under Linux x86_64.

Ok to commit ?

François

Index: include/bits/stl_algobase.h
===
--- include/bits/stl_algobase.h	(révision 276259)
+++ include/bits/stl_algobase.h	(copie de travail)
@@ -78,18 +78,18 @@
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /*
-   * A constexpr wrapper for __builtin_memmove.
+   * A constexpr wrapper for memcopy.
* @param __num The number of elements of type _Tp (not bytes).
*/
   template
 _GLIBCXX14_CONSTEXPR
-inline void*
-__memmove(_Tp* __dst, const _Tp* __src, size_t __num)
+inline void
+__memcopy(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
 {
 #ifdef __cpp_lib_is_constant_evaluated
   if (std::is_constant_evaluated())
 	{
-	  for(; __num > 0; --__num)
+	  for (; __num != 0; --__num)
 	{
 	  if constexpr (_IsMove)
 		*__dst = std::move(*__src);
@@ -98,15 +98,40 @@
 	  ++__src;
 	  ++__dst;
 	}
-	  return __dst;
 	}
   else
 #endif
-	return __builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
-  return __dst;
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
 }
 
   /*
+   * A constexpr wrapper for memmove.
+   * @param __num The number of elements of type _Tp (not bytes).
+   */
+  template
+_GLIBCXX14_CONSTEXPR
+inline void
+__memmove(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
+{
+#ifdef __cpp_lib_is_constant_evaluated
+  if (std::is_constant_evaluated())
+	{
+	  __dst += __num;
+	  __src += __num;
+	  for (; __num != 0; --__num)
+	{
+	  if constexpr (_IsMove)
+		*--__dst = std::move(*--__src);
+	  else
+		*--__dst = *--__src;
+	}
+	}
+  else
+#endif
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
+}
+
+  /*
* A constexpr wrapper for __builtin_memcmp.
* @param __num The number of elements of type _Tp (not bytes).
*/
@@ -446,7 +471,7 @@
 #endif
 	  const ptrdiff_t _Num = __last - __first;
 	  if (_Num)
-	std::__memmove<_IsMove>(__result, __first, _Num);
+	std::__memcopy<_IsMove>(__result, __first, _Num);
 	  return __result + _Num;
 	}
 };
@@ -676,12 +701,6 @@
 			 && __is_pointer<_BI2>::__value
 			 && __are_same<_ValueType1, _ValueType2>::__value);
 
-#ifdef __cpp_lib_is_constant_evaluated
-  if (std::is_constant_evaluated())
-	return std::__copy_move_backward::__copy_move_b(__first, __last,
-			__result);
-#endif
   return std::__copy_move_backward<_IsMove, __simple,
    _Category>::__copy_move_b(__first,
  __last,
Index: testsuite/25_algorithms/copy/constexpr.cc
===
--- testsuite/25_algorithms/copy/constexpr.cc	(révision 276259)
+++ testsuite/25_algorithms/copy/constexpr.cc	(copie de travail)
@@ -24,12 +24,12 @@
 constexpr bool
 

[PATCH] Implement C++20 P1023 for __debug::array

2019-09-30 Thread François Dumont

This is a missing part of C++20 P1023 for __debug::array.

    Implement C++20 p1023 - constexpr comparison operators for 
__debug::array.

    * include/debug/array: Add C++20 constexpr to comparison operators.
    * testsuite/23_containers/array/tuple_interface/get_debug_neg.cc: Adapt
    dg-error line numbers.
    * testsuite/23_containers/array/tuple_interface/
    tuple_element_debug_neg.cc: Likewise.


Tested under Linux x86_64 normal and debug modes.

François

diff --git a/libstdc++-v3/include/debug/array b/libstdc++-v3/include/debug/array
index 2f8eb842eb8..5566a087f9a 100644
--- a/libstdc++-v3/include/debug/array
+++ b/libstdc++-v3/include/debug/array
@@ -234,16 +234,19 @@ namespace __debug
 
   // Array comparisons.
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator==(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
 { return std::equal(__one.begin(), __one.end(), __two.begin()); }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator!=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
 { return !(__one == __two); }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator<(const array<_Tp, _Nm>& __a, const array<_Tp, _Nm>& __b)
 {
@@ -252,16 +255,19 @@ namespace __debug
 }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator>(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
 { return __two < __one; }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator<=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
 { return !(__one > __two); }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 operator>=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
 { return !(__one < __two); }
diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
index f9880b4b0f5..3c60a435491 100644
--- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_debug_neg.cc
@@ -27,6 +27,6 @@ int n1 = std::get<1>(a);
 int n2 = std::get<1>(std::move(a));
 int n3 = std::get<1>(ca);
 
-// { dg-error "static assertion failed" "" { target *-*-* } 288 }
-// { dg-error "static assertion failed" "" { target *-*-* } 297 }
-// { dg-error "static assertion failed" "" { target *-*-* } 305 }
+// { dg-error "static assertion failed" "" { target *-*-* } 294 }
+// { dg-error "static assertion failed" "" { target *-*-* } 303 }
+// { dg-error "static assertion failed" "" { target *-*-* } 311 }
diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
index 10b1681c0c4..a6b44eb57fe 100644
--- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/tuple_element_debug_neg.cc
@@ -22,4 +22,4 @@
 
 typedef std::tuple_element<1, std::array>::type type;
 
-// { dg-error "static assertion failed" "" { target *-*-* } 331 }
+// { dg-error "static assertion failed" "" { target *-*-* } 376 }


Re: [PATCH] Fix algo constexpr tests in Debug mode

2019-09-30 Thread François Dumont

On 9/30/19 11:03 AM, Jonathan Wakely wrote:

On 28/09/19 23:12 +0200, François Dumont wrote:

Here is what I just commited.


Thanks. In my branch I had a lot more _GLIBCXX20_CONSTEXPR additions
in the debug mode headers. Is it not needed on __check_singular,
__foreign_iterator etc. ?


Yes, I know, I just limited myself to fixing testsuite tests for the 
moment. I'll check if those need it too.




I try to use the asm trick in the _GLIBCXX_DEBUG_VERIFY_COND_AT but 
didn't notice any enhancement. So for now I kept my solution to just 
have a non-constexpr call compiler error.


You won't see any improvement in the testsuite, because the compiler
flags for the testsuite suppress diagnostic notes. But I'd expect it
to give better output for users with the default compiler flags.

Ok, I didn't know, I'll give it another try then outside testsuite.



diff --git a/libstdc++-v3/include/bits/stl_algo.h 
b/libstdc++-v3/include/bits/stl_algo.h

index a672f8b2b39..f25b8b76df6 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -5054,8 +5054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
   *  @param  __last1   Another iterator.
   *  @param  __last2   Another iterator.
   *  @param  __result  An iterator pointing to the end of the merged 
range.
-   *  @return An iterator pointing to the first element 
not less

-   *  than @e val.
+   *  @return   An output iterator equal to @p __result + (__last1 - 
__first1)

+   *    + (__last2 - __first2).
   *
   *  Merges the ranges @p [__first1,__last1) and @p 
[__first2,__last2) into

   *  the sorted range @p [__result, __result + (__last1-__first1) +
@@ -5102,8 +5102,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
   *  @param  __last2   Another iterator.
   *  @param  __result  An iterator pointing to the end of the merged 
range.

   *  @param  __comp    A functor to use for comparisons.
-   *  @return An iterator pointing to the first element "not 
less

-   *  than" @e val.
+   *  @return   An output iterator equal to @p __result + (__last1 - 
__first1)

+   *    + (__last2 - __first2).
   *
   *  Merges the ranges @p [__first1,__last1) and @p 
[__first2,__last2) into

   *  the sorted range @p [__result, __result + (__last1-__first1) +


These changes are fine but should have been a separate, unrelated
commit.


Ok, sorry, I consider that just a comment change was fine.






@@ -157,10 +192,16 @@ namespace __gnu_debug
   *  otherwise.
  */
  template
+    _GLIBCXX20_CONSTEXPR
    inline bool
    __valid_range(_InputIterator __first, _InputIterator __last,
  typename _Distance_traits<_InputIterator>::__type& __dist)
    {
+#ifdef __cpp_lib_is_constant_evaluated
+  if (std::is_constant_evaluated())
+    // Detected by the compiler directly.
+    return true;
+#endif


Should this be using the built-in, not the C++20 function?


In practice it's probably equivalent, because the function is only
going to be constant-evaluated when called from C++20 code, and in
that case the std::is_constant_evaluated() function will be available.



Yes, this is why I did it this way. And moreover it is using std::pair 
move assignment operator which is also C++20 constexpr.




It just seems inconsistent to use the built-in in one place and not in
the other.


It is also the reason why the simple simple __valid_range is not using 
the other anymore.


Maybe once I'll have check all the algo calls I'll find out that this 
one need _GLIBCXX_CONSTEXPR.


I got the sensation that library is being 'constexpr' decorated only 
when needed and when properly Standardise so are the Debug helpers.


François




[PATCH] Fix algo constexpr tests in Debug mode

2019-09-28 Thread François Dumont

Here is what I just commited.

I try to use the asm trick in the _GLIBCXX_DEBUG_VERIFY_COND_AT but 
didn't notice any enhancement. So for now I kept my solution to just 
have a non-constexpr call compiler error.


I fix my patch to use __builtin_is_constant_evaluated rather than 
std::is_constant_evaluated in __valid_range.


    * include/bits/stl_algobase.h (__memmove): Return _Tp*.
    (__memmove): Loop as long as __n is not 0.
    (__copy_move<>::__copy_m): Likewise.
    (__copy_move_backward<>::__copy_move_b): Likewise.
    * testsuite/25_algorithms/copy/constexpr.cc: Add check on copied 
values.

    * testsuite/25_algorithms/copy_backward/constexpr.cc: Likewise.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.

    * include/debug/forward_list
(_Sequence_traits<__debug::forward_list<>>::_S_size): Returns __dp_sign
    distance when not empty.
    * include/debug/list
    (_Sequence_traits<__debug::list<>>::_S_size): Likewise.
    * include/debug/helper_functions.h (__dp_sign_max_size): New
    _Distance_precision enum entry.
    * include/debug/safe_iterator.h
    (__copy_move_a(_II, _II, const _Safe_iterator<>&)): Check for output
    iterator _M_can_advance as soon as input range distance precision is
    strictly higher than __dp_size.
    (__copy_move_a(const _Safe_iterator<>&, const _Safe_iterator<>&,
    const _Safe_iterator<>&)): Likewise.
    (__copy_move_backward_a(_II, _II, const _Safe_iterator<>&)): Likewise.
    (__copy_move_backward_a(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(_II, _II, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.

François

On 9/27/19 6:45 PM, Jonathan Wakely wrote:

On 27/09/19 18:24 +0200, François Dumont wrote:

On 9/27/19 2:11 PM, Jonathan Wakely wrote:

On 19/09/19 22:27 +0200, François Dumont wrote:

Hi

    I start working on making recently added constexpr tests to 
work in Debug mode.


The attached patch seems to be necessary for that, right?



On my side I had done this, almost the same.

For the moment there is a FIXME in macros.h to find out how to 
generate a nice compilation error when the condition is not meant.


static_assert can't be called in this context, too bad.

I also try to define a function with a 
__attribute__((__error__("because"))) attribute. But when I make it 
constexpr gcc complains about missing definition. When I provide a 
definition gcc complains that this attribute must be on a 
declaration. And when I split declaration and definition gcc does not 
produce the expected compilation error.


Yes, I've tried similar things without success.

Unless you have the solution I consider that we need help from the 
front-end.


For the moment if Debug mode finds a problem it will be reported as 
_M_error function not being constexpr !


A reasonable workaround is to do:

#ifdef _GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED
 if (__builtin_is_constant_evaluated())
   asm("Debug Mode assertion failed");
 else
#endif
 if (!(Cond))
   __gnu_debug::_Error_formatter::...

The builtin is available even for C++98, whereas
std::is_constant_evaluated() is only available for C++20.

This produces errors that include lines like:

asm.cc:12:17:   in ‘constexpr’ expansion of ‘f(-1)’
asm.cc:4:7: error: inline assembly is not a constant expression
   4 |   asm("debug mode assertion failed");
 |   ^~~
asm.cc:8:3: note: in expansion of macro ‘CHECK’
   8 |   _GLIBCXX_ASSERT(i > 0);
 |   ^
asm.cc:4:7: note: only unevaluated inline assembly is allowed in a 
‘constexpr’ function in C++2a

   4 |   asm("debug mode assertion failed");
 |   ^~~
asm.cc:8:3: note: in expansion of macro ‘CHECK’
   8 |   CHECK(i > 0);
 |   ^

It's not ideal, but it does show the failed condition and the text
"debug mode assertion failed" (or whatever message you choose to use
there).





diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index a672f8b2b39..f25b8b76df6 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -5054,8 +5054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
*  @param  __last1   Another iterator.
*  @param  __last2   Another iterator.
*  @param  __result  An iterator pointing to the end of the merged range.
-   *  @return An iterator pointing to the first element not less
-   *  than @e val.
+   *  @return   An output iterator equal to @p __result + (__last1 - __first1)
+   *+ (__last2 - __first2).
*
*  Merges the ranges @p [__first1,__last1) and @p 

Re: copy/copy_backward/fill/fill_n/equal rework

2019-09-27 Thread François Dumont

On 9/27/19 2:28 PM, Jonathan Wakely wrote:

On 09/09/19 20:34 +0200, François Dumont wrote:

Hi

    This patch improves stl_algobase.h 
copy/copy_backward/fill/fill_n/equal implementations. The 
improvements are:


- activation of algo specialization for __gnu_debug::_Safe_iterator 
(w/o _GLIBCXX_DEBUG mode)


- activation of algo specialization for _Deque_iterator even if mixed 
with another kind of iterator.


- activation of algo specializations __copy_move_a2 for something 
else than pointers. For example this code:


std::vector v { 'a', 'b',  };

ostreambuf_iterator out(std::cout);

std::copy(v.begin(), v.end(), out);

is not calling the specialization __copy_move_a2(const char*, const 
char*, ostreambuf_iterator<>);


It also fix a _GLIBCXX_DEBUG issue where the __niter_base 
specialization was wrongly removing the _Safe_iterator<> layer. The 
testsuite/25_algorithms/copy/debug/1_neg.cc test case was failing on 
a debug assertion because _after_ the copy we were trying to 
increment the vector iterator after past-the-end. Of course the 
problem is the _after_, Debug mode should detect this _before_ it 
takes place which it does now.


Note that std::fill_n is now making use of std::fill for some 
optimizations dealing with random access iterators.


Performances are very good:


This looks good, but I'm unable to apply the patch:


error: patch failed: libstdc++-v3/include/bits/deque.tcc:967
error: libstdc++-v3/include/bits/deque.tcc: patch does not apply
error: patch failed: libstdc++-v3/include/bits/stl_algobase.h:499
error: libstdc++-v3/include/bits/stl_algobase.h: patch does not apply

Could you regenerate the patch (against a clean master tree) and
resend? Thanks.


Here it is, thanks.

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index 3f77b4f079c..ab996ca52fa 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -967,155 +967,247 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
 }
 
+_GLIBCXX_END_NAMESPACE_CONTAINER
+
   // Overload for deque::iterators, exploiting the "segmented-iterator
   // optimization".
-  template
+  template
 void
-fill(const _Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
-	 const _Deque_iterator<_Tp, _Tp&, _Tp*>& __last, const _Tp& __value)
+__fill_a1(const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
+	  const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __last,
+	  const _VTp& __value)
 {
-  typedef typename _Deque_iterator<_Tp, _Tp&, _Tp*>::_Self _Self;
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> _Iter;
+  if (__first._M_node != __last._M_node)
+	{
+	  std::__fill_a1(__first._M_cur, __first._M_last, __value);
 
-  for (typename _Self::_Map_pointer __node = __first._M_node + 1;
-   __node < __last._M_node; ++__node)
-	std::fill(*__node, *__node + _Self::_S_buffer_size(), __value);
+	  for (typename _Iter::_Map_pointer __node = __first._M_node + 1;
+	   __node < __last._M_node; ++__node)
+	std::__fill_a1(*__node, *__node + _Iter::_S_buffer_size(), __value);
+
+	  std::__fill_a1(__last._M_first, __last._M_cur, __value);
+	}
+  else
+	std::__fill_a1(__first._M_cur, __last._M_cur, __value);
+}
 
+  template
+_OI
+__copy_move_dit(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __last,
+		_OI __result)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> _Iter;
   if (__first._M_node != __last._M_node)
 	{
-	  std::fill(__first._M_cur, __first._M_last, __value);
-	  std::fill(__last._M_first, __last._M_cur, __value);
+	  __result
+	= std::__copy_move_a1<_IsMove>(__first._M_cur, __first._M_last,
+	   __result);
+
+	  for (typename _Iter::_Map_pointer __node = __first._M_node + 1;
+	   __node != __last._M_node; ++__node)
+	__result
+	  = std::__copy_move_a1<_IsMove>(*__node,
+	 *__node + _Iter::_S_buffer_size(),
+	 __result);
+
+	  return std::__copy_move_a1<_IsMove>(__last._M_first, __last._M_cur,
+	  __result);
 	}
-  else
-	std::fill(__first._M_cur, __last._M_cur, __value);
+
+  return std::__copy_move_a1<_IsMove>(__first._M_cur, __last._M_cur,
+	  __result);
 }
 
-  template
-_Deque_iterator<_Tp, _Tp&, _Tp*>
-copy(_Deque_iterator<_Tp, const _Tp&, const _Tp*> __first,
-	 _Deque_iterator<_Tp, const _Tp&, const _Tp*> __last,
-	 _Deque_iterator<_Tp, _Tp&, _Tp*> __result)
+  template
+_OI
+__copy_move_a1(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first,
+		   _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __last,
+		   _OI __result)
+

Re: [PATCH] Help compiler detect invalid code

2019-09-27 Thread François Dumont

On 9/27/19 2:11 PM, Jonathan Wakely wrote:

On 19/09/19 22:27 +0200, François Dumont wrote:

Hi

    I start working on making recently added constexpr tests to work 
in Debug mode.


The attached patch seems to be necessary for that, right?



On my side I had done this, almost the same.

For the moment there is a FIXME in macros.h to find out how to generate 
a nice compilation error when the condition is not meant.


static_assert can't be called in this context, too bad.

I also try to define a function with a 
__attribute__((__error__("because"))) attribute. But when I make it 
constexpr gcc complains about missing definition. When I provide a 
definition gcc complains that this attribute must be on a declaration. 
And when I split declaration and definition gcc does not produce the 
expected compilation error.


Unless you have the solution I consider that we need help from the 
front-end.


For the moment if Debug mode finds a problem it will be reported as 
_M_error function not being constexpr !


François

diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index a672f8b2b39..f25b8b76df6 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -5054,8 +5054,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
*  @param  __last1   Another iterator.
*  @param  __last2   Another iterator.
*  @param  __result  An iterator pointing to the end of the merged range.
-   *  @return An iterator pointing to the first element not less
-   *  than @e val.
+   *  @return   An output iterator equal to @p __result + (__last1 - __first1)
+   *+ (__last2 - __first2).
*
*  Merges the ranges @p [__first1,__last1) and @p [__first2,__last2) into
*  the sorted range @p [__result, __result + (__last1-__first1) +
@@ -5102,8 +5102,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
*  @param  __last2   Another iterator.
*  @param  __result  An iterator pointing to the end of the merged range.
*  @param  __compA functor to use for comparisons.
-   *  @return An iterator pointing to the first element "not less
-   *  than" @e val.
+   *  @return   An output iterator equal to @p __result + (__last1 - __first1)
+   *+ (__last2 - __first2).
*
*  Merges the ranges @p [__first1,__last1) and @p [__first2,__last2) into
*  the sorted range @p [__result, __result + (__last1-__first1) +
diff --git a/libstdc++-v3/include/debug/functions.h b/libstdc++-v3/include/debug/functions.h
index f87838692c6..8c385b87244 100644
--- a/libstdc++-v3/include/debug/functions.h
+++ b/libstdc++-v3/include/debug/functions.h
@@ -219,6 +219,7 @@ namespace __gnu_debug
   // Can't check if an input iterator sequence is sorted, because we
   // can't step through the sequence.
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted_aux(const _InputIterator&, const _InputIterator&,
std::input_iterator_tag)
@@ -227,6 +228,7 @@ namespace __gnu_debug
   // Can verify if a forward iterator sequence is in fact sorted using
   // std::__is_sorted
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted_aux(_ForwardIterator __first, _ForwardIterator __last,
std::forward_iterator_tag)
@@ -245,6 +247,7 @@ namespace __gnu_debug
   // Can't check if an input iterator sequence is sorted, because we can't step
   // through the sequence.
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted_aux(const _InputIterator&, const _InputIterator&,
_Predicate, std::input_iterator_tag)
@@ -253,6 +256,7 @@ namespace __gnu_debug
   // Can verify if a forward iterator sequence is in fact sorted using
   // std::__is_sorted
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted_aux(_ForwardIterator __first, _ForwardIterator __last,
_Predicate __pred, std::forward_iterator_tag)
@@ -270,31 +274,26 @@ namespace __gnu_debug
 
   // Determine if a sequence is sorted.
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted(const _InputIterator& __first, const _InputIterator& __last)
 {
-  // Verify that the < operator for elements in the sequence is a
-  // StrictWeakOrdering by checking that it is irreflexive.
-  __glibcxx_assert(__first == __last || !(*__first < *__first));
-
   return __check_sorted_aux(__first, __last,
 std::__iterator_category(__first));
 }
 
   template
+_GLIBCXX20_CONSTEXPR
 inline bool
 __check_sorted(const _InputIterator& __first, const _InputIterator& __last,
_Predicate __pred)
 {
-  // Verify that the predicate is StrictWeakOrdering by checking that it
-  // is irreflexive.
-  __glibcxx_assert(__first == __last || !__pred(*__first, *__first));
-
   return __check_sorted_

Re: [PATCH] PR libstdc++/91910 fix data race in Debug Mode destructors

2019-09-27 Thread François Dumont

On 9/26/19 3:20 PM, Jonathan Wakely wrote:
Fix data race when _Safe_iterator_base::_M_detach() runs concurrently 
with

the _Safe_container_base destructor.

PR libstdc++/91910
* src/c++11/debug.cc (_Safe_iterator_base::_M_detach()): Load pointer
atomically and lock the mutex before accessing the sequence.
(_Safe_iterator_base::_M_reset()): Clear _M_sequence atomically.

Tested x86_64-linux. I plan to commit this to trunk unless somebody
sees a problem with it.


Thanks to the additional _M_sequence check in _M_detach_single() this 
patch looks good.


François



Re: [PATCH] Help compiler detect invalid code

2019-09-25 Thread François Dumont
Some more tests have revealed  a small problem in is_sorted test. It was 
revealed by the Debug mode but not in a clean ways so for the moment I 
prefer to fix it and I'll add a _neg test when Debug is able to report 
it correctly.


I've also added a _neg test for equal which doesn't need Debug mode.

OK to commit ?

François



On 9/20/19 7:08 AM, François Dumont wrote:
I already realized that previous patch will be too controversial to be 
accepted.


In this new version I just implement a real memmove in __memmove so 
that in copy_backward there is no need for a shortcut to a more 
defensive code.


I'll see if in Debug mode I can do something.

François


On 9/19/19 10:27 PM, François Dumont wrote:

Hi

    I start working on making recently added constexpr tests to work 
in Debug mode.


    It appears that the compiler is able to detect code mistakes 
pretty well as long we don't try to hide the code intention with a 
defensive approach. This is why I'd like to propose to replace '__n > 
0' conditions with '__n != 0'.


    The result is demonstrated by the constexpr_neg.cc tests. What do 
you think ?


    * include/bits/stl_algobase.h (__memmove): Return _Tp*.
    (__memmove): Loop as long as __n is not 0.
    (__copy_move<>::__copy_m): Likewise.
    (__copy_move_backward<>::__copy_move_b): Likewise.
    * testsuite/25_algorithms/copy/constexpr.cc: Add check on copied 
values.

    * testsuite/25_algorithms/copy_backward/constexpr.cc: Likewise.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.

    I'll submit the patch to fix Debug mode depending on the decision 
for this one.


François





diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc++-v3/include/bits/stl_algobase.h
index 4eba053ac75..94a79b85d15 100644
--- a/libstdc++-v3/include/bits/stl_algobase.h
+++ b/libstdc++-v3/include/bits/stl_algobase.h
@@ -83,27 +83,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
   template
 _GLIBCXX14_CONSTEXPR
-inline void*
-__memmove(_Tp* __dst, const _Tp* __src, size_t __num)
+inline void
+__memmove(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
 {
 #ifdef __cpp_lib_is_constant_evaluated
   if (std::is_constant_evaluated())
 	{
-	  for(; __num > 0; --__num)
+	  __dst += __num;
+	  __src += __num;
+	  for (; __num != 0; --__num)
 	{
 	  if constexpr (_IsMove)
-		*__dst = std::move(*__src);
+		*--__dst = std::move(*--__src);
 	  else
-		*__dst = *__src;
-	  ++__src;
-	  ++__dst;
+		*--__dst = *--__src;
 	}
-	  return __dst;
 	}
   else
 #endif
-	return __builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
-  return __dst;
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
 }
 
   /*
@@ -730,12 +728,6 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 			 && __is_pointer<_BI2>::__value
 			 && __are_same<_ValueType1, _ValueType2>::__value);
 
-#ifdef __cpp_lib_is_constant_evaluated
-  if (std::is_constant_evaluated())
-	return std::__copy_move_backward::__copy_move_b(__first, __last,
-			   __result);
-#endif
   return std::__copy_move_backward<_IsMove, __simple,
    _Category>::__copy_move_b(__first,
  __last,
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
index 67910b8773e..a0460603496 100644
--- a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
@@ -24,12 +24,12 @@
 constexpr bool
 test()
 {
-  constexpr std::array ca0{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}};
+  constexpr std::array ca0{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}};
   std::array ma0{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
 
   const auto out6 = std::copy(ca0.begin(), ca0.begin() + 8, ma0.begin() + 2);
 
-  return out6 == ma0.begin() + 10;
+  return out6 == ma0.begin() + 10 && *(ma0.begin() + 2) == 1 && *out6 == 0;
 }
 
 static_assert(test());
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
new file mode 100644
index 000..49052467409
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2019 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 Gener

Re: copy/copy_backward/fill/fill_n/equal rework

2019-09-24 Thread François Dumont

Ping ?

On 9/9/19 8:34 PM, François Dumont wrote:

Hi

    This patch improves stl_algobase.h 
copy/copy_backward/fill/fill_n/equal implementations. The improvements 
are:


- activation of algo specialization for __gnu_debug::_Safe_iterator 
(w/o _GLIBCXX_DEBUG mode)


- activation of algo specialization for _Deque_iterator even if mixed 
with another kind of iterator.


- activation of algo specializations __copy_move_a2 for something else 
than pointers. For example this code:


std::vector v { 'a', 'b',  };

ostreambuf_iterator out(std::cout);

std::copy(v.begin(), v.end(), out);

is not calling the specialization __copy_move_a2(const char*, const 
char*, ostreambuf_iterator<>);


It also fix a _GLIBCXX_DEBUG issue where the __niter_base 
specialization was wrongly removing the _Safe_iterator<> layer. The 
testsuite/25_algorithms/copy/debug/1_neg.cc test case was failing on a 
debug assertion because _after_ the copy we were trying to increment 
the vector iterator after past-the-end. Of course the problem is the 
_after_, Debug mode should detect this _before_ it takes place which 
it does now.


Note that std::fill_n is now making use of std::fill for some 
optimizations dealing with random access iterators.


Performances are very good:

Before:

copy_backward_deque_iterators.cc    deque 2 deque 1084r 1084u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector 3373r 3372u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque 3316r 3316u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 3610r 
3609u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 3552r 
3552u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 10528r 10528u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2161r 2162u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         752r 
751u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector       3300r 
3299u    0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque       3144r 
3140u    0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      3340r 
3338u    1s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      3132r 
3132u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     10013r 
10012u    0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2274r 
2275u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque       8676r 
8675u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs vector      5870r 
5870u    0s 0mem    0pf
equal_deque_iterators.cc     vector vs deque      3163r 
3163u    0s 0mem    0pf
equal_deque_iterators.cc     int deque vs char vector     5845r 
5845u    0s 0mem    0pf
equal_deque_iterators.cc     char vector vs int deque     3307r 
3307u    0s 0mem    0pf


After:

copy_backward_deque_iterators.cc    deque 2 deque  697r  697u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector  219r  218u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque  453r  453u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 1914r 
1915u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 2112r 
2111u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 7770r 7771u 
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2194r 2193u 
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         505r 
504u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector        221r 
221u    0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque        398r 
397u    0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      1770r 
1767u    0s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      1995r 
1993u    0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     7650r 
7641u    2s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2270r 
2270u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque        769r 
768u    0s 0mem    0pf
equal_deque_iterators.cc     deque vs vector       231r 
230u    0s 0mem    0pf
equal_deque_iterators.cc     vector vs deque       397r 
397u    0s 0mem    0pf
equal_deque_iterators.cc     int deque vs char vector     1541r 
1541u    0s 0mem    0pf
equal_deque_iterators.cc     char vector vs int deque     1623r 
1623u    0s 0mem    0pf


In Debug Mode it is of course even better. I haven't had the patience 
to run the benches before the patch, i

Re: [PATCH] Help compiler detect invalid code

2019-09-19 Thread François Dumont
I already realized that previous patch will be too controversial to be 
accepted.


In this new version I just implement a real memmove in __memmove so that 
in copy_backward there is no need for a shortcut to a more defensive code.


I'll see if in Debug mode I can do something.

François


On 9/19/19 10:27 PM, François Dumont wrote:

Hi

    I start working on making recently added constexpr tests to work 
in Debug mode.


    It appears that the compiler is able to detect code mistakes 
pretty well as long we don't try to hide the code intention with a 
defensive approach. This is why I'd like to propose to replace '__n > 
0' conditions with '__n != 0'.


    The result is demonstrated by the constexpr_neg.cc tests. What do 
you think ?


    * include/bits/stl_algobase.h (__memmove): Return _Tp*.
    (__memmove): Loop as long as __n is not 0.
    (__copy_move<>::__copy_m): Likewise.
    (__copy_move_backward<>::__copy_move_b): Likewise.
    * testsuite/25_algorithms/copy/constexpr.cc: Add check on copied 
values.

    * testsuite/25_algorithms/copy_backward/constexpr.cc: Likewise.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.

    I'll submit the patch to fix Debug mode depending on the decision 
for this one.


François



diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc++-v3/include/bits/stl_algobase.h
index 4eba053ac75..94a79b85d15 100644
--- a/libstdc++-v3/include/bits/stl_algobase.h
+++ b/libstdc++-v3/include/bits/stl_algobase.h
@@ -83,27 +83,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
   template
 _GLIBCXX14_CONSTEXPR
-inline void*
-__memmove(_Tp* __dst, const _Tp* __src, size_t __num)
+inline void
+__memmove(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
 {
 #ifdef __cpp_lib_is_constant_evaluated
   if (std::is_constant_evaluated())
 	{
-	  for(; __num > 0; --__num)
+	  __dst += __num;
+	  __src += __num;
+	  for (; __num != 0; --__num)
 	{
 	  if constexpr (_IsMove)
-		*__dst = std::move(*__src);
+		*--__dst = std::move(*--__src);
 	  else
-		*__dst = *__src;
-	  ++__src;
-	  ++__dst;
+		*--__dst = *--__src;
 	}
-	  return __dst;
 	}
   else
 #endif
-	return __builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
-  return __dst;
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
 }
 
   /*
@@ -730,12 +728,6 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 			 && __is_pointer<_BI2>::__value
 			 && __are_same<_ValueType1, _ValueType2>::__value);
 
-#ifdef __cpp_lib_is_constant_evaluated
-  if (std::is_constant_evaluated())
-	return std::__copy_move_backward::__copy_move_b(__first, __last,
-			   __result);
-#endif
   return std::__copy_move_backward<_IsMove, __simple,
    _Category>::__copy_move_b(__first,
  __last,
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
index 67910b8773e..fc70db6dae7 100644
--- a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
@@ -22,14 +22,25 @@
 #include 
 
 constexpr bool
-test()
+test1()
 {
-  constexpr std::array ca0{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}};
+  constexpr std::array ca0{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}};
   std::array ma0{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
 
   const auto out6 = std::copy(ca0.begin(), ca0.begin() + 8, ma0.begin() + 2);
 
-  return out6 == ma0.begin() + 10;
+  return out6 == ma0.begin() + 10 && *(ma0.begin() + 2) == 1 && *out6 == 0;
 }
 
-static_assert(test());
+static_assert(test1());
+
+constexpr bool
+test2()
+{
+  std::array ma0{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}};
+  const auto out6 = std::copy(ma0.begin(), ma0.begin() + 8, ma0.begin() + 2);
+
+  return out6 == ma0.begin() + 10 && *(ma0.begin() + 9) == 7;
+}
+
+static_assert(test2());
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
new file mode 100644
index 000..49052467409
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2019 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 

[PATCH] Help compiler detect invalid code

2019-09-19 Thread François Dumont

Hi

    I start working on making recently added constexpr tests to work in 
Debug mode.


    It appears that the compiler is able to detect code mistakes pretty 
well as long we don't try to hide the code intention with a defensive 
approach. This is why I'd like to propose to replace '__n > 0' 
conditions with '__n != 0'.


    The result is demonstrated by the constexpr_neg.cc tests. What do 
you think ?


    * include/bits/stl_algobase.h (__memmove): Return _Tp*.
    (__memmove): Loop as long as __n is not 0.
    (__copy_move<>::__copy_m): Likewise.
    (__copy_move_backward<>::__copy_move_b): Likewise.
    * testsuite/25_algorithms/copy/constexpr.cc: Add check on copied 
values.

    * testsuite/25_algorithms/copy_backward/constexpr.cc: Likewise.
    * testsuite/25_algorithms/copy/constexpr_neg.cc: New.
    * testsuite/25_algorithms/copy_backward/constexpr.cc: New.

    I'll submit the patch to fix Debug mode depending on the decision 
for this one.


François

diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc++-v3/include/bits/stl_algobase.h
index 4eba053ac75..33593197b4f 100644
--- a/libstdc++-v3/include/bits/stl_algobase.h
+++ b/libstdc++-v3/include/bits/stl_algobase.h
@@ -83,13 +83,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
   template
 _GLIBCXX14_CONSTEXPR
-inline void*
-__memmove(_Tp* __dst, const _Tp* __src, size_t __num)
+inline _Tp*
+__memmove(_Tp* __dst, const _Tp* __src, ptrdiff_t __num)
 {
 #ifdef __cpp_lib_is_constant_evaluated
   if (std::is_constant_evaluated())
 	{
-	  for(; __num > 0; --__num)
+	  for (; __num != 0; --__num)
 	{
 	  if constexpr (_IsMove)
 		*__dst = std::move(*__src);
@@ -100,10 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
 	  return __dst;
 	}
-  else
 #endif
-	return __builtin_memmove(__dst, __src, sizeof(_Tp) * __num);
-  return __dst;
+  return static_cast<_Tp*>(
+	__builtin_memmove(__dst, __src, sizeof(_Tp) * __num));
 }
 
   /*
@@ -398,7 +397,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__copy_m(_II __first, _II __last, _OI __result)
 	{
 	  typedef typename iterator_traits<_II>::difference_type _Distance;
-	  for(_Distance __n = __last - __first; __n > 0; --__n)
+	  for (_Distance __n = __last - __first; __n != 0; --__n)
 	{
 	  *__result = *__first;
 	  ++__first;
@@ -418,7 +417,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__copy_m(_II __first, _II __last, _OI __result)
 	{
 	  typedef typename iterator_traits<_II>::difference_type _Distance;
-	  for(_Distance __n = __last - __first; __n > 0; --__n)
+	  for (_Distance __n = __last - __first; __n != 0; --__n)
 	{
 	  *__result = std::move(*__first);
 	  ++__first;
@@ -446,8 +445,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 	  const ptrdiff_t _Num = __last - __first;
 	  if (_Num)
-	std::__memmove<_IsMove>(__result, __first, _Num);
-	  return __result + _Num;
+	return std::__memmove<_IsMove>(__result, __first, _Num);
+	  return __result;
 	}
 };
 
@@ -671,7 +670,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 	{
 	  typename iterator_traits<_BI1>::difference_type
 	__n = __last - __first;
-	  for (; __n > 0; --__n)
+	  for (; __n != 0; --__n)
 	*--__result = *--__last;
 	  return __result;
 	}
@@ -688,7 +687,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 	{
 	  typename iterator_traits<_BI1>::difference_type
 	__n = __last - __first;
-	  for (; __n > 0; --__n)
+	  for (; __n != 0; --__n)
 	*--__result = std::move(*--__last);
 	  return __result;
 	}
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
index 67910b8773e..a0460603496 100644
--- a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr.cc
@@ -24,12 +24,12 @@
 constexpr bool
 test()
 {
-  constexpr std::array ca0{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}};
+  constexpr std::array ca0{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}};
   std::array ma0{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
 
   const auto out6 = std::copy(ca0.begin(), ca0.begin() + 8, ma0.begin() + 2);
 
-  return out6 == ma0.begin() + 10;
+  return out6 == ma0.begin() + 10 && *(ma0.begin() + 2) == 1 && *out6 == 0;
 }
 
 static_assert(test());
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
new file mode 100644
index 000..34e20be97eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy/constexpr_neg.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019 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; 

[PATCH] Improve _Safe_iterator _M_distance_to

2019-09-16 Thread François Dumont
    Here is the patch to improve _Safe_iterator<>::_M_get_distance_to 
implementation.


I introduced a new _Distance_precision  __sp_sign_max_size for occasions 
where we can't find out the exact size of a range but still get the max 
size of it. Thanks to this the performance tests are much better when 
dealing with list iterators:


Normal mode:

copy_backward_deque_iterators.cc    deque 2 list 5616r 5616u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 1586r 1586u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     5495r 5495u    
0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2400r 2400u    
0s 0mem    0pf


Debug mode:

copy_backward_deque_iterators.cc    deque 2 list 5789r 5785u    
1s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 1656r 1655u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     5792r 5793u    
0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2636r 2636u    
0s 0mem    0pf


Tested under Linux x86_64.

I'll commit once other patches are in.


    * include/debug/forward_list
(_Sequence_traits<__debug::forward_list<>>::_S_size): Returns __dp_sign
    distance when not empty.
    * include/debug/list
    (_Sequence_traits<__debug::list<>>::_S_size): Likewise.
    * include/debug/helper_functions.h (__dp_sign_max_size): New
    _Distance_precision enum entry.
    * include/debug/safe_iterator.h
    (__copy_move_a(_II, _II, const _Safe_iterator<>&)): Check for output
    iterator _M_can_advance as soon as input range distance precision is
    strictly higher than __dp_size.
    (__copy_move_a(const _Safe_iterator<>&, const _Safe_iterator<>&,
    const _Safe_iterator<>&)): Likewise.
    (__copy_move_backward_a(_II, _II, const _Safe_iterator<>&)): Likewise.
    (__copy_move_backward_a(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(_II, _II, const _Safe_iterator<>&)): Likewise.
    (__equal_aux(const _Safe_iterator<>&,
    const _Safe_iterator<>&, const _Safe_iterator<>&)): Likewise.

François

diff --git a/libstdc++-v3/include/debug/forward_list b/libstdc++-v3/include/debug/forward_list
index e30b09e..f1756ddec9d 100644
--- a/libstdc++-v3/include/debug/forward_list
+++ b/libstdc++-v3/include/debug/forward_list
@@ -911,7 +911,7 @@ namespace __gnu_debug
   _S_size(const std::__debug::forward_list<_Tp, _Alloc>& __seq)
   {
 	return __seq.empty()
-	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_equality);
+	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_sign);
   }
 };
 
diff --git a/libstdc++-v3/include/debug/helper_functions.h b/libstdc++-v3/include/debug/helper_functions.h
index b7aeafef12a..9429bb90a55 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h
@@ -50,10 +50,11 @@ namespace __gnu_debug
*/
   enum _Distance_precision
 {
-  __dp_none,	// Not even an iterator type
-  __dp_equality,	//< Can compare iterator equality, only
-  __dp_sign,	//< Can determine equality and ordering
-  __dp_exact	//< Can determine distance precisely
+  __dp_none,		// Not even an iterator type
+  __dp_equality,		//< Can compare iterator equality, only
+  __dp_sign,		//< Can determine equality and ordering
+  __dp_sign_max_size,	//< __dp_sign and gives max range size
+  __dp_exact		//< Can determine distance precisely
 };
 
   template= 0;
 	}
diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list
index 5eb9a6094e3..140546a633e 100644
--- a/libstdc++-v3/include/debug/list
+++ b/libstdc++-v3/include/debug/list
@@ -916,7 +916,7 @@ namespace __gnu_debug
   _S_size(const std::__debug::list<_Tp, _Alloc>& __seq)
   {
 	return __seq.empty()
-	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_equality);
+	  ? std::make_pair(0, __dp_exact) : std::make_pair(1, __dp_sign);
   }
 };
 #endif
diff --git a/libstdc++-v3/include/debug/safe_iterator.h b/libstdc++-v3/include/debug/safe_iterator.h
index 1665c95f5df..584f8eec5b0 100644
--- a/libstdc++-v3/include/debug/safe_iterator.h
+++ b/libstdc++-v3/include/debug/safe_iterator.h
@@ -984,7 +984,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __glibcxx_check_valid_range2(__first, __last, __dist);
   __glibcxx_check_can_increment(__result, __dist.first);
 
-  if (__dist.second == ::__gnu_debug::__dp_exact
+  if (__dist.second > ::__gnu_debug::__dp_sign
 	  && __result._M_can_advance(__dist.first, true))
 	return ::__gnu_debug::_Safe_iterator<_Ite, _Seq, _Cat>(
 		std::__copy_move_a<_IsMove>(__first, __last, __result.base()),
@@ -1008,7 +1008,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   if (__dist.second > ::__gnu_debug::__dp_equality)
 	{
-	  if (__dist.second == ::__gnu_debug::__dp_exact
+	  if (__dist.second > 

[PATCH] lexicographical_comparison enhancements

2019-09-16 Thread François Dumont

Hi

This is the next part of:

https://gcc.gnu.org/ml/libstdc++/2019-09/msg00048.html

This time it is to improve behavior of std::lexicographical_compare for 
deque or safe iterators.


To do so I had to make lc internal implementation return int rather than 
bool otherwise it is impossible to have a chunk base approach for the 
deque iterators. I'm surprised that std::lexicographical_compare returns 
a bool by the way, usually comparisons return int.


Tested under Linux x86_64 normal and debug modes.

Ok to commit ?


    * include/bits/stl_algobase.h
    (__lexicographical_compare_impl): Return int.
    (__lexicographical_compare::__lc): Likewise.
    (__lexicographical_compare_aux1(_II1, _II1, _II2, _II2)): New.
    (__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
    _II2, _II2)): New.
    (__lexicographical_compare_aux1(_II1, _II1,
    _Deque_iterator<>, _Deque_iterator<>)): New.
    (__lexicographical_compare_aux1(_Deque_iterator<>, _Deque_iterator<>,
    _Deque_iterator<>, _Deque_iterator<>)): New.
    (__lexicographical_compare_aux): Adapt, call later.
    (__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
    _II2, _II2)): New.
    (__lexicographical_compare_aux(_II1, _II1,
    _Safe_iterator<>, _Safe_iterator<>)): New.
    (__lexicographical_compare_aux(_Safe_iterator<>, _Safe_iterator<>,
    _Safe_iterator<>, _Safe_iterator<>)): New.
    (std::lexicographical_compare): Adapt, call later.
    * include/bits/stl_deque.h (__lexicographical_compare_aux1): New
    declarations.
    * include/bits/deque.tcc (__lex_cmp_dit): New.
    (__lexicographical_compare_aux1): New definitions.
    * include/debug/safe_iterator.h (__lexicographical_compare_aux): New.
    * testsuite/25_algorithms/lexicographical_compare/1.cc (test6, test7):
    New.
    * testsuite/25_algorithms/lexicographical_compare/deque_iterators/1.cc:
    New.

François

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index ab996ca52fa..db8f1b03eec 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -1208,6 +1208,98 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
   return true;
 }
 
+  template
+int
+__lex_cmp_dit(
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __first1,
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>& __last1,
+	_II __first2, _II __last2)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> _Iter;
+  typedef typename _Iter::difference_type difference_type;
+
+  if (__first1._M_node != __last1._M_node)
+	{
+	  difference_type __len = __last2 - __first2;
+	  difference_type __flen
+	= std::min(__len, __first1._M_last - __first1._M_cur);
+	  if (int __ret = std::__lexicographical_compare_aux1(
+	  __first1._M_cur, __first1._M_last, __first2, __first2 + __flen))
+	return __ret;
+
+	  __first2 += __flen;
+	  __len -= __flen;
+	  __flen = std::min(__len, _Iter::_S_buffer_size());
+	  for (typename _Iter::_Map_pointer __node = __first1._M_node + 1;
+	   __node != __last1._M_node;
+	   __first2 += __flen, __len -= __flen,
+	   __flen = std::min(__len, _Iter::_S_buffer_size()),
+	   ++__node)
+	if (int __ret = std::__lexicographical_compare_aux1(
+		  *__node, *__node + _Iter::_S_buffer_size(),
+		  __first2, __first2 + __flen))
+	  return __ret;
+
+	  return std::__lexicographical_compare_aux1(
+	__last1._M_first, __last1._M_cur, __first2, __last2);
+	}
+
+  return std::__lexicographical_compare_aux1(
+	  __first1._M_cur, __last1._M_cur, __first2, __last2);
+}
+
+  template
+typename __gnu_cxx::__enable_if<
+  __is_random_access_iter<_II2>::__value, int>::__type
+__lexicographical_compare_aux1(
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
+		_II2 __first2, _II2 __last2)
+{ return std::__lex_cmp_dit(__first1, __last1, __first2, __last2); }
+
+  template
+int
+__lexicographical_compare_aux1(
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __first1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp1, _Ref1, _Ptr1> __last1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
+{ return std::__lex_cmp_dit(__first1, __last1, __first2, __last2); }
+
+  template
+typename __gnu_cxx::__enable_if<
+  __is_random_access_iter<_II1>::__value, int>::__type
+__lexicographical_compare_aux1(
+		_II1 __first1, _II1 __last1,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __first2,
+		_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
+{
+  typedef _GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> _Iter;
+  typedef typename _Iter::difference_type difference_type;
+
+  difference_type __len = __last1 - __first1;
+  while (__len > 0)
+	{
+	  const difference_type __flen = __first2._M_node == 

copy/copy_backward/fill/fill_n/equal rework

2019-09-09 Thread François Dumont

Hi

    This patch improves stl_algobase.h 
copy/copy_backward/fill/fill_n/equal implementations. The improvements are:


- activation of algo specialization for __gnu_debug::_Safe_iterator (w/o 
_GLIBCXX_DEBUG mode)


- activation of algo specialization for _Deque_iterator even if mixed 
with another kind of iterator.


- activation of algo specializations __copy_move_a2 for something else 
than pointers. For example this code:


std::vector v { 'a', 'b',  };

ostreambuf_iterator out(std::cout);

std::copy(v.begin(), v.end(), out);

is not calling the specialization __copy_move_a2(const char*, const 
char*, ostreambuf_iterator<>);


It also fix a _GLIBCXX_DEBUG issue where the __niter_base specialization 
was wrongly removing the _Safe_iterator<> layer. The 
testsuite/25_algorithms/copy/debug/1_neg.cc test case was failing on a 
debug assertion because _after_ the copy we were trying to increment the 
vector iterator after past-the-end. Of course the problem is the 
_after_, Debug mode should detect this _before_ it takes place which it 
does now.


Note that std::fill_n is now making use of std::fill for some 
optimizations dealing with random access iterators.


Performances are very good:

Before:

copy_backward_deque_iterators.cc    deque 2 deque 1084r 1084u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector 3373r 3372u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque 3316r 3316u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 3610r 
3609u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 3552r 
3552u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 10528r 10528u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2161r 2162u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         752r 751u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector       3300r 3299u    
0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque       3144r 3140u    
0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      3340r 3338u    
1s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      3132r 3132u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     10013r 
10012u    0s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2274r 2275u    
0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque       8676r 8675u    
0s 0mem    0pf
equal_deque_iterators.cc     deque vs vector      5870r 5870u    
0s 0mem    0pf
equal_deque_iterators.cc     vector vs deque      3163r 3163u    
0s 0mem    0pf
equal_deque_iterators.cc     int deque vs char vector     5845r 5845u    
0s 0mem    0pf
equal_deque_iterators.cc     char vector vs int deque     3307r 3307u    
0s 0mem    0pf


After:

copy_backward_deque_iterators.cc    deque 2 deque  697r  697u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 vector  219r  218u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    vector 2 deque  453r  453u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    int deque 2 char vector 1914r 
1915u    0s 0mem    0pf
copy_backward_deque_iterators.cc    char vector 2 int deque 2112r 
2111u    0s 0mem    0pf
copy_backward_deque_iterators.cc    deque 2 list 7770r 7771u    
0s 0mem    0pf
copy_backward_deque_iterators.cc    list 2 deque 2194r 2193u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 deque         505r 504u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 vector        221r 221u    
0s 0mem    0pf
copy_deque_iterators.cc      vector 2 deque        398r 397u    
0s 0mem    0pf
copy_deque_iterators.cc      int deque 2 char vector      1770r 1767u    
0s 0mem    0pf
copy_deque_iterators.cc      char vector 2 int deque      1995r 1993u    
0s 0mem    0pf
copy_deque_iterators.cc      deque 2 list     7650r 7641u    
2s 0mem    0pf
copy_deque_iterators.cc      list 2 deque     2270r 2270u    
0s 0mem    0pf
equal_deque_iterators.cc     deque vs deque        769r 768u    
0s 0mem    0pf
equal_deque_iterators.cc     deque vs vector       231r 230u    
0s 0mem    0pf
equal_deque_iterators.cc     vector vs deque       397r 397u    
0s 0mem    0pf
equal_deque_iterators.cc     int deque vs char vector     1541r 1541u    
0s 0mem    0pf
equal_deque_iterators.cc     char vector vs int deque     1623r 1623u    
0s 0mem    0pf


In Debug Mode it is of course even better. I haven't had the patience to 
run the benches before the patch, it just takes hours to run. So here is 

[PATCH] Fix unused malloc return value warning

2019-08-29 Thread François Dumont

Hi

    I am having this warning:

/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/util/testsuite_performance.h:170: 
attention: ignoring return value of « void* malloc(size_t) » declared 
with attribute « warn_unused_result » [-Wunused-result]

  170 |   malloc(0); // Needed for some implementations.

    Ok to fix it with attached patch ?

    It seems trivial but I wonder if I shouldn't keep the malloc 
returned pointer and free it properly ?


    Or maybe just remove the malloc cause there is not clear comment 
explaining why it's needed and I haven't found much in SVN audit trail.


    * testsuite_files/util/testsuite_performance.h
    (resource_counter::start): Ignore unused malloc(0) result.

François

diff --git a/libstdc++-v3/testsuite/util/testsuite_performance.h b/libstdc++-v3/testsuite/util/testsuite_performance.h
index 556c78159be..8abc77cf31a 100644
--- a/libstdc++-v3/testsuite/util/testsuite_performance.h
+++ b/libstdc++-v3/testsuite/util/testsuite_performance.h
@@ -167,7 +167,7 @@ namespace __gnu_test
 {
   if (getrusage(who, _begin) != 0 )
 	memset(_begin, 0, sizeof(rusage_begin));
-  malloc(0); // Needed for some implementations.
+  void* p __attribute__((unused)) = malloc(0); // Needed for some implementations.
   allocation_begin = mallinfo();
 }
 



Re: PR 90409 Deque fiil/copy/move/copy_backward/move_backward/equal overloads

2019-08-26 Thread François Dumont

Hi

    I am eventually working on another implementation to acheive the 
same result with less changes and codes. I think you'll prefer this one.


    So don't spend any time on this patch proposal, a new one will come 
in a couple of days.


François



Deque iterators lexicographical_compare overloads

2019-07-25 Thread François Dumont

And here are the lexicographical_compare overloads.

I am submitting this seperately from the others cause some might argue 
that it is a lot of code for a scope limited to the moment to unsigned 
byte types.


I had to overload __lexicographical_compare_aux here cause the 
std::lexicographical_compare returning a bool can't be implemented in 
chunk unless you double the comparisons to find out when 2 ranges are 
equivalent.



    * include/bits/stl_deque.h
    (std::__lexicographical_compare_aux): New overloads for deque 
iterators.

    * include/bits/deque.tcc
    (std::__detail::__lc_from_dit): New.
    (std::__detail::__lc_to_dit): New.
    (std::__lexicographical_compare_aux): New overloads definitions, use
    latters.
    * include/debug/deque
    (std::__lexicographical_compare_aux): New overloads for deque debug
    iterators.
    * include/bits_stl_algobase.h:
    (__lexicographical_compare_impl): Return int.
    (__lexicographical_compare<>::__lc): Likewise.
    (_Deque_iterator<>): New type declaration.
    (__lexicographical_compare_aux): Add overload declarations for deque
    iterators normal and debug modes.
    (__lexicographical_compare_aux): Return int.
    (std::lexicographical_compare): Adapt.
    * testsuite/25_algorithms/lexicographical_compare/1.cc (test6): New.
    (test7): New.
    * testsuite/25_algorithms/lexicographical_compare/deque_iterators/1.cc:
    New.

Tested uner Linux x86_64 normal and debug modes.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index 9db869fb666..87514992f50 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -1167,6 +1167,80 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 	return true;
   }
 
+template
+  int
+  __lc_from_dit(
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&,
+	  const _Tp*>& __first1,
+	const _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&,
+	  const _Tp*>& __last1,
+	_II __first2, _II __last2)
+  {
+	typedef typename _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&,
+			 const _Tp*>::_Self
+	  _Self;
+	typedef typename _Self::difference_type difference_type;
+
+	if (__first1._M_node != __last1._M_node)
+	  {
+	difference_type __len = __last2 - __first2;
+	difference_type __flen
+	  = std::min(__len, __first1._M_last() - __first1._M_cur);
+	if (int __ret = std::__lexicographical_compare_aux(
+		__first1._M_cur, __first1._M_last(), __first2, __first2 + __flen))
+	  return __ret;
+
+	__first2 += __flen;
+	__len -= __flen;
+	__flen = std::min(__len, _Self::_S_buffer_size());
+	for (typename _Self::_Map_pointer __node = __first1._M_node + 1;
+		 __node != __last1._M_node;
+		 __first2 += __flen, __len -= __flen,
+		   __flen = std::min(__len, _Self::_S_buffer_size()), ++__node)
+	  if (int __ret = std::__lexicographical_compare_aux(
+	*__node, *__node + _Self::_S_buffer_size(),
+	__first2, __first2 + __flen))
+		return __ret;
+
+	return std::__lexicographical_compare_aux(
+		__last1._M_first, __last1._M_cur, __first2, __last2);
+	  }
+
+	return std::__lexicographical_compare_aux(
+		__first1._M_cur, __last1._M_cur, __first2, __last2);
+  }
+
+template
+  int
+  __lc_to_dit(_II __first1, _II __last1,
+	_GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&, const _Tp*> __first2,
+	_GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&, const _Tp*> __last2)
+  {
+	typedef typename _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&,
+			 const _Tp*>::_Self
+	  _Self;
+	typedef typename _Self::difference_type difference_type;
+
+	difference_type __len = __last1 - __first1;
+	while (__len > 0)
+	  {
+	const difference_type __flen = __first2._M_node == __last2._M_node
+	  ? __last2._M_cur - __first2._M_cur
+	  : __first2._M_last() - __first2._M_cur;
+	const difference_type __clen
+	  = std::min(__len, __flen);
+	if (int __ret = std::__lexicographical_compare_aux(
+		__first1, __first1 + __clen, __first2._M_cur, __first2._M_cur + __flen))
+	  return __ret;
+
+	__first1 += __clen;
+	__len -= __clen;
+	__first2 += __clen;
+	  }
+
+	return __first1 == __last1 ? (__first2 != __last2 ? -1 : 0) : 1;
+  }
+
 #if __cplusplus >= 201103L
 template
   _OI
@@ -1394,6 +1468,36 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
   return __detail::__equal_to_dit(__first1, __last1, __first2);
 }
 
+  template
+typename __gnu_cxx::__enable_if<
+  __are_same::iterator_category,
+		 std::random_access_iterator_tag>::__value,
+  int>::__type
+__lexicographical_compare_aux(
+  _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&, const _Tp*> __first1,
+  _GLIBCXX_STD_C::_Deque_iterator<_Tp, const _Tp&, const _Tp*> __last1,
+  _II __first2, _II __last2)
+{ return __detail::__lc_from_dit(__first1, __last1, __first2, __last2); }
+
+  template
+int
+__lexicographical_compare_aux(
+  

[PATCH] Fix bad comment copy/paste

2019-07-25 Thread François Dumont

Committed as trivial.

    * testsuite/util/testsuite_iterators.h
    (bidirectional_iterator_wrapper): Fix type comment.
    (random_access_iterator_wrapper): Likewise.

François

diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h
index ac646a59cb8..42e42740df9 100644
--- a/libstdc++-v3/testsuite/util/testsuite_iterators.h
+++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h
@@ -344,7 +344,7 @@ namespace __gnu_test
* @brief bidirectional_iterator wrapper for pointer
*
* This class takes a pointer and wraps it to provide exactly
-   * the requirements of a forward_iterator. It should not be
+   * the requirements of a bidirectional_iterator. It should not be
* instantiated directly, but generated from a test_container
*/
   template
@@ -408,7 +408,7 @@ namespace __gnu_test
* @brief random_access_iterator wrapper for pointer
*
* This class takes a pointer and wraps it to provide exactly
-   * the requirements of a forward_iterator. It should not be
+   * the requirements of a random_access_iterator. It should not be
* instantiated directly, but generated from a test_container
*/
   template



Add std::copy_n overload for istreambuf_iterator

2019-07-19 Thread François Dumont

It sounds reasonable to overload std::copy_n for istreambuf_iterator.

    * include/bits/stl_algo.h (copy_n(istreambuf_iterator<>, _Size, 
_OIte)):

    New declaration.
    * include/bits/streambuf_iterator.h (istreambuf_iterator<>): Declare
    std::copy_n for istreambuf_iterator of char types to be friend.
    (std::copy_n(istreambuf_iterator<>, _Size, _OIte)): New overload.
    * include/std/streambuf(basic_streambuf<>): Declare std::copy_n for
    istreambuf_iterator of char types to be friend.
    * testsuite/25_algorithms/copy_n/istreambuf_iterator.cc: New.
    * testsuite/25_algorithms/copy_n/istreambuf_iterator_neg.cc: New.

Tested under Linux x86_64, normal and debug modes.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index 478f012def8..ec651e2cc45 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -771,6 +771,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	 _OutputIterator __result, random_access_iterator_tag)
 { return std::copy(__first, __first + __n, __result); }
 
+  template
+typename enable_if<__is_char<_CharT>::__value,
+		   _OutputIterator>::type
+copy_n(istreambuf_iterator<_CharT, char_traits<_CharT> >,
+	   _Size __n, _OutputIterator __result);
+
   /**
*  @brief Copies the range [first,first+n) into [result,result+n).
*  @ingroup mutating_algorithms
diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h b/libstdc++-v3/include/bits/streambuf_iterator.h
index 2f4ff494a3a..c682fa91bde 100644
--- a/libstdc++-v3/include/bits/streambuf_iterator.h
+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
@@ -80,6 +80,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__copy_move_a2(istreambuf_iterator<_CharT2>,
 		   istreambuf_iterator<_CharT2>, _CharT2*);
 
+#if __cplusplus >= 201103L
+  template
+	friend typename enable_if<__is_char<_CharT2>::__value,
+  _OutputIterator>::type
+	copy_n(istreambuf_iterator<_CharT2>, _Size, _OutputIterator);
+#endif
+
   template
 	friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
 istreambuf_iterator<_CharT2> >::__type
@@ -367,6 +374,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   return __result;
 }
 
+#if __cplusplus >= 201103L
+  template
+typename enable_if<__is_char<_CharT>::__value, _OutputIterator>::type
+copy_n(istreambuf_iterator<_CharT> __it, _Size __n,
+	   _OutputIterator __result)
+{
+  if (__n == 0)
+	return __result;
+
+  __glibcxx_assert(__n > 0);
+  __glibcxx_requires_cond(!__it._M_at_eof(),
+			  _M_message(__gnu_debug::__msg_inc_istreambuf)
+			  ._M_iterator(__it));
+
+  using traits_type = typename istreambuf_iterator<_CharT>::traits_type;
+  const auto __eof = traits_type::eof();
+
+  auto __sb = __it._M_sbuf;
+  while (__n > 0)
+	{
+	  streamsize __size = __sb->egptr() - __sb->gptr();
+	  if (__size == 0)
+	{
+	  if (traits_type::eq_int_type(__sb->underflow(), __eof))
+		{
+		  __glibcxx_requires_cond(__n == 0,
+		_M_message(__gnu_debug::__msg_inc_istreambuf)
+	  ._M_iterator(__it));
+		  break;
+		}
+
+	  __size =  __sb->egptr() - __sb->gptr();
+	}
+
+	  streamsize __xsize = std::min(__size, __n);
+	  __result = std::copy(__sb->gptr(), __sb->gptr() + __xsize, __result);
+	  __sb->__safe_gbump(__xsize);
+	  __n -= __xsize;
+	}
+
+  return __result;
+}
+#endif
+
   template
 typename __gnu_cxx::__enable_if<__is_char<_CharT>::__value,
 		  		istreambuf_iterator<_CharT> >::__type
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index d9ca981d704..4f62ebf4d95 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -155,6 +155,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 __copy_move_a2(istreambuf_iterator<_CharT2>,
 		   istreambuf_iterator<_CharT2>, _CharT2*);
 
+#if __cplusplus >= 201103L
+  template
+	friend typename enable_if<__is_char<_CharT2>::__value,
+  _OutputIterator>::type
+	copy_n(istreambuf_iterator<_CharT2>, _Size, _OutputIterator);
+#endif
+
   template
 friend typename __gnu_cxx::__enable_if<__is_char<_CharT2>::__value,
   istreambuf_iterator<_CharT2> >::__type
diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
new file mode 100644
index 000..ebd769cf7c0
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/istreambuf_iterator.cc
@@ -0,0 +1,59 @@
+// Copyright (C) 2019 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 

Re: sized delete in _Temporary_buffer<>

2019-07-19 Thread François Dumont

(2nd sent attempt as text this time.)

Good spot, fixed with attached patch, committed as trivial.

2019-07-19  François Dumont 

    * include/bits/stl_tempbuf.h (__detail::__return_temporary_buffer): Fix
    sized deallocation size computation.


On 7/19/19 9:46 PM, Morwenn Ed wrote:
If I'm not mistaken this patch allocates N*sizeof(_Tp) bytes of 
storage and deallocates N bytes when sized deallocation is enabled?


Shouldn't __return_temporary_buffer deallocate N*sizeof(_Tp) instead 
to match the value passed to new?



*De :* libstdc++-ow...@gcc.gnu.org  de la 
part de François Dumont 

*Envoyé :* jeudi 18 juillet 2019 07:41
*À :* libstd...@gcc.gnu.org ; gcc-patches 


*Objet :* sized delete in _Temporary_buffer<>
As we adopted the sized deallocation in the new_allocator why not doing
the same in _Temporary_buffer<>.

 * include/bits/stl_tempbuf.h (__detail::__return_temporary_buffer):
New.
 (~_Temporary_buffer()): Use latter.
 (_Temporary_buffer(_FIterator, size_type)): Likewise.

Tested w/o activating sized deallocation. I'll try to run tests with
this option activated.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/bits/stl_tempbuf.h b/libstdc++-v3/include/bits/stl_tempbuf.h
index bb7c2cd1334..ce3f3624437 100644
--- a/libstdc++-v3/include/bits/stl_tempbuf.h
+++ b/libstdc++-v3/include/bits/stl_tempbuf.h
@@ -71,7 +71,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 size_t __len __attribute__((__unused__)))
   {
 #if __cpp_sized_deallocation
-	::operator delete(__p, __len);
+	::operator delete(__p, __len * sizeof(_Tp));
 #else
 	::operator delete(__p);
 #endif


Re: Fix failing tests after PR libstdc++/85965

2019-07-18 Thread François Dumont
Got it, it is my PR 68303 patch which was introducing this regression. I 
fix it to restore those assertions.


You'll see once the awaiting hashtable patches are in...

On 7/18/19 12:18 PM, Jonathan Wakely wrote:

On 18/07/19 07:41 +0200, François Dumont wrote:

Since commit 5d3695d03b7bdade9f4d05d2b those tests are failing.

    * testsuite/23_containers/unordered_map/48101_neg.cc: Adapt dg-error
    after PR libstdc++/85965 fix.
    * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
    * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
    * testsuite/23_containers/unordered_set/48101_neg.cc

It is quite trivial but I wonder if there is another plan to restore 
those static assertions differently.


Ok to commit ?


No. I don't see these failures. With the first change applied, I see a
new failure.

The patch seems wrong.







Fix failing tests after PR libstdc++/85965

2019-07-17 Thread François Dumont

Since commit 5d3695d03b7bdade9f4d05d2b those tests are failing.

    * testsuite/23_containers/unordered_map/48101_neg.cc: Adapt dg-error
    after PR libstdc++/85965 fix.
    * testsuite/23_containers/unordered_multimap/48101_neg.cc: Likewise.
    * testsuite/23_containers/unordered_multiset/48101_neg.cc: Likewise.
    * testsuite/23_containers/unordered_set/48101_neg.cc

It is quite trivial but I wonder if there is another plan to restore 
those static assertions differently.


Ok to commit ?

François

diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc
index 8d823dfa476..77c0e9ce681 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_map/48101_neg.cc
@@ -27,7 +27,4 @@ test01()
   c2.find(2); // { dg-error "here" }
 }
 
-// { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
-// { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
-// { dg-prune-output "use of deleted function" }
 // { dg-prune-output "no match for call" }
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc
index a81615b3607..7db7dcb2b5d 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/48101_neg.cc
@@ -27,7 +27,4 @@ test01()
   c2.find(2); // { dg-error "here" }
 }
 
-// { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
-// { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
-// { dg-prune-output "use of deleted function" }
 // { dg-prune-output "no match for call" }
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc
index 03ddb898d6c..444ca210c58 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/48101_neg.cc
@@ -29,8 +29,5 @@ test01()
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
-// { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
-// { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
-// { dg-prune-output "use of deleted function" }
 // { dg-prune-output "must have the same value_type as its allocator" }
 // { dg-prune-output "no match for call" }
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
index e79d3769248..2c02620bccb 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/48101_neg.cc
@@ -29,8 +29,5 @@ test01()
 }
 
 // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
-// { dg-error "hash function must be invocable" "" { target *-*-* } 0 }
-// { dg-error "key equality predicate must be invocable" "" { target *-*-* } 0 }
-// { dg-prune-output "use of deleted function" }
 // { dg-prune-output "must have the same value_type as its allocator" }
 // { dg-prune-output "no match for call" }



sized delete in _Temporary_buffer<>

2019-07-17 Thread François Dumont
As we adopted the sized deallocation in the new_allocator why not doing 
the same in _Temporary_buffer<>.


    * include/bits/stl_tempbuf.h (__detail::__return_temporary_buffer): 
New.

    (~_Temporary_buffer()): Use latter.
    (_Temporary_buffer(_FIterator, size_type)): Likewise.

Tested w/o activating sized deallocation. I'll try to run tests with 
this option activated.


Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_tempbuf.h b/libstdc++-v3/include/bits/stl_tempbuf.h
index b6ad9ee6a46..bb7c2cd1334 100644
--- a/libstdc++-v3/include/bits/stl_tempbuf.h
+++ b/libstdc++-v3/include/bits/stl_tempbuf.h
@@ -63,6 +63,21 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
+  namespace __detail
+  {
+template
+  inline void
+  __return_temporary_buffer(_Tp* __p,
+size_t __len __attribute__((__unused__)))
+  {
+#if __cpp_sized_deallocation
+	::operator delete(__p, __len);
+#else
+	::operator delete(__p);
+#endif
+  }
+  }
+
   /**
*  @brief Allocates a temporary buffer.
*  @param  __len  The number of objects of type Tp.
@@ -112,7 +127,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 return_temporary_buffer(_Tp* __p)
 { ::operator delete(__p); }
 
-
   /**
*  This class is used in two places: stl_algo.h and ext/memory,
*  where it is wrapped as the temporary_buffer class.  See
@@ -165,7 +179,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   ~_Temporary_buffer()
   {
 	std::_Destroy(_M_buffer, _M_buffer + _M_len);
-	std::return_temporary_buffer(_M_buffer);
+	std::__detail::__return_temporary_buffer(_M_buffer, _M_len);
   }
 
 private:
@@ -185,7 +199,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 __ucr(_Pointer __first, _Pointer __last,
 	  _ForwardIterator __seed)
 {
-	  if(__first == __last)
+	  if (__first == __last)
 	return;
 
 	  _Pointer __cur = __first;
@@ -244,22 +258,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 _Temporary_buffer(_ForwardIterator __seed, size_type __original_len)
 : _M_original_len(__original_len), _M_len(0), _M_buffer(0)
 {
-  __try
-	{
-	  std::pair __p(std::get_temporary_buffer<
-	value_type>(_M_original_len));
-	  _M_buffer = __p.first;
-	  _M_len = __p.second;
-	  if (_M_buffer)
-	std::__uninitialized_construct_buf(_M_buffer, _M_buffer + _M_len,
-	   __seed);
-	}
-  __catch(...)
+  std::pair __p(
+		std::get_temporary_buffer(_M_original_len));
+
+  if (__p.first)
 	{
-	  std::return_temporary_buffer(_M_buffer);
-	  _M_buffer = 0;
-	  _M_len = 0;
-	  __throw_exception_again;
+	  __try
+	{
+	  std::__uninitialized_construct_buf(__p.first, __p.first + __p.second,
+		 __seed);
+	  _M_buffer = __p.first;
+	  _M_len = __p.second;
+	}
+	  __catch(...)
+	{
+	  std::__detail::__return_temporary_buffer(__p.first, __p.second);
+	  __throw_exception_again;
+	}
 	}
 }
 


Re: Deque fiil/copy/move/copy_backward/move_backward/equal overloads

2019-07-16 Thread François Dumont

Ping ?

On 6/19/19 7:32 PM, François Dumont wrote:
I wanted to implement Debug overloads for those already existing 
overloads but then realized that those algos could be generalized. 
This way we will benefit from the memmove replacement when operating 
with C array or std::array or std::vector iterators.


I might do the same for lexicographical_compare one day.

The ChangeLog below is quite huge so I attached it. I wonder if I 
could use deque::iterator and deque::const_iterator in place of the 
_Deque_iterator<> to reduce it ?


Tested under Linux x86_64 normal and debug modes, ok to commit ?

François





Re: [C++ PATCH] Speed up inplace_merge algorithm & fix inefficient logic(PR libstdc++/83938)

2019-07-16 Thread François Dumont

Hi

    I eventually spent much more time working on the inplace_merge 
performance bench.


    And the results do not confirm the theory:

Before patch:
inplace_merge.cc     bench 1 / 1 memory        243r  227u   
17s  1216mem    5pf
inplace_merge.cc     bench 1 / 4 memory        297r 278u   
18s   480mem    0pf
inplace_merge.cc     bench 1 /64 memory       373r 354u   
18s   480mem    0pf
inplace_merge.cc     bench 0 / 1 memory    12r 11u 
0s   480mem    0pf


After the patch to reduce memory allocation:
inplace_merge.cc     bench 1 / 1 memory        245r  227u   
18s  1216mem    0pf
inplace_merge.cc     bench 1 / 4 memory        292r 273u   
19s   480mem    0pf
inplace_merge.cc     bench 1 /64 memory       373r 356u   
17s   480mem    0pf
inplace_merge.cc     bench 0 / 1 memory    11r 11u 
0s   480mem    0pf


With the __len1 > __len2 condition change:
inplace_merge.cc     bench 1 / 1 memory        244r  225u   
20s  1216mem    0pf
inplace_merge.cc     bench 1 / 4 memory        379r 361u   
17s   480mem    0pf
inplace_merge.cc     bench 1 /64 memory        640r 625u   
16s   480mem    0pf
inplace_merge.cc     bench 0 / 1 memory         11r 11u    
0s   480mem    0pf


When there is no memory limit the results are identical of course. 
Otherwise as soon as memory is limited performance starts to decrease 
with the condition change on __len1 vs __len2.


Could you give the bench you use to demonstrate the enhancement ? I also 
wonder why your patch doesn't change consistently the same condition in 
__merge_without_buffer ?


For the moment I'd like to propose the attached patch that is to say the 
reduction on the amount of allocated memory and the new/modified benches.


Note that as soon as I forbid any memory allocation I also reduce the 
size of the range to merge cause the implementation rely on recursivity 
and so could end-up in a stack overflow. Maybe I need to check for 
simulator targets like several other tests ? Unless simulators do not 
run the performance tests ?


Regarding this stack overflow issue, is there some routine to find out 
how many levels of function calls can be added before reaching the stack 
overflow ? I could perhaps call __builtin_alloca and check the result 
but that smells. If I could find out this we could fallback on an 
iterative approach to complete the merge.


    PR libstdc++/83938
    * include/bits/stl_algo.h:
    (__inplace_merge): Take temporary buffer length from smallest range.
    (__stable_sort): Limit temporary buffer length.
    * include/bits/stl_tempbuf.h (get_temporary_buffer): Change __len
    computation in the loop.
    * testsuite/25_algorithms/inplace_merge/1.cc (test3): Test all possible
    pivot positions.
    * testsuite/performance/25_algorithms/inplace_merge.cc: New.
    * testsuite/performance/25_algorithms/stable_sort.cc: Rework to allow
    execution with different memory constraints.

Ok to commit ?

François

On 6/9/19 4:27 PM, François Dumont wrote:

On 12/21/18 9:57 PM, Jonathan Wakely wrote:

On 29/10/18 07:06 +0100, François Dumont wrote:

Hi

    Some feedback regarding this patch ?


Sorry this got missed, please resubmit during stage 1.

You haven't CC'd the original patch author (chang jc) to give them a
chance to comment on your proposed changes to the patch.

The attached PDF on PR libstdc++/83938 has extensive discussion of the
performance issue, but I don't see any for your version. Some
performance benchmarks for your version would be helpful.


Here is this patch again.

This time it is much closer to John's original one, I just kept my 
change on the size of the temporary buffer which doesn't need to be as 
large as it is currently allocated, especially with John's patch.


The performance tests are showing the good improvements, attached are 
the before/after. Surprisingly I do not see any change regarding 
allocated memory, I guess measures are not accurate enough. However 
working on them also show that current implementation is fragile. If 
you reduce the allowed allocated memory too much you end up with a 
stack overflow because of the recursive implementation.


I already have a patch to keep on trying to allocate memory as long as 
above a given threshold rather than 0 but I am also working on making 
implementation non-recursive. We'll see if even a buffer of size 1 is 
better than no buffer at all then.


    PR libstdc++/83938
    * include/bits/stl_algo.h:
    (__merge_adaptive): When buffer too small consider smallest range 
first

    and cut based on buffer size.
    (__inplace_merge): Take temporary buffer length from smallest range.
    (__stable_sort): Limit temporary buffer length.
    * include/bits/stl_tempbuf.h (get_temporary_buffer): Change function
    to reduce requested buffer len

Re: std::vector code cleanup fixes optimizations

2019-06-24 Thread François Dumont

Hi

    Any feedback regarding this patch ?

Thanks

On 5/14/19 7:46 AM, François Dumont wrote:

Hi

    This is the patch on vector to:

- Optimize sizeof in Versioned namespace mode. We could go one step 
further by removing _M_p from _M_finish and just transform it into an 
offset but it is a little bit more impacting for the code.


- Implement the swap optimization already done on main std::vector 
template class.


- Fix move constructor so that it is noexcept no matter allocator move 
constructor noexcept qualification


- Optimize move constructor with allocator when allocator type is 
always equal.


- Use shortcuts in C++11 by skipping the _M_XXX_dispatch methods. 
Those are now defined only in pre-C++11 mode, I can't see any abi 
issue in doing so.


    * include/bits/stl_bvector.h
    [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
    _Bit_type*.
    (_Bvector_impl_data(const _Bvector_impl_data&)): Default.
    (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
    (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): Default.
(_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
    (_Bvector_impl_data::_M_reset()): Likewise.
    (_Bvector_impl_data::_M_swap_data): New.
    (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement 
explicitely.
    (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&, 
_Bvector_impl&&)): New.
    (_Bvector_base::_Bvector_base(_Bvector_base&&, const 
allocator_type&)):

    New, use latter.
    (vector::vector(vector&&, const allocator_type&, true_type)): New, 
use

    latter.
    (vector::vector(vector&&, const allocator_type&, false_type)): New.
    (vector::vector(vector&&, const allocator_type&)): Use latters.
    (vector::vector(const vector&, const allocator_type&)): Adapt.
    [__cplusplus >= 201103](vector::vector(_InputIt, _InputIt,
    const allocator_type&)): Use _M_initialize_range.
    (vector::operator[](size_type)): Use iterator operator[].
    (vector::operator[](size_type) const): Use const_iterator operator[].
    (vector::swap(vector&)): Adapt.
    (vector::_M_initialize(size_type)): Add assertions on allocators.
    Use _M_swap_data.
    [__cplusplus >= 201103](vector::insert(const_iterator, _InputIt,
    _InputIt)): Use _M_insert_range.
    [__cplusplus >= 201103](vector::_M_initialize_dispatch): Remove.
    [__cplusplus >= 201103](vector::_M_insert_dispatch): Remove.
    * testsuite/23_containers/vector/bool/allocator/swap.cc: Adapt.
    * 
testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:

    Add check.

Tested under Linux x86_64, normal and debug modes.

Ok to commit ?

François





Re: Deque fiil/copy/move/copy_backward/move_backward/equal overloads

2019-06-20 Thread François Dumont
And thanks for noticing that not only user code will be improved but 
also our own algos !


I'll reference this PR if accepted.


On 6/20/19 10:38 AM, Morwenn Ed wrote:

That's actually a solution to bug 90409, thanks for it :)

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90409

Morwenn


*De :* libstdc++-ow...@gcc.gnu.org  de la 
part de François Dumont 

*Envoyé :* mercredi 19 juin 2019 19:32
*À :* libstd...@gcc.gnu.org; gcc-patches
*Objet :* Deque fiil/copy/move/copy_backward/move_backward/equal 
overloads

I wanted to implement Debug overloads for those already existing
overloads but then realized that those algos could be generalized. This
way we will benefit from the memmove replacement when operating with C
array or std::array or std::vector iterators.

I might do the same for lexicographical_compare one day.

The ChangeLog below is quite huge so I attached it. I wonder if I could
use deque::iterator and deque::const_iterator in place of the
_Deque_iterator<> to reduce it ?

Tested under Linux x86_64 normal and debug modes, ok to commit ?

François





Re: Review Hashtable extract node API

2019-06-20 Thread François Dumont

On 6/19/19 12:47 AM, Jonathan Wakely wrote:

On 18/06/19 22:42 +0200, François Dumont wrote:

On 6/18/19 12:54 PM, Jonathan Wakely wrote:

On 18/06/19 07:52 +0200, François Dumont wrote:

A small regression noticed while merging.

We shouldn't keep on using a moved-from key_type instance.

Ok to commit ? Feel free to do it if you prefer, I'll do so at end 
of Europe day otherwise.



diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
b/libstdc++-v3/include/bits/hashtable_policy.h

index f5809c7443a..7e89e1b44c4 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -743,7 +743,8 @@ namespace __detail
std::tuple<>()
  };
  auto __pos
-    = __h->_M_insert_unique_node(__k, __bkt, __code, __node._M_node);
+    = 
__h->_M_insert_unique_node(__h->_M_extract()(__node._M_node->_M_v()),

+ __bkt, __code, __node._M_node);
  __node._M_node = nullptr;
  return __pos->second;
    }


I can't create an example where this causes a problem, because the key
passed to _M_insert_unique_node is never used. So it doesn't matter
that it's been moved from.

So I have to wonder why we just added the key parameter to that
function, if it's never used.


I think you've been influence by my patch. I was using a 
"_NodeAccessor" which wasn't giving access to the node without taking 
owership so I needed to pass the key properly to compute new bucket 
index in case of rehash.


But with your approach this change to the _M_insert_unique_node was 
simply unecessary so here is a patch to cleanup this part.


Ha! I see, thanks. So I should have removed that key_type parameter
again after removing the NodeAccessor stuff.



Ok to commit ?


No, because that would restore the original signature of the
_M_insert_unique_node function, but it has changed contract. Old
callers who expect that function to delete the node would now leak
memory if an exception is thrown.

Oh, yes, abi, I tend to forget even if the recent PR 90920 remind me 
about that, sorry.

If we change the contract of the function we need to change its
mangled name, so that callers expecting the old contract will not use
the new function.

I'll think about the best way to do that ...


Something like what's attached ?

I still use _GLIBCXX_INLINE_VERSION to tag functions that are kept just 
for abi-compatibility.


Ok to commit after having run tests ?

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab579a7059e..2ea75a24f1c 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -693,19 +693,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __node_base*
   _M_get_previous_node(size_type __bkt, __node_base* __n);
 
-  // Insert node __n with key __k and hash code __code, in bucket __bkt
-  // if no rehash (assumes no element with same key already present).
+  // Insert node __n with hash code __code, in bucket __bkt if no
+  // rehash (assumes no element with same key already present).
   // Takes ownership of __n if insertion succeeds, throws otherwise.
   iterator
-  _M_insert_unique_node(const key_type& __k, size_type __bkt,
-			__hash_code __code, __node_type* __n,
-			size_type __n_elt = 1);
+  _M_insert_node(true_type, size_type __bkt, __hash_code,
+		 __node_type* __n, size_type __n_elt = 1);
+
+#if !_GLIBCXX_INLINE_VERSION
+  // Insert node with hash code __code, in bucket bkt if no rehash (assumes
+  // no element with its key already present). Take ownership of the node,
+  // deallocate it on exception.
+  iterator
+  _M_insert_unique_node(size_type __bkt, __hash_code __code,
+			__node_type* __n, size_type __n_elt = 1);
+#endif
 
   // Insert node __n with key __k and hash code __code.
   // Takes ownership of __n if insertion succeeds, throws otherwise.
   iterator
-  _M_insert_multi_node(__node_type* __hint, const key_type& __k,
+  _M_insert_node(false_type, __node_type* __hint,
+		 __hash_code __code, __node_type* __n);
+
+#if !_GLIBCXX_INLINE_VERSION
+  // Insert node with hash code __code. Take ownership of the node,
+  // deallocate it on exception.
+  iterator
+  _M_insert_multi_node(__node_type* __hint,
 			   __hash_code __code, __node_type* __n);
+#endif
 
   template
 	std::pair
@@ -831,7 +847,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	else
 	  {
 		__ret.position
-		  = _M_insert_unique_node(__k, __bkt, __code, __nh._M_ptr);
+		  = _M_insert_node(true_type{}, __bkt, __code, __nh._M_ptr);
 		__nh._M_ptr = nullptr;
 		__ret.inserted = true;
 	  }
@@ -851,7 +867,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	const key_type& __k = __nh._M_key();
 	auto __code = this->_M_hash_code(__k);
 	auto __ret
-	  = _M_insert_multi_node(__hint._M_cur, __k, __code, __nh._M_ptr);
+	  = _M_insert_node(fa

unordered_multimap/unordered_multiset optimizations

2019-06-19 Thread François Dumont

Hi

    Still influenced by PR 68303 this patch:

- Extend usage of find within other methods. It simplify code and will 
allow to implement the PR in less places if we decide to do so.


- Get rid of several bucket index comparison for non-unique key 
containers this way we have less hash code computations.


It also adds _M_update_bbegin cause I plan to propose another 
optimization regarding this special bucket maintenance.



    * include/bits/hashtable_policy.h (_Map_base<>::at): Use
    _Hashtable<>::find.
(_Hashtable_base<>::_Equal_hash_code<>::_S_node_equals):New.
    (_Hashtable_base<>::_M_node_equals): New, use latter.
    * include/bits/hashtable.h (_Hashtable<>::_M_update_bbegin): New.
    (_Hashtable<>::_M_assign): Use latter.
    (_Hashtable<>::_M_move_assign): Likewise.
    (_Hashtable<>(_Hashtable<>&&)): Likewise.
    (_Hashtable<>(_Hashtable<>&&, const allocator_type&)): Likewise.
    (_Hashtable<>::swap): Likewise.
    (_Hashtable<>::find): Build iterator directly from _M_find_node result.
    (_Hashtable<>::count): Use _Hashtable<>::find.
    (_Hashtable<>::equal_range): Likewise.
    (_Hashtable<>::_M_erase(false_type, const key_type&)): Use
    _M_node_equals.

Tested under Linux x86_64, ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index 41edafaa2e3..95c409a4d10 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -359,6 +359,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // numerous checks in the code to avoid 0 modulus.
   __bucket_type		_M_single_bucket	= nullptr;
 
+  void
+  _M_update_bbegin()
+  {
+	if (_M_begin())
+	  _M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  }
+
+  void
+  _M_update_bbegin(__node_type* __n)
+  {
+	_M_before_begin._M_nxt = __n;
+	_M_update_bbegin();
+  }
+
   bool
   _M_uses_single_bucket(__bucket_type* __bkts) const
   { return __builtin_expect(__bkts == &_M_single_bucket, false); }
@@ -1152,8 +1166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__node_type* __ht_n = __ht._M_begin();
 	__node_type* __this_n = __node_gen(__ht_n);
 	this->_M_copy_code(__this_n, __ht_n);
-	_M_before_begin._M_nxt = __this_n;
-	_M_buckets[_M_bucket_index(__this_n)] = &_M_before_begin;
+	_M_update_bbegin(__this_n);
 
 	// Then deal with other nodes.
 	__node_base* __prev_n = __this_n;
@@ -1214,15 +1227,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_buckets = &_M_single_bucket;
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
+
   _M_bucket_count = __ht._M_bucket_count;
   _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
   _M_element_count = __ht._M_element_count;
   std::__alloc_on_move(this->_M_node_allocator(), __ht._M_node_allocator());
 
-  // Fix buckets containing the _M_before_begin pointers that can't be
-  // moved.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be moved.
+  _M_update_bbegin();
   __ht._M_reset();
 }
 
@@ -1293,10 +1305,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  _M_single_bucket = __ht._M_single_bucket;
 	}
 
-  // Update, if necessary, bucket pointing to before begin that hasn't
-  // moved.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be moved.
+  _M_update_bbegin();
 
   __ht._M_reset();
 }
@@ -1348,11 +1358,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  else
 	_M_buckets = __ht._M_buckets;
 
-	  _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
-	  // Update, if necessary, bucket pointing to before begin that hasn't
+	  // Fix bucket containing the _M_before_begin pointer that can't be
 	  // moved.
-	  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
+	  _M_update_bbegin(__ht._M_begin());
+
 	  __ht._M_reset();
 	}
   else
@@ -1420,14 +1429,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   std::swap(_M_element_count, __x._M_element_count);
   std::swap(_M_single_bucket, __x._M_single_bucket);
 
-  // Fix buckets containing the _M_before_begin pointers that can't be
-  // swapped.
-  if (_M_begin())
-	_M_buckets[_M_bucket_index(_M_begin())] = &_M_before_begin;
-
-  if (__x._M_begin())
-	__x._M_buckets[__x._M_bucket_index(__x._M_begin())]
-	  = &__x._M_before_begin;
+  // Fix bucket containing the _M_before_begin pointer that can't be swap.
+  _M_update_bbegin();
+  __x._M_update_bbegin();
 }
 
   template_M_hash_code(__k);
   std::size_t __bkt = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__bkt, __k, __code);
-  return __p ? iterator(__p) : end();
+  return iterator(_M_find_node(__bkt, __k, __code));
 }
 
   template_M_hash_code(__k);
   std::size_t __bkt 

Re: Review Hashtable extract node API

2019-06-18 Thread François Dumont

On 6/18/19 12:54 PM, Jonathan Wakely wrote:

On 18/06/19 07:52 +0200, François Dumont wrote:

A small regression noticed while merging.

We shouldn't keep on using a moved-from key_type instance.

Ok to commit ? Feel free to do it if you prefer, I'll do so at end of 
Europe day otherwise.



diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
b/libstdc++-v3/include/bits/hashtable_policy.h

index f5809c7443a..7e89e1b44c4 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -743,7 +743,8 @@ namespace __detail
std::tuple<>()
  };
  auto __pos
-    = __h->_M_insert_unique_node(__k, __bkt, __code, __node._M_node);
+    = 
__h->_M_insert_unique_node(__h->_M_extract()(__node._M_node->_M_v()),

+ __bkt, __code, __node._M_node);
  __node._M_node = nullptr;
  return __pos->second;
    }


I can't create an example where this causes a problem, because the key
passed to _M_insert_unique_node is never used. So it doesn't matter
that it's been moved from.

So I have to wonder why we just added the key parameter to that
function, if it's never used.


I think you've been influence by my patch. I was using a "_NodeAccessor" 
which wasn't giving access to the node without taking owership so I 
needed to pass the key properly to compute new bucket index in case of 
rehash.


But with your approach this change to the _M_insert_unique_node was 
simply unecessary so here is a patch to cleanup this part.


Ok to commit ?




As far as I can tell, it would only be used for a non-default range
hash function, and I don't care about that. Frankly I find the
policy-based _Hashtable completely unmaintainable and I'd gladly get
rid of all of it that isn't needed for the std::unordered_xxx
containers. The non-standard extensions are not used by anybody,
apparently not tested properly (or this regression should have been
noticed) and make the code too complicated.
I never consider removing this but it would indeed make maintainance 
easy. I'll add to my TODO.


We're adding new parameters that have to be passed around even though
they're never used by 99.999% of users. No wonder the code is only
fast at -O3.







diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab579a7059e..41edafaa2e3 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -693,11 +693,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __node_base*
   _M_get_previous_node(size_type __bkt, __node_base* __n);
 
-  // Insert node __n with key __k and hash code __code, in bucket __bkt
-  // if no rehash (assumes no element with same key already present).
+  // Insert node __n with hash code __code, in bucket __bkt if no
+  // rehash (assumes no element with same key already present).
   // Takes ownership of __n if insertion succeeds, throws otherwise.
   iterator
-  _M_insert_unique_node(const key_type& __k, size_type __bkt,
+  _M_insert_unique_node(size_type __bkt,
 			__hash_code __code, __node_type* __n,
 			size_type __n_elt = 1);
 
@@ -831,7 +831,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	else
 	  {
 		__ret.position
-		  = _M_insert_unique_node(__k, __bkt, __code, __nh._M_ptr);
+		  = _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
 		__nh._M_ptr = nullptr;
 		__ret.inserted = true;
 	  }
@@ -918,8 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  if (_M_find_node(__bkt, __k, __code) == nullptr)
 		{
 		  auto __nh = __src.extract(__pos);
-		  _M_insert_unique_node(__k, __bkt, __code, __nh._M_ptr,
-	__n_elt);
+		  _M_insert_unique_node(__bkt, __code, __nh._M_ptr, __n_elt);
 		  __nh._M_ptr = nullptr;
 		  __n_elt = 1;
 		}
@@ -1671,7 +1670,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return std::make_pair(iterator(__p), false);
 
 	// Insert the node
-	auto __pos = _M_insert_unique_node(__k, __bkt, __code, __node._M_node);
+	auto __pos = _M_insert_unique_node(__bkt, __code, __node._M_node);
 	__node._M_node = nullptr;
 	return { __pos, true };
   }
@@ -1705,9 +1704,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 auto
 _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
 	   _H1, _H2, _Hash, _RehashPolicy, _Traits>::
-_M_insert_unique_node(const key_type& __k, size_type __bkt,
-			  __hash_code __code, __node_type* __node,
-			  size_type __n_elt)
+_M_insert_unique_node(size_type __bkt, __hash_code __code,
+			  __node_type* __node, size_type __n_elt)
 -> iterator
 {
   const __rehash_state& __saved_state = _M_rehash_policy._M_state();
@@ -1718,7 +1716,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   if (__do_rehash.first)
 	{
 	  _M_rehash(__do_rehash.second, __saved_state);
-	  __bkt = _M_bucket_index(__k, __code);
+	  __bkt = _M_bucket_index(this->_M_extract()(__node->_M_v()), __code);
 	}
 
   this->_M_store_code(__node, __

Re: Review Hashtable extract node API

2019-06-17 Thread François Dumont

A small regression noticed while merging.

We shouldn't keep on using a moved-from key_type instance.

Ok to commit ? Feel free to do it if you prefer, I'll do so at end of 
Europe day otherwise.


François


On 6/17/19 12:28 PM, Jonathan Wakely wrote:

On 07/06/19 18:39 +0200, François Dumont wrote:

On 6/5/19 6:22 PM, Jonathan Wakely wrote:

On 04/06/19 19:19 +0200, François Dumont wrote:

Hi

    Here is a patch to enhance the _Hashtable extract node API and 
fix a FIXME request.


    The enhancement to the extract node Api is that extract(const 
key_type&) do not call extract(const_iterator) anymore. Doing so we 
had to loop again through bucket nodes to find the previous node to 
the one to extract. Even if a bucket shall not contain many nodes 
(in unique key mode) it's easy to avoid it.


Nice.

    To fix the FIXME I introduced a node smart pointer type 
managing the node lifetime. The node is extracted from this smart 
pointer only when there can't be any exception raised. In the 
context of the node extract api the node handle is considered as a 
smart pointer. So the node handle will remain owner of the node in 
case of exception when reinserting it, I hope it is the expected 
behavior.


Yes, that's right.

I was going to suggest just using the node handle type instead of
inventing a new smart pointer, but the handle type uses std::optional
so isn't available for C++11/14.


I considered it too, or even use shared_ptr but thought it was overkill.





    * include/bits/hashtable_policy.h
    (struct _NodeSmartPointer<_NodeAlloc>): New.
    (_Map_base<>::operator[](const key_type&)): Use latter, adapt.
    (_Map_base<>::operator[](key_type&&)): Likewise.
    * include/bits/hashtable.h
    (_Hashtable<>::__node_sp_t): New.
    (_Hashtable<>::_M_insert_unique_node(size_type, __hash_code,
    __node_type*, size_type)): Replace by...
(_Hashtable<>::_M_insert_unique_node<_NodeAccessor>(const key_type&,
    size_type, __hash_code, const _NodeAccessor&, size_type)): 
...that.

    (_Hashtable<>::_M_insert_multi_node(__node_type*, __hash_code,
    __node_type*)): Replace by...
(_Hashtable<>::_M_insert_multi_node<_NodeAccessor>(__node_type*,
    __hash_code, const _NodeAccessor&)): ...that.
    (_Hashtable<>::_M_reinsert_node): Adapt.
    (_Hashtable<>::_M_reinsert_node_multi): Adapt.
    (_Hashtable<>::_M_extract_node(size_t, __node_base*)): New.
    (_Hashtable<>::extract(const_iterator)): Use latter.
    (_Hashtable<>::extract(const _Key&)): Likewise.
    (_Hashtable<>::_M_merge_unique): Adapt.
    (_Hashtable<>::_M_emplace<_Args>(true_type, _Args&&...)): Adapt.
(_Hashtable<>::_M_emplace<_Args>(const_iterator, false_type,
    _Args&&...)): Adapt.

Tested under Linux x86_64.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/bits/hashtable.h 
b/libstdc++-v3/include/bits/hashtable.h

index e2e3f016a35..307865b96bf 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -197,6 +197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  using __hash_cached = typename __traits_type::__hash_cached;
  using __node_type = __detail::_Hash_node<_Value, 
__hash_cached::value>;

  using __node_alloc_type = __alloc_rebind<_Alloc, __node_type>;
+  using __node_sp_t = 
__detail::_NodeSmartPointer<__node_alloc_type>;


  using __hashtable_alloc = 
__detail::_Hashtable_alloc<__node_alloc_type>;


@@ -669,18 +670,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  __node_base*
  _M_get_previous_node(size_type __bkt, __node_base* __n);

-  // Insert node with hash code __code, in bucket bkt if no 
rehash (assumes
-  // no element with its key already present). Take ownership 
of the node,

-  // deallocate it on exception.
+  // Insert node with key __k and hash code __code, in bucket 
__bkt if no

+  // rehash (assumes no element with its key already present).
+  template
iterator
-  _M_insert_unique_node(size_type __bkt, __hash_code __code,
-    __node_type* __n, size_type __n_elt = 1);
+    _M_insert_unique_node(const key_type& __k, size_type __bkt,
+  __hash_code __code, const _NodeAccessor&,
+  size_type __n_elt = 1);

-  // Insert node with hash code __code. Take ownership of the 
node,

-  // deallocate it on exception.
+  // Insert node with hash code __code.
+  template
iterator
-  _M_insert_multi_node(__node_type* __hint,
-   __hash_code __code, __node_type* __n);
+    _M_insert_multi_node(__node_type* __hint, __hash_code __code,
+ const _NodeAccessor& __node_accessor);


It looks like most times you call these functions you pass an
identical lambda expression, but each of those lambda expressions will
create a unique type. 

Re: libbacktrace integration for _GLIBCXX_DEBUG mode

2019-06-13 Thread François Dumont

Here is a new proposal which I think take into account all your remarks.

I discovered the great "%.*s" printf format so I was able to do some 
cleanup on the function name without any allocation.


I also agree that counting the '>' or '<' is not reliable so I remove 
this and limit the cleanup to the __cxx1998 namespace and the __ 
uglification, it is still better than nothing.


I complete the doc as advised. I also added a note about making sure 
that _GLIBCXX_DEBUG_BACKTRACE is defined consistently throughout the 
application otherwise it would break the famous ODR rule.


I introduced a _GLIBCXX_DEBUG_USE_LIBBACKTRACE to know when the system 
is able to handle libbacktrace even if the user didn't activate it. I 
could undef if when I am not building the library but I don't remember 
if there is a macro to signal that library is being built ?


Here is an output sample now:

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:177:
In function:
    gnu_debug::basic_string<_CharT, _Traits, 
_Allocator>::basic_string(const

    _CharT*, gnu_debug::basic_string<_CharT, _Traits,
    _Allocator>::size_type, const _Allocator&) [with _CharT = char; 
_Traits

    = std::char_traits; _Allocator = std::allocator;
    gnu_debug::basic_string<_CharT, _Traits, _Allocator>::size_type = long
    unsigned int]

Backtrace:
    0x400afd char const* gnu_debug::check_stringlong>(char const*, unsigned long, char const*, unsigned int, char const*)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:56
    0x400afd char const* gnu_debug::check_stringlong>(char const*, unsigned long, char const*, unsigned int, char const*)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:49
    0x400afd gnu_debug::basic_string, 
std::allocator >::basic_string(char const*, unsigned long, 
std::allocator const&)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/string:177
    0x400afd test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/21_strings/basic_string/debug/1_neg.cc:27
    0x4009c8 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/21_strings/basic_string/debug/1_neg.cc:32

Error: __s != 0 || __n == 0.
XFAIL: 21_strings/basic_string/debug/1_neg.cc execution test

    * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]:
    Include .
    [_GLIBCXX_DEBUG_BACKTRACE && BACKTRACE_SUPPORTED]:
    Include .
    [(!_GLIBCXX_DEBUG_BACKTRACE || !BACKTRACE_SUPPORTED) &&
    _GLIBCXX_USE_C99_STDINT_TR1]: Include .
    [BACKTRACE_SUPPORTED || _GLIBCXX_USE_C99_STDINT_TR1]
    (_GLIBCXX_DEBUG_USE_LIBBACKTRACE): New.
    [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_error_cb): New.
    [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_full_cb): New.
    [_GLIBCXX_DEBUG_USE_LIBBACKTRACE](__backtrace_state): New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_Bt_full_t):
    New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_print_backtrace):
    New.
[_GLIBCXX_DEBUG_USE_LIBBACKTRACE](_Error_formatter::_M_backtrace_state):
    New.
    [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
    (_Error_formatter::_M_backtrace_full_func): New.
    * src/c++11/debug.cc: Include .
    (_Print_func_t): New.
    (print_word): Use '%.*s' format in fprintf to render only expected
    chars.
    (print_raw(PrintContext&, const char*, ptrdiff_t)): New.
    (print_function(PrintContext&, const char*, _Print_func_t)): New.
    (print_type): Use latter.
    (print_string(PrintContext&, const char*, const _Parameter*, size_t)):
    Change signature to...
    (print_string(PrintContext&, const char*, ptrdiff_t, const _Parameter*,
    size_t)): ...this and adapt. Remove intermediate buffer to render input
    string.
    (print_string(PrintContext&, const char*, ptrdiff_t)): New.
    [_GLIBCXX_DEBUG_USE_LIBBACKTRACE]
    (print_backtrace(void*, uintptr_t, const char*, int, const char*)): 
New.

    (_Error_formatter::_M_error()): Adapt.
    * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.
    * doc/xml/manual/using.xml: Likewise.

Tested under Linux x86_64 normal and debug modes.

Ok to commit ?

François


diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 23a5df975a2..ebfc1b89fd7 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -162,6 +162,13 @@ which always works correctly.
   GLIBCXX_DEBUG_MESSAGE_LENGTH can be used to request a
   different length.
 
+Starting with GCC 10 libstdc++ is able to use
+  http://www.w3.org/1999/xlink;
+  xlink:href="https://github.com/ianlancetaylor/libbacktrace;>libbacktrace
+  to produce backtraces on error. Use -D_GLIBCXX_DEBUG_BACKTRACE to
+  activate it. Note that when defined, if libbacktrace is not properly installed,
+  compilation will fail. You'll also have to use -lbacktrace to build
+  your application.
 
 
 Using a Specific Debug Container
diff --git a/libstdc++-v3/doc/xml/manual/using.xml 

Re: [C++ PATCH] Speed up inplace_merge algorithm & fix inefficient logic(PR libstdc++/83938)

2019-06-09 Thread François Dumont

On 12/21/18 9:57 PM, Jonathan Wakely wrote:

On 29/10/18 07:06 +0100, François Dumont wrote:

Hi

    Some feedback regarding this patch ?


Sorry this got missed, please resubmit during stage 1.

You haven't CC'd the original patch author (chang jc) to give them a
chance to comment on your proposed changes to the patch.

The attached PDF on PR libstdc++/83938 has extensive discussion of the
performance issue, but I don't see any for your version. Some
performance benchmarks for your version would be helpful.


Here is this patch again.

This time it is much closer to John's original one, I just kept my 
change on the size of the temporary buffer which doesn't need to be as 
large as it is currently allocated, especially with John's patch.


The performance tests are showing the good improvements, attached are 
the before/after. Surprisingly I do not see any change regarding 
allocated memory, I guess measures are not accurate enough. However 
working on them also show that current implementation is fragile. If you 
reduce the allowed allocated memory too much you end up with a stack 
overflow because of the recursive implementation.


I already have a patch to keep on trying to allocate memory as long as 
above a given threshold rather than 0 but I am also working on making 
implementation non-recursive. We'll see if even a buffer of size 1 is 
better than no buffer at all then.


    PR libstdc++/83938
    * include/bits/stl_algo.h:
    (__merge_adaptive): When buffer too small consider smallest range first
    and cut based on buffer size.
    (__inplace_merge): Take temporary buffer length from smallest range.
    (__stable_sort): Limit temporary buffer length.
    * include/bits/stl_tempbuf.h (get_temporary_buffer): Change function
    to reduce requested buffer length on allocation failure.
    * testsuite/performance/25_algorithms/inplace_merge.cc: New.
    * testsuite/performance/25_algorithms/stable_sort.cc: Rework to allow
    execution with different level of memory.

François

inplace_merge.ccreverse   19r   19u
0s48mem0pf 
inplace_merge.ccforwards   0r0u
0s 0mem0pf 
inplace_merge.ccrandom18r   18u
0s 0mem0pf 
inplace_merge.ccreverse   10r   10u
0s 0mem0pf 
inplace_merge.ccforwards   8r8u
0s 0mem0pf 
inplace_merge.ccrandom22r   22u
0s 0mem0pf 
inplace_merge.ccbench 1/2 memory1891r 1884u
7s   928mem0pf 
inplace_merge.ccreverse   19r   18u
0s 0mem0pf 
inplace_merge.ccforwards   0r0u
0s 0mem0pf 
inplace_merge.ccrandom18r   18u
0s 0mem0pf 
inplace_merge.ccreverse   10r   10u
0s 0mem0pf 
inplace_merge.ccforwards   3r3u
0s 0mem0pf 
inplace_merge.ccrandom22r   22u
0s 0mem0pf 
inplace_merge.ccbench 1/4 memory1867r 1861u
6s   192mem0pf 
inplace_merge.ccreverse   17r   17u
0s 0mem0pf 
inplace_merge.ccforwards   0r0u
0s 0mem0pf 
inplace_merge.ccrandom25r   24u
0s 0mem0pf 
inplace_merge.ccreverse   28r   28u
0s 0mem0pf 
inplace_merge.ccforwards   0r0u
0s 0mem0pf 
inplace_merge.ccrandom   289r  289u
0s 0mem0pf 
inplace_merge.ccbench no memory 2155r 2149u
6s   192mem0pf 
stable_sort.cc  reverse  240r  240u
1s 0mem0pf 
stable_sort.cc  forwards 205r  205u
0s 0mem0pf 
stable_sort.cc  random   493r  493u
0s 0mem0pf 
stable_sort.cc  bench full memory945r  939u
5s   752mem0pf 
stable_sort.cc  reverse  236r  236u
0s 0mem0pf 
stable_sort.cc  forwards 199r  199u
0s 0mem0pf 
stable_sort.cc  random   487r  487u
0s 0mem0pf 
stable_sort.cc  bench 1/2 memory

Re: Review Hashtable extract node API

2019-06-07 Thread François Dumont

On 6/5/19 6:22 PM, Jonathan Wakely wrote:

On 04/06/19 19:19 +0200, François Dumont wrote:

Hi

    Here is a patch to enhance the _Hashtable extract node API and 
fix a FIXME request.


    The enhancement to the extract node Api is that extract(const 
key_type&) do not call extract(const_iterator) anymore. Doing so we 
had to loop again through bucket nodes to find the previous node to 
the one to extract. Even if a bucket shall not contain many nodes (in 
unique key mode) it's easy to avoid it.


Nice.

    To fix the FIXME I introduced a node smart pointer type managing 
the node lifetime. The node is extracted from this smart pointer only 
when there can't be any exception raised. In the context of the node 
extract api the node handle is considered as a smart pointer. So the 
node handle will remain owner of the node in case of exception when 
reinserting it, I hope it is the expected behavior.


Yes, that's right.

I was going to suggest just using the node handle type instead of
inventing a new smart pointer, but the handle type uses std::optional
so isn't available for C++11/14.


I considered it too, or even use shared_ptr but thought it was overkill.





    * include/bits/hashtable_policy.h
    (struct _NodeSmartPointer<_NodeAlloc>): New.
    (_Map_base<>::operator[](const key_type&)): Use latter, adapt.
    (_Map_base<>::operator[](key_type&&)): Likewise.
    * include/bits/hashtable.h
    (_Hashtable<>::__node_sp_t): New.
    (_Hashtable<>::_M_insert_unique_node(size_type, __hash_code,
    __node_type*, size_type)): Replace by...
(_Hashtable<>::_M_insert_unique_node<_NodeAccessor>(const key_type&,
    size_type, __hash_code, const _NodeAccessor&, size_type)): ...that.
    (_Hashtable<>::_M_insert_multi_node(__node_type*, __hash_code,
    __node_type*)): Replace by...
(_Hashtable<>::_M_insert_multi_node<_NodeAccessor>(__node_type*,
    __hash_code, const _NodeAccessor&)): ...that.
    (_Hashtable<>::_M_reinsert_node): Adapt.
    (_Hashtable<>::_M_reinsert_node_multi): Adapt.
    (_Hashtable<>::_M_extract_node(size_t, __node_base*)): New.
    (_Hashtable<>::extract(const_iterator)): Use latter.
    (_Hashtable<>::extract(const _Key&)): Likewise.
    (_Hashtable<>::_M_merge_unique): Adapt.
    (_Hashtable<>::_M_emplace<_Args>(true_type, _Args&&...)): Adapt.
    (_Hashtable<>::_M_emplace<_Args>(const_iterator, false_type,
    _Args&&...)): Adapt.

Tested under Linux x86_64.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/bits/hashtable.h 
b/libstdc++-v3/include/bits/hashtable.h

index e2e3f016a35..307865b96bf 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -197,6 +197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  using __hash_cached = typename __traits_type::__hash_cached;
  using __node_type = __detail::_Hash_node<_Value, 
__hash_cached::value>;

  using __node_alloc_type = __alloc_rebind<_Alloc, __node_type>;
+  using __node_sp_t = 
__detail::_NodeSmartPointer<__node_alloc_type>;


  using __hashtable_alloc = 
__detail::_Hashtable_alloc<__node_alloc_type>;


@@ -669,18 +670,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  __node_base*
  _M_get_previous_node(size_type __bkt, __node_base* __n);

-  // Insert node with hash code __code, in bucket bkt if no 
rehash (assumes
-  // no element with its key already present). Take ownership of 
the node,

-  // deallocate it on exception.
+  // Insert node with key __k and hash code __code, in bucket 
__bkt if no

+  // rehash (assumes no element with its key already present).
+  template
iterator
-  _M_insert_unique_node(size_type __bkt, __hash_code __code,
-    __node_type* __n, size_type __n_elt = 1);
+    _M_insert_unique_node(const key_type& __k, size_type __bkt,
+  __hash_code __code, const _NodeAccessor&,
+  size_type __n_elt = 1);

-  // Insert node with hash code __code. Take ownership of the node,
-  // deallocate it on exception.
+  // Insert node with hash code __code.
+  template
iterator
-  _M_insert_multi_node(__node_type* __hint,
-   __hash_code __code, __node_type* __n);
+    _M_insert_multi_node(__node_type* __hint, __hash_code __code,
+ const _NodeAccessor& __node_accessor);


It looks like most times you call these functions you pass an
identical lambda expression, but each of those lambda expressions will
create a unique type. That means you create different instantiations
of the function templates even though they do exactly the same thing.

That's just generating multiple copies of identical code. Passing in a
function object to provide the node pointer doesn't really seem
necessary anyway, so if it results i

Re: libbacktrace integration for _GLIBCXX_DEBUG mode

2019-06-07 Thread François Dumont
elete >, std::allocatorstd::default_delete > > >;

    }
XFAIL: 23_containers/vector/debug/insert7_neg.cc execution test

AFTER:
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:
In function:
    std::__debug::vector<_Tp, _Allocator>::iterator
    std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,

    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
    _InputIterator =
std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator*, 


    std::vector> >,
    std::__debug::vector >,
    std::random_access_iterator_tag> >;  = 
void; _Tp
    = std::unique_ptr; _Allocator = 
std::allocator

    >; std::__debug::vector<_Tp, _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator*, 


    std::vector> >,
    std::__debug::vector >,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::iterator = 
__gnu_cxx::__normal_iterator*,

    std::vector> >; std::__debug::vector<_Tp,
    _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator*, std::vector> >,
    std::__debug::vector >,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::const_iterator = __gnu_cxx::__normal_iterator*, std::vector> >]

Backtrace:
    0x4029ff 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iteratorstd::default_delete >*, std::vectorstd::default_delete >> >> std::__debug::vectorstd::default_delete 
>>::insert >*, std::vector >> >> >, void>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator > const*, std::vector >> >>, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >*, std::vector >> >> >, std::move_iterator<__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >*, std::vector >> >> >)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606
    0x4030cc test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:34
    0x401458 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/insert7_neg.cc:42

Error: attempt to insert with an iterator range [__first, __last) from this
container.

Objects involved in the operation:
    iterator "__first" @ 0x0x7fffdbf5ea10 {
  type = 
std::move_iterator<__gnu_cxx::__normal_iteratorstd::default_delete >*, std::vectorstd::default_delete >> > > (mutable iterator);

  state = dereferenceable (start-of-sequence);
  references sequence with type 
'std::__debug::vector >>' 
@ 0x0x7fffdbf5edf0

    }
    iterator "__last" @ 0x0x7fffdbf5ea10 {
  type = 
std::move_iterator<__gnu_cxx::__normal_iteratorstd::default_delete >*, std::vectorstd::default_delete >> > > (mutable iterator);

  state = past-the-end;
  references sequence with type 
'std::__debug::vector >>' 
@ 0x0x7fffdbf5edf0

    }
    sequence "this" @ 0x0x7fffdbf5edf0 {
  type = std::__debug::vectorstd::default_delete >>;

    }
XFAIL: 23_containers/vector/debug/insert7_neg.cc execution test

It is just better, not ideal of course. And you are right, current code 
can fail to do the proper thing.


I am clearly not the first one to try such a thing and to struggle to do 
so. So just tell me if you prefer I keep the original labels and I'll do so.


François


On 6/6/19 11:43 PM, Jonathan Wakely wrote:

On 06/06/19 22:33 +0200, François Dumont wrote:

Here is what I come up with.


Regarding allocation in print_function I would also prefer to avoid 
it. But this patch also aim at creating a backtrace_state object in 
case of UB so the alloc is perhaps not so important. I can't use 
string_view as I need to modify it to display only a part of it 


I was only referring to these strings, which allocated memory on every
call to print_function, but you don't modify:

+    const string cxx1998 = "__cxx1998::";
+    const string allocator = ", std::allocator<";
+    const string safe_iterator = "__gnu_debug::_Safe_iterator<";

I see you've changed them now though.

through fsprintf. I could try to use "%.*s" however. I haven't also 
consider your remark about template parameters containing '<' yet.




+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+#  if defined(__has_include) && 
!__has_include()

+#   error No libbacktrace backtrace-supported.h file found.
+#  endif
+#  include 
+# endif
+# if !BACKTRACE_SUPPORTED
+#  error libbacktrace not supported.
+# endif
+# include 
+#else
+# include  // For uintptr_t.


Please use  and std::uintptr_t.


I did so but then realized that to do so I had to be in C++11 mode. I 
used tr1/cstdint in pre-C++11 mode.


Ugh, right, of course, sorry.

Then I guess  is better than relying on TR1 (even though
 isn't technically part of C++98 either).







Re: libbacktrace integration for _GLIBCXX_DEBUG mode

2019-06-06 Thread François Dumont

Here is what I come up with.


Regarding allocation in print_function I would also prefer to avoid it. 
But this patch also aim at creating a backtrace_state object in case of 
UB so the alloc is perhaps not so important. I can't use string_view as 
I need to modify it to display only a part of it through fsprintf. I 
could try to use "%.*s" however. I haven't also consider your remark 
about template parameters containing '<' yet.




+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+#  if defined(__has_include) && !__has_include()
+#   error No libbacktrace backtrace-supported.h file found.
+#  endif
+#  include 
+# endif
+# if !BACKTRACE_SUPPORTED
+#  error libbacktrace not supported.
+# endif
+# include 
+#else
+# include  // For uintptr_t.


Please use  and std::uintptr_t.


I did so but then realized that to do so I had to be in C++11 mode. I 
used tr1/cstdint in pre-C++11 mode.





+// Extracted from libbacktrace.
+typedef void (*backtrace_error_callback) (void*, const char*, int);
+
+typedef int (*backtrace_full_callback) (void*, uintptr_t, const 
char*, int,

+    const char*);


These typedefs should use __reserved_names.


+struct backtrace_state;


Although this one can't use a reserved name, unless we're going to
create opaque wrappers around the libbacktrace type. Introducing t his
non-reserved name means that defining _GLIBCXX_DEBUG makes the library
non-conforming.

It would be possible to avoid declaring this struct, by making
_M_backtrace_state a void* and creating a wrapper function for
backtrace_create_state, and a weak symbol in the library. I'll have to
think about this more 


My main problem was to be able to respect the ODR even when 
!BACKTRACE_SUPPORTED. To do so I eventually realized that I had to limit 
the feature to system where uintptr_t is available which I detect thanks 
to the _GLIBCXX_USE_C99_STDINT_TR1 macro which is used both in  
and .


If you think it is fine I'll document it.

François

diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml b/libstdc++-v3/doc/xml/manual/debug_mode.xml
index 23a5df975a2..680b9d5999d 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -162,6 +162,13 @@ which always works correctly.
   GLIBCXX_DEBUG_MESSAGE_LENGTH can be used to request a
   different length.
 
+Starting with GCC 10 libstdc++ is able to use
+  http://www.w3.org/1999/xlink;
+  xlink:href="https://github.com/ianlancetaylor/libbacktrace;>libbacktrace
+  to produce backtraces on error. Use -D_GLIBCXX_DEBUG_BACKTRACE to
+  activate it. Note that if not properly installed or if libbacktrace is not
+  supported, compilation will fail. You'll also have to use
+  -lbacktrace to build your application.
 
 
 Using a Specific Debug Container
diff --git a/libstdc++-v3/doc/xml/manual/using.xml b/libstdc++-v3/doc/xml/manual/using.xml
index d7fbfe9584d..5769722192c 100644
--- a/libstdc++-v3/doc/xml/manual/using.xml
+++ b/libstdc++-v3/doc/xml/manual/using.xml
@@ -1128,6 +1128,16 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2 hello.cc -o test.exe
 	extensions and libstdc++-specific behavior into errors.
   
 
+_GLIBCXX_DEBUG_BACKTRACE
+
+  
+	Undefined by default. Considered only if _GLIBCXX_DEBUG
+	is defined. When defined, checks for http://www.w3.org/1999/xlink;
+	xlink:href="https://github.com/ianlancetaylor/libbacktrace;>libbacktrace
+	support and use it to display backtraces on
+	debug mode assertions.
+  
+
 _GLIBCXX_PARALLEL
 
   Undefined by default. When defined, compiles user code
@@ -1634,6 +1644,17 @@ A quick read of the relevant part of the GCC
   header will remain compatible between different GCC releases.
 
 
+
+External Libraries
+
+
+  GCC 10 debug mode is able
+  produce backtraces thanks to http://www.w3.org/1999/xlink;
+  xlink:href="https://github.com/ianlancetaylor/libbacktrace;>libbacktrace.
+  To use the library you should define _GLIBCXX_DEBUG_BACKTRACE
+  and link with -lbacktrace.
+
+
   
 
   Concurrency
diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index 220379994c0..9e5962a4744 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -31,6 +31,38 @@
 
 #include 
 
+#if defined(_GLIBCXX_DEBUG_BACKTRACE)
+# if !defined(BACKTRACE_SUPPORTED)
+#  include 
+# endif
+#endif
+
+#if BACKTRACE_SUPPORTED
+# define _GLIBCXX_DEBUG_USE_BACKTRACE 1
+# include 
+typedef backtrace_error_callback __backtrace_error_cb;
+typedef backtrace_full_callback __backtrace_full_cb;
+typedef backtrace_state __backtrace_state;
+#elif defined (_GLIBCXX_USE_C99_STDINT_TR1)
+# define _GLIBCXX_DEBUG_USE_BACKTRACE 1
+
+# if __cplusplus >= 201103L
+#  include  // For std::uintptr_t.
+typedef int (*__backtrace_full_cb) (void*, std::uintptr_t, const char*,
+int, const char*);
+# else
+#  include 

Review Hashtable extract node API

2019-06-04 Thread François Dumont

Hi

    Here is a patch to enhance the _Hashtable extract node API and fix 
a FIXME request.


    The enhancement to the extract node Api is that extract(const 
key_type&) do not call extract(const_iterator) anymore. Doing so we had 
to loop again through bucket nodes to find the previous node to the one 
to extract. Even if a bucket shall not contain many nodes (in unique key 
mode) it's easy to avoid it.


    To fix the FIXME I introduced a node smart pointer type managing 
the node lifetime. The node is extracted from this smart pointer only 
when there can't be any exception raised. In the context of the node 
extract api the node handle is considered as a smart pointer. So the 
node handle will remain owner of the node in case of exception when 
reinserting it, I hope it is the expected behavior.


    * include/bits/hashtable_policy.h
    (struct _NodeSmartPointer<_NodeAlloc>): New.
    (_Map_base<>::operator[](const key_type&)): Use latter, adapt.
    (_Map_base<>::operator[](key_type&&)): Likewise.
    * include/bits/hashtable.h
    (_Hashtable<>::__node_sp_t): New.
    (_Hashtable<>::_M_insert_unique_node(size_type, __hash_code,
    __node_type*, size_type)): Replace by...
(_Hashtable<>::_M_insert_unique_node<_NodeAccessor>(const key_type&,
    size_type, __hash_code, const _NodeAccessor&, size_type)): ...that.
    (_Hashtable<>::_M_insert_multi_node(__node_type*, __hash_code,
    __node_type*)): Replace by...
(_Hashtable<>::_M_insert_multi_node<_NodeAccessor>(__node_type*,
    __hash_code, const _NodeAccessor&)): ...that.
    (_Hashtable<>::_M_reinsert_node): Adapt.
    (_Hashtable<>::_M_reinsert_node_multi): Adapt.
    (_Hashtable<>::_M_extract_node(size_t, __node_base*)): New.
    (_Hashtable<>::extract(const_iterator)): Use latter.
    (_Hashtable<>::extract(const _Key&)): Likewise.
    (_Hashtable<>::_M_merge_unique): Adapt.
    (_Hashtable<>::_M_emplace<_Args>(true_type, _Args&&...)): Adapt.
    (_Hashtable<>::_M_emplace<_Args>(const_iterator, false_type,
    _Args&&...)): Adapt.

Tested under Linux x86_64.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index e2e3f016a35..307865b96bf 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -197,6 +197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   using __hash_cached = typename __traits_type::__hash_cached;
   using __node_type = __detail::_Hash_node<_Value, __hash_cached::value>;
   using __node_alloc_type = __alloc_rebind<_Alloc, __node_type>;
+  using __node_sp_t = __detail::_NodeSmartPointer<__node_alloc_type>;
 
   using __hashtable_alloc = __detail::_Hashtable_alloc<__node_alloc_type>;
 
@@ -669,18 +670,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __node_base*
   _M_get_previous_node(size_type __bkt, __node_base* __n);
 
-  // Insert node with hash code __code, in bucket bkt if no rehash (assumes
-  // no element with its key already present). Take ownership of the node,
-  // deallocate it on exception.
+  // Insert node with key __k and hash code __code, in bucket __bkt if no
+  // rehash (assumes no element with its key already present).
+  template
 	iterator
-  _M_insert_unique_node(size_type __bkt, __hash_code __code,
-			__node_type* __n, size_type __n_elt = 1);
+	_M_insert_unique_node(const key_type& __k, size_type __bkt,
+			  __hash_code __code, const _NodeAccessor&,
+			  size_type __n_elt = 1);
 
-  // Insert node with hash code __code. Take ownership of the node,
-  // deallocate it on exception.
+  // Insert node with hash code __code.
+  template
 	iterator
-  _M_insert_multi_node(__node_type* __hint,
-			   __hash_code __code, __node_type* __n);
+	_M_insert_multi_node(__node_type* __hint, __hash_code __code,
+			 const _NodeAccessor& __node_accessor);
 
   template
 	std::pair
@@ -805,9 +807,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
 	else
 	  {
-		__ret.position
-		  = _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
-		__nh._M_ptr = nullptr;
+		__ret.position = _M_insert_unique_node(__k, __bkt, __code,
+[&__nh]()
+{ return std::exchange(__nh._M_ptr, nullptr); });
 		__ret.inserted = true;
 	  }
 	  }
@@ -818,33 +820,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   iterator
   _M_reinsert_node_multi(const_iterator __hint, node_type&& __nh)
   {
-	iterator __ret;
 	if (__nh.empty())
-	  __ret = end();
-	else
-	  {
+	  return end();
+
 	__glibcxx_assert(get_allocator() == __nh.get_allocator());
 
 	auto __code = this->_M_hash_code(__nh._M_key());
-	auto __node = std::exchange(__nh._M_ptr, nullptr);
-	// FIXME: this deallocates the node on exception.
-	__ret = _M_insert_multi_node(__hint._M_cur, __code, __node);
-	  }
-	return __ret;
+	return _M_insert_multi_node(__hint._M_cur, __code,
+			  [&__nh]()
+			  { return std::exchange(__nh._M_ptr, nullptr); });
   }
 
+   

Re: Hashtable Small size optimization

2019-06-03 Thread François Dumont
Sorry for the mis-understanding but the core of this patch has already 
been committed after your approval here:


https://gcc.gnu.org/ml/libstdc++/2019-05/msg00023.html

It was considered as PR 90277 fix but eventually I also change tests to 
make those implementation-details agnostics.


However the performance test didn't make it, I'll re-submit it in 
another patch more dedicated to small size optimization.


François


On 5/31/19 1:44 PM, Jonathan Wakely wrote:

Re https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00903.html


On 15/10/18 22:46 +0200, François Dumont wrote:

I started considering PR libstdc++/68303.

First thing was to write a dedicated performance test case, it is the 
unordered_small_size.cc I'd like to add with this patch.


Great, more performance tests are always good.

The first runs show a major difference between tr1 and std 
implementations, tr1 being much better:


std::tr1::unordered_set without hash code cached: 1st insert    
   9r    9u    1s  14725920mem    0pf
std::tr1::unordered_set with hash code cached: 1st insert       
8r    9u    0s  14719680mem    0pf
std::unordered_set without hash code cached: 1st insert      
17r   17u    0s  16640080mem    0pf
std::unordered_set with hash code cached: 1st insert      14r   
14u    0s  16638656mem    0pf


I had a look in gdb to find out why and the answer was quite obvious. 
For 20 insertions tr1 implementation bucket count goes through [11, 
23] whereas for std it is [2, 5, 11, 23], so 2 more expensive rehash.


As unordered containers are dedicated to rather important number of 
elements I propose to review the rehash policy with this patch so 
that std also starts at 11 on the 1st insertion. After the patch 
figures are:


std::tr1::unordered_set without hash code cached: 1st insert    
   9r    9u    0s  14725920mem    0pf
std::tr1::unordered_set with hash code cached: 1st insert       
8r    8u    0s  14719680mem    0pf
std::unordered_set without hash code cached: 1st insert      
15r   15u    0s  16640128mem    0pf
std::unordered_set with hash code cached: 1st insert      12r   
12u    0s  16638688mem    0pf


OK, that seems worthwhile then.

Moreover I noticed that performance tests are built with -O2, is that 
intentional ? The std implementation uses more abstractions than tr1, 
looks like building with -O3 optimizes away most of those 
abstractions making tr1 and std implementation much closer:


Yes, I think it's intentional that -O2 is used, because I think
that's the most commonly-used optimisation level. We don't want to
std::tr1::unordered_set without hash code cached: 1st insert    
   2r    1u    1s 14725952mem    0pf
std::tr1::unordered_set with hash code cached: 1st insert       
2r    1u    0s  14719536mem    0pf
std::unordered_set without hash code cached: 1st insert       
2r    2u    0s  16640064mem    0pf
std::unordered_set with hash code cached: 1st insert       2r    
2u    0s  16638608mem    0pf


Hmm, interesting. I wonder if we can determine what is not being
optimized at -O2, and either tweak the code or improve the compiler.

Note that this patch also rework the alternative rehash policy based 
on powers of 2 so that it also starts with a larger number of bucket 
(16) and respects LWG2156.


Last I had to wider the memory column so that alignment is preserved 
even when memory diff is negative.


Tested under Linux x86_64.

Ok to commit ?


Does this patch still apply cleanly? I think it would be good to
commit it now, early in stage 1.







Re: libbacktrace integration for _GLIBCXX_DEBUG mode

2019-05-29 Thread François Dumont

On 5/29/19 12:06 AM, Jonathan Wakely wrote:

On 23/05/19 07:39 +0200, François Dumont wrote:

Hi

    So here what I come up with.

    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 


Thanks for making this opt-in.

it and there is a detectable issue with libbacktrace then I generate 
a compilation error. I want to avoid users defining it but having no 
backtrace in the end in the debug assertion.


Why do you want to avoid that?

This means users can't just define the macro in their makefiles
unconditionally, they have to check if libbacktrace is installed and
supported. That might mean having platform-specific conditionals in
the makefile to only enable it sometimes.

What harm does it do to just ignore the _GLIBCXX_DEBUG_BACKTRACE macro
if backtraces can't be enabled? Or just use #warning instead of
#error?

What I want to avoid is PR  saying that despite _GLIBCXX_DEBUG_BACKTRACE 
being defined there isn't any backtrace displayed in the assertion message.


Maybe I can still fail to compile if I can't include 
backtrace-supported.h to make clear that the -I... option is missing but 
ignore if !BACKTRACE_SUPPORTED. Would it be fine ?





    With this new setup I manage to run testsuite with it like that:

export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/
make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 
-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug


    An example of result:

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606: 


In function:
    std::__debug::vector<_Tp, _Allocator>::iterator
    std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,

    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
    _InputIterator = int*;  = void; _Tp = int;
    _Allocator = std::allocator; std::__debug::vector<_Tp,
    _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >, std::__debug::vector,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::iterator = __gnu_cxx::__normal_iteratorstd::vector

    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >, std::__debug::vector,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::const_iterator = __gnu_cxx::__normal_iteratorint*, std::

    vector >]

Backtrace:
    0x402718 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iteratorstd::vector >> std::__debug::vector::insertvoid>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iteratorconst*, std::vector >>, int*, int*)
/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606 


    0x402718 test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29 


    0x401428 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34 



Error: attempt to insert with an iterator range [__first, __last) 
from this

container.

Objects involved in the operation:
    iterator "__first" @ 0x0x7fff730b96b0 {
  type = int* (mutable iterator);
    }
    iterator "__last" @ 0x0x7fff730b96b8 {
  type = int* (mutable iterator);
    }
    sequence "this" @ 0x0x7fff730b9720 {
  type = std::__debug::vector;
    }


This is nice.


Yes, I forgot to say that I made an effort to clean a little the 
demangle names but I think you saw it already.


I was surprised to see that the content of the __PRETTY_FUNCTION__ macro 
is quite different from the demangling of the same symbol, too bad.




diff --git a/libstdc++-v3/doc/xml/manual/debug_mode.xml 
b/libstdc++-v3/doc/xml/manual/debug_mode.xml

index 570c17ba28a..27873151dae 100644
--- a/libstdc++-v3/doc/xml/manual/debug_mode.xml
+++ b/libstdc++-v3/doc/xml/manual/debug_mode.xml
@@ -104,9 +104,11 @@
The following library components provide extra debugging
  capabilities in debug mode:

+ std::array (no safe 
iterators)
std::basic_string (no safe iterators and 
see note below)

std::bitset
std::deque
+ std::forward_list
std::list
std::map
std::multimap
@@ -160,6 +162,13 @@ which always works correctly.
  GLIBCXX_DEBUG_MESSAGE_LENGTH can be used to request a
  different length.

+Starting with GCC 10 libstdc++ has integrated


I think "integrated" is the wrong word, because you haven't made
libbacktrace and libstdc++ into a single thing. You've just added the
ability for libstdc++ to use libbacktrace.

I suggest "libstdc++ is able to use"


+  http://www.w3.org/1999/xlink;
+ 
xlink:href="https://github.com/ianlan

Re: Hashtable comment cleanups & renamings

2019-05-27 Thread François Dumont
I had miss some occurences of __bucket_hint to replace with 
__bucket_count_hint so here is a new version.


Ok to commit with the simple ChangeLog entry below ?


On 5/21/19 7:42 AM, François Dumont wrote:

Here is a simplified form.

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

Ok to commit ?

François

On 5/17/19 10:24 PM, Jonathan Wakely wrote:

On 17/05/19 18:19 +0200, François Dumont wrote:

Hi

    I got tired of '__n' being used in _Hashtable for many different 
purposes: node, bucket, bucket count, bucket hint. It makes the code 
difficult to read. This code makes sure that __n is a node except is 
some very limited use cases where the method name is clear enough to 
tell what __n means.


    So I'd like to commit this patch which only change that and some 
comments before moving forward to more serious stuff. The only code 
change is a use of auto return type on _M_allocate_node.


    My main concern is the ChangeLog entry. Is the following entry ok ?

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

    Tested under Linux x86_64 (even if it can't be otherwise)

François




@@ -350,24 +347,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  _M_base_alloc() { return *this; }

  __bucket_type*
-  _M_allocate_buckets(size_type __n)
+  _M_allocate_buckets(size_type __bkt_count)


This is a much more helpful name, thanks.


@@ -439,30 +436,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  { }

  explicit
-  _Hashtable(size_type __n,
+  _Hashtable(size_type __bkt_hint,


This seems less helpful. Would __num_bkts_hint be clearer?
Or for consistency, __bkt_count_hint?


@@ -1415,9 +1414,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> iterator
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
-  return __p ? iterator(__p) : end();
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);
+  return __n ? iterator(__n) : end();


Is __n really an improvement over __p here?

If you're changing it, __node or __ptr might be an improvement, but
changing __p to __n seems like unnecessary churn.

I'm not convinced that __n is a big enough improvement over __p to
bother changing dozens of lines, for not much benefit. All those
changes will make it slower to use git blame to track down when thigns
changed, and will make it harder to review diffs between trunk and
older branches.



@@ -1479,17 +1478,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> pair
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);

-  if (__p)
+  if (__n)
{
-  __node_type* __p1 = __p->_M_next();
-  while (__p1 && _M_bucket_index(__p1) == __n
- && this->_M_equals(__k, __code, __p1))
-    __p1 = __p1->_M_next();
+  __node_type* __n1 = __n->_M_next();


__p1 is not a good name, but __n1 is no better.

At least with __p the second pointer could be __q, which is a fairly
idiomatic pairing of letters :-)

How about __first and __last? Or __n and __next?  Even __n1 and __n2
seems better than __n and __n1. Those pointers end up being used for
the 'first' and 'second' members of a pair, so __n1 and __n2 makes
more sense than setting 'first' from __n and 'second' from __n1.

But I don't feel strongly about it, so if it's just me who dislikes
__n and __n1 then it doesn't matter.

diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
b/libstdc++-v3/include/bits/hashtable_policy.h

index a4d2a97f4f3..bb2e7b762ff 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -181,7 +181,7 @@ namespace __detail
   *  @tparam _Cache_hash_code  Boolean value. True if the value of
   *  the hash function is stored along with the value. This is a
   *  time-space tradeoff.  Storing it may improve lookup speed by
-   *  reducing the number of times we need to call the _Equal
+   *  reducing the number of times we need to call the _Hash


Doesn't it reduce both?

In _M_equals we don't bother calling the _Equal predicate if the
cached hash code doesn't match the one for the key we're comparing.







diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab24b5bb537..33711ea4573 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -253,7 +253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_Equal, _H1, _H2, _Hash,
 	_RehashPolicy, _Traits>;
 
-  using __reuse_or_alloc_node_type

Re: libbacktrace integration for _GLIBCXX_DEBUG mode

2019-05-22 Thread François Dumont

Hi

    So here what I come up with.

    _GLIBCXX_DEBUG_BACKTRACE controls the feature. If the user define 
it and there is a detectable issue with libbacktrace then I generate a 
compilation error. I want to avoid users defining it but having no 
backtrace in the end in the debug assertion.


    With this new setup I manage to run testsuite with it like that:

export LD_LIBRARY_PATH=/home/fdt/dev/gcc/install/lib/
make CXXFLAGS='-D_GLIBCXX_DEBUG_BACKTRACE 
-I/home/fdt/dev/gcc/install/include -lbacktrace' check-debug


    An example of result:

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606:
In function:
    std::__debug::vector<_Tp, _Allocator>::iterator
    std::__debug::vector<_Tp, 
_Allocator>::insert(std::__debug::vector<_Tp,

    _Allocator>::const_iterator, _InputIterator, _InputIterator) [with
    _InputIterator = int*;  = void; _Tp = int;
    _Allocator = std::allocator; std::__debug::vector<_Tp,
    _Allocator>::iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >, std::__debug::vector,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::iterator = __gnu_cxx::__normal_iteratorstd::vector

    >; std::__debug::vector<_Tp, _Allocator>::const_iterator =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator >, std::__debug::vector,
    std::random_access_iterator_tag>; typename 
std::iterator_traits
    std::vector<_Tp, _Alloc>::const_iterator>::iterator_category =
    std::random_access_iterator_tag; typename std::vector<_Tp,
    _Alloc>::const_iterator = __gnu_cxx::__normal_iteratorstd::

    vector >]

Backtrace:
    0x402718 
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iteratorstd::vector >> std::__debug::vector::insertvoid>(__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iteratorconst*, std::vector >>, int*, int*)

/home/fdt/dev/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/debug/vector:606
    0x402718 test01()
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:29
    0x401428 main
/home/fdt/dev/gcc/git/libstdc++-v3/testsuite/23_containers/vector/debug/57779_neg.cc:34

Error: attempt to insert with an iterator range [__first, __last) from this
container.

Objects involved in the operation:
    iterator "__first" @ 0x0x7fff730b96b0 {
  type = int* (mutable iterator);
    }
    iterator "__last" @ 0x0x7fff730b96b8 {
  type = int* (mutable iterator);
    }
    sequence "this" @ 0x0x7fff730b9720 {
  type = std::__debug::vector;
    }
XFAIL: 23_containers/vector/debug/57779_neg.cc execution test


    * include/debug/formatter.h [_GLIBCXX_DEBUG_BACKTRACE]: Include
     and .
    [!_GLIBCXX_DEBUG_BACKTRACE]: Include .
    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_error_callback): New.
    [!_GLIBCXX_DEBUG_BACKTRACE](backtrace_full_callback): New.
    [!_GLIBCXX_DEBUG_BACKTRACE](struct backtrace_state): New declaration.
    (_Error_formatter::_Bt_full_t): New function pointer type.
    (_Error_formatter::_M_print_backtrace): New.
    (_Error_formatter::_M_backtrace_state): New.
    (_Error_formatter::_M_backtrace_full_func): New.
    * src/c++11/debug.cc: Include  and .
    (PrintContext::_M_demangle_name): New.
    (_Print_func_t): New.
    (print_word(PrintContext&, const char*)): New.
    (print_raw(PrintContext&, const char*)): New.
    (print_function(PrintContext&, const char*, _Print_func_t)): New.
    (print_type): Use latter.
    (print_string(PrintContext&, const char*)): New.
    (print_backtrace(void*, uintptr_t, const char*, int, const char*)):
    New.
    (_Error_formatter::_M_error()): Adapt.
    * doc/xml/manual/debug_mode.xml: Document _GLIBCXX_DEBUG_BACKTRACE.

Tested under Linux x86_64.

Ok to commit ?

François


On 12/21/18 10:03 PM, Jonathan Wakely wrote:

On 21/12/18 22:47 +0200, Ville Voutilainen wrote:
On Fri, 21 Dec 2018 at 22:35, Jonathan Wakely  
wrote:

>    I also explcitely define BACKTRACE_SUPPORTED to 0 to make sure
>libstdc++ has no libbacktrace dependency after usual build.



I'm concerned about the requirement to link to libbacktrace
explicitly (which will break existing makefiles and build systems that
currently use debug mode in testing).


But see what Francois wrote, "I also explcitely define
BACKTRACE_SUPPORTED to 0 to make sure
libstdc++ has no libbacktrace dependency after usual build."


Yes, but if you happen to install libbacktrace headers, the behaviour
for users building their own code changes. I agree that if you install
those headers, it's probably for a reason, but it might be a different
reason to "so that libstdc++ prints better backtraces".


Also, some of the glibc team pointed out to me that running *any*
extra code after undefined behaviour has been detected is a potential
risk. The less that you do between detecting UB and calling abort(),
the better. Giving the users more 

Re: [PATCH] tbb-backend effective target should check ability to link TBB

2019-05-21 Thread François Dumont

On 5/21/19 10:51 PM, Jonathan Wakely wrote:

On 21/05/19 22:47 +0200, François Dumont wrote:

On 5/21/19 3:50 PM, Jonathan Wakely wrote:

On 20/05/19 21:41 -0700, Thomas Rodgers wrote:

With the addition of "-ltbb" to the v3_target_compile flags (so as to,
you know, actually try to link tbb).

Tested x86_64-linux, committed to trunk.


This didn't work, I still get a FAIL for every pstl test when
tbb.x86_64 and tbb-devel.x86_64 are installed but not tbb.i686.

Adding -v to RUNTESTFLAGS shows -ltbb wasn't being added to the
command, and because the test program didn't actually refer to any TBB
symbols, it still linked successfully.

But the test program still do not refer any TBB symbol. Why is it 
better ?


Because -ltbb means the linker will give an error if there is no
suitable libtbb. That's true even if no symbols from it are needed.
Try it.


Ok, good to know.

Thanks




Re: [PATCH] tbb-backend effective target should check ability to link TBB

2019-05-21 Thread François Dumont

On 5/21/19 3:50 PM, Jonathan Wakely wrote:

On 20/05/19 21:41 -0700, Thomas Rodgers wrote:

With the addition of "-ltbb" to the v3_target_compile flags (so as to,
you know, actually try to link tbb).

Tested x86_64-linux, committed to trunk.


This didn't work, I still get a FAIL for every pstl test when
tbb.x86_64 and tbb-devel.x86_64 are installed but not tbb.i686.

Adding -v to RUNTESTFLAGS shows -ltbb wasn't being added to the
command, and because the test program didn't actually refer to any TBB
symbols, it still linked successfully.

But the test program still do not refer any TBB symbol. Why is it better 
? It looks like it could be a preprocess test.





Re: Hashtable comment cleanups & renamings

2019-05-20 Thread François Dumont

Here is a simplified form.

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

Ok to commit ?

François

On 5/17/19 10:24 PM, Jonathan Wakely wrote:

On 17/05/19 18:19 +0200, François Dumont wrote:

Hi

    I got tired of '__n' being used in _Hashtable for many different 
purposes: node, bucket, bucket count, bucket hint. It makes the code 
difficult to read. This code makes sure that __n is a node except is 
some very limited use cases where the method name is clear enough to 
tell what __n means.


    So I'd like to commit this patch which only change that and some 
comments before moving forward to more serious stuff. The only code 
change is a use of auto return type on _M_allocate_node.


    My main concern is the ChangeLog entry. Is the following entry ok ?

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

    Tested under Linux x86_64 (even if it can't be otherwise)

François




@@ -350,24 +347,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  _M_base_alloc() { return *this; }

  __bucket_type*
-  _M_allocate_buckets(size_type __n)
+  _M_allocate_buckets(size_type __bkt_count)


This is a much more helpful name, thanks.


@@ -439,30 +436,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  { }

  explicit
-  _Hashtable(size_type __n,
+  _Hashtable(size_type __bkt_hint,


This seems less helpful. Would __num_bkts_hint be clearer?
Or for consistency, __bkt_count_hint?


@@ -1415,9 +1414,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> iterator
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
-  return __p ? iterator(__p) : end();
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);
+  return __n ? iterator(__n) : end();


Is __n really an improvement over __p here?

If you're changing it, __node or __ptr might be an improvement, but
changing __p to __n seems like unnecessary churn.

I'm not convinced that __n is a big enough improvement over __p to
bother changing dozens of lines, for not much benefit. All those
changes will make it slower to use git blame to track down when thigns
changed, and will make it harder to review diffs between trunk and
older branches.



@@ -1479,17 +1478,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> pair
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);

-  if (__p)
+  if (__n)
{
-  __node_type* __p1 = __p->_M_next();
-  while (__p1 && _M_bucket_index(__p1) == __n
- && this->_M_equals(__k, __code, __p1))
-    __p1 = __p1->_M_next();
+  __node_type* __n1 = __n->_M_next();


__p1 is not a good name, but __n1 is no better.

At least with __p the second pointer could be __q, which is a fairly
idiomatic pairing of letters :-)

How about __first and __last? Or __n and __next?  Even __n1 and __n2
seems better than __n and __n1. Those pointers end up being used for
the 'first' and 'second' members of a pair, so __n1 and __n2 makes
more sense than setting 'first' from __n and 'second' from __n1.

But I don't feel strongly about it, so if it's just me who dislikes
__n and __n1 then it doesn't matter.

diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
b/libstdc++-v3/include/bits/hashtable_policy.h

index a4d2a97f4f3..bb2e7b762ff 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -181,7 +181,7 @@ namespace __detail
   *  @tparam _Cache_hash_code  Boolean value. True if the value of
   *  the hash function is stored along with the value. This is a
   *  time-space tradeoff.  Storing it may improve lookup speed by
-   *  reducing the number of times we need to call the _Equal
+   *  reducing the number of times we need to call the _Hash


Doesn't it reduce both?

In _M_equals we don't bother calling the _Equal predicate if the
cached hash code doesn't match the one for the key we're comparing.





diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab24b5bb537..444c247a315 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -253,7 +253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_Equal, _H1, _H2, _Hash,
 	_RehashPolicy, _Traits>;
 
-  using __reuse_or_alloc_node_type =
+  using __reuse_or_alloc_node_gen_t =
 	__detail::_ReuseOrAllocNode<__node_alloc_type>;
 
   // Metaprogramming for picking apart hash caching.
@@ -278,9 +278,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VE

Re: [PATCH] improve performance of std::allocator::deallocate

2019-05-20 Thread François Dumont

On 5/20/19 1:14 PM, Jonathan Wakely wrote:

-  r1->deallocate(p, 2);
+  r1->deallocate(p, 2, alignof(char));
+  __builtin_printf("%d\n", (int)bytes_allocated);


Was this last line really intended to be added ?



Re: Hashtable comment cleanups & renamings

2019-05-19 Thread François Dumont

On 5/17/19 10:24 PM, Jonathan Wakely wrote:

On 17/05/19 18:19 +0200, François Dumont wrote:

Hi

    I got tired of '__n' being used in _Hashtable for many different 
purposes: node, bucket, bucket count, bucket hint. It makes the code 
difficult to read. This code makes sure that __n is a node except is 
some very limited use cases where the method name is clear enough to 
tell what __n means.


    So I'd like to commit this patch which only change that and some 
comments before moving forward to more serious stuff. The only code 
change is a use of auto return type on _M_allocate_node.


    My main concern is the ChangeLog entry. Is the following entry ok ?

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

    Tested under Linux x86_64 (even if it can't be otherwise)

François




@@ -350,24 +347,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  _M_base_alloc() { return *this; }

  __bucket_type*
-  _M_allocate_buckets(size_type __n)
+  _M_allocate_buckets(size_type __bkt_count)


This is a much more helpful name, thanks.


@@ -439,30 +436,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  { }

  explicit
-  _Hashtable(size_type __n,
+  _Hashtable(size_type __bkt_hint,


This seems less helpful. Would __num_bkts_hint be clearer?
Or for consistency, __bkt_count_hint?


I thought also about a longer name like this one but then I considered 
that I didn't want to make it too long and that '__bkt_hint' was enough 
know that this parameter was related to the buckets. But I can use 
__bkt_count_hint if you prefer.





@@ -1415,9 +1414,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> iterator
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
-  return __p ? iterator(__p) : end();
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);
+  return __n ? iterator(__n) : end();


Is __n really an improvement over __p here?


Outside any context no but within _Hashtable implementation __n is 
already used most of the time to indicate a node. This is patch just try 
to fix this 'most of the time' part.





If you're changing it, __node or __ptr might be an improvement, but
changing __p to __n seems like unnecessary churn.

I'm not convinced that __n is a big enough improvement over __p to
bother changing dozens of lines, for not much benefit. All those
changes will make it slower to use git blame to track down when thigns
changed, and will make it harder to review diffs between trunk and
older branches.


Yes, this is why I wanted to commit it outside of any real change so 
that this commit can be ignore from any git log or blame more easily.


So I can limit the patch to renaming __n occurences into __bkt, 
__bkt_count_hint, __bkt_count when possible and leave other __n but also 
__p & al untouch.






@@ -1479,17 +1478,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    -> pair
    {
  __hash_code __code = this->_M_hash_code(__k);
-  std::size_t __n = _M_bucket_index(__k, __code);
-  __node_type* __p = _M_find_node(__n, __k, __code);
+  std::size_t __bkt = _M_bucket_index(__k, __code);
+  __node_type* __n = _M_find_node(__bkt, __k, __code);

-  if (__p)
+  if (__n)
{
-  __node_type* __p1 = __p->_M_next();
-  while (__p1 && _M_bucket_index(__p1) == __n
- && this->_M_equals(__k, __code, __p1))
-    __p1 = __p1->_M_next();
+  __node_type* __n1 = __n->_M_next();


__p1 is not a good name, but __n1 is no better.

At least with __p the second pointer could be __q, which is a fairly
idiomatic pairing of letters :-)

How about __first and __last? Or __n and __next?  Even __n1 and __n2
seems better than __n and __n1. Those pointers end up being used for
the 'first' and 'second' members of a pair, so __n1 and __n2 makes
more sense than setting 'first' from __n and 'second' from __n1.

But I don't feel strongly about it, so if it's just me who dislikes
__n and __n1 then it doesn't matter.

diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
b/libstdc++-v3/include/bits/hashtable_policy.h

index a4d2a97f4f3..bb2e7b762ff 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -181,7 +181,7 @@ namespace __detail
   *  @tparam _Cache_hash_code  Boolean value. True if the value of
   *  the hash function is stored along with the value. This is a
   *  time-space tradeoff.  Storing it may improve lookup speed by
-   *  reducing the number of times we need to call the _Equal
+   *  reducing the number of times we need to call the _Hash


Doesn't it reduce both?

In _M_equals we don't bother calling the _Equal predicate if the
cached hash code doesn't match the one for the key we're comparing.




Sure it reduces both, I'll just add _Hash (but first)



Deque rotate on current node

2019-05-19 Thread François Dumont

Hi

  std::deque is supposed to be like a double-queue that you can attack 
from front and back. But currrently its implementation makes it behave 
differently when starting from a fresh deque. If push_back then all goes 
well, it is copy/move to the current internal 'node'. If push_front then 
a new 'node' is created and on next pop_back the initial node will be 
deallocated without having ever be used.


  This patch changes this behavior. As long as deque is empty we can 
play with its state to make it push_back- or push_front-ready. It will 
also benefit to the usage of deque in the std::stack.


  More generally it could really improve scenario in which deque is 
used as queue of elements exchanged between different threads. As you 
need to make sure that consumers are faster than producers then your 
deque should remain empty most of the time and so this proposed patch 
should avoid nodes to be allocated/deallocated all the time.


    * include/bits/deque.tcc (deque<>::_M_push_back_aux):
    Rotate on current node if deque is empty.
    (deue<>::_M_push_front_aux): Likewise.

Tested under Linux x86_64, ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
index 2053afe1d69..245e3e712d8 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -507,6 +507,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   _M_push_back_aux(const value_type& __t)
 #endif
   {
+	if (empty())
+	  {
+	// Move iterators to point to the current node begin.
+	this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_first;
+	this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first;
+#if __cplusplus >= 201103L
+	emplace_back(std::forward<_Args>(__args)...);
+#else
+	push_back(__t);
+#endif
+	return;
+	  }
+
 	if (size() == max_size())
 	  __throw_length_error(
 	  __N("cannot create std::deque larger than max_size()"));
@@ -546,6 +559,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   _M_push_front_aux(const value_type& __t)
 #endif
   {
+	if (empty())
+	  {
+	// Move iterators to point to the current node end.
+	this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_last - 1;
+	this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - 1;
+#if __cplusplus >= 201103L
+	emplace_front(std::forward<_Args>(__args)...);
+#else
+	push_front(__t);
+#endif
+	return;
+	  }
+
 	if (size() == max_size())
 	  __throw_length_error(
 	  __N("cannot create std::deque larger than max_size()"));


Hashtable comment cleanups & renamings

2019-05-17 Thread François Dumont

Hi

    I got tired of '__n' being used in _Hashtable for many different 
purposes: node, bucket, bucket count, bucket hint. It makes the code 
difficult to read. This code makes sure that __n is a node except is 
some very limited use cases where the method name is clear enough to 
tell what __n means.


    So I'd like to commit this patch which only change that and some 
comments before moving forward to more serious stuff. The only code 
change is a use of auto return type on _M_allocate_node.


    My main concern is the ChangeLog entry. Is the following entry ok ?

    Rename variables and cleanup comments.
    * include/bits/hashtable_policy.h
    * include/bits/hashtable.h

    Tested under Linux x86_64 (even if it can't be otherwise)

François

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ab24b5bb537..78e6aeed5b1 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -253,7 +253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_Equal, _H1, _H2, _Hash,
 	_RehashPolicy, _Traits>;
 
-  using __reuse_or_alloc_node_type =
+  using __reuse_or_alloc_node_gen_t =
 	__detail::_ReuseOrAllocNode<__node_alloc_type>;
 
   // Metaprogramming for picking apart hash caching.
@@ -278,9 +278,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		"Cache the hash code or qualify your functors involved"
 		" in hash code and bucket index computation with noexcept");
 
-  // Following two static assertions are necessary to guarantee
-  // that local_iterator will be default constructible.
-
   // When hash codes are cached local iterator inherits from H2 functor
   // which must then be default constructible.
   static_assert(__if_hash_cached>::value,
@@ -331,7 +328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _RehashPolicy		_M_rehash_policy;
 
   // A single bucket used when only need for 1 bucket. Especially
-  // interesting in move semantic to leave hashtable with only 1 buckets
+  // interesting in move semantic to leave hashtable with only 1 bucket
   // which is not allocated so that we can have those operations noexcept
   // qualified.
   // Note that we can't leave hashtable with 0 bucket without adding
@@ -350,24 +347,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _M_base_alloc() { return *this; }
 
   __bucket_type*
-  _M_allocate_buckets(size_type __n)
+  _M_allocate_buckets(size_type __bkt_count)
   {
-	if (__builtin_expect(__n == 1, false))
+	if (__builtin_expect(__bkt_count == 1, false))
 	  {
 	_M_single_bucket = nullptr;
 	return &_M_single_bucket;
 	  }
 
-	return __hashtable_alloc::_M_allocate_buckets(__n);
+	return __hashtable_alloc::_M_allocate_buckets(__bkt_count);
   }
 
   void
-  _M_deallocate_buckets(__bucket_type* __bkts, size_type __n)
+  _M_deallocate_buckets(__bucket_type* __bkts, size_type __bkt_count)
   {
 	if (_M_uses_single_bucket(__bkts))
 	  return;
 
-	__hashtable_alloc::_M_deallocate_buckets(__bkts, __n);
+	__hashtable_alloc::_M_deallocate_buckets(__bkts, __bkt_count);
   }
 
   void
@@ -394,10 +391,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_assign(const _Hashtable&, const _NodeGenerator&);
 
   void
-  _M_move_assign(_Hashtable&&, std::true_type);
+  _M_move_assign(_Hashtable&&, true_type);
 
   void
-  _M_move_assign(_Hashtable&&, std::false_type);
+  _M_move_assign(_Hashtable&&, false_type);
 
   void
   _M_reset() noexcept;
@@ -439,30 +436,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   { }
 
   explicit
-  _Hashtable(size_type __n,
+  _Hashtable(size_type __bkt_hint,
 		 const _H1& __hf = _H1(),
 		 const key_equal& __eql = key_equal(),
 		 const allocator_type& __a = allocator_type())
-  : _Hashtable(__n, __hf, _H2(), _Hash(), __eql,
+  : _Hashtable(__bkt_hint, __hf, _H2(), _Hash(), __eql,
 		   __key_extract(), __a)
   { }
 
   template
 	_Hashtable(_InputIterator __f, _InputIterator __l,
-		   size_type __n = 0,
+		   size_type __bkt_hint = 0,
 		   const _H1& __hf = _H1(),
 		   const key_equal& __eql = key_equal(),
 		   const allocator_type& __a = allocator_type())
-	: _Hashtable(__f, __l, __n, __hf, _H2(), _Hash(), __eql,
+	: _Hashtable(__f, __l, __bkt_hint, __hf, _H2(), _Hash(), __eql,
 		 __key_extract(), __a)
 	{ }
 
   _Hashtable(initializer_list __l,
-		 size_type __n = 0,
+		 size_type __bkt_hint = 0,
 		 const _H1& __hf = _H1(),
 		 const key_equal& __eql = key_equal(),
 		 const allocator_type& __a = allocator_type())
-  : _Hashtable(__l.begin(), __l.end(), __n, __hf, _H2(), _Hash(), __eql,
+  : _Hashtable(__l.begin(), __l.end(), __bkt_hint,
+		   __hf, _H2(), _Hash(), __eql,
 		   __key_extract(), __a)
   { }
 
@@ -485,7 +483,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _Hashtable&
   operator=(initializer_list __l)
   {
-	__reuse_or_alloc_node_type __roan(_M_begin(), 

Re: Deque code cleanup and optimizations

2019-05-16 Thread François Dumont
Here is the simplified patch. I put back the _M_map checks, we'll see 
later if those can be removed.


    * include/bits/stl_deque.h
    (_Deque_iterator<>::__ptr_to): Remove, use std::__ptr_rebind.
    (_Deque_base(_Deque_base&&, const allocator_type&)): New.
    (_Deque_base::_Deque_impl_data): New.
    (_Deque_base::_Deque_impl): Inherit latter.
    (_Deque_base::_Deque_impl::_M_swap_data): Move...
    (_Deque_base::_Deque_impl_data::_M_swap_data): ... here.
    (_Deque_base::_Deque_impl()): Add noexcept qualification.
    (_Deque_base::_Deque_impl(_Deque_impl&&, _Tp_alloc_type&&)): New.
    (_Deque_base::_Deque_impl::_M_get_Tp_allocator()): Remove static_cast.
    (deque<>::deque()): Default.
    (deque<>::deque(deque&&)): Default.
    (deque<>::deque(deque&&, const allocator_type&, false_type)): New.
    (deque<>::deque(deque&&, const allocator_type&, true_type)): New.
    (deque<>::deque(deque&&, const allocator_type&)): Delegate to latters.
    (deque<>::deque<_It>(_It, _It, const allocator_type&)): Use
    _M_range_initialize.
    (deque<>::assign<_It>(_It, _It)): Use _M_assign_aux.
    (deque<>::resize(size_type, const value_type&)): Share a single
    implementation.
    (deque<>::insert<_It>(const_iterator, _It, _It)): Use
    _M_range_insert_aux.
    [__cplusplus >= 201103L](_M_initialize_dispatch): Remove.
    [__cplusplus >= 201103L](_M_assign_dispatch): Remove.
    [__cplusplus >= 201103L](_M_insert_dispatch): Remove.
    * testsuite/23_containers/deque/allocator/default_init.cc: New.

Tested under Linux x86_64.

Ok to commit ?

François

On 5/10/19 3:38 PM, Jonathan Wakely wrote:

This seems generally OK, but ...

On Fri, 10 May 2019, 05:59 François Dumont wrote:

  I remove several _M_map != nullptr checks cause in current
implementation it can't be null. I have several patches following this
one to support it but in this case we will be using a different code path.


You can't remove those checks. If _M_map can ever be null now or in
the future, then we need the checks. Otherwise code compiled today
would break if passed a deque compiled with a future GCC that allows
the map to be null.

I'm curious how you plan to support it though, I don't think it's
possible without an ABI break.


  (_Deque_base::_Deque_impl::_M_move_impl()): Remove _M_impl._M_map
check.

_M_move_impl and the constructor that calls it can be removed
completely, because https://cplusplus.github.io/LWG/issue2593 means
that the same allocator can still be used after moving from it. That
function only exists to handle the case where an allocator changes
value after being moved from.



diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index 358bbda3902..22a7ac8da2e 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -115,15 +115,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   typedef _Tp**	   _Map_pointer;
 #else
 private:
-  template
-	using __ptr_to = typename pointer_traits<_Ptr>::template rebind<_Up>;
   template
-	using __iter = _Deque_iterator<_Tp, _CvTp&, __ptr_to<_CvTp>>;
+	using __iter = _Deque_iterator<_Tp, _CvTp&, __ptr_rebind<_Ptr, _CvTp>>;
 public:
   typedef __iter<_Tp>   iterator;
   typedef __iter   const_iterator;
-  typedef __ptr_to<_Tp>		_Elt_pointer;
-  typedef __ptr_to<_Elt_pointer>	_Map_pointer;
+  typedef __ptr_rebind<_Ptr, _Tp>			   _Elt_pointer;
+  typedef __ptr_rebind<_Ptr, _Elt_pointer>		   _Map_pointer;
 #endif
 
   static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT
@@ -401,7 +399,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	_Map_alloc_type;
   typedef __gnu_cxx::__alloc_traits<_Map_alloc_type> _Map_alloc_traits;
 
-public:
   typedef _Alloc		  allocator_type;
 
   allocator_type
@@ -436,6 +433,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  this->_M_impl._M_swap_data(__x._M_impl);
   }
 
+  _Deque_base(_Deque_base&& __x, const allocator_type& __a)
+  : _M_impl(std::move(__x._M_impl), _Tp_alloc_type(__a))
+  { __x._M_initialize_map(0); }
+
   _Deque_base(_Deque_base&& __x, const allocator_type& __a, size_t __n)
   : _M_impl(__a)
   {
@@ -456,56 +457,73 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
   ~_Deque_base() _GLIBCXX_NOEXCEPT;
 
-protected:
   typedef typename iterator::_Map_pointer _Map_pointer;
 
-  //This struct encapsulates the implementation of the std::deque
-  //standard container and at the same time makes use of the EBO
-  //for empty allocators.
-  struct _Deque_impl
-  : public _Tp_alloc_type
+  struct _Deque_impl_data
   {
 	_Map_pointer _M_map;
 	size_t _M_map_size;
 	iterator _M_s

Re: LWG2593 Move from allocator state is preserved

2019-05-16 Thread François Dumont

2 other tests needed to be adapted in 21_strings. Attached patch applied.

2019-05-17  François Dumont 

    Move from state of allocators (LWG2593)
    * include/bits/stl_deque.h
    (_Deque_base(_Deque_base&&, false_type)): Remove.
    (_Deque_base(_Deque_base&&, true_type)): Remove.
    (_Deque_base(_Deque_base&&)): Adapt.
    (_Deque_base::_M_move_impl()): Remove.
    * testsuite/util/testsuite_allocator.h
    (propagating_allocator(propagating_allocator&&)): Preserve move from
    state.
    * testsuite/23_containers/deque/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/forward_list/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/23_containers/list/allocator/move_assign.cc (test02): 
Adapt.

    * testsuite/23_containers/map/allocator/move_assign.cc (test02): Adapt.
    * testsuite/23_containers/multimap/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/multiset/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/set/allocator/move_assign.cc (test02): Adapt.
    * testsuite/23_containers/unordered_map/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_multimap/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_multiset/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_set/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/vector/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/vector/bool/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/21_strings/basic_string/allocator/char/move_assign.cc
    (test02): Adapt.
    * testsuite/21_strings/basic_string/allocator/wchar_t/move_assign.cc
    (test02): Adapt.


On 5/16/19 11:05 AM, Jonathan Wakely wrote:

On 16/05/19 07:48 +0200, François Dumont wrote:

Hi

    Let's apply this resolution first before moving forward with the 
std::deque implementation.


    Move from state of allocators (LWG2593)
    * include/bits/stl_deque.h
    (_Deque_base(_Deque_base&&, false_type)): Remove.
    (_Deque_base(_Deque_base&&, true_type)): Remove.
    (_Deque_base(_Deque_base&&)): Adapt.
    (_Deque_base::_M_move_impl()): Remove.
    * testsuite/util/testsuite_allocator.h
    (propagating_allocator(propagating_allocator&&)): Preserve move from
    state.
    * testsuite/23_containers/deque/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/forward_list/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/23_containers/list/allocator/move_assign.cc (test02): 
Adapt.
    * testsuite/23_containers/map/allocator/move_assign.cc (test02): 
Adapt.
    * testsuite/23_containers/multimap/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/23_containers/multiset/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/23_containers/set/allocator/move_assign.cc (test02): 
Adapt.

    * testsuite/23_containers/unordered_map/allocator/move_assign.cc
    (test02): Adapt.
    * 
testsuite/23_containers/unordered_multimap/allocator/move_assign.cc

    (test02): Adapt.
    * 
testsuite/23_containers/unordered_multiset/allocator/move_assign.cc

    (test02): Adapt.
    * testsuite/23_containers/unordered_set/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/vector/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/vector/bool/allocator/move_assign.cc 
(test02):

    Adapt.

I only run 23_containers tests with success so far, I'll complete the 
run before committing.


Nice, thanks for doing this.


Ok to commit ?


Yes, although I'd like one change ...

diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h 
b/libstdc++-v3/testsuite/util/testsuite_allocator.h

index d817ac4e838..a98869ed14f 100644
--- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
+++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
@@ -465,12 +465,12 @@ namespace __gnu_test
  return *this;
  }

-  // postcondition: a.get_personality() == 0
+  // postcondition: LWG2593 a.get_personality() un-changed.
  propagating_allocator(propagating_allocator&& a) noexcept
-  : base_alloc()
-  { swap_base(a); }
+  : base_alloc(std::move(a.base()))
+  { /*swap_base(a);*/ }


I don't think we should keep the /*swap_base(a);*/ comment. It just
confusing to have commented-out code that implements an old
behaviour.

OK for trunk with that /*...*/ comment removed.

Thanks again.





diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index c050d1bf023..358bbda3902 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -428,11 +428,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   { /* Caller must initialize map. */ }
 
 #if __cplusplus >= 201103L
-  _

LWG2593 Move from allocator state is preserved

2019-05-15 Thread François Dumont

Hi

    Let's apply this resolution first before moving forward with the 
std::deque implementation.


    Move from state of allocators (LWG2593)
    * include/bits/stl_deque.h
    (_Deque_base(_Deque_base&&, false_type)): Remove.
    (_Deque_base(_Deque_base&&, true_type)): Remove.
    (_Deque_base(_Deque_base&&)): Adapt.
    (_Deque_base::_M_move_impl()): Remove.
    * testsuite/util/testsuite_allocator.h
    (propagating_allocator(propagating_allocator&&)): Preserve move from
    state.
    * testsuite/23_containers/deque/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/forward_list/allocator/move_assign.cc 
(test02):

    Adapt.
    * testsuite/23_containers/list/allocator/move_assign.cc (test02): 
Adapt.

    * testsuite/23_containers/map/allocator/move_assign.cc (test02): Adapt.
    * testsuite/23_containers/multimap/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/multiset/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/set/allocator/move_assign.cc (test02): Adapt.
    * testsuite/23_containers/unordered_map/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_multimap/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_multiset/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/unordered_set/allocator/move_assign.cc
    (test02): Adapt.
    * testsuite/23_containers/vector/allocator/move_assign.cc (test02):
    Adapt.
    * testsuite/23_containers/vector/bool/allocator/move_assign.cc 
(test02):

    Adapt.

I only run 23_containers tests with success so far, I'll complete the 
run before committing.


Ok to commit ?

François


diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index c050d1bf023..358bbda3902 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -428,11 +428,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   { /* Caller must initialize map. */ }
 
 #if __cplusplus >= 201103L
-  _Deque_base(_Deque_base&& __x, false_type)
-  : _M_impl(__x._M_move_impl())
-  { }
-
-  _Deque_base(_Deque_base&& __x, true_type)
+  _Deque_base(_Deque_base&& __x)
   : _M_impl(std::move(__x._M_get_Tp_allocator()))
   {
 	_M_initialize_map(0);
@@ -440,10 +436,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  this->_M_impl._M_swap_data(__x._M_impl);
   }
 
-  _Deque_base(_Deque_base&& __x)
-  : _Deque_base(std::move(__x), typename _Alloc_traits::is_always_equal{})
-  { }
-
   _Deque_base(_Deque_base&& __x, const allocator_type& __a, size_t __n)
   : _M_impl(__a)
   {
@@ -555,29 +547,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   enum { _S_initial_map_size = 8 };
 
   _Deque_impl _M_impl;
-
-#if __cplusplus >= 201103L
-private:
-  _Deque_impl
-  _M_move_impl()
-  {
-	if (!_M_impl._M_map)
-	  return std::move(_M_impl);
-
-	// Create a copy of the current allocator.
-	_Tp_alloc_type __alloc{_M_get_Tp_allocator()};
-	// Put that copy in a moved-from state.
-	_Tp_alloc_type __sink __attribute((__unused__)) {std::move(__alloc)};
-	// Create an empty map that allocates using the moved-from allocator.
-	_Deque_base __empty{__alloc};
-	__empty._M_initialize_map(0);
-	// Now safe to modify current allocator and perform non-throwing swaps.
-	_Deque_impl __ret{std::move(_M_get_Tp_allocator())};
-	_M_impl._M_swap_data(__ret);
-	_M_impl._M_swap_data(__empty._M_impl);
-	return __ret;
-  }
-#endif
 };
 
   template
diff --git a/libstdc++-v3/testsuite/23_containers/deque/allocator/move_assign.cc b/libstdc++-v3/testsuite/23_containers/deque/allocator/move_assign.cc
index 49d9c24ae9e..2eb66af9040 100644
--- a/libstdc++-v3/testsuite/23_containers/deque/allocator/move_assign.cc
+++ b/libstdc++-v3/testsuite/23_containers/deque/allocator/move_assign.cc
@@ -45,7 +45,7 @@ void test02()
   test_type v2(1, alloc_type(2));
   v2 = std::move(v1);
   VERIFY( it == v2.begin() );
-  VERIFY(0 == v1.get_allocator().get_personality());
+  VERIFY(1 == v1.get_allocator().get_personality());
   VERIFY(1 == v2.get_allocator().get_personality());
 }
 
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/allocator/move_assign.cc b/libstdc++-v3/testsuite/23_containers/forward_list/allocator/move_assign.cc
index 44592baf1a6..118ac235391 100644
--- a/libstdc++-v3/testsuite/23_containers/forward_list/allocator/move_assign.cc
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/allocator/move_assign.cc
@@ -48,7 +48,7 @@ void test02()
   test_type v2(alloc_type(2));
   v2.push_front(T());
   v2 = std::move(v1);
-  VERIFY(0 == v1.get_allocator().get_personality());
+  VERIFY(1 == v1.get_allocator().get_personality());
   VERIFY(1 == v2.get_allocator().get_personality());
   VERIFY( it == v2.begin() );
 }
diff --git a/libstdc++-v3/testsuite/23_containers/list/allocator/move_assign.cc 

Re: [PATCH] Remove redundant accessors in hash tables

2019-05-15 Thread François Dumont

On 5/15/19 5:37 PM, Jonathan Wakely wrote:

François,
I noticed that _Hash_code_base and _Hashtable_base have a number of
member functions which are overloaded for const and non-const:

   const _Equal&
   _M_eq() const { return _EqualEBO::_S_cget(*this); }

   _Equal&
   _M_eq() { return _EqualEBO::_S_get(*this); }

The non-const ones seem to be unnecessary. They're used in the _M_swap
member functions, but all other uses could (and probably should) call
the const overload to get a const reference to the function object.

If we make the _M_swap members use the EBO accessors directly then we
can get rid of the non-const accessors. That makes overload resolution
simpler for the compiler (as there's only one function to choose from)
and should result in slightly smaller code when inlining is not
enabled.

Do you see any problem with this patch?


I think it is more a Pavlov behavior, always providing const and 
non-const no matter what.


No problem to simplify this.



Re: Deque code cleanup and optimizations

2019-05-13 Thread François Dumont

On 5/10/19 3:38 PM, Jonathan Wakely wrote:

This seems generally OK, but ...

On Fri, 10 May 2019, 05:59 François Dumont wrote:

  I remove several _M_map != nullptr checks cause in current
implementation it can't be null. I have several patches following this
one to support it but in this case we will be using a different code path.


You can't remove those checks. If _M_map can ever be null now or in
the future, then we need the checks. Otherwise code compiled today
would break if passed a deque compiled with a future GCC that allows
the map to be null.

I'm curious how you plan to support it though, I don't think it's
possible without an ABI break.


No miracle, I plan to propose it only in versioned namespace mode, it 
does break ABI.


But in this implementation those current checks are useless, I'll double 
check.





  (_Deque_base::_Deque_impl::_M_move_impl()): Remove _M_impl._M_map
check.

_M_move_impl and the constructor that calls it can be removed
completely, because https://cplusplus.github.io/LWG/issue2593 means
that the same allocator can still be used after moving from it. That
function only exists to handle the case where an allocator changes
value after being moved from.


Good, I'll cleanup that and propose a new patch.

François



std::vector code cleanup fixes optimizations

2019-05-13 Thread François Dumont

Hi

    This is the patch on vector to:

- Optimize sizeof in Versioned namespace mode. We could go one step 
further by removing _M_p from _M_finish and just transform it into an 
offset but it is a little bit more impacting for the code.


- Implement the swap optimization already done on main std::vector 
template class.


- Fix move constructor so that it is noexcept no matter allocator move 
constructor noexcept qualification


- Optimize move constructor with allocator when allocator type is always 
equal.


- Use shortcuts in C++11 by skipping the _M_XXX_dispatch methods. Those 
are now defined only in pre-C++11 mode, I can't see any abi issue in 
doing so.


    * include/bits/stl_bvector.h
    [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
    _Bit_type*.
    (_Bvector_impl_data(const _Bvector_impl_data&)): Default.
    (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
    (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): Default.
(_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
    (_Bvector_impl_data::_M_reset()): Likewise.
    (_Bvector_impl_data::_M_swap_data): New.
    (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement explicitely.
    (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&, _Bvector_impl&&)): 
New.

    (_Bvector_base::_Bvector_base(_Bvector_base&&, const allocator_type&)):
    New, use latter.
    (vector::vector(vector&&, const allocator_type&, true_type)): New, use
    latter.
    (vector::vector(vector&&, const allocator_type&, false_type)): New.
    (vector::vector(vector&&, const allocator_type&)): Use latters.
    (vector::vector(const vector&, const allocator_type&)): Adapt.
    [__cplusplus >= 201103](vector::vector(_InputIt, _InputIt,
    const allocator_type&)): Use _M_initialize_range.
    (vector::operator[](size_type)): Use iterator operator[].
    (vector::operator[](size_type) const): Use const_iterator operator[].
    (vector::swap(vector&)): Adapt.
    (vector::_M_initialize(size_type)): Add assertions on allocators.
    Use _M_swap_data.
    [__cplusplus >= 201103](vector::insert(const_iterator, _InputIt,
    _InputIt)): Use _M_insert_range.
    [__cplusplus >= 201103](vector::_M_initialize_dispatch): Remove.
    [__cplusplus >= 201103](vector::_M_insert_dispatch): Remove.
    * testsuite/23_containers/vector/bool/allocator/swap.cc: Adapt.
    * testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
    Add check.

Tested under Linux x86_64, normal and debug modes.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index 280d40f60c5..d6fcc2d848d 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -441,7 +441,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
   struct _Bvector_impl_data
   {
+#if !_GLIBCXX_INLINE_VERSION
 	_Bit_iterator	_M_start;
+#else
+	// We don't need the offset field for the start, it's always zero.
+	struct {
+	  _Bit_type* _M_p;
+	  // Allow assignment from iterators (assume offset is zero):
+	  void operator=(_Bit_iterator __it) { _M_p = __it._M_p; }
+	} _M_start;
+#endif
 	_Bit_iterator	_M_finish;
 	_Bit_pointer	_M_end_of_storage;
 
@@ -450,33 +459,38 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	{ }
 
 #if __cplusplus >= 201103L
+	_Bvector_impl_data(const _Bvector_impl_data&) = default;
+	_Bvector_impl_data&
+	operator=(const _Bvector_impl_data&) = default;
+
 	_Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
-	: _M_start(__x._M_start), _M_finish(__x._M_finish)
-	, _M_end_of_storage(__x._M_end_of_storage)
+	: _Bvector_impl_data(__x)
 	{ __x._M_reset(); }
 
 	void
 	_M_move_data(_Bvector_impl_data&& __x) noexcept
 	{
-	  this->_M_start = __x._M_start;
-	  this->_M_finish = __x._M_finish;
-	  this->_M_end_of_storage = __x._M_end_of_storage;
+	  *this = __x;
 	  __x._M_reset();
 	}
 #endif
 
 	void
 	_M_reset() _GLIBCXX_NOEXCEPT
+	{ *this = _Bvector_impl_data(); }
+
+	void
+	_M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
 	{
-	  _M_start = _M_finish = _Bit_iterator();
-	  _M_end_of_storage = _Bit_pointer();
+	  // Do not use std::swap(_M_start, __x._M_start), etc as it loses
+	  // information used by TBAA.
+	  std::swap(*this, __x);
 	}
   };
 
   struct _Bvector_impl
 	: public _Bit_alloc_type, public _Bvector_impl_data
   {
-	public:
 	_Bvector_impl() _GLIBCXX_NOEXCEPT_IF(
 	  is_nothrow_default_constructible<_Bit_alloc_type>::value)
 	: _Bit_alloc_type()
@@ -487,7 +501,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	{ }
 
 #if __cplusplus >= 201103L
-	_Bvector_impl(_Bvector_impl&&) = default;
+	// Not defaulted, to enforce noexcept(true) even when
+	// !is_nothrow_move_constructible<_Bit_alloc_type>.
+	_Bvector_impl(_Bvector_impl&& __x) noexcept
+	: _Bit_alloc_type(std::move(__x)), _Bvector_impl_data(std::move(__x))
+	{ }
+
+	_Bvector_impl(_Bit_alloc_type&& __a, _Bvector_impl&& __x) noexcept
+	: 

Re: Make vector iterator operators hidden friends

2019-05-11 Thread François Dumont

On 5/10/19 3:59 PM, Jonathan Wakely wrote:

On 10/05/19 14:40 +0100, Jonathan Wakely wrote:

On Thu, 9 May 2019 at 06:49, François Dumont wrote:


Hi

 Patch similar to the one I just apply for deque iterator including
NRVO copy ellision fix.

 * include/bits/stl_bvector.h
 (operator==(const _Bit_iterator_base&, const 
_Bit_iterator_base&)):

 Make hidden friend.
 (operator<(const _Bit_iterator_base&, const _Bit_iterator_base&)):
 Likewise.
 (operator!=(const _Bit_iterator_base&, const 
_Bit_iterator_base&)):

 Likewise.
 (operator>(const _Bit_iterator_base&, const _Bit_iterator_base&)):
 Likewise.
 (operator<=(const _Bit_iterator_base&, const 
_Bit_iterator_base&)):

 Likewise.
 (operator>=(const _Bit_iterator_base&, const 
_Bit_iterator_base&)):

 Likewise.
 (operator-(const _Bit_iterator_base&, const _Bit_iterator_base&)):
 Likewise.
 (_Bit_iterator::operator+(difference_type)): Likewise and allow 
NRVO

 copy elision.
 (_Bit_iterator::operator-(difference_type)): Likewise.
 (operator+(ptrdiff_t, const _Bit_iterator&)): Make hidden friend.
 (_Bit_const_iterator::operator+(difference_type)): Likewise and 
allow

 NRVO copy elision.
 (_Bit_const_iterator::operator-(difference_type)): Likewise.
 (operator+(ptrdiff_t, const _Bit_const_iterator&)): Make hidden 
friend.


These const_iterator overloads seem to be missing the NRVO fix.


But the patch looks good otherwise, so OK for trunk with the NRVO
changes. Thanks.


Indeed, I had put it in the ChangeLog but forgotten to adapt code.

Attached patch committed.

François


diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index c60f4f0ef1c..280d40f60c5 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -182,40 +182,40 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   _M_offset = static_cast(__n);
 }
 
-bool
-operator==(const _Bit_iterator_base& __i) const
-{ return _M_p == __i._M_p && _M_offset == __i._M_offset; }
+friend bool
+operator==(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return __x._M_p == __y._M_p && __x._M_offset == __y._M_offset; }
 
-bool
-operator<(const _Bit_iterator_base& __i) const
+friend bool
+operator<(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
 {
-  return _M_p < __i._M_p
-	|| (_M_p == __i._M_p && _M_offset < __i._M_offset);
+  return __x._M_p < __y._M_p
+	|| (__x._M_p == __y._M_p && __x._M_offset < __y._M_offset);
 }
 
-bool
-operator!=(const _Bit_iterator_base& __i) const
-{ return !(*this == __i); }
+friend bool
+operator!=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__x == __y); }
 
-bool
-operator>(const _Bit_iterator_base& __i) const
-{ return __i < *this; }
+friend bool
+operator>(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return __y < __x; }
 
-bool
-operator<=(const _Bit_iterator_base& __i) const
-{ return !(__i < *this); }
+friend bool
+operator<=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__y < __x); }
 
-bool
-operator>=(const _Bit_iterator_base& __i) const
-{ return !(*this < __i); }
-  };
+friend bool
+operator>=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__x < __y); }
 
-  inline ptrdiff_t
-  operator-(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
-  {
-return (int(_S_word_bit) * (__x._M_p - __y._M_p)
-	+ __x._M_offset - __y._M_offset);
-  }
+friend ptrdiff_t
+operator-(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{
+  return (int(_S_word_bit) * (__x._M_p - __y._M_p)
+	  + __x._M_offset - __y._M_offset);
+}
+  };
 
   struct _Bit_iterator : public _Bit_iterator_base
   {
@@ -280,29 +280,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   return *this;
 }
 
-iterator
-operator+(difference_type __i) const
+reference
+operator[](difference_type __i) const
+{ return *(*this + __i); }
+
+friend iterator
+operator+(const iterator& __x, difference_type __n)
 {
-  iterator __tmp = *this;
-  return __tmp += __i;
+  iterator __tmp = __x;
+  __tmp += __n;
+  return __tmp;
 }
 
-iterator
-operator-(difference_type __i) const
+friend iterator
+operator+(difference_type __n, const iterator& __x)
+{ return __x + __n; }
+
+friend iterator
+operator-(const iterator& __x, difference_type __n)
 {
-  iterator __tmp = *this;
-

Deque code cleanup and optimizations

2019-05-09 Thread François Dumont

Hi

    This patch implements a number of simplifications and optimizations 
already done to other containers like std::vector


- Default default and move constructors

- The std::swap optimization

- The construction always equal allocator optimization

- Shortcuts on method calls.

    I remove several _M_map != nullptr checks cause in current 
implementation it can't be null. I have several patches following this 
one to support it but in this case we will be using a different code path.


    Now that we take shortcuts in several methods in C++11 there are 
some that are simply unused in C++11 mode. For the moment I kept them as 
long as we are not in versioned namespace scope in order to maintain abi 
compatibility, I wonder if I really need to consider abi 
backward-compatibility here ?


    * include/bits/stl_deque.h (_Deque_base(_Deque_base&&, false_type)):
    Make private.
    (_Deque_base(_Deque_base&&, true_type)): Likewise. Remove _M_map check.
    (_Deque_base(_Deque_base&&, const allocator_type&)): New.
    (_Deque_base(_Deque_base&&, const allocator_type&, size_t)): Remove
    _M_map check.
    (_Deque_base::_Deque_impl_data): New.
    (_Deque_base::_Deque_impl): Inherit latter.
    (_Deque_base::_Deque_impl::_M_swap_data): Move...
    (_Deque_base::_Deque_impl_data::_M_swap_data): ... here.
    (_Deque_base::_Deque_impl()): Add noexcept qualification.
    (_Deque_base::_Deque_impl(_Deque_impl&&, _Tp_alloc_type&&)): New.
    (_Deque_base::_Deque_impl::_M_get_Tp_allocator()): Remove static_cast.
    (_Deque_base::_Deque_impl::_M_move_impl()): Remove _M_impl._M_map 
check.

    (deque<>::deque()): Default.
    (deque<>::deque(deque&&)): Default.
    (deque<>::deque(deque&&, const allocator_type&, false_type)): New.
    (deque<>::deque(deque&&, const allocator_type&, true_type)): New.
    (deque<>::deque(deque&&, const allocator_type&)): Delegate to latters.
    (deque<>::deque<_It>(_It, _It, const allocator_type&)): Use
    _M_range_initialize.
    (deque<>::assign<_It>(_It, _It)): Use _M_assign_aux.
    (deque<>::resize(size_type, const value_type&)): Share a single
    implementation.
    (deque<>::insert<_It>(const_iterator, _It, _It)): Use
    _M_range_insert_aux.
    [__cplusplus >= 201103L && 
_GLIBCXX_INLINE_VERSION](_M_initialize_dispatch):

    Remove.
    [__cplusplus >= 201103L && 
_GLIBCXX_INLINE_VERSION](_M_assign_dispatch):

    Remove.
    [__cplusplus >= 201103L && 
_GLIBCXX_INLINE_VERSION](_M_insert_dispatch):

    Remove.
    * testsuite/23_containers/deque/allocator/default_init.cc: New.

Tested under Linux x86_64 normal and debug modes.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index c050d1bf023..92c34207baa 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -401,7 +401,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	_Map_alloc_type;
   typedef __gnu_cxx::__alloc_traits<_Map_alloc_type> _Map_alloc_traits;
 
-public:
   typedef _Alloc		  allocator_type;
 
   allocator_type
@@ -428,6 +427,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   { /* Caller must initialize map. */ }
 
 #if __cplusplus >= 201103L
+private:
   _Deque_base(_Deque_base&& __x, false_type)
   : _M_impl(__x._M_move_impl())
   { }
@@ -436,84 +436,100 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   : _M_impl(std::move(__x._M_get_Tp_allocator()))
   {
 	_M_initialize_map(0);
-	if (__x._M_impl._M_map)
 	this->_M_impl._M_swap_data(__x._M_impl);
   }
 
+protected:
   _Deque_base(_Deque_base&& __x)
   : _Deque_base(std::move(__x), typename _Alloc_traits::is_always_equal{})
   { }
 
+  _Deque_base(_Deque_base&& __x, const allocator_type& __a)
+  : _M_impl(std::move(__x._M_impl), _Tp_alloc_type(__a))
+  { __x._M_initialize_map(0); }
+
   _Deque_base(_Deque_base&& __x, const allocator_type& __a, size_t __n)
   : _M_impl(__a)
   {
 	if (__x.get_allocator() == __a)
-	  {
-	if (__x._M_impl._M_map)
 	  {
 	_M_initialize_map(0);
 	this->_M_impl._M_swap_data(__x._M_impl);
 	  }
-	  }
 	else
-	  {
 	  _M_initialize_map(__n);
   }
-  }
 #endif
 
   ~_Deque_base() _GLIBCXX_NOEXCEPT;
 
-protected:
   typedef typename iterator::_Map_pointer _Map_pointer;
 
-  //This struct encapsulates the implementation of the std::deque
-  //standard container and at the same time makes use of the EBO
-  //for empty allocators.
-  struct _Deque_impl
-  : public _Tp_alloc_type
+  struct _Deque_impl_data
   {
 	_Map_pointer _M_map;
 	size_t _M_map_size;
 	iterator _M_start;
 	iterator _M_finish;
 
-	_Deque_impl()
-	: _Tp_alloc_type(), _M_map(), _M_map_size(0),
-	  _M_start(), _M_finish()
+	_Deque_impl_data() _GLIBCXX_NOEXCEPT
+	: _M_map(), _M_map_size(), _M_start(), _M_finish()
+	{ }
+
+#if __cplusplus >= 201103L
+	_Deque_impl_data(const _Deque_impl_data&) = default;
+	_Deque_impl_data&
+	

Make vector iterator operators hidden friends

2019-05-08 Thread François Dumont

Hi

    Patch similar to the one I just apply for deque iterator including 
NRVO copy ellision fix.


    * include/bits/stl_bvector.h
    (operator==(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Make hidden friend.
    (operator<(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (operator!=(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (operator>(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (operator<=(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (operator>=(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (operator-(const _Bit_iterator_base&, const _Bit_iterator_base&)):
    Likewise.
    (_Bit_iterator::operator+(difference_type)): Likewise and allow NRVO
    copy elision.
    (_Bit_iterator::operator-(difference_type)): Likewise.
    (operator+(ptrdiff_t, const _Bit_iterator&)): Make hidden friend.
    (_Bit_const_iterator::operator+(difference_type)): Likewise and allow
    NRVO copy elision.
    (_Bit_const_iterator::operator-(difference_type)): Likewise.
    (operator+(ptrdiff_t, const _Bit_const_iterator&)): Make hidden friend.

Tested under linux x86_64 normal mode.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index c60f4f0ef1c..6b9fb2b42b3 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -182,40 +182,40 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   _M_offset = static_cast(__n);
 }
 
-bool
-operator==(const _Bit_iterator_base& __i) const
-{ return _M_p == __i._M_p && _M_offset == __i._M_offset; }
+friend bool
+operator==(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return __x._M_p == __y._M_p && __x._M_offset == __y._M_offset; }
 
-bool
-operator<(const _Bit_iterator_base& __i) const
+friend bool
+operator<(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
 {
-  return _M_p < __i._M_p
-	|| (_M_p == __i._M_p && _M_offset < __i._M_offset);
+  return __x._M_p < __y._M_p
+	|| (__x._M_p == __y._M_p && __x._M_offset < __y._M_offset);
 }
 
-bool
-operator!=(const _Bit_iterator_base& __i) const
-{ return !(*this == __i); }
+friend bool
+operator!=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__x == __y); }
 
-bool
-operator>(const _Bit_iterator_base& __i) const
-{ return __i < *this; }
+friend bool
+operator>(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return __y < __x; }
 
-bool
-operator<=(const _Bit_iterator_base& __i) const
-{ return !(__i < *this); }
+friend bool
+operator<=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__y < __x); }
 
-bool
-operator>=(const _Bit_iterator_base& __i) const
-{ return !(*this < __i); }
-  };
+friend bool
+operator>=(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{ return !(__x < __y); }
 
-  inline ptrdiff_t
-  operator-(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
-  {
-return (int(_S_word_bit) * (__x._M_p - __y._M_p)
-	+ __x._M_offset - __y._M_offset);
-  }
+friend ptrdiff_t
+operator-(const _Bit_iterator_base& __x, const _Bit_iterator_base& __y)
+{
+  return (int(_S_word_bit) * (__x._M_p - __y._M_p)
+	  + __x._M_offset - __y._M_offset);
+}
+  };
 
   struct _Bit_iterator : public _Bit_iterator_base
   {
@@ -280,29 +280,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   return *this;
 }
 
-iterator
-operator+(difference_type __i) const
+reference
+operator[](difference_type __i) const
+{ return *(*this + __i); }
+
+friend iterator
+operator+(const iterator& __x, difference_type __n)
 {
-  iterator __tmp = *this;
-  return __tmp += __i;
+  iterator __tmp = __x;
+  __tmp += __n;
+  return __tmp;
 }
 
-iterator
-operator-(difference_type __i) const
+friend iterator
+operator+(difference_type __n, const iterator& __x)
+{ return __x + __n; }
+
+friend iterator
+operator-(const iterator& __x, difference_type __n)
 {
-  iterator __tmp = *this;
-  return __tmp -= __i;
+  iterator __tmp = __x;
+  __tmp -= __n;
+  return __tmp;
 }
-
-reference
-operator[](difference_type __i) const
-{ return *(*this + __i); }
   };
 
-  inline _Bit_iterator
-  operator+(ptrdiff_t __n, const _Bit_iterator& __x)
-  { return __x + __n; }
-
   struct _Bit_const_iterator : public _Bit_iterator_base
   {
 typedef bool reference;
@@ -370,29 +372,29 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   return *this;
 }
 
-const_iterator
-operator+(difference_type __i) const
+const_reference
+operator[](difference_type __i) const
+{ 

Re: Make deque iterator operators hidden friends

2019-05-08 Thread François Dumont

Thanks for the tip, nice to know.

Attached patch applied.

François

On 5/8/19 8:28 PM, Jonathan Wakely wrote:

On 08/05/19 18:50 +0200, François Dumont wrote:
Here is a patch to reduce number of operators exposed at std 
namespace scope.


    * include/bits/stl_deque.h
    (_Deque_iterator<>::operator+(difference_type)): Make hidden friend.
    (_Deque_iterator<>::operator-(difference_type)): Likewise.
    (operator==(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator!=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator<(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator<=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator>(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator>=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.

Tested under Linux x86_64 normal/debug modes.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/bits/stl_deque.h 
b/libstdc++-v3/include/bits/stl_deque.h

index 7a7a42aa903..72420c27646 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -238,24 +238,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return *this;
  }

-  _Self
-  operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-    _Self __tmp = *this;
-    return __tmp += __n;
-  }
-
  _Self&
  operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
  { return *this += -__n; }

-  _Self
-  operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-    _Self __tmp = *this;
-    return __tmp -= __n;
-  }
-


These two are already not visible at namespace-scope, but it doesn't
hurt to make them hidden friends for consistency.


+  friend _Self
+  operator+(const _Self& __x, difference_type __n) 
_GLIBCXX_NOEXCEPT

+  {
+    _Self __tmp = __x;
+    return __tmp += __n;


I know you've just reused the original implementation, but it has a
problem that we might as well fix while touching t his code.

This function prevents NRVO copy elision, because __tmp += __n returns
a reference, which gets copied into the return value.

If you write it like this then the copy is elided, and __tmp is
constructed directly in the return value slot:

 _Self __tmp = __x;
 __tmp += __n;
 return __tmp;



+  friend _Self
+  operator-(const _Self& __x, difference_type __n) 
_GLIBCXX_NOEXCEPT

+  {
+    _Self __tmp = __x;
+    return __tmp -= __n;


Same problem here. It would be better to write:

 _Self __tmp = __x;
 __tmp -= __n;
 return __tmp;

OK for trunk with those two changes, thanks.





diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index 7a7a42aa903..c050d1bf023 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -238,24 +238,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	return *this;
   }
 
-  _Self
-  operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-	_Self __tmp = *this;
-	return __tmp += __n;
-  }
-
   _Self&
   operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
   { return *this += -__n; }
 
-  _Self
-  operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-	_Self __tmp = *this;
-	return __tmp -= __n;
-  }
-
   reference
   operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
   { return *(*this + __n); }
@@ -272,123 +258,118 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	_M_first = *__new_node;
 	_M_last = _M_first + difference_type(_S_buffer_size());
   }
-};
-
-  // Note: we also provide overloads whose operands are of the same type in
-  // order to avoid ambiguous overload resolution when std::rel_ops operators
-  // are in scope (for additional details, see libstdc++/3628)
-  template
-inline bool
-operator==(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	   const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return __x._M_cur == __y._M_cur; }
-
-  template
-inline bool
-operator==(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return __x._M_cur == __y._M_cur; }
-
-  template
-inline bool
-operator!=(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	   const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return !(__x == __y); }
-
-  template
-inline bool
-operator!=(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return !(__x == __y); }
-
-  template
-inline bool
-operator<(const _Deque_it

Make deque iterator operators hidden friends

2019-05-08 Thread François Dumont
Here is a patch to reduce number of operators exposed at std namespace 
scope.


    * include/bits/stl_deque.h
    (_Deque_iterator<>::operator+(difference_type)): Make hidden friend.
    (_Deque_iterator<>::operator-(difference_type)): Likewise.
    (operator==(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator!=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator<(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator<=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator>(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.
    (operator>=(const _Deque_iterator<>&, const _Deque_iterator<>&)):
    Likewise.

Tested under Linux x86_64 normal/debug modes.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index 7a7a42aa903..72420c27646 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -238,24 +238,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	return *this;
   }
 
-  _Self
-  operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-	_Self __tmp = *this;
-	return __tmp += __n;
-  }
-
   _Self&
   operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
   { return *this += -__n; }
 
-  _Self
-  operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
-  {
-	_Self __tmp = *this;
-	return __tmp -= __n;
-  }
-
   reference
   operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
   { return *(*this + __n); }
@@ -272,123 +258,116 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	_M_first = *__new_node;
 	_M_last = _M_first + difference_type(_S_buffer_size());
   }
-};
-
-  // Note: we also provide overloads whose operands are of the same type in
-  // order to avoid ambiguous overload resolution when std::rel_ops operators
-  // are in scope (for additional details, see libstdc++/3628)
-  template
-inline bool
-operator==(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	   const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return __x._M_cur == __y._M_cur; }
-
-  template
-inline bool
-operator==(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return __x._M_cur == __y._M_cur; }
 
-  template
-inline bool
-operator!=(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	   const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return !(__x == __y); }
-
-  template
-inline bool
-operator!=(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return !(__x == __y); }
-
-  template
-inline bool
-operator<(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	  const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return (__x._M_node == __y._M_node) ? (__x._M_cur < __y._M_cur)
-	  : (__x._M_node < __y._M_node); }
-
-  template
-inline bool
-operator<(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	  const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return (__x._M_node == __y._M_node) ? (__x._M_cur < __y._M_cur)
-	  : (__x._M_node < __y._M_node); }
-
-  template
-inline bool
-operator>(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	  const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return __y < __x; }
-
-  template
-inline bool
-operator>(const _Deque_iterator<_Tp, _RefL, _PtrL>& __x,
-	  const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
-{ return __y < __x; }
-
-  template
-inline bool
-operator<=(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
-	   const _Deque_iterator<_Tp, _Ref, _Ptr>& __y) _GLIBCXX_NOEXCEPT
-{ return !(__y < __x); }
+  friend bool
+  operator==(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+  { return __x._M_cur == __y._M_cur; }
+
+  // Note: we also provide overloads whose operands are of the same type in
+  // order to avoid ambiguous overload resolution when std::rel_ops operators
+  // are in scope (for additional details, see libstdc++/3628)
+  template
+	friend bool
+	operator==(const _Self& __x,
+		   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
+	{ return __x._M_cur == __y._M_cur; }
+
+  friend bool
+  operator!=(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+  { return !(__x == __y); }
+
+  template
+	friend bool
+	operator!=(const _Self& __x,
+		   const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
+	{ return !(__x == __y); }
+
+  friend bool
+  operator<(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
+  {
+	return (__x._M_node == __y._M_node)
+	  ? (__x._M_cur < __y._M_cur) : (__x._M_node < __y._M_node);
+  }
 
-  template

Re: [Bug libstdc++/90277] Debug Mode test failures

2019-05-06 Thread François Dumont

Hi

    I just prefer to make the tests implementation-agnostic using reserve.

    I check that without the patch to initiate the hashtable with 11 
buckets I reproduce the failures and that with this patch it is fine.


    PR libstdc++/90277
    * testsuite/23_containers/unordered_multiset/insert/24061-multiset.cc
    (test01): Reserve for number of insertions to avoid rehash during test.
    * testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc
    (test01): Likewise.
    * testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc
    (test01): Likewise.
    (test02): Likewise.
    (test03): Likewise.

    I plan to commit it this evening if not told otherwise.

François


On 5/6/19 3:04 PM, redi at gcc dot gnu.org wrote:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90277

--- Comment #4 from Jonathan Wakely  ---
(In reply to François Dumont from comment #3)

As stated in my message this patch can be consider as a fix for this PR as
it reserve buckets for 11 buckets so iterators are not invalidated during
execution of the tests. You shouldn't have anymore failures, do you ?

But maybe you would prefer to make those tests independent of this
implementation detail. I'll do that too.

I think it's OK to rely on the detail as long as the tests make that explicit.
A comment saying that they assume there's no rehashing would be OK, even better
would be to use VERIFY to assert that the count doesn't change. If we make the
tests actually verify there's no rehashing, then it's OK to assume no hashing
:-)

Currently it's just a silent assumption, which isn't even true any more.



diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc
index 717e9208e4a..1f7a590267e 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/24061-multimap.cc
@@ -32,7 +32,8 @@ void test01()
   typedef Mmap::value_type value_type;
 
   Mmap mm1;
-  
+  mm1.reserve(3);
+
   iterator it1 = mm1.insert(mm1.begin(),
 			value_type("all the love in the world", 1));
   VERIFY( mm1.size() == 1 );
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/hint.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/hint.cc
index a71a6a9539a..c38afc57438 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/hint.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/insert/hint.cc
@@ -29,6 +29,7 @@ void test01()
   typedef typename Map::value_type Pair;
 
   Map m;
+  m.reserve(3);
 
   auto it1 = m.insert(Pair(0, 0));
   VERIFY( it1 != m.end() );
@@ -58,6 +59,7 @@ void test02()
   typedef typename Map::value_type Pair;
 
   Map m;
+  m.reserve(5);
 
   auto it1 = m.insert(Pair(0, 0));
   auto it2 = m.insert(it1, Pair(1, 0));
@@ -89,6 +91,7 @@ void test03()
   typedef typename Map::value_type Pair;
 
   Map m;
+  m.reserve(3);
 
   auto it1 = m.insert(Pair(0, 0));
   VERIFY( it1 != m.end() );
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/insert/24061-multiset.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/insert/24061-multiset.cc
index 04833a67188..0c1029f27ff 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/insert/24061-multiset.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/insert/24061-multiset.cc
@@ -31,6 +31,7 @@ void test01()
   typedef Mset::const_iterator const_iterator;
 
   Mset ms1;
+  ms1.reserve(3);
   
   iterator it1 = ms1.insert(ms1.begin(), "all the love in the world");
   VERIFY( ms1.size() == 1 );


<    1   2   3   4   5   6   7   8   9   10   >