- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I want copy array content including possible memory allocation for the recipient array in side a subroutine but have to allow for the option that the recipient array and the donor array are the same, or that the donor array is a section of the recipient array. I tried to filter the latter cases via the "loc" function but this seems to fail when the the array section is contiguous but does not start at position one.
A small example program which I was expecting NOT to work .......... but i works:
program test implicit none integer, allocatable :: i1(:) integer :: i allocate(i1(10),source=(/(i,i=1,10)/)) !call subi1(i1,i1) !call subi1(i1,i1(1:8)) call subi1(i1,i1(4:8)) write(*,*) allocated(i1) write(*,"(*(g0:"",""))") i1 contains subroutine subi1(a,b) integer, intent(inout), allocatable :: a(:) integer, intent(in) :: b(:) write(*,*) loc(a)==loc(b) if(allocated(a)) deallocate(a) allocate(a,source=b) end subroutine subi1 end program test
I compiled it with
ifort -check all -check arg_temp_created -fno-inline-functions 1.f90
It ran with all three different calls, whereas I was expecting it not to run at all. I know that fortran requires subroutine arguments to point to non-overlappingg memory locations, and I wanted to use "loc" to filter for violation, but this filter fails at the third call (it reports F whereas the donor is a subset of the recipient and no temporary argument was created).
Now the questions are: A) did it run just accidentally and B) if so, is there any why to catch the last call in a way that one can infer that the second array is a subset of the first array?
any suggestions.
Thanks a lot.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
So I found that it worked accidentally and a filter for the last case is
subroutine subi1(a,b) integer, intent(inout), allocatable :: a(:) integer, intent(in) :: b(:) integer :: l,i l=loc(b(1)) do i=1,size(a) if(loc(a(i))/=l) cycle !!do something to accommodate for this return end do if(allocated(a)) deallocate(a) allocate(a,source=b) end subroutine subi1
However, that requires to iterate over "a" which might be detrimental to performance.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A) Yes. B) No.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
>>call subi1(i1,i1(4:8))
This violates the aliasing of arguments requirement of Fortran.
subroutine subi1(a,b) integer, intent(inout), allocatable :: a(:) integer, intent(in) :: b(:) ! **** b may vioalate antialiasing rule integer, allocatable :: b_copy(:) ! in the event that b is subsection of a b_copy = b ! requires reallocate left hand side if(allocated(a)) deallocate(a) ! optional with reallocate left hand side allocate(a,source=b_copy) ! b_copy will be automatically deallocated end subroutine subi1
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi
thanks for the response.
with "!!do something to accommodate for this" in my second post I meant exactly what Jim described.
subroutine subi1(a,b) integer, intent(inout), allocatable :: a(:) integer, intent(in) :: b(:) integer, allocatable :: b_copy(:) integer :: l,i if(allocated(a)) then l=loc(b(1)) do i=1,size(a) if(loc(a(i))/=l) cycle b_copy=b;deallocate(a);a=b_copy return end do end if if(allocated(a)) deallocate(a) allocate(a,source=b) end subroutine subi1
the question is whether that is sufficient to determine whether "b" is a section of "a". I don't see why this is not a solution (as suggested by Steve).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This might be better:
subroutine subi1(a,b) integer, intent(inout), allocatable :: a(:) integer, intent(in) :: b(:) integer, allocatable :: b_copy(:) if(allocated(a)) then ! assure b not alias with a if( loc(b(size(b))) < loc(a(1)) .or. loc(b(1)) > loc(a(size(a))) ) then a = b ! realloc lhs will take care of deallocation and reallocation return endif ! alias b_copy = b a = b_copy ! realloc lhs will take care of deallocation and reallocation return endif a = b ! realloc lhs will take care of allocation ! *** call subi1(i1,i1(4:8)) invalid when i1 not allocated end subroutine subi1
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Jim.
Thanks for the suggestions.
Do you say that "loc" will yield an increasing sequence of numbers when applied to all ram locations, starting with say 0 and ending with say N?? If so, is there an description of "loc" which verifies that? From what I found from the compiler suppliers (Intel, gnu) I wouldn't be able to make that inference.
Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The language rules are still being violated by the attempted workarounds in #4, #5, and #6. If the thing associated with the dummy `b` is part of the thing associated with the actual argument `a`, then the allocation status of `a` must not be changed at all (F2018 15.5.2.13). The workarounds attempt to deal with one possible symptom of the violation, but the violation of the language rules is still there. (Note compilers take advantage of these rules (that's one reason why the rules are there...) - given the rules the temporary b_copy variable is fair picking for an clever optimiser to eliminate.)
An assignment statement in the otherwise calling scope would be a simple solution. Alternatively, break the association between the actual arguments by enclosing the argument corresponding to the INTENT(IN) `b` dummy in parentheses.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Ian H
I tried to get my head around what "call subi1(i1,(i1(4:8)))" would actually mean and it occurs to me that it will force the compiler to make a temporary copy of "i1(4:8)", similar to call-by-value.
If that's correct I would assume that compiling with "-check arg_temp_created" should generated a message at run time. But that doesn't seem to happen?? So either the assumption about the temporary array is wrong or the compiler is not showing up when using this syntax.
Any idea.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Because b is an assumed-shape array and the effective argument is a contiguous slice, no copy is made. The descriptor passed has the base address and the shape.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If you look at the generated assembly for a parenthesised expression as an actual argument, you will see the creation of a temporary.
I don't know what the design intent is for "-check arg_temp_created", but I presume that option warns for the situation where the need for the temporary is implicit, not explicit.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have to believe you guys as I am not familiar with assembly. But from making trials with large arrays where the code segfaults without parenthesis I found that the parenthesis are doing the trick.
Cheers
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH,
given the rules the temporary b_copy variable is fair picking for an clever optimiser to eliminate.)
An assignment statement in the otherwise calling scope would be a simple solution. Alternatively, break the association between the actual arguments by enclosing the argument corresponding to the INTENT(IN) `b` dummy in parentheses.
Then wouldn't the same fair picking optimization occur at the call as well?
To avoid fair picking of optimization, perhaps attributing b_copy with VOLATILE might suffice. But then the compiler designer is caught in the position of having a "must modify" versus "can eliminate".
Assuming call subi1(i1,(i1(4:8))) works, this (problem with alias) might be would be obfuscated, should the a and b arguments at the call be dummy arguments themselves. And in which case the caller wouldn't know they were aliases, and as such would always have to call with ()'s around the 2nd argument. This would be unnecessary work when they weren't aliases, and easily overlooked with many points of call. Placing the alias test inside the called subroutine is the safest place to put it. You would have to assure that the code optimization would not circumvent your intentions.
subroutine subi1(a,b) !DIR$ NOOPTIMIZE ...
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
IanH (Blackbelt) wrote:I don't know what the design intent is for "-check arg_temp_created", but I presume that option warns for the situation where the need for the temporary is implicit, not explicit.
That option's diagnostics are limited to creation of temps as actual arguments, such as passing a non-contiguous array slice to a routine that wants a contiguous one. It doesn't fire for many other places where temps are created.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page