This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: DR#236 analysis
- From: Michael Veksler <VEKSLER at il dot ibm dot com>
- To: Geoff Keating <geoffk at geoffk dot org>
- Cc: gcc at gcc dot gnu dot org, Ian Lance Taylor <ian at wasabisystems dot com>, "Joseph S. Myers" <joseph at codesourcery dot com>
- Date: Sun, 5 Dec 2004 18:39:07 +0200
- Subject: 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?