diff mbox series

[Bug,libstdc++/70303] Value-initialized debug iterators

Message ID bace2ce3-6ab4-3e98-ca60-c181d3cbd1de@gmail.com
State New
Headers show
Series [Bug,libstdc++/70303] Value-initialized debug iterators | expand

Commit Message

François Dumont Jan. 31, 2021, 3:59 p.m. UTC
After the debug issue has been fixed in PR 98466 the problem was not in 
the debug iterator implementation itself but in the deque iterator 
operator- implementation.

     libstdc++: Make deque iterator operator- usable with value-init 
iterators

     N3644 implies that operator- can be used on value-init iterators. 
We now return
     0 if both iterators are value initialized. If only one is value 
initialized we
     keep the UB by returning the result of a normal computation which 
is an unexpected
     value.

     libstdc++/ChangeLog:

             PR libstdc++/70303
             * include/bits/stl_deque.h 
(std::deque<>::operator-(iterator, iterator)):
             Return 0 if both iterators are value-initialized.
             * testsuite/23_containers/deque/70303.cc: New test.
             * testsuite/23_containers/vector/70303.cc: New test.

Tested under Linux x86_64, ok to commit ?

François

Comments

Jonathan Wakely Feb. 1, 2021, 5:43 p.m. UTC | #1
On 31/01/21 16:59 +0100, François Dumont via Libstdc++ wrote:
>After the debug issue has been fixed in PR 98466 the problem was not 
>in the debug iterator implementation itself but in the deque iterator 
>operator- implementation.
>
>    libstdc++: Make deque iterator operator- usable with value-init 
>iterators
>
>    N3644 implies that operator- can be used on value-init iterators. 
>We now return
>    0 if both iterators are value initialized. If only one is value 
>initialized we
>    keep the UB by returning the result of a normal computation which 
>is an unexpected
>    value.
>
>    libstdc++/ChangeLog:
>
>            PR libstdc++/70303
>            * include/bits/stl_deque.h 
>(std::deque<>::operator-(iterator, iterator)):
>            Return 0 if both iterators are value-initialized.
>            * testsuite/23_containers/deque/70303.cc: New test.
>            * testsuite/23_containers/vector/70303.cc: New test.
>
>Tested under Linux x86_64, ok to commit ?

OK.

I don't like adding the branch there though. Even with the
__builtin_expect it causes worse code to be generated than the
original.

This would be branchless, but a bit harder to understand:

     return difference_type(__x._S_buffer_size())
       * (__x._M_node - __y._M_node - int(__x._M_node == __y._M_node))
       + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);

Please commit the fix and we can think about it later.

>François
>

>diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
>index d41c27717a3..04b70b77621 100644
>--- a/libstdc++-v3/include/bits/stl_deque.h
>+++ b/libstdc++-v3/include/bits/stl_deque.h
>@@ -352,9 +352,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>       friend difference_type
>       operator-(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>       {
>-	return difference_type(_S_buffer_size())
>-	  * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>-	  + (__y._M_last - __y._M_cur);
>+	if (__builtin_expect(__x._M_node || __y._M_node, true))
>+	  return difference_type(_S_buffer_size())
>+	    * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>+	    + (__y._M_last - __y._M_cur);
>+
>+	return 0;
>       }
> 
>       // _GLIBCXX_RESOLVE_LIB_DEFECTS
>@@ -366,9 +369,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> 	operator-(const _Self& __x,
> 		  const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
> 	{
>-	  return difference_type(_S_buffer_size())
>-	    * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>-	    + (__y._M_last - __y._M_cur);
>+	  if (__builtin_expect(__x._M_node || __y._M_node, true))
>+	    return difference_type(_S_buffer_size())
>+	      * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>+	      + (__y._M_last - __y._M_cur);
>+
>+	  return 0;
> 	}
> 
>       friend _Self
>diff --git a/libstdc++-v3/testsuite/23_containers/deque/70303.cc b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
>new file mode 100644
>index 00000000000..e0e63694170
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
>@@ -0,0 +1,67 @@
>+// Copyright (C) 2021 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 }
>+
>+#include <deque>
>+#include <testsuite_hooks.h>
>+
>+// PR libstdc++/70303
>+
>+void test01()
>+{
>+  typedef typename std::deque<int>::iterator It;
>+  It it = It();
>+  VERIFY(it == it);
>+  VERIFY(!(it != it));
>+  VERIFY(it - it == 0);
>+  VERIFY(!(it < it));
>+  VERIFY(!(it > it));
>+  VERIFY(it <= it);
>+  VERIFY(it >= it);
>+
>+  typedef typename std::deque<int>::const_iterator CIt;
>+  CIt cit = CIt();
>+  VERIFY(cit == cit);
>+  VERIFY(!(cit != cit));
>+  VERIFY(cit - cit == 0);
>+  VERIFY(!(cit < cit));
>+  VERIFY(!(cit > cit));
>+  VERIFY(cit <= cit);
>+  VERIFY(cit >= cit);
>+
>+  VERIFY(it == cit);
>+  VERIFY(!(it != cit));
>+  VERIFY(cit == it);
>+  VERIFY(!(cit != it));
>+  VERIFY(it - cit == 0);
>+  VERIFY(cit - it == 0);
>+  VERIFY(!(it < cit));
>+  VERIFY(!(it > cit));
>+  VERIFY(it <= cit);
>+  VERIFY(it >= cit);
>+  VERIFY(!(cit < it));
>+  VERIFY(!(cit > it));
>+  VERIFY(cit <= it);
>+  VERIFY(cit >= it);
>+}
>+
>+int main()
>+{
>+  test01();
>+  return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/23_containers/vector/70303.cc b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
>new file mode 100644
>index 00000000000..af18a0503d8
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
>@@ -0,0 +1,67 @@
>+// Copyright (C) 2021 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 }
>+
>+#include <vector>
>+#include <testsuite_hooks.h>
>+
>+// PR libstdc++/70303
>+
>+void test01()
>+{
>+  typedef typename std::vector<int>::iterator It;
>+  It it = It();
>+  VERIFY(it == it);
>+  VERIFY(!(it != it));
>+  VERIFY(it - it == 0);
>+  VERIFY(!(it < it));
>+  VERIFY(!(it > it));
>+  VERIFY(it <= it);
>+  VERIFY(it >= it);
>+
>+  typedef typename std::vector<int>::const_iterator CIt;
>+  CIt cit = CIt();
>+  VERIFY(cit == cit);
>+  VERIFY(!(cit != cit));
>+  VERIFY(cit - cit == 0);
>+  VERIFY(!(cit < cit));
>+  VERIFY(!(cit > cit));
>+  VERIFY(cit <= cit);
>+  VERIFY(cit >= cit);
>+
>+  VERIFY(it == cit);
>+  VERIFY(!(it != cit));
>+  VERIFY(cit == it);
>+  VERIFY(!(cit != it));
>+  VERIFY(it - cit == 0);
>+  VERIFY(cit - it == 0);
>+  VERIFY(!(it < cit));
>+  VERIFY(!(it > cit));
>+  VERIFY(it <= cit);
>+  VERIFY(it >= cit);
>+  VERIFY(!(cit < it));
>+  VERIFY(!(cit > it));
>+  VERIFY(cit <= it);
>+  VERIFY(cit >= it);
>+}
>+
>+int main()
>+{
>+  test01();
>+  return 0;
>+}
François Dumont Feb. 1, 2021, 6:30 p.m. UTC | #2
On 01/02/21 6:43 pm, Jonathan Wakely wrote:
> On 31/01/21 16:59 +0100, François Dumont via Libstdc++ wrote:
>> After the debug issue has been fixed in PR 98466 the problem was not 
>> in the debug iterator implementation itself but in the deque iterator 
>> operator- implementation.
>>
>>     libstdc++: Make deque iterator operator- usable with value-init 
>> iterators
>>
>>     N3644 implies that operator- can be used on value-init iterators. 
>> We now return
>>     0 if both iterators are value initialized. If only one is value 
>> initialized we
>>     keep the UB by returning the result of a normal computation which 
>> is an unexpected
>>     value.
>>
>>     libstdc++/ChangeLog:
>>
>>             PR libstdc++/70303
>>             * include/bits/stl_deque.h 
>> (std::deque<>::operator-(iterator, iterator)):
>>             Return 0 if both iterators are value-initialized.
>>             * testsuite/23_containers/deque/70303.cc: New test.
>>             * testsuite/23_containers/vector/70303.cc: New test.
>>
>> Tested under Linux x86_64, ok to commit ?
>
> OK.
>
> I don't like adding the branch there though. Even with the
> __builtin_expect it causes worse code to be generated than the
> original.
>
> This would be branchless, but a bit harder to understand:
>
>     return difference_type(__x._S_buffer_size())
>       * (__x._M_node - __y._M_node - int(__x._M_node == __y._M_node))
>       + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>
> Please commit the fix and we can think about it later.

I do not think this work because for value-init iterators we have 
__x._M_node == __y._M_node == nullptr so the diff would give 
-_S_buffer_size().

But I got the idear, I'll prepare the patch.

>
>> François
>>
>
>> diff --git a/libstdc++-v3/include/bits/stl_deque.h 
>> b/libstdc++-v3/include/bits/stl_deque.h
>> index d41c27717a3..04b70b77621 100644
>> --- a/libstdc++-v3/include/bits/stl_deque.h
>> +++ b/libstdc++-v3/include/bits/stl_deque.h
>> @@ -352,9 +352,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>       friend difference_type
>>       operator-(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>>       {
>> -    return difference_type(_S_buffer_size())
>> -      * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>> -      + (__y._M_last - __y._M_cur);
>> +    if (__builtin_expect(__x._M_node || __y._M_node, true))
>> +      return difference_type(_S_buffer_size())
>> +        * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>> +        + (__y._M_last - __y._M_cur);
>> +
>> +    return 0;
>>       }
>>
>>       // _GLIBCXX_RESOLVE_LIB_DEFECTS
>> @@ -366,9 +369,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>     operator-(const _Self& __x,
>>           const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) 
>> _GLIBCXX_NOEXCEPT
>>     {
>> -      return difference_type(_S_buffer_size())
>> -        * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>> -        + (__y._M_last - __y._M_cur);
>> +      if (__builtin_expect(__x._M_node || __y._M_node, true))
>> +        return difference_type(_S_buffer_size())
>> +          * (__x._M_node - __y._M_node - 1) + (__x._M_cur - 
>> __x._M_first)
>> +          + (__y._M_last - __y._M_cur);
>> +
>> +      return 0;
>>     }
>>
>>       friend _Self
>> diff --git a/libstdc++-v3/testsuite/23_containers/deque/70303.cc 
>> b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
>> new file mode 100644
>> index 00000000000..e0e63694170
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
>> @@ -0,0 +1,67 @@
>> +// Copyright (C) 2021 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 }
>> +
>> +#include <deque>
>> +#include <testsuite_hooks.h>
>> +
>> +// PR libstdc++/70303
>> +
>> +void test01()
>> +{
>> +  typedef typename std::deque<int>::iterator It;
>> +  It it = It();
>> +  VERIFY(it == it);
>> +  VERIFY(!(it != it));
>> +  VERIFY(it - it == 0);
>> +  VERIFY(!(it < it));
>> +  VERIFY(!(it > it));
>> +  VERIFY(it <= it);
>> +  VERIFY(it >= it);
>> +
>> +  typedef typename std::deque<int>::const_iterator CIt;
>> +  CIt cit = CIt();
>> +  VERIFY(cit == cit);
>> +  VERIFY(!(cit != cit));
>> +  VERIFY(cit - cit == 0);
>> +  VERIFY(!(cit < cit));
>> +  VERIFY(!(cit > cit));
>> +  VERIFY(cit <= cit);
>> +  VERIFY(cit >= cit);
>> +
>> +  VERIFY(it == cit);
>> +  VERIFY(!(it != cit));
>> +  VERIFY(cit == it);
>> +  VERIFY(!(cit != it));
>> +  VERIFY(it - cit == 0);
>> +  VERIFY(cit - it == 0);
>> +  VERIFY(!(it < cit));
>> +  VERIFY(!(it > cit));
>> +  VERIFY(it <= cit);
>> +  VERIFY(it >= cit);
>> +  VERIFY(!(cit < it));
>> +  VERIFY(!(cit > it));
>> +  VERIFY(cit <= it);
>> +  VERIFY(cit >= it);
>> +}
>> +
>> +int main()
>> +{
>> +  test01();
>> +  return 0;
>> +}
>> diff --git a/libstdc++-v3/testsuite/23_containers/vector/70303.cc 
>> b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
>> new file mode 100644
>> index 00000000000..af18a0503d8
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
>> @@ -0,0 +1,67 @@
>> +// Copyright (C) 2021 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 }
>> +
>> +#include <vector>
>> +#include <testsuite_hooks.h>
>> +
>> +// PR libstdc++/70303
>> +
>> +void test01()
>> +{
>> +  typedef typename std::vector<int>::iterator It;
>> +  It it = It();
>> +  VERIFY(it == it);
>> +  VERIFY(!(it != it));
>> +  VERIFY(it - it == 0);
>> +  VERIFY(!(it < it));
>> +  VERIFY(!(it > it));
>> +  VERIFY(it <= it);
>> +  VERIFY(it >= it);
>> +
>> +  typedef typename std::vector<int>::const_iterator CIt;
>> +  CIt cit = CIt();
>> +  VERIFY(cit == cit);
>> +  VERIFY(!(cit != cit));
>> +  VERIFY(cit - cit == 0);
>> +  VERIFY(!(cit < cit));
>> +  VERIFY(!(cit > cit));
>> +  VERIFY(cit <= cit);
>> +  VERIFY(cit >= cit);
>> +
>> +  VERIFY(it == cit);
>> +  VERIFY(!(it != cit));
>> +  VERIFY(cit == it);
>> +  VERIFY(!(cit != it));
>> +  VERIFY(it - cit == 0);
>> +  VERIFY(cit - it == 0);
>> +  VERIFY(!(it < cit));
>> +  VERIFY(!(it > cit));
>> +  VERIFY(it <= cit);
>> +  VERIFY(it >= cit);
>> +  VERIFY(!(cit < it));
>> +  VERIFY(!(cit > it));
>> +  VERIFY(cit <= it);
>> +  VERIFY(cit >= it);
>> +}
>> +
>> +int main()
>> +{
>> +  test01();
>> +  return 0;
>> +}
>
Jonathan Wakely Feb. 1, 2021, 7:09 p.m. UTC | #3
On 01/02/21 19:30 +0100, François Dumont via Libstdc++ wrote:
>On 01/02/21 6:43 pm, Jonathan Wakely wrote:
>>On 31/01/21 16:59 +0100, François Dumont via Libstdc++ wrote:
>>>After the debug issue has been fixed in PR 98466 the problem was 
>>>not in the debug iterator implementation itself but in the deque 
>>>iterator operator- implementation.
>>>
>>>    libstdc++: Make deque iterator operator- usable with 
>>>value-init iterators
>>>
>>>    N3644 implies that operator- can be used on value-init 
>>>iterators. We now return
>>>    0 if both iterators are value initialized. If only one is 
>>>value initialized we
>>>    keep the UB by returning the result of a normal computation 
>>>which is an unexpected
>>>    value.
>>>
>>>    libstdc++/ChangeLog:
>>>
>>>            PR libstdc++/70303
>>>            * include/bits/stl_deque.h 
>>>(std::deque<>::operator-(iterator, iterator)):
>>>            Return 0 if both iterators are value-initialized.
>>>            * testsuite/23_containers/deque/70303.cc: New test.
>>>            * testsuite/23_containers/vector/70303.cc: New test.
>>>
>>>Tested under Linux x86_64, ok to commit ?
>>
>>OK.
>>
>>I don't like adding the branch there though. Even with the
>>__builtin_expect it causes worse code to be generated than the
>>original.
>>
>>This would be branchless, but a bit harder to understand:
>>
>>    return difference_type(__x._S_buffer_size())
>>      * (__x._M_node - __y._M_node - int(__x._M_node == __y._M_node))
>>      + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>>
>>Please commit the fix and we can think about it later.
>
>I do not think this work because for value-init iterators we have 
>__x._M_node == __y._M_node == nullptr so the diff would give 
>-_S_buffer_size().
>
>But I got the idear, I'll prepare the patch.

Yeah, I just came back to the computer to say that it's wrong. But
maybe this:

    return difference_type(_S_buffer_size())
      * (__x._M_node - __y._M_node - int(__x._M_node && __y._M_node))
      + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);

For value-init'd iterators we'd get _S_buffer_size() * 0 + 0 - 0

We could even just use int(__x._M_node != 0) because if one is null
and the other isn't, it's UB anyway.
François Dumont Feb. 8, 2021, 9:27 p.m. UTC | #4
On 01/02/21 8:09 pm, Jonathan Wakely wrote:
> On 01/02/21 19:30 +0100, François Dumont via Libstdc++ wrote:
>> On 01/02/21 6:43 pm, Jonathan Wakely wrote:
>>> On 31/01/21 16:59 +0100, François Dumont via Libstdc++ wrote:
>>>> After the debug issue has been fixed in PR 98466 the problem was 
>>>> not in the debug iterator implementation itself but in the deque 
>>>> iterator operator- implementation.
>>>>
>>>>     libstdc++: Make deque iterator operator- usable with value-init 
>>>> iterators
>>>>
>>>>     N3644 implies that operator- can be used on value-init 
>>>> iterators. We now return
>>>>     0 if both iterators are value initialized. If only one is value 
>>>> initialized we
>>>>     keep the UB by returning the result of a normal computation 
>>>> which is an unexpected
>>>>     value.
>>>>
>>>>     libstdc++/ChangeLog:
>>>>
>>>>             PR libstdc++/70303
>>>>             * include/bits/stl_deque.h 
>>>> (std::deque<>::operator-(iterator, iterator)):
>>>>             Return 0 if both iterators are value-initialized.
>>>>             * testsuite/23_containers/deque/70303.cc: New test.
>>>>             * testsuite/23_containers/vector/70303.cc: New test.
>>>>
>>>> Tested under Linux x86_64, ok to commit ?
>>>
>>> OK.
>>>
>>> I don't like adding the branch there though. Even with the
>>> __builtin_expect it causes worse code to be generated than the
>>> original.
>>>
>>> This would be branchless, but a bit harder to understand:
>>>
>>>     return difference_type(__x._S_buffer_size())
>>>       * (__x._M_node - __y._M_node - int(__x._M_node == __y._M_node))
>>>       + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>>>
>>> Please commit the fix and we can think about it later.
>>
>> I do not think this work because for value-init iterators we have 
>> __x._M_node == __y._M_node == nullptr so the diff would give 
>> -_S_buffer_size().
>>
>> But I got the idear, I'll prepare the patch.
>
> Yeah, I just came back to the computer to say that it's wrong. But
> maybe this:
>
>     return difference_type(_S_buffer_size())
>       * (__x._M_node - __y._M_node - int(__x._M_node && __y._M_node))
>       + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>
> For value-init'd iterators we'd get _S_buffer_size() * 0 + 0 - 0
>
> We could even just use int(__x._M_node != 0) because if one is null
> and the other isn't, it's UB anyway.
>
>
This is what I've tested with success. As it is a recent kind of 
regression you might want to consider it now.

     libstdc++: Remove execution branch in deque iterator operator-

     libstdc++-v3/ChangeLog:

             * include/bits/stl_deque.h
             (std::operator-(deque::iterator, deque::iterator)): Replace 
if/then with
             a null pointer test.

Ok to commit ?

François
Jonathan Wakely Feb. 9, 2021, 11:15 a.m. UTC | #5
On 08/02/21 22:27 +0100, François Dumont wrote:
>On 01/02/21 8:09 pm, Jonathan Wakely wrote:
>>On 01/02/21 19:30 +0100, François Dumont via Libstdc++ wrote:
>>>On 01/02/21 6:43 pm, Jonathan Wakely wrote:
>>>>On 31/01/21 16:59 +0100, François Dumont via Libstdc++ wrote:
>>>>>After the debug issue has been fixed in PR 98466 the problem 
>>>>>was not in the debug iterator implementation itself but in the 
>>>>>deque iterator operator- implementation.
>>>>>
>>>>>    libstdc++: Make deque iterator operator- usable with 
>>>>>value-init iterators
>>>>>
>>>>>    N3644 implies that operator- can be used on value-init 
>>>>>iterators. We now return
>>>>>    0 if both iterators are value initialized. If only one is 
>>>>>value initialized we
>>>>>    keep the UB by returning the result of a normal 
>>>>>computation which is an unexpected
>>>>>    value.
>>>>>
>>>>>    libstdc++/ChangeLog:
>>>>>
>>>>>            PR libstdc++/70303
>>>>>            * include/bits/stl_deque.h 
>>>>>(std::deque<>::operator-(iterator, iterator)):
>>>>>            Return 0 if both iterators are value-initialized.
>>>>>            * testsuite/23_containers/deque/70303.cc: New test.
>>>>>            * testsuite/23_containers/vector/70303.cc: New test.
>>>>>
>>>>>Tested under Linux x86_64, ok to commit ?
>>>>
>>>>OK.
>>>>
>>>>I don't like adding the branch there though. Even with the
>>>>__builtin_expect it causes worse code to be generated than the
>>>>original.
>>>>
>>>>This would be branchless, but a bit harder to understand:
>>>>
>>>>    return difference_type(__x._S_buffer_size())
>>>>      * (__x._M_node - __y._M_node - int(__x._M_node == __y._M_node))
>>>>      + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>>>>
>>>>Please commit the fix and we can think about it later.
>>>
>>>I do not think this work because for value-init iterators we have 
>>>__x._M_node == __y._M_node == nullptr so the diff would give 
>>>-_S_buffer_size().
>>>
>>>But I got the idear, I'll prepare the patch.
>>
>>Yeah, I just came back to the computer to say that it's wrong. But
>>maybe this:
>>
>>    return difference_type(_S_buffer_size())
>>      * (__x._M_node - __y._M_node - int(__x._M_node && __y._M_node))
>>      + (__x._M_cur - __x._M_first) + (__y._M_last - __y._M_cur);
>>
>>For value-init'd iterators we'd get _S_buffer_size() * 0 + 0 - 0
>>
>>We could even just use int(__x._M_node != 0) because if one is null
>>and the other isn't, it's UB anyway.
>>
>>
>This is what I've tested with success. As it is a recent kind of 
>regression you might want to consider it now.
>
>    libstdc++: Remove execution branch in deque iterator operator-
>
>    libstdc++-v3/ChangeLog:
>
>            * include/bits/stl_deque.h
>            (std::operator-(deque::iterator, deque::iterator)): 
>Replace if/then with
>            a null pointer test.
>
>Ok to commit ?

OK, thanks!


>François
>

>diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
>index 04b70b77621..8bba7a3740f 100644
>--- a/libstdc++-v3/include/bits/stl_deque.h
>+++ b/libstdc++-v3/include/bits/stl_deque.h
>@@ -352,12 +352,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>       friend difference_type
>       operator-(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
>       {
>-	if (__builtin_expect(__x._M_node || __y._M_node, true))
>-	  return difference_type(_S_buffer_size())
>-	    * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>-	    + (__y._M_last - __y._M_cur);
>-
>-	return 0;
>+	return difference_type(_S_buffer_size())
>+	  * (__x._M_node - __y._M_node - int(__x._M_node != 0))
>+	  + (__x._M_cur - __x._M_first)
>+	  + (__y._M_last - __y._M_cur);
>       }
> 
>       // _GLIBCXX_RESOLVE_LIB_DEFECTS
>@@ -369,12 +367,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> 	operator-(const _Self& __x,
> 		  const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
> 	{
>-	  if (__builtin_expect(__x._M_node || __y._M_node, true))
>-	    return difference_type(_S_buffer_size())
>-	      * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
>-	      + (__y._M_last - __y._M_cur);
>-
>-	  return 0;
>+	  return difference_type(_S_buffer_size())
>+	    * (__x._M_node - __y._M_node - int(__x._M_node != 0))
>+	    + (__x._M_cur - __x._M_first)
>+	    + (__y._M_last - __y._M_cur);
> 	}
> 
>       friend _Self
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h
index d41c27717a3..04b70b77621 100644
--- a/libstdc++-v3/include/bits/stl_deque.h
+++ b/libstdc++-v3/include/bits/stl_deque.h
@@ -352,9 +352,12 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       friend difference_type
       operator-(const _Self& __x, const _Self& __y) _GLIBCXX_NOEXCEPT
       {
-	return difference_type(_S_buffer_size())
-	  * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
-	  + (__y._M_last - __y._M_cur);
+	if (__builtin_expect(__x._M_node || __y._M_node, true))
+	  return difference_type(_S_buffer_size())
+	    * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
+	    + (__y._M_last - __y._M_cur);
+
+	return 0;
       }
 
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
@@ -366,9 +369,12 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	operator-(const _Self& __x,
 		  const _Deque_iterator<_Tp, _RefR, _PtrR>& __y) _GLIBCXX_NOEXCEPT
 	{
-	  return difference_type(_S_buffer_size())
-	    * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
-	    + (__y._M_last - __y._M_cur);
+	  if (__builtin_expect(__x._M_node || __y._M_node, true))
+	    return difference_type(_S_buffer_size())
+	      * (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
+	      + (__y._M_last - __y._M_cur);
+
+	  return 0;
 	}
 
       friend _Self
diff --git a/libstdc++-v3/testsuite/23_containers/deque/70303.cc b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
new file mode 100644
index 00000000000..e0e63694170
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/70303.cc
@@ -0,0 +1,67 @@ 
+// Copyright (C) 2021 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 }
+
+#include <deque>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/70303
+
+void test01()
+{
+  typedef typename std::deque<int>::iterator It;
+  It it = It();
+  VERIFY(it == it);
+  VERIFY(!(it != it));
+  VERIFY(it - it == 0);
+  VERIFY(!(it < it));
+  VERIFY(!(it > it));
+  VERIFY(it <= it);
+  VERIFY(it >= it);
+
+  typedef typename std::deque<int>::const_iterator CIt;
+  CIt cit = CIt();
+  VERIFY(cit == cit);
+  VERIFY(!(cit != cit));
+  VERIFY(cit - cit == 0);
+  VERIFY(!(cit < cit));
+  VERIFY(!(cit > cit));
+  VERIFY(cit <= cit);
+  VERIFY(cit >= cit);
+
+  VERIFY(it == cit);
+  VERIFY(!(it != cit));
+  VERIFY(cit == it);
+  VERIFY(!(cit != it));
+  VERIFY(it - cit == 0);
+  VERIFY(cit - it == 0);
+  VERIFY(!(it < cit));
+  VERIFY(!(it > cit));
+  VERIFY(it <= cit);
+  VERIFY(it >= cit);
+  VERIFY(!(cit < it));
+  VERIFY(!(cit > it));
+  VERIFY(cit <= it);
+  VERIFY(cit >= it);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/70303.cc b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
new file mode 100644
index 00000000000..af18a0503d8
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/70303.cc
@@ -0,0 +1,67 @@ 
+// Copyright (C) 2021 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 }
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+// PR libstdc++/70303
+
+void test01()
+{
+  typedef typename std::vector<int>::iterator It;
+  It it = It();
+  VERIFY(it == it);
+  VERIFY(!(it != it));
+  VERIFY(it - it == 0);
+  VERIFY(!(it < it));
+  VERIFY(!(it > it));
+  VERIFY(it <= it);
+  VERIFY(it >= it);
+
+  typedef typename std::vector<int>::const_iterator CIt;
+  CIt cit = CIt();
+  VERIFY(cit == cit);
+  VERIFY(!(cit != cit));
+  VERIFY(cit - cit == 0);
+  VERIFY(!(cit < cit));
+  VERIFY(!(cit > cit));
+  VERIFY(cit <= cit);
+  VERIFY(cit >= cit);
+
+  VERIFY(it == cit);
+  VERIFY(!(it != cit));
+  VERIFY(cit == it);
+  VERIFY(!(cit != it));
+  VERIFY(it - cit == 0);
+  VERIFY(cit - it == 0);
+  VERIFY(!(it < cit));
+  VERIFY(!(it > cit));
+  VERIFY(it <= cit);
+  VERIFY(it >= cit);
+  VERIFY(!(cit < it));
+  VERIFY(!(cit > it));
+  VERIFY(cit <= it);
+  VERIFY(cit >= it);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}