> -----Original Message----- > From: Jonathan Wakely <jwak...@redhat.com> > Sent: Friday, February 14, 2025 6:49 AM > To: Andrew Pinski (QUIC) <quic_apin...@quicinc.com> > Cc: gcc-patches@gcc.gnu.org; libstd...@gcc.gnu.org > Subject: Re: [PATCH] libstc++: Improve list assumption after > constructor [PR118865] > > On Fri, 14 Feb 2025 at 03:03, Andrew Pinski > <quic_apin...@quicinc.com> wrote: > > > > The code example here does: > > ``` > > if (begin == end) __builtin_unreachable(); std::list nl(begin, > end); > > > > for (auto it = nl.begin(); it != nl.end(); it++) { ... > > } > > /* Remove the first element of the list. */ > nl.erase(nl.begin()); ``` > > > > And we get a warning because because we jump threaded > the case were we > > think the list was empty from the for loop BUT we populated > it without > > an empty array. So can help the compiler here by adding that > after > > initializing the list with non empty array, that the list will not > be empty either. > > > > This is able to remove the -Wfree-nonheap-object warning in > the first > > reduced testcase (with the fix for `begin == end` case added) > in the > > PR 118865; the second reduced testcase has been filed off as > PR 118867. > > > > Bootstrapped and tested on x86_64-linux-gnu. > > > > libstdc++-v3/ChangeLog: > > > > PR libstdc++/118865 > > * include/bits/stl_list.h (_M_initialize_dispatch): Add an > > unreachable if the iterator was not empty that the list > will > > now be not empty. > > > > Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> > > --- > > libstdc++-v3/include/bits/stl_list.h | 6 ++++++ > > 1 file changed, 6 insertions(+) > > > > diff --git a/libstdc++-v3/include/bits/stl_list.h > > b/libstdc++-v3/include/bits/stl_list.h > > index be33eeb03d4..f987d8b9d0a 100644 > > --- a/libstdc++-v3/include/bits/stl_list.h > > +++ b/libstdc++-v3/include/bits/stl_list.h > > @@ -2384,12 +2384,18 @@ > _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > _M_initialize_dispatch(_InputIterator __first, > _InputIterator __last, > > __false_type) > > { > > + bool __notempty = __first != __last; > > for (; __first != __last; ++__first) #if __cplusplus >= > > 201103L > > emplace_back(*__first); > > #else > > push_back(*__first); > > #endif > > + if (__notempty) > > + { > > + if (begin() == end()) > > + __builtin_unreachable(); > > + } > > Would there be any benefit in also telling the compiler that > when first==last that the resulting list *is* empty?
I was debating if that would be useful but then I looked and noticed the compiler could already figure it. The reason is begin for a list is a wrapper around an addition of a pointer and a constant. Pre is able to optimize the case which then will be used to jump thread later on. Also I think a too complex leading to the unreachable will not help the compiler out at all. An example is: ``` int *g(int); int f(int **a, int b, int c, int *d) { *a = d; for(int i = 0; i < c; i++) *a = g(i); #ifdef ADD_UNREACHABLE if (c >= 0) if (*a == d) __builtin_unreachable(); #endif if (*a == d) return 1; return 0; } ``` GCC is able to optimize away the last condition for the case of `(c <= 0)` and when defining ADD_UNREACHABLE can now optimize away the condition fully. > > so e.g. > > const bool __empty = __first == __last; > // loop > if (__empty != (begin() == end())) > __builtin_unreachable(); > > > Testing first == last twice for the first iteration bugs me a little, > but I assume that can be optimized? Or should we use a do- > while? Yes does get optimized away. See above. For the typical iterator the comparison, will be max 2 loads followed by a comparison of that (or no loads) and GCC's early passes does a nice job at optimizing that. Thanks, Andrew Pinski > > if (__first != __last) > { > do > { > #if __cplusplus >= 201103L > emplace_back(*__first); #else > push_back(*__first); > #endif > } while (++__first != __last); > if (begin() == end()) > __builtin_unreachable(); > }