std:vec for classes with constructor?

Jonathan Wakely jwakely@redhat.com
Fri Aug 7 07:57:46 GMT 2020


On 07/08/20 08:48 +0200, Richard Biener wrote:
>On Thu, Aug 6, 2020 at 9:24 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>>
>> On 06/08/20 19:58 +0200, Aldy Hernandez wrote:
>> >
>> >
>> >On 8/6/20 6:30 PM, Jonathan Wakely wrote:
>> >>On 06/08/20 16:17 +0200, Aldy Hernandez wrote:
>> >>>
>> >>>
>> >>>On 8/6/20 12:48 PM, Jonathan Wakely wrote:
>> >>>>On 06/08/20 12:31 +0200, Richard Biener wrote:
>> >>>>>On Thu, Aug 6, 2020 at 12:19 PM Jonathan Wakely
>> >>>>><jwakely@redhat.com> wrote:
>> >>>>>>
>> >>>>>>On 06/08/20 06:16 +0100, Richard Sandiford wrote:
>> >>>>>>>Andrew MacLeod via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
>> >>>>>>>>On 8/5/20 12:54 PM, Richard Biener via Gcc-patches wrote:
>> >>>>>>>>>On August 5, 2020 5:09:19 PM GMT+02:00, Martin Jambor
>> >>>>>><mjambor@suse.cz> wrote:
>> >>>>>>>>>>On Fri, Jul 31 2020, Aldy Hernandez via Gcc-patches wrote:
>> >>>>>>>>>>[...]
>> >>>>>>>>>>
>> >>>>>>>>>>>* ipa-cp changes from vec<value_range> to std::vec<value_range>.
>> >>>>>>>>>>>
>> >>>>>>>>>>>We are using std::vec to ensure constructors are run, which they
>> >>>>>>>>>>aren't
>> >>>>>>>>>>>in our internal vec<> implementation.  Although we
>> >>>>>>>>>>>usually
>> >>>>>>steer away
>> >>>>>>>>>>>from using std::vec because of interactions with our GC system,
>> >>>>>>>>>>>ipcp_param_lattices is only live within the pass
>> >>>>>>>>>>>and
>> >>>>>>allocated with
>> >>>>>>>>>>calloc.
>> >>>>>>>>>>Ummm... I did not object but I will save the URL of
>> >>>>>>>>>>this
>> >>>>>>message in the
>> >>>>>>>>>>archive so that I can waive it in front of anyone
>> >>>>>>>>>>complaining why I
>> >>>>>>>>>>don't use our internal vec's in IPA data structures.
>> >>>>>>>>>>
>> >>>>>>>>>>But it actually raises a broader question: was this
>> >>>>>>supposed to be an
>> >>>>>>>>>>exception, allowed only not to complicate the irange
>> >>>>>>>>>>patch
>> >>>>>>further, or
>> >>>>>>>>>>will this be generally accepted thing to do when
>> >>>>>>>>>>someone
>> >>>>>>wants to have
>> >>>>>>>>>>a
>> >>>>>>>>>>vector of constructed items?
>> >>>>>>>>>It's definitely not what we want. You have to find
>> >>>>>>>>>another
>> >>>>>>solution to this problem.
>> >>>>>>>>>
>> >>>>>>>>>Richard.
>> >>>>>>>>>
>> >>>>>>>>
>> >>>>>>>>Why isn't it what we want?
>> >>>>>>>>
>> >>>>>>>>This is a small vector local to the pass so it doesn't
>> >>>>>>>>interfere with
>> >>>>>>>>our PITA GTY.
>> >>>>>>>>The class is pretty straightforward, but we do need a constructor to
>> >>>>>>>>initialize the pointer and the max-size field.  There is
>> >>>>>>>>no
>> >>>>>>allocation
>> >>>>>>>>done per element, so a small number of elements have a
>> >>>>>>>>couple
>> >>>>>>of fields
>> >>>>>>>>initialized per element. We'd have to loop to do that anyway.
>> >>>>>>>>
>> >>>>>>>>GCC's vec<> does not provide he ability to run a
>> >>>>>>>>constructor,
>> >>>>>>std::vec
>> >>>>>>>>does.
>> >>>>>>>
>> >>>>>>>I realise you weren't claiming otherwise, but: that could
>> >>>>>>>be fixed :-)
>> >>>>>>
>> >>>>>>It really should be.
>> >>>>>>
>> >>>>>>Artificial limitations like that are just a booby trap for the unwary.
>> >>>>>
>> >>>>>It's probably also historic because we couldn't even implement
>> >>>>>the case of re-allocation correctly without std::move, could we?
>> >>>>
>> >>>>I don't see why not. std::vector worked fine without std::move, it's
>> >>>>just more efficient with std::move, and can be used with a wider set
>> >>>>of element types.
>> >>>>
>> >>>>When reallocating you can just copy each element to the new storage
>> >>>>and destroy the old element. If your type is non-copyable then you
>> >>>>need std::move, but I don't think the types I see used with vec<> are
>> >>>>non-copyable. Most of them are trivially-copyable.
>> >>>>
>> >>>>I think the benefit of std::move to GCC is likely to be permitting
>> >>>>cheap copies to be made where previously they were banned for
>> >>>>performance reasons, but not because those copies were impossible.
>> >>>
>> >>>For the record, neither value_range nor int_range<N> require any
>> >>>allocations.  The sub-range storage resides in the stack or
>> >>>wherever it was defined.  However, it is definitely not a POD.
>> >>>
>> >>>Digging deeper, I notice that the original issue that caused us to
>> >>>use std::vector was not in-place new but the safe_grow_cleared.
>> >>>The original code had:
>> >>>
>> >>>>    auto_vec<value_range, 32> known_value_ranges;
>> >>>>...
>> >>>>...
>> >>>>    if (!vr.undefined_p () && !vr.varying_p ())
>> >>>>         {
>> >>>>           if (!known_value_ranges.length ())
>> >>>>             known_value_ranges.safe_grow_cleared (count);
>> >>>>             known_value_ranges[i] = vr;
>> >>>>         }
>> >>>
>> >>>I would've gladly kept the auto_vec, had I been able to do call
>> >>>the constructor by doing an in-place new:
>> >>>
>> >>>>                   if (!vr.undefined_p () && !vr.varying_p ())
>> >>>>                     {
>> >>>>                       if (!known_value_ranges.length ())
>> >>>>-                         known_value_ranges.safe_grow_cleared (count);
>> >>>>+                         {
>> >>>>+                           known_value_ranges.safe_grow_cleared
>> >>>>(count);
>> >>>>+                           for (int i = 0; i < count; ++i)
>> >>>>+                             new (&known_value_ranges[i])
>> >>>>value_range ();
>> >>>>+                         }
>> >>>>                       known_value_ranges[i] = vr;
>> >>>>                     }
>> >>>>                 }
>> >>>
>> >>>But alas, compiling yields:
>> >>>
>> >>>>In file included from /usr/include/wchar.h:35,
>> >>>>                from /usr/include/c++/10/cwchar:44,
>> >>>>                from /usr/include/c++/10/bits/postypes.h:40,
>> >>>>                from /usr/include/c++/10/iosfwd:40,
>> >>>>                from /usr/include/gmp-x86_64.h:34,
>> >>>>                from /usr/include/gmp.h:59,
>> >>>>                from /home/aldyh/src/gcc/gcc/system.h:687,
>> >>>>                from /home/aldyh/src/gcc/gcc/ipa-fnsummary.c:55:
>> >>>>/home/aldyh/src/gcc/gcc/vec.h: In instantiation of ‘static
>> >>>>size_t vec<T, A, vl_embed>::embedded_size(unsigned int) [with T
>> >>>>= int_range<1>; A = va_heap; size_t = long unsigned int]’:
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:288:58:   required from ‘static
>> >>>>void va_heap::reserve(vec<T, va_heap, vl_embed>*&, unsigned int,
>> >>>>bool) [with T = int_range<1>]’
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:1746:20:   required from ‘bool
>> >>>>vec<T>::reserve(unsigned int, bool) [with T = int_range<1>]’
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:1766:10:   required from ‘bool
>> >>>>vec<T>::reserve_exact(unsigned int) [with T = int_range<1>]’
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:1894:3:   required from ‘void
>> >>>>vec<T>::safe_grow(unsigned int) [with T = int_range<1>]’
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:1912:3:   required from ‘void
>> >>>>vec<T>::safe_grow_cleared(unsigned int) [with T = int_range<1>]’
>> >>>>/home/aldyh/src/gcc/gcc/ipa-fnsummary.c:634:51:   required from here
>> >>>>/home/aldyh/src/gcc/gcc/vec.h:1285:20: warning: ‘offsetof’
>> >>>>within non-standard-layout type ‘vec_embedded’ {aka
>> >>>>‘vec<int_range<1>, va_heap, vl_embed>’} is
>> >>>>conditionally-supported [-Winvalid-offsetof]
>> >>>>1285 |   return offsetof (vec_embedded, m_vecdata) + alloc * sizeof (T);
>> >>>>     |                    ^
>> >>
>> >>Can we just avoid using offsetof there?
>> >>
>> >>Untested ...
>> >>
>> >>--- a/gcc/vec.h
>> >>+++ b/gcc/vec.h
>> >>@@ -1281,8 +1281,8 @@ template<typename T, typename A>
>> >>  inline size_t
>> >>  vec<T, A, vl_embed>::embedded_size (unsigned alloc)
>> >>  {
>> >>-  typedef vec<T, A, vl_embed> vec_embedded;
>> >>-  return offsetof (vec_embedded, m_vecdata) + alloc * sizeof (T);
>> >>+  size_t offset = (char*)&m_vecdata - (char*)this;
>> >>+  return offset + alloc * sizeof (T);
>> >>  }
>> >>
>> >>
>> >
>> >Now we have:
>> >
>> >In file included from /home/aldyh/src/gcc/gcc/rtl.h:30,
>> >                 from /home/aldyh/src/gcc/gcc/genpreds.c:27:
>> >/home/aldyh/src/gcc/gcc/vec.h: In static member function ‘static
>> >size_t vec<T, A, vl_embed>::embedded_size(unsigned int)’:
>> >/home/aldyh/src/gcc/gcc/vec.h:1284:27: error: invalid use of member
>> >‘vec<T, A, vl_embed>::m_vecdata’ in static member function
>> > 1284 |   size_t offset = (char*)&m_vecdata - (char*)this;
>> >      |                           ^~~~~~~~~
>> >/home/aldyh/src/gcc/gcc/vec.h:626:5: note: declared here
>> >  626 |   T m_vecdata[1];
>> >      |     ^~~~~~~~~
>> >/home/aldyh/src/gcc/gcc/vec.h:1284:46: error: ‘this’ is unavailable
>> >for static member functions
>> > 1284 |   size_t offset = (char*)&m_vecdata - (char*)this;
>> >      |                                              ^~~~
>> >
>> >
>>
>> Oh sorry, I didn't realise it was static.
>
>Yeah, we eventually have no object when we need the offset.  Does
>offsetof refusing to operate on this mean that using a temporary
>object on the stack like the following can yield a different answer
>from the "real" object?  I guess examples where it breaks boil
>down to virtual inheritance.  Anyway, so the following _should_ work ...
>
>template<typename T, typename A>
>inline size_t
>vec<T, A, vl_embed>::embedded_size (unsigned alloc)
>{
>  typedef vec<T, A, vl_embed> vec_embedded;

This typedef is redundant, the name 'vec' in thos scope refers to the
current instantiation, so you can just say 'vec'.

>  vec_embedded tem;

i.e. 'vec tem;' here, and remove the typedef on the previous line.

>  return (char *)&tem.m_vecdata - (char *)&tem + alloc * sizeof (T);
>}
>
>?

Yes, that should work fine. In general you wouldn't want to create an
object just to do this, but creating and destroying a vec has no side
effects so is fine.




More information about the Gcc-patches mailing list