Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28381 Discussions

ELEMENTAL subroutine and dummy with VALUE attribute

Harald1
New Contributor II
1,741 Views

The following code exhibits a difference between Intel and some other compilers:

program p
  implicit none
  integer :: a(4) = [-1, -2, -3, -4]
  integer :: n(4) = [4, 2, 1, 3]
  call s (a(n), a)
  print *, a
contains
  elemental subroutine s (x, y)
    integer, value       :: x
    integer, intent(out) :: y
    y = x
  end
end

While NAG and Cray print:

 -4 -2 -1 -3

Intel prints:

          -4          -2          -4          -4

My interpretation is that while NAG and Cray create a temporary for a(n), Intel does not.

Q1: is the code legal?

Q2: if so, who is right?

Q3: does the following excerpt from F2018 apply and explain why a temporary is needed here?

! 15.5.2.3 Argument association
! (4) A present dummy argument with the VALUE attribute becomes argument
! associated with a definable anonymous data object whose initial value is
! the value of the actual argument.

 

0 Kudos
16 Replies
FortranFan
Honored Contributor II
1,723 Views

For whatever it's worth, my take on the standard is as follows.

  1. Strictly considering the standard, the code in the original post does not conform, there is what is colloquially mentioned as an aliasing issue with the actual arguments, and the standard places the onus on the program writer to not bring that about.  The processor is not required to detect and report the problem.
  2. Under the circumstances, there is not the question of "who is right?"
  3. The standard does not mention an array temporary if that is what is your mind.

Placing aside point 1 above for a moment with "aliasing", the rest of the standard semantics with the `ELEMENTAL` procedure and the `VALUE` attribute inform a processor to take the code in the original post and deal with it as follows:

   integer :: a(4) = [-1, -2, -3, -4]
   integer :: n(4) = [4, 2, 1, 3]
   do i = 1, size(a)
      block
         integer :: anonymous_object
         anonymous_object = a(n(i)) 
         call some_sub(anonymous_object, a(i))
      end block 
   end do
   print *, a
contains
   subroutine some_sub(x, y)
      integer :: x
      integer, intent(out) :: y
      y = x
   end
end

 

0 Kudos
Harald1
New Contributor II
1,682 Views

@FortranFan wrote:

Placing aside point 1 above for a moment with "aliasing", the rest of the standard semantics with the `ELEMENTAL` procedure and the `VALUE` attribute inform a processor to take the code in the original post and deal with it as follows:

   integer :: a(4) = [-1, -2, -3, -4]
   integer :: n(4) = [4, 2, 1, 3]
   do i = 1, size(a)
      block
         integer :: anonymous_object
         anonymous_object = a(n(i)) 
         call some_sub(anonymous_object, a(i))
      end block 
   end do
   print *, a
contains
   subroutine some_sub(x, y)
      integer :: x
      integer, intent(out) :: y
      y = x
   end
end

Can you point to the places in the standard supporting your interpretation?

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,698 Views

FWIW, the VALUE attribute should have required that the value of the actual argument be placed onto the stack as opposed to a reference to the object. Without placing the value on the stack, recursive routines (using value) could not be written.

 

Also, due to value attribute on x (assuming calling code obeys convention) makes x not an alias of an element of the array y.

 

Lets see what Steve has to say about this.

 

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,696 Views

Harold,

 

Please try an experiment . Add BIND(C) to the subroutine declaration.

 

Jim Dempsey

0 Kudos
Harald1
New Contributor II
1,684 Views

Jim,

 

this is not allowed in F2018:

 

C1546 An elemental procedure shall not have the BIND attribute.

 

And it is correctly diagnosed by ifort

 

Harald

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,671 Views

The key words in the standard are:

15.8.3 Elemental subroutine actual arguments
 1 In a reference to an elemental subroutine, if the actual arguments corresponding to INTENT (OUT) and INTENT (INOUT) dummy arguments are arrays, the values of the elements, if any, of the results are the same as would be obtained if the subroutine had been applied separately, in array element order, to corresponding elements of each array actual argument.

So, let's rewrite the elemental call as a loop of scalar calls:

program p
  implicit none
  integer :: a(4) = [-1, -2, -3, -4]
  integer :: n(4) = [4, 2, 1, 3]
  integer :: i
  do i=1,4
    call s(a(n(i)),a(i))
  end do
  !call s (a(n), a)
  print *, a
contains
  elemental subroutine s (x, y)
    integer, value       :: x
    integer, intent(out) :: y
    y = x
  end
end

If we do that, what does NAG Fortran give?

D:\Projects>nagfor -o t.exe t.f90
NAG Fortran Compiler Release 7.1(Hanzomon) Build 7114
[NAG Fortran Compiler normal termination]

D:\Projects>t.exe
 -4 -2 -4 -4

I also agree with @FortranFan that the original code violates the standard's aliasing rules, though that is more subtle due to the VALUE attribute.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,653 Views

Steve,

Can you explain how a VALUE argument can be an alias of anything?

