Created attachment 25674 [details] Demonstrator When I create a controlled type and an access type, the address returned by the 'new' is 16 bytes greater than the address determined within the allocator. I've attached an example which shows this with limited controlled, but it also happens with non-limited. I originally reported this to AdaCore as [K916-011 public] with regard to fixed-size storage pools on GNAT GPL 2011 on Mac OS X. Now, however, * GCC 4.7.0 fails on Mac OS X with both fixed-size and global pools, * GNAT GPL 2011 fails similarly on Debian 6 (32 bit), so I've taken the liberty of marking the Severity as 'major'. The attached code contains instrumented versions of the library allocators, and should be compiled "gnatmake -a allocation_test.adb". Running with $ gcc -v Using built-in specs. COLLECT_GCC=/opt/gcc-4.7-180524/bin/gcc COLLECT_LTO_WRAPPER=/opt/gcc-4.7-180524/libexec/gcc/x86_64-apple-darwin11/4.7.0/lto-wrapper Target: x86_64-apple-darwin11 Configured with: ../gcc-trunk-svn/configure CC='gcc -D_FORTIFY_SOURCE=0' --prefix=/opt/gcc-4.7-180524 --disable-multilib --enable-languages=c,ada --build=x86_64-apple-darwin11 Thread model: posix gcc version 4.7.0 20111026 (experimental) [trunk revision 180524] (GCC) I get $ ./allocation_test -------------------------------------------------- Allocation from fixed-size pool First_Free = 0 Pool_Size allocated 88 at 16#7FDD68400928#, The_Pool at 16#7FDD68400928# A ( 1) allocated at 16#7FDD68400938# or 140588913527096 First_Free = 0 Pool_Size allocated 88 at 16#7FDD68400970#, The_Pool at 16#7FDD68400928# A ( 2) allocated at 16#7FDD68400980# or 140588913527168 First_Free = 0 Pool_Size allocated 88 at 16#7FDD684009B8#, The_Pool at 16#7FDD68400928# A ( 3) allocated at 16#7FDD684009C8# or 140588913527240 First_Free = 0 Pool_Size allocated 88 at 16#7FDD68400A00#, The_Pool at 16#7FDD68400928# A ( 4) allocated at 16#7FDD68400A10# or 140588913527312 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa? @h?( @h? bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb @h?p @h? cccccccccccccccccccccccccccccccccccccccccccccccc?#?? @h? dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd -------------------------------------------------- Allocation from global pool Pool_Global allocated 88 at 140588913527376 B ( 1) allocated at 16#7FDD68400A60# or 140588913527392 Pool_Global allocated 88 at 140588913527472 B ( 2) allocated at 16#7FDD68400AC0# or 140588913527488 Pool_Global allocated 88 at 140588913527568 B ( 3) allocated at 16#7FDD68400B20# or 140588913527584 Pool_Global allocated 88 at 140588913527664 B ( 4) allocated at 16#7FDD68400B80# or 140588913527680 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd -------------------------------------------------- The corruption in the first section, the fixed-size storage pool, is obvious; there should be 4 lines, each containing 64 'a's, 'b's etc. The reason is to be seen in the trace output; for example, in the fixed-size section, Pool_Size allocated 88 at 16#7FDD68400928#, The_Pool at 16#7FDD68400928# A ( 1) allocated at 16#7FDD68400928# or 140588913527096 the internally allocated address is 16#7FDD68400928# but the value seen by the calling routine is 16#7FDD68400928#, 16#10# greater. In the default section, the corruption is less obvious, presumably because malloc() is allocating spare space anyway, but in the trace output Pool_Global allocated 88 at 140588913527376 B ( 1) allocated at 16#7FDD68400A60# or 140588913527392 the internal and external addresses are again off by 16 (I had to use GNAT.IO internally to avoid elaboration circularity).
I've now traced the reason for this. Controlled objects require some memory to be allocated to manage the chains of objects which are to be finalised when a scope is exited (this may not be strictly accurate, but is probably close enough for this PR). It used to be the case that System.Finalization_Root.Root_Controlled contained the necessary 2 pointers. At r177275 this was changed so that System.Finalization_Root.Root_Controlled is a null record; instead, System.Storage_Pools.Subpools.Allocate_Any_Controlled adds the necessary space to the allocated size and adjusts the returned pointer, to support Ada.Finalization.Heap_Managenent: Ada.Finalization.Heap_Management provides key functionality associated with controlled objects on the heap, their creation, finalization and reclamation. Type Finalization_Collection is effectively a wrapper which sits ontop of a storage pool and performs all necessary bookkeeping for all the objects it contains. Each access-to-controlled or access-to-class-wide type receives a collection as part of its expansion. The compiler generates buffer code and invokes Allocate / Deallocate to create and destroy allocated controlled objects. The trouble is that this means that the attribute Max_Size_In_Storage_Elements is now *wrong* for any controlled object. It seems to me that this new approach is a remarkably non-Ada way of addressing the problem; the original design is precisely the way that it should be addressed. Of course I have absolutely no objection to delegating this management to Ada.Finalization.Heap_Management (I think this should now in fact be System.Finalization_Masters). But System.Finalization_Root.Root_Controlled should contain a System.Finalization_Masters.FM_Node.
Created attachment 25699 [details] Simpler demonstrator
(In reply to comment #1) > It seems to me that this new approach is a remarkably non-Ada way of addressing > the problem; the original design is precisely the way that it should be > addressed. Of course I have absolutely no objection to delegating this > management to Ada.Finalization.Heap_Management (I think this should now in fact > be System.Finalization_Masters). But System.Finalization_Root.Root_Controlled > should contain a System.Finalization_Masters.FM_Node. Apologies all round; this isn't correct, because a type with a component of a controlled type also needs finalization support; for example, type E is record Str : Ada.Strings.Unbounded.Unbounded_String; end record; type E_P is access E; for E_P'Storage_Size use E'Max_Size_In_Storage_Elements * 4;
This problem has been fixed in the released 4.7.0.
Indeed.