This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [EXTERNAL] [Fortran] PR37336 - FIINAL patch [1/n]: Implement the finalization wrapper subroutine


Hi Tobias,

Thanks for your work on this.  This is a big step.  I would add to your
list the following:

(4) If the entity is of extended type and the parent type has a component
that is finalizable, the parent component's component is finalized.

In ForTrilnos, we need for this to happen even when the parent is abstract
but has a finalizable component.  So far, the IBM, NAG, and Cray compilers
support this use case and we've had enough dialogue with committee members
that I'm confident it's required by the standard, although I can't cite
the specific part of the standard that requires it.

Please copy my staff member Karla Morris on any replies.  Thanks again!

Damian


On 8/13/12 1:05 PM, "Tobias Burnus" <burnus@net-b.de> wrote:

>Dear all,
>
>Attached is the first part of a patch which will implement finalization
>support and polymorphic freeing in gfortran.
>
>
>It addresses two needs:
>
>a) For polymorphic ("CLASS") variables, allocatable components have to
>be freed; however, at compile time only the allocatable components of
>the declared type are known ­ and the dynamic type might have more
>
>b) Fortran 2003 allows finalization subroutines ("FINAL", destructors),
>which can be elemental, scalar or for a given rank (any array type is
>allowed). Those should be called for DEALLOCATE, leaving the scope
>(unless saved), intrinsic assignment and with intent(out).
>
>
>The finalization is done as follows (F2008, "4.5.6.2 The finalization
>process")
>
>"(1) If the dynamic type of the entity has a final subroutine whose
>dummy argument has the same kind type parameters and rank as the entity
>being finalized, it is called with the entity as an actual argument.
>Otherwise, if there is an elemental final subroutine whose dummy
>argument has the same kind type parameters as the entity being
>finalized, it is called with the entity as an actual argument.
>Otherwise, no subroutine is called at this point.
>
>"(2) All finalizable components that appear in the type definition are
>finalized in a processor-dependent order. If the entity being finalized
>is an array, each finalizable component of each element of that entity
>is finalized separately.
>
>"(3) If the entity is of extended type and the parent type is
>finalizable, the parent component is finalized."
>
>
>The idea is to create a wrapper function which handles those steps - and
>attach a reference to the dynamic type (i.e. add it via proc-pointer to
>the vtable). Additionally, the wrapper can be directly called for TYPE.
>
>
>The attached patch implements the generation of the wrapper subroutine;
>it does not yet implement the actual calls. The wrapper is generated on
>Fortran AST level and creates code similar to
>
>subroutine final_wrapper_for_type_t (array)
>type(t), intent(inout) :: array(..)
>integer, pointer :: ptr
>integer(c_intptr_t) :: i, addr
>
>select case (rank (array))
>case (3)
>call final_rank3 (array)
>case default:
>do i = 0, size (array)-1
>addr = transfer (c_loc (array), addr) + i * STORAGE_SIZE (array)
>call c_f_pointer (transfer (addr, c_ptr), ptr)
>call elemental_final (ptr)
>end do
>end select
>
>! For all noninherited allocatable components, call
>! DEALLOCATE(array(:)%comp, stat=ignore)
>! scalarized as above
>
>call final_wrapper_of_parent (array(...)%parent)
>end subroutine final_wrapper_for_type_t
>
>
>Note 1: The call to the parent type requires packing support for
>assumed-rank arrays, which has not yet been implemented (also required
>for TS29113, though not for this usage). That is, without further
>patches, the wrapper will only work for scalars or if the parent has no
>wrapper subroutine.
>
>Note 2: The next step will be to add the calls to the wrapper, starting
>with an explicit DEALLOCATE.
>
>
>I intent to commit the patch, when approved, without allowing FINAL at
>resolution time; that way there is no false impression that finalization
>actually works.
>
>Build and regtested on x86-64-gnu-linux.
>OK for the trunk?
>
>* * *
>
>Note: The patch will break gfortran's OOP ABI. It does so by adding
>"_final" to the virtual table (vtab).
>
>I think breaking the ABI for this functionality is unavoidable. The ABI
>change only affects code which uses the CLASS (polymorphic variables)
>and the issue only raises if one mixes old with new code for the same
>derived type. However, if one does so (e.g. by incomplete
>recompilation), segfaults and similar issues will occur. Hence, I am
>considering to bump the .mod version; that will effectively force a
>recompilation and thus avoid the issue. The down side is that it will
>also break packages (e.g. of Linux distributions) which ship .mod files
>(sorry!). What do you think?
>
>I think it could then be combined with Janus' proc-pointer patch, which
>changes the assembler name of (non-Bind(C)) procedure pointers, declared
>at module level. Again, by forcing recompilation, the .mod version bump
>should ensure that users don't see the ABI breakage. His patch is at
>http://gcc.gnu.org/ml/fortran/2012-04/msg00033.html (I think is okay,
>but I believe it has not yet been reviewed.)
>
>Tobias
>
>PS: I used the following test case to test whether the wrapper
>generation and scalarization works; it properly prints 11,22,33,44,55,66
>and also the dump looks okay for various versions.
>
>The scalarization code should work relatively well; there is only one
>call to an external function: For SIZE gfortran - for what ever reason -
>doesn't generate inline code, but calls libgfortran.
>
>
>But now the test code:
>
>module m
>type tt
>end type tt
>
>type t
>! type(tt), allocatable :: comp1
>integer :: val
>contains
>final bar1
>end type t
>
>type t1t
>! type(tt), allocatable :: comp1
>integer :: val
>!contains
>! final bar1
>end type t1t
>
>type, extends(t) :: t2
>type(tt), allocatable :: comp2
>contains
>final bar2
>end type t2
>
>class(t), allocatable, save :: a
>class(t2), allocatable, save :: b
>
>contains
>impure elemental subroutine bar1(x)
>! subroutine bar1(x)
>type(t), intent(inout) :: x!(:)
>print *, 'bar1, ....'
>print *, '..........', x%val
>end subroutine bar1
>subroutine bar2(y)
>type(t2),intent(inout) :: y(:,:)
>end subroutine bar2
>end
>
>use m
>use iso_c_binding
>type(t1t) ::x(3,2)
>
>interface
>subroutine fini(x) bind(C,name="__m_MOD___final_m_T")
>type(*) :: x(..)
>end subroutine
>end interface
>
>x%val = reshape([11,22,33,44,55,66],shape(x))
>print *, storage_size(x)
>call fini(x)
>end
>
>
>And one example for a dump:
>
>__final_m_T (struct array7_t & restrict array)
>{
>integer(kind=8) idx;
>integer(kind=8) nelem;
>struct t * ptr;
>
>{
>struct array7_t * D.1977;
>
>D.1977 = (struct array7_t *) array;
>nelem = (integer(kind=8)) (integer(kind=4)) _gfortran_size0 (D.1977) + -1;
>}
>switch ((integer(kind=4)) array->dtype & 7)
>{
>default:;
>{
>integer(kind=8) D.1981;
>
>D.1981 = nelem;
>idx = 0;
>if (idx <= D.1981)
>{
>while (1)
>{
>{
>logical(kind=4) D.1991;
>
>{
>integer(kind=8) transfer.3;
>integer(kind=8) D.1989;
>integer(kind=8) D.1988;
>static integer(kind=8) C.1987 = 0;
>void * D.1986;
>void * D.1985;
>integer(kind=8) D.1984;
>
>D.1985 = (void *) array->data;
>D.1986 = D.1985;
>D.1984 = 8;
>D.1988 = 8;
>__builtin_memcpy ((void *) &transfer.3, (void *) &D.1986, MAX_EXPR
><MIN_EXPR <D.1988, D.1984>, 0>);
>ptr = (struct t *) (idx * 4 + transfer.3);
>}
>bar1 (ptr);
>L.11:;
>D.1991 = idx == D.1981;
>idx = idx + 1;
>if (D.1991) goto L.12;
>}
>}
>}
>L.12:;
>}
>goto L.9;
>}
>L.9:;
>L.8:;
>}



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]