program p
  implicit none
  integer :: a(4) = [-1, -2, -3, -4]
  integer :: n(4) = [4, 2, 1, 3]
  ! when the 1st argument is by VALUE
  call s ( (a(n)), a) ! explicitly construct a temporary (then places on stack as value)
  call s (a(n), a)    ! the interface implicitly constructs a temporary on stack
  print *, a
contains
  elemental subroutine s (x, y)
    integer, value       :: x
    integer, intent(out) :: y
    y = x
  end
end

Also note, while X has no intent specified, permits OUT, any modification to X would modify the stack value, and not the callers actual argument (which was copied to stack by value).

 

Jim Dempsey

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,642 Views

Jim, it's Y that is being modified, not X. The mistake here is thinking that the entire array a(n) is computed once and saved in a temporary due to VALUE. As the standard says, the reference to the elemental subroutine behaves as if it were a series of scalar calls, and once one of those calls modifies an element of array a, that can change the value of one of the elements of a(n).

 

0 Kudos
Harald1
New Contributor II
1,636 Views

Steve,

just to make it crystal clear: the key formulation is 15.8.3 saying "... if the subroutine had been applied separately, in array element order, to corresponding elements of each array actual argument." instead of "... effective argument."?
(This would mean that NAG and Cray are wrong.)

Thanks,

Harald

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,580 Views

>>The mistake here is thinking that the entire array a(n) is computed once and saved in a temporary due to VALUE. 

VALUE is attributed to the scalar X. Being value argument, this is a stack local copy of the original calling argument A(2) and thus cannot possibly be an alias of any element of array Y (array A in the calling argument). For this subroutine, there is no requirement for a temporary array of A/Y to construct the return value. IOW a copy of X is made, not a temporary copy of Y.

 

IMHO (for this elemental example) if the standard requires a temporary array for Y = X, then the standard is wrong....

.OR. the implementation is wrong in the event that X (A(2)) is permitted to be passed by reference and thus presenting an alias.

 

Also, on the

   call s(a(2), a)

Should the interface to s .NOT. be known then the arguments are aliased (the call is non-conforming).

However, because the interface is known, the actual argument for the first argument is the stack copy of a(2) and thus becomes inaliasable with the second argument.

IOW, this specific call does not violate the no aliases rule and thus is conforming.

 

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
1,631 Views

I don't think "actual argument" is inappropriate. And no, NAG and Cray aren't wrong because your code is not standard-conforming.

0 Kudos
JohnNichols
Valued Contributor III
1,619 Views

It has been interesting following this thread.  Essentially which number do I have?

Add in a sensor that allows you to read a register, but it does not guarantee that there is anything meaningful in the register adds a dimension to the problem.  

The other challenge is the manual is always behind the current iteration of the device.   To read one temp register and check for the errors is about 300 lines of code.  Then it looks like this.  Anyway, life in the slow lane. 

Screenshot_20221126_024818.png

0 Kudos
JohnNichols
Valued Contributor III
1,572 Views
program p
  implicit none
  integer :: a(4) = [-1, -2, -3, -4]
  integer :: n(4) = [4, 2, 1, 3]
  ! when the 1st argument is by VALUE
  call s ( (a(n)), a) ! explicitly construct a temporary (then places on stack as value)
  call s (a(n), a)    ! the interface implicitly constructs a temporary on stack
  print *, a
contains
  elemental subroutine s (x, y)
    integer, value       :: x
    integer, intent(out) :: y
    y = x
  end
end

The Intel version - all I have to use on the first call gets:

Screenshot_20221127_125002.png

It should not have picked up the fourth element of A on the first run through the system --  

Screenshot_20221127_125034.png

On the second run it gets the value correctly as it does after that 

 

I do not use elemental functions but from a Fortran manual

-------------------------------------------------------------

Elemental functions are defined as scalar operators, with a single scalar dummy argument and a scalar return value, but they may be invoked with arrays as actual arguments in which case the function will be applied element-wise, with a conforming array return value. Such functions must also be pure functions, with no side effects, and can be indicated with the elemental prefix. Since it is implied that an elemental function is also pure, the pure prefix is not necessary.

Elemental subroutines may be defined in a similar way, however, side effects are permitted for intent(out) or intent(inout) arguments.

The main benefit of elemental procedures is that advance knowledge that a function is elemental simplifies parallel execution.

 

---------------------------------------------------------------------------------------------------------------------------------------------

The Intel is not following the rules, unless elementwise does not mean 1, 2,3, 4  but means 4,2,3,4.  

 

0 Kudos
JohnNichols
Valued Contributor III
1,572 Views

There is really nothing wrong with the call from I Iike to do recursion, but the compiler has a bug.  

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,562 Views

I don't agree that there's a bug. See my earlier post where I cite the standard and show an "unrolled" version and its result.

0 Kudos
JohnNichols
Valued Contributor III
1,558 Views

call s ( (a(n)), a)

So now the call to a(n)  counts up n from i = 1 to 4 and using the n(i) returns in the first instance a(4). 

That makes the code difficult to read,  but if that is the correct method then the answer is correct.  But how many layers deep can you make the call.  It is a shorthand for a do loop then

 

(a(b(c(n)))

0 Kudos
Reply