Bug 49733

Summary: Missed optimization: Variable value not propagated to remove "if" condition
Product: gcc Reporter: Tobias Burnus <burnus>
Component: middle-endAssignee: Not yet assigned to anyone <unassigned>
Status: UNCONFIRMED ---    
Severity: normal Keywords: missed-optimization
Priority: P3    
Version: 4.7.0   
Target Milestone: ---   
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed:

Description Tobias Burnus 2011-07-13 13:10:32 UTC
Based on the discussion at http://gcc.gnu.org/ml/gcc/2011-07/msg00208.html.

In Fortran, the "if" condition can be removed in the following example as "call some_function()" may not modify it. This optimization is done by the Fortran compilers of NAG, Cray, Sun, Open64 and PathScale. But gfortran (and Intel's and PGI's compiler) do not optimize the "if" away, which can be seen from the presence of the "foobar_" function in the dump.


   subroutine sub(non_aliasing_var)
     interface
       subroutine some_function()
       end subroutine some_function
     end interface


     integer :: non_aliasing_var
     non_aliasing_var = 5
     call some_function()
     if (non_aliasing_var /= 5) call foobar_()
   end subroutine sub

Optimized dump:

sub (integer(kind=4) & restrict non_aliasing_var)
{
  integer(kind=4) D.1550;
<bb 2>:
  *non_aliasing_var_1(D) = 5;
  some_function ();
  D.1550_2 = *non_aliasing_var_1(D);
  if (D.1550_2 != 5)
    goto <bb 3>;
  else
    goto <bb 4>;
<bb 3>:
  foobar_ (); [tail call]
<bb 4>:
  return;
}


NOTE: Fortran has a case (coarrays, ASYNCHRONOUS), where the current behaviour is correct: That is, no aliasing of variables in the scoping unit, but the value might change due to asynchronous I/O / data communication (ASYNCHRONOUS) - or via one-sided communication (coarrays), where the "WAIT" or SYNC might be hidden in some function call. For those cases, the current behaviour with "restrict" is correct.
Cf. ASYNCHRONOUS: F2008, Sect. 5.3.4 and PDTR 29113 (ftp://ftp.nag.co.uk/sc22wg5/N1851-N1900/N1866.pdf)
Cf. Coarray: F2008; especially Section 8.5 and, in particular, Subsection 8.5.2.


Regarding the example above, one finds in the Fortran standard (F2008, ftp://ftp.nag.co.uk/sc22wg5/N1851-N1900/N1866.pdf) the following, which applies as the dummy argument "non_aliasing_var" is neither a POINTER nor has it the TARGET attribute.

"While an entity is associated with a dummy argument, the following restrictions hold.
[...]
"(3) Action that affects the value of the entity or any subobject of it shall be taken only through the dummy argument unless
(a) the dummy argument has the POINTER attribute or
(b) the dummy argument has the TARGET attribute, the dummy argument does not have INTENT(IN), the dummy argument is a scalar object or an assumed-shape array without the CONTIGUOUS attribute, and the actual argument is a target other than an array section with a vector subscript.

(4) If the value of the entity or any subobject of it is affected through the dummy argument, then at any time during the invocation and execution of the procedure, either before or after the definition, it may be referenced only through that dummy argument unless
(a) the dummy argument has the POINTER attribute or
(b) the dummy argument has the TARGET attribute, the dummy argument does not have INTENT(IN), the dummy argument is a scalar object or an assumed-shape array without the CONTIGUOUS attribute, and the actual argument is a target other than an array section with a
vector subscript."
Comment 1 Tobias Burnus 2011-07-13 13:42:26 UTC
C test case matching the Fortran one, which also shows that the "if" is not optimized away.

void some_function(void);

void
sub (int *restrict non_aliasing_var)
{
  *non_aliasing_var = 5;
  some_function ();
  if (*non_aliasing_var != 5)
    foobar_();
}
Comment 2 Tobias Burnus 2011-07-13 14:06:17 UTC
(In reply to comment #1)
> C test case matching the Fortran one, which also shows that the "if" is not
> optimized away.

According to Ian (cf. thread linked to in comment 0), C99's restrict does not guarantee that the value remains the same after a function call. Assuming that's the case, then C does not seem to have an equivalent qualifier to Fortran's nontarget/nonasynchronous/nonpointer* variables. - And, hence, some tree flag is missing to for such nonpointer* Fortran variables. (* pointer in the Fortran sense.)

I failed to see this from C99's "6.7.3.1 Formal definition of restrict", which is written in a rather convoluted way.
Comment 3 Richard Biener 2011-07-13 14:17:38 UTC
is

  program test

  integer :: a

  subroutine bar()
  a = 1
  end

  subroutine sub(non_aliasing_var)
  integer :: non_aliasing_var
  non_aliasing_var = 5
  bar()
  if (non_aliasing_var /= 5) call foobar()
  end

  sub(a)
  end

invalid then?  GCC assumes that any function can modify any global
variable unless interprocedural analysis can prove otherwise.

It gets more interesting when you consider

  subroutine sub(non_aliasing_var)
  integer :: non_aliasing_var
  non_aliasing_var = 5
  sub(a)
! or even
! sub (non_aliasing_var)
  if (non_aliasing_var /= 5) call foobar()
  end

(or even hide the recursion by going through an external dispatcher)

Does that make the variable "aliased"?  Or is that invalid as well
(ok, add whatever is required to allow sub be called recursively - what then?)
Comment 4 Tobias Burnus 2011-07-13 14:42:43 UTC
(In reply to comment #3)
> is
[...]
> invalid then?

Yes, the does not even have the correct syntax. I assume you means something like:

module m
  integer :: global
end module m

program main
  use m
  implicit none
  call test(global)
contains
  subroutine mod_global()
    global = 7
  end subroutine mod_global
  subroutine mod2(y)
    integer :: y
    y = 9
  end subroutine mod2
  subroutine test(x)
    integer :: x
    x = 5
    call mod_global() ! Invalid, modifies "x"
    global = 8  ! Also invalid
    call mod2(global) ! Ditto.
    print *, x  ! May print 5, 7, 8, 9, or other garbage
  end subroutine
end program main

 ! or even
 ! sub (non_aliasing_var)
   if (non_aliasing_var /= 5) call foobar()

Well, "call sub (non_aliasing_var)" *may* modify "non_aliasing_var" - but it may not modify a global variable directly, if it is associated with the actual argument.

(And for recursion: That's only allowed if the procedure is marked as RECURSIVE.)


As written, the Fortran standard states (quote in comment 0):

If A is used as actual argument to the dummy argument D then:

a) Anything which *modifies* "A" should only be done by using explicitly "D", unless "D" is a POINTER or "D" has the TARGET attribute (and some other conditions).

b) Anything which *reads* "A": If the value is modified through "D", only "D" may be used to access the value. (With same exceptions for TARGET and POINTER - and, of course, only applying until the function with the dummy "D" returns).
Comment 5 Richard Biener 2011-07-14 09:03:58 UTC
Ah, so passing the non-aliasing-var to a function which takes a POINTER
argument, returning that pointer and modifying it in the function that
takes the non-aliasing-var is ok then?

The Fortran semantics seem unclear enough to me to doubt that there is a
clean orthogonal representation for the middle-end.
Comment 6 Tobias Burnus 2011-07-14 09:25:31 UTC
(In reply to comment #5)
> Ah, so passing the non-aliasing-var to a function which takes a POINTER
> argument, returning that pointer and modifying it in the function that
> takes the non-aliasing-var is ok then?

POINTER in the C/middle-end or in the Fortran sense?

Passing a non-(Fortran-)POINTER variable as actual argument to a (Fortran-)POINTER dummy argument is only valid, if the dummy is INTENT(IN) and if the actual argument is TARGET. The INTENT(IN) makes sure that one does not change the address to which the pointer points - and the TARGET avoids aliasing issues. (Cf. "12.5.2.7 Pointer dummy variables".)

Thus, "non-aliasing-var" may not be passed passed to a POINTER dummy argument.