[libcxx-commits] [PATCH] D117240: [libc++] Fix bug in ranges::advance and refactor the tests

Jonathan Wakely via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jan 26 08:18:47 PST 2022


jwakely added inline comments.


================
Comment at: libcxx/include/__iterator/advance.h:171
       // If |n| >= |bound - i|, equivalent to `ranges::advance(i, bound)`.
       if (const auto __M = __bound - __i; __magnitude_geq(__n, __M)) {
         (*this)(__i, __bound);
----------------
Quuxplusone wrote:
> ldionne wrote:
> > CaseyCarter wrote:
> > > Quuxplusone wrote:
> > > > I would prefer to inline it at its one call site, here, rather than generalize it unnecessarily.
> > > > 
> > > > Unfortunately https://eel.is/c++draft/range.iter.op.advance#6.1.1 seems to mandate that even when `__n == 0`, we need to evaluate `__bound - __i` and then, if that's also zero, we must `__i = __bound` instead of simply returning immediately with `0`. Is there any blanket-wording loophole that gets us out of that obligation? This code could get much simpler if we were allowed to put `if (__n == 0) return 0;` on line 168.
> > > > 
> > > > Also, notice that if `!(bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>)`, then comparing `__n <= __M` is going to be superfluous — we know `__n` can't be negative unless `(bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>)`, so, if `!(bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>)`, it would be really nice not to generate any test for that possibility.
> > > > 
> > > > https://godbolt.org/z/jE66PEP4E — I haven't really looked at this code but it sure //looks// like libc++ is doing something suboptimal right now, doesn't it?
> > > > Unfortunately https://eel.is/c++draft/range.iter.op.advance#6.1.1 seems to mandate that even when `__n == 0`, we need to evaluate `__bound - __i` and then, if that's also zero, we must `__i = __bound` instead of simply returning immediately with `0`. Is there any blanket-wording loophole that gets us out of that obligation? This code could get much simpler if we were allowed to put `if (__n == 0) return 0;` on line 168.
> > > 
> > > It is unfortunate that the wording requires the difference to be performed when `n == 0`, and the assignment to be performed even when `n == 0` and `last - first == 0`, which implies `first == last` already. MSVC does compute the distance (I think `n == 0` is a weird enough corner not to be worth the cost of checking) but not the assignment when `n == 0`.  I suggest you implement what you think is most performant, and file an LWG issue to allow your implementation and ours.
> > > 
> > > I would prefer to inline it at its one call site, here, rather than generalize it unnecessarily.
> > 
> > After looking at the code generation and seeing that we are indeed paying for the generality, I agree that we should special case it: https://godbolt.org/z/Ez6W9Yfej. I'll still use a lambda just to avoid having a super complicated inlined `if` condition. I tried various implementations and yours was basically the most efficient and correct one. Even using `if-else` instead of a ternary somehow produces slightly worse code.
> > 
> > > [about evaluating `__bound - __i` when `__n == 0`]
> > 
> > I still think it's better not to generate a special check for `__n == 0`, since that should be fairly rare (as Casey mentioned).
> > 
> > > https://godbolt.org/z/jE66PEP4E — I haven't really looked at this code but it sure looks like libc++ is doing something suboptimal right now, doesn't it?
> > 
> > I looked, and I think it boils down to libstdc++ not protecting against signed integer overflow like what we fixed back in D106735. @jwakely , you might be interested in taking a look, here's a distilled version of libc++ vs libstdc++: https://godbolt.org/z/aYbq1nxhf
> > 
> I continue to see no reason to name `__magnitude_geq`, nor to define `__M` as `const` ( https://quuxplusone.github.io/blog/2022/01/23/dont-const-all-the-things/ ), nor to define `__M` using C++20 if-syntax instead of making it an ordinary declaration on its own line. But whatevs.
> Vice versa, I'd prefer to see lines 154-155 either done as four lines, or done as one ternary. It's basically a (Fortran ;)) three-way arithmetic `IF`: if `a<0` do one thing, else if `a==0` do another, else if `a>0` do a third.
> 
> It occurs to me that (unless I screwed up the math) it can also be done branchless:
> ```
>     auto __M = __bound - __i;
>     if (((__n < 0) | (__n >= __M)) & ((__n > 0) | (__n <= __M))) {
>         __i = std::move(__bound);
>         return __n - __M;
>     }
> 
>     __i += __n;
>     return 0;
> ```
> but this does not seem like better codegen on GCC, and not a clear-cut winner on Clang either, so I'd go with what you've got.
>  I suggest you implement what you think is most performant, and file an LWG issue to allow your implementation and ours.

Yes, please do. I don't care about `n==0` but doing a pointless assignment for `bound - i == 0` is pointless.

>  @jwakely , you might be interested in taking a look

Thanks, I'd missed the reflector thread about it last July.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D117240/new/

https://reviews.llvm.org/D117240



More information about the libcxx-commits mailing list