Add support for IFX RTL Memory Management Functions
- Dominant language
- LLVM
- Stars
- 1.7k
- Forks
- 188
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 22
Description
# 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](https://github.com/mom-ocean/MOM6):
```llvm
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
---
```llvm
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 )
```
These functions are for allocating arrays and objects:
- `for_alloc_allocatable_handle`: for allocatable arrays `type(Obj), allocatable :: objectArray(:)`
- `for_alloc_allocatable`: for allocatable objects `type(Obj), allocatable :: object`
- `for_allocate_handle`: for array pointers `type(Obj), pointer :: objectArrayPtr(:)`
- `for_allocate`: for object pointers `type(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.
---
```llvm
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 )
```
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
! ___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
```
---
```llvm
i8* @for_realloc_lhs(i8* nocapture , i8* nocapture readonly , i32 )
```
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
! ___Fortran___
real, allocatable :: fpArray1(:)
real, allocatable :: fpArray2(:)
...
fpArray1 = fpArray2 ! for_realloc_lhs
```
---
```llvm
i8* @for_array_copy_in(i8* nocapture readonly )
void @for_array_copy_out(i8* nocapture , i8* nocapture )
```
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.
---
```llvm
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 )
```
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).
---
```llvm
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 )
```
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
! ___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
! ___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.
Contributor guide
Assessment
This issue has not been assessed yet.