This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: DR#236 analysis







Geoff Keating <geoffk@geoffk.org> on 03/12/2004 23:21:51:

>
> On 03/12/2004, at 12:55 PM, Ian Lance Taylor wrote:
>
> > Geoffrey Keating <geoffk@geoffk.org> writes:
> >
> >>>> For malloc-ed memory, I strongly support the
> >>>> idea that the first store should determine its type and the type
> >>>> can't
> >>>> change after that.
> >>>
...
> > In BFD, we have structures like this in allocated memory:
> >
> > struct elf_link_hash_entry
> > {
> >   ...
> >
> >   union
> >   {
....
> >     struct elf_link_hash_entry *weakdef;
...
> >     unsigned long elf_hash_value;
> >   } u;
....
> >
> > Here u.weakdef is used in the first pass of the link, through
> > adjust_dynamic_symbol.  u.elf_hash_value is used starting at
> > size_dynamic_sections.
> >
> > This is a simple hack to decrease linker memory usage.  It is portable
> > and, I believe, fully standards compliant.  It is a case in which
> > malloc'ed memory changes type after the first store.  Certainly it can
> > be avoided, but that is not the point.  I think the code is correct
> > and reasonable, and I think gcc should not break it.
> >
> > As I say, I may have misunderstood your suggestion.
>
> I see.  Yes, that's not what I meant.  In this case, the type of the
> malloc-ed memory is 'struct elf_link_hash_entry', and that's what's not
> allowed to change; what I want prohibited is code like:
>
>    void * mem = malloc (max (sizeof (int), sizeof (float));
>    *(int *)mem = 1;
>    *(float *)mem = 2.0;
>
> where the top-level type of the memory changes.

Can you elaborate?  I think that user memory allocators should
be OK with the above clarification, but this is not clear.

Consider a user defined memory manager:
      template <class T> my_allocator;
      list<some_struct, my_allocator<some_struct> > myListObject;
Now, a reasonable implementation of (for this case when size=1):
  1.  my_allocator<T>::allocate(size_t size)
  2.  my_allocator<T>::deallocate(T*ptr, size_t size)

Will allocate a chunk of pool memory, break it into chunks,
and manage a free list of
  union  free_list_element {
      free_list_element *next_free_list_element ;
      char element_memory[sizeof(T)];
      T *next_element_as_T;  // This may be needed...
  };

Everything can be done without a single casting of pointers.

Now repeating, T* p=allocate(1) and deallocate(p,1) will change the
type of the same pointer to T* and back to free_list_element.

This use should be safe (and it used to be safe).

Note that since `allocate' and `deallocate' are inline functions,
the compiler will mess things up if aliasing rules are changed
in the "wrong" direction.

Thinking more about it, I think that my example is not that safe
even with today's aliasing rules (unless 'char' comes to the rescue).
The problematic scenario is:
      T *p1=allocator.allocate(1);
      p1->a= 1;
      int val= p1->a;
      allocator.deallocate(p1, 1);

in this case, it is possible (in theory) that since p1->a is not
accessed through `union' members, aliasing rules will think
that it is not dependent on `union' stuff going on in deallocate.
This may lead to `val' being initialized with the value of
a pointer to `next_free_list_element'.

This is a total mess..... Can't aliasing rules be formulated
to allow for such a use?



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]