| <!--===- docs/Aliasing.md |
| |
| Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| See https://llvm.org/LICENSE.txt for license information. |
| SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| --> |
| |
| # Aliasing in Fortran |
| |
| ```{contents} |
| --- |
| local: |
| --- |
| ``` |
| |
| ## Introduction |
| |
| References to the ISO Fortran language standard here are given by subclause number |
| or constraint number and pertain to Fortran 2018. |
| |
| ## Dummy Arguments |
| |
| ### Basic rule |
| |
| Fortran famously passes actual arguments by reference, and forbids callers |
| from associating multiple arguments on a call to conflicting storage when |
| doing so would cause the called subprogram to write to a bit of that |
| storage by means of one dummy argument and read or write that same bit |
| by means of another. |
| For example: |
| ``` |
| function f(a,b,j,k) |
| real a(*), b(*) |
| a(j) = 1. |
| b(k) = 2. |
| f = a(j) ! can optimize to: f = 1. |
| end function |
| ``` |
| |
| This prohibition applies to programs (or programmers) and has been in place |
| since Fortran acquired subroutines and functions in Fortran II. |
| |
| A Fortran compiler is free to assume that a program conforms with this rule |
| when optimizing; and while obvious violations should of course be diagnosed, |
| the programmer bears the responsibility to understand and comply with this rule. |
| |
| It should be noted that this restriction on dummy argument aliasing works |
| "both ways", in general. |
| Modifications to a dummy argument cannot affect other names by which that |
| bit of storage may be known; |
| conversely, modifications to anything other than a dummy argument cannot |
| affect that dummy argument. |
| |
| When a subprogram modifies storage by means of a particular dummy argument, |
| Fortran's prohibition against dummy argument aliasing is not limited just to other |
| dummy arguments, but to any other name by which that storage might be visible. |
| For example: |
| ``` |
| module m |
| real x |
| contains |
| function f(y) |
| real y |
| x = 1. |
| y = 2. |
| f = x ! can optimize to: f = 1. |
| end function |
| subroutine bad |
| print *, f(x) ! nonconforming usage! |
| end subroutine |
| end module |
| ``` |
| |
| Similar examples can be written using variables in `COMMON` blocks, host-association |
| in internal subprograms, and so forth. |
| |
| Further, the general rule that a dummy argument by which some particular bit |
| of storage has been modified must be the only means by which that storage is |
| referenced during the lifetime of a subprogram extends to cover any associations |
| with that dummy argument via pointer association, argument association in |
| procedure references deeper on the call chain, and so on. |
| |
| ### Complications |
| |
| Subclause 15.5.2.13 ("Restrictions on entities associated with dummy arguments"), |
| which the reader is encouraged to try to understand despite its opacity, |
| formalizes the rules for aliasing of dummy arguments. |
| |
| In addition to the "basic rule" above, Fortran imposes these additional |
| requirements on programs. |
| |
| 1. When a dummy argument is `ALLOCATABLE` or `POINTER`, it can be deallocated |
| or reallocated only through the dummy argument during the life of the |
| subprogram. |
| 1. When a dummy argument has a derived type with a component, possibly nested, |
| that is `ALLOCATABLE` or `POINTER`, the same restriction applies. |
| 1. If a subprogram ever deallocates or reallocates a dummy argument or one of its |
| components, the program cannot access that data by any other means, even |
| before the change in allocation. |
| |
| That subclause also *relaxes* the rules against dummy argument aliasing in |
| some situations. |
| |
| 1. When a dummy argument is a `POINTER`, it is essentially treated like any |
| other pointer for the purpose of alias analysis (see below), and its |
| status as a dummy argument is reduced to being relevant only for |
| deallocation and reallocation (see above). |
| 1. When a dummy argument is a `TARGET`, the actual argument is really |
| a variable (not an expression or something that needs to be passed via |
| a temporary), and that variable could be a valid data target in a pointer |
| assignment statement, then the compiler has to worry about aliasing |
| between that dummy argument and pointers if some other circumstances |
| apply. (See the standard, this one is weird and complicated!) |
| 1. Aliasing doesn't extend its restrictions to what other images might do |
| to a coarray dummy argument's associated local storage during the lifetime |
| of a subprogram -- i.e., other images don't have to worry about avoiding |
| accesses to the local image's storage when its coarray nature is explicit |
| in the declaration of the dummy argument. |
| (But when the local image's storage is associated with a non-coarray dummy |
| argument, the rules still apply. |
| In other words, the compiler doesn't have to worry about corrays unless |
| it sees coarrays.) |
| |
| ### Implications for inlining |
| |
| A naive implementation of inlining might rewrite a procedure reference |
| something like this: |
| ``` |
| module m |
| contains |
| function addto(x, y) |
| real, intent(in out) :: x |
| real, intent(in) :: y |
| x = x + y |
| addto = y |
| end function |
| function f(a,j,k) |
| real a(*) |
| a(k) = 1. |
| f = addto(a(j), a(k)) ! optimizable to 1. |
| end function |
| end module |
| ``` |
| |
| becoming, after inline expansion at the Fortran language level, |
| |
| ``` |
| function f(a,j,k) |
| real a(*) |
| a(k) = 1. |
| a(j) = a(j) + a(k) |
| f = a(k) ! no longer optimizable to 1. |
| end function |
| ``` |
| |
| The problem for a compiler is this: at the Fortran language level, no |
| language construct has the same useful guarantees against aliasing as |
| dummy arguments have. |
| A program transformation that changes dummy arguments into something |
| else needs to implement in its internal or intermediate representations |
| some kind of metadata that preserves assumptions against aliasing. |
| |
| ### `INTENT(IN)` |
| |
| A dummy argument may have an`INTENT` attribute. |
| The relevant case for alias analysis is `INTENT(IN)`, as constraint |
| C844 prohibits the appearance of an `INTENT(IN)` non-pointer dummy |
| argument in any "variable definition context" (19.6.7), which is |
| Fortran's way of saying that it might be at risk of modification. |
| |
| It would be great if the compiler could assume that an actual argument |
| that corresponds to an `INTENT(IN)` dummy argument is unchanged after |
| the called subprogram returns. |
| Unfortunately, the language has holes that admit ways by which an |
| `INTENT(IN)` dummy argument may change, even in a conforming program |
| (paragraph 2 and note 4 in subclause 8.5.10 notwithstanding). |
| In particular, Fortran nowhere states that a non-pointer `INTENT(IN)` |
| dummy argument is not "definable". |
| |
| 1. `INTENT(IN)` does not prevent the same variable from also being |
| associated with another dummy argument in the same call *without* |
| `INTENT(IN)` and being modified thereby, which is conforming so |
| long as the subprogram never references the dummy argument that |
| has `INTENT(IN)`. |
| In other words, `INTENT(IN)` is necessary but not sufficient to |
| guarantee safety from modification. |
| 1. A dummy argument may have `INTENT(IN)` and `TARGET` attributes, |
| and in a non-`PURE` subprogram this would allow modification of |
| its effective argument by means of a local pointer. |
| 1. An `INTENT(IN)` dummy argument may be forwarded to another |
| procedure's dummy argument with no `INTENT` attribute, and is |
| susceptible to being modified during that call. |
| This case includes references to procedures with implicit |
| interfaces. |
| |
| So, for the purposes of use/def/kill analysis, associating a variable with |
| a non-`PURE` procedure's non-pointer dummy argument may be fraught |
| even when `INTENT(IN)` is present without `VALUE`. |
| |
| Arguing the other side of this: |
| an interoperable procedure's `INTENT(IN)` dummy |
| arguments are forbidden from being modified, and it would be odd |
| for calls to foreign C functions to be safer than native calls (18.7). |
| |
| ### `VALUE` |
| |
| A dummy argument with the `VALUE` attribute is effectively meant to |
| be copied into a temporary for a call and not copied back into |
| its original variable (if any). |
| A `VALUE` dummy argument is therefore as safe from aliasing as |
| a local variable of the subprogram is. |
| |
| ## Pointers and targets |
| |
| Modern Fortran's pointers can't associate with arbitrary data. |
| They can be pointed only at objects that have the explicit `TARGET` |
| attribute, or at the targets of other pointers. |
| |
| A variable that does not have the `TARGET` attribute is generally |
| safe from aliasing with pointers (but see exceptions below). |
| And generally, pointers must be assumed to alias all other pointers and |
| all `TARGET` data (perhaps reduced with data flow analysis). |
| |
| A `VOLATILE` pointer can only point to a `VOLATILE` target, and |
| a non-`VOLATILE` pointer cannot. |
| A clever programmer might try to exploit this requirement to |
| clarify alias analysis, but I have not encountered such usage |
| so far. |
| |
| ### The `TARGET` hole for dummy arguments |
| |
| An actual argument that doesn't have the `TARGET` attribute can still be |
| associated with a dummy argument that *is* a target. |
| This allows a non-target variable to become a target during the lifetime |
| of a call. |
| In a non-`PURE` subprogram (15.7), a pointer may be assigned to such a |
| dummy argument or to a portion of it. |
| Such a pointer has a valid lifetime that ends when the subprogram does. |
| |
| ### Valid lifetimes of pointers to dummy arguments |
| |
| The Fortran standard doesn't mention compiler-generated and -populated |
| temporary storage in the context of argument association in 15.5.2, |
| apart from `VALUE`, but instead tries to name all of the circumstances |
| in which an actual argument's value may have to be transmitted by means |
| of a temporary in each of the paragraphs that constrain the usable |
| lifetimes of a pointer that has been pointed to a dummy argument |
| during a call. |
| It would be more clear, I think, had the standard simply described |
| the reasons for which an actual argument might have to occupy temporary |
| storage, and then just said that pointers to temporaries must not be |
| used once those temporaries no longer exist. |
| |
| ### Lack of pointer target `INTENT` |
| |
| `INTENT` attributes for dummy pointer arguments apply to the pointer |
| itself, not to the data to which the pointer points. |
| Fortran still has no means of declaring a read-only pointer. |
| Fortran also has no rule against associating read-only data with a pointer. |
| |
| ### Cray pointers |
| |
| Cray pointers are, or were, an extension that attempted to provide |
| some of the capabilities of modern pointers and allocatables before those |
| features were standardized. |
| They had some aliasing restrictions; in particular, Cray pointers were |
| not allowed to alias each other. |
| |
| They are now more or less obsolete and we have no plan in place to |
| support them. |
| |
| ## Type considerations |
| |
| Pointers with distinct types may alias so long as their types are |
| compatible in the sense of the standard. |
| |
| Pointers to derived types and `COMPLEX` may alias with pointers to the |
| types of their components. |
| For example: |
| ``` |
| complex, pointer :: pz(:) |
| real, pointer :: pa(:) |
| pa => z(:)%re ! points to all of the real components |
| ``` |
| |
| ### Shape and rank |
| |
| Array rank is not a material consideration to alias analysis. |
| Two pointers may alias even if their ranks or shapes differ. |
| For example, a pointer may associate with a column in a matrix |
| to which another pointer associates; |
| or a matrix pointer with only one column or one row may associate |
| with a vector. |
| |
| It is also possible in Fortran to "remap" target data by establishing |
| a pointer of arbitrary rank as a view of a storage sequence. |
| For example: |
| ``` |
| real, target :: vector(100) |
| real, pointer :: matrix(:,:) |
| matrix(1:10,1:10) => v ! now vector's elements look like a matrix |
| ``` |
| |
| ## Selectors in `ASSOCIATE`, `SELECT TYPE`, and `CHANGE TEAM` |
| |
| Selectors in `ASSOCIATE` and related constructs may associate with |
| either expression values or variables. |
| In the case of variables, the language imposes no restriction on |
| aliasing during the lifetime of the construct, and the compiler must |
| not assume that a selector works in a manner that is analogous to |
| that of a dummy argument. |
| |
| ## Allocatables |
| |
| There really isn't anything special about `ALLOCATABLE` objects |
| from the perspective of aliasing, apart from rules (above) that requiring |
| `ALLOCATABLE` dummy arguments be (de)allocated only by way of the dummy |
| argument. |
| |
| Because an `ALLOCATABLE` dummy argument preserves the values of lower |
| bounds and can be assumed to be contiguous, some programmers advocate |
| the use of explicitly `ALLOCATABLE` dummy arguments even when subprograms |
| do not modify their allocation status. |
| The usual aliasing restrictions still apply, even when the same `ALLOCATABLE` |
| is associated with two or more dummy arguments on a call. |
| |
| ## `ASYNCHRONOUS` and `VOLATILE` |
| |
| These attributes can, unlike any other, be scoped in Fortran by means of |
| redeclaration in a `BLOCK` construct or nested procedure. |
| |
| `ASYNCHRONOUS` data must be assumed to be read or written by some other |
| agent during its lifetime. |
| For example, Fortran's asynchronous I/O capabilities might be implemented |
| in a runtime support library by means of threading or explicitly asynchronous |
| system calls. |
| An MPI implementation might use `ASYNCHRONOUS` dummy arguments to indicate |
| that data transfers may take place during program execution in some way that |
| is not visible to the Fortran compiler. |
| |
| The optimizer must handle `ASYNCHRONOUS` and `VOLATILE` data with great care. |
| Reads and writes of `ASYNCHRONOUS` data cannot be moved across statements |
| that might initiate or complete background operations. |
| Reads and writes of `VOLATILE` data should be treated like `volatile` in C: |
| there are no "dead" writes, reads cannot be CSE'd, and both operations should |
| be properly fenced. |
| |
| ## Storage assocation via `EQUIVALENCE` |
| |
| A non-allocatable object, or parts of one, may have multiple names in Fortran |
| via `EQUIVALENCE`. |
| These objects cannot be have the `POINTER` or `TARGET` attributes. |
| Their offsets in static, stack, or `COMMON` storage is resolved by semantics |
| prior to lowering. |
| |