Add support for IFX RTL Memory Management Functions
Add Support for IFX RTL Memory Management Functions
I'm looking to add support for the following IFX runtime library memory management, that are present in MOM6:
i32 @for_alloc_allocatable_handle(i64, i8** nocapture , i32 , i8* )
i32 @for_alloc_allocatable(i64 , i8** nocapture , i32 )
i32 @for_allocate_handle(i64 , i8** nocapture , i32 , i8* )
i32 @for_allocate(i64 , i8** nocapture , i32 )
i32 @for_dealloc_allocatable_handle(i8* nocapture readonly , i32 , i8* )
i32 @for_dealloc_allocatable(i8* nocapture readonly , i32 )
i32 @for_deallocate(i8* nocapture readonly , i32 )
i8* @for_realloc_lhs(i8* nocapture , i8* nocapture readonly , i32 )
i8* @for_array_copy_in(i8* nocapture readonly )
void @for_array_copy_out(i8* nocapture , i8* nocapture )
i32 @for_alloc_assign_v2(i8* nocapture , i8* nocapture , i8* , i8* nocapture , i32 )
i32 @for_alloc_copy(i8* nocapture readonly , i8* nocapture readonly , i8* , i8* , i32 )
i32 @for_dealloc_all_nocheck(i8* nocapture , i8* nocapture , i32 )
i32 @for_dealloc_all_nopdtlen(i8* nocapture , i8* nocapture , i32 )
i32 @for_deallocate_all(i8* nocapture , i8* nocapture , i32 )
Below I have provided some initial details about the functions, but I will continue to update these details.
IFX Functions
i32 @for_alloc_allocatable_handle(i64 <size>, i8** nocapture <memptr>, i32 <flags>, i8* <coarray>)
i32 @for_alloc_allocatable(i64 <size>, i8** nocapture <memptr>, i32 <flags>)
i32 @for_allocate_handle(i64 <size>, i8** nocapture <memptr>, i32 <flags>, i8* <coarray>)
i32 @for_allocate(i64 <size>, i8** nocapture <memptr>, i32 <flags>)
These functions are for allocating arrays and objects:
for_alloc_allocatable_handle: for allocatable arraystype(Obj), allocatable :: objectArray(:)for_alloc_allocatable: for allocatable objectstype(Obj), allocatable :: objectfor_allocate_handle: for array pointerstype(Obj), pointer :: objectArrayPtr(:)for_allocate: for object pointerstype(Obj), pointer :: objectPtr
The first argument is the size of the allocation in bytes, the second is the ptr at which to store the allocation, and the third is a bitflag. The first and third function, also have a fourth argument that is reserved for coarray support, but this isn't currently supported in IFX (and coarrays aren't used in MOM6). Any nested allocatable (or pointer) objects/arrays are left unallocated. Also the returned integer is sometimes used.
i32 @for_dealloc_allocatable_handle(i8* nocapture readonly <ptr>, i32 <flags>, i8* <coarray>)
i32 @for_dealloc_allocatable(i8* nocapture readonly <ptr>, i32 <flags>)
i32 @for_deallocate(i8* nocapture readonly <ptr>, i32 <flags>)
The first argument is the pointer to deallocate, and the second is a bitflag. The first function also has a third argument that is reserved for coarray support. These functions don't deallocate nested allocatable (or pointer) objects/arrays, and again the returned integer is sometimes used.
The function for_dealloc_allocatable_handle can be used as the counterpart for_alloc_allocatable_handle and for_allocate_handle when one is deallocating objects without nested allocatabes. While the function for_dealloc_allocatable can be the counterpart of both for_alloc_allocatable and for_allocate under same conditions.
The flag argument of for_dealloc_allocatable_handle, and for_dealloc_allocatable seems to have 2 main ways of being constructed, and the choice between them seems to be based on whether the deallocation corresponds to an explicit deallocation i.e. deallocate(...) in the source code, or an implicit deallocation i.e. cleaning up local allocatable variables at the end of a function.
In MOM6 for_deallocate is rare, and it only seems to be used for deallocating some variables marked intent(out) on entry to the function. Also I've only see the number 262144 used as the flag argument for this function.
! ___Fortran___
type :: Point
integer :: x, y
end type
type(Point), allocatable :: objectArray(:)
type(Point), pointer :: objectArrayPtr(:)
type(Point), allocatable :: object
type(Point), pointer :: objectPtr
...
allocate(objectArray(length)) ! for_alloc_allocatable_handle
allocate(objectArrayPtr(length)) ! for_allocate_handle
allocate(object) ! for_alloc_allocatable
allocate(objectPtr) ! for_allocate
...
deallocate(objectArray) ! for_dealloc_allocatable_handle
deallocate(objectArrayPtr) ! for_dealloc_allocatable_handle
deallocate(object) ! for_dealloc_allocatable
deallocate(objectPtr) ! for_dealloc_allocatable
i8* @for_realloc_lhs(i8* nocapture <lhsptr>, i8* nocapture readonly <shapeptr>, i32 <flags>)
This function is used for reallocating the LHS of an assignment to an allocatable array, to the same shape and size as the RHS.
The first argument is a pointer to an allocatable array (on the LHS of an assignment) that is to be reallocated to the same shape/size as array pointed to by the second argument. The flag seems to be constructed in the same way every time, and I have not seen an example where the return pointer is used. Also it is common to create temporary local variables for use as the second argument.
! ___Fortran___
real, allocatable :: fpArray1(:)
real, allocatable :: fpArray2(:)
...
fpArray1 = fpArray2 ! for_realloc_lhs
i8* @for_array_copy_in(i8* nocapture readonly <srcptr>)
void @for_array_copy_out(i8* nocapture <srcptr>, i8* nocapture <dstptr>)
The function for_array_copy_in returns a pointer to a block of memory that is a copy of the memory held by the first argument. While the function for_array_copy_out copys the memory held by the first argument, in the second argument.
i32 @for_alloc_assign_v2(i8* nocapture <srcdesc>, i8* nocapture <srcptr>, i8* <dstdesc>, i8* nocapture <dstptr>, i32 <flags>)
i32 @for_alloc_copy(i8* nocapture readonly <srcdesc>, i8* nocapture readonly <srcptr>, i8* <dstdesc>, i8* <dstptr>, i32 <flags>)
The for_alloc_assign_v2 is used for both allocation, and assignment when the LHS of an assignment is an object of nested allocatables. While at the moment I'm not 100% sure about the the function for_alloc_copy, but I think it copies both the first and second argument, in to the third and fourth arguments respectively (though I have only seen examples where the fourth and fifth arguments are null, and 0 respectively).
i32 @for_dealloc_all_nocheck(i8* nocapture <desc>, i8* nocapture <ptr>, i32 <flags>)
i32 @for_dealloc_all_nopdtlen(i8* nocapture <desc>, i8* nocapture <ptr>, i32 <flags>)
i32 @for_deallocate_all(i8* nocapture <desc>, i8* nocapture <ptr>, i32 <flags>)
These deallocation functions are used for deallocating nested allocatables all at once.
The first argument is a pointer to an object that describes the nesting of allocatable objects in the second argument. The second argument is the pointer to deallocate, and the third argument is a flag.
! ___Fortran___
type :: arrayContainer
integer(kind=8) :: dummy
real, allocatable :: array(:)
end type
...
type(arrayContainer) :: v
type(arrayContainer) :: v_copy
...
allocate(v%array(size))
...
v_copy = v ! for_alloc_assign_v2
...
! for_dealloc_all_nocheck (at the end of the function)
Remarks
- Procedure arguments that are marked with the attribute,
intent(out), are deallocated on entry to a function if they are allocated.! ___Fortran___ subroutine foo(x_in, x_out) real, allocatable, intent(in) :: x_in(:) real, allocatable, intent(out) :: x_out(:) ! deallocated on entry ... end subroutine - All local allocatable variables are deallocated at the end of a procedure if they have been allocated.