diff options
Diffstat (limited to 'gcc/ada/trans.c')
-rw-r--r-- | gcc/ada/trans.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/gcc/ada/trans.c b/gcc/ada/trans.c index cd3c6a6..449f0f7 100644 --- a/gcc/ada/trans.c +++ b/gcc/ada/trans.c @@ -4565,7 +4565,9 @@ gnat_to_gnu (Node_Id gnat_node) tree gnu_obj_type; tree gnu_actual_obj_type = 0; tree gnu_obj_size; - int align; + unsigned int align; + unsigned int default_allocator_alignment + = get_target_default_allocator_alignment () * BITS_PER_UNIT; /* If this is a thin pointer, we must dereference it to create a fat pointer, then go back below to a thin pointer. The @@ -4621,6 +4623,35 @@ gnat_to_gnu (Node_Id gnat_node) gnu_ptr, gnu_byte_offset); } + /* If the object was allocated from the default storage pool, the + alignement was greater than what the allocator provides, and this + is not a fat or thin pointer, what we have in gnu_ptr here is an + address dynamically adjusted to match the alignment requirement + (see build_allocator). What we need to pass to free is the + initial allocator's return value, which has been stored just in + front of the block we have. */ + + if (No (Procedure_To_Call (gnat_node)) && align > default_allocator_alignment + && ! TYPE_FAT_OR_THIN_POINTER_P (gnu_ptr_type)) + { + /* We set GNU_PTR + as * (void **)((void *)GNU_PTR - (void *)sizeof(void *)) + in two steps: */ + + /* GNU_PTR (void *) = (void *)GNU_PTR - (void *)sizeof (void *)) */ + gnu_ptr + = build_binary_op (MINUS_EXPR, ptr_void_type_node, + convert (ptr_void_type_node, gnu_ptr), + convert (ptr_void_type_node, + TYPE_SIZE_UNIT (ptr_void_type_node))); + + /* GNU_PTR (void *) = *(void **)GNU_PTR */ + gnu_ptr + = build_unary_op (INDIRECT_REF, NULL_TREE, + convert (build_pointer_type (ptr_void_type_node), + gnu_ptr)); + } + gnu_result = build_call_alloc_dealloc (gnu_ptr, gnu_obj_size, align, Procedure_To_Call (gnat_node), Storage_Pool (gnat_node), |