the following program crashes with an access violation when compiled with "ifort /debug /recursive bug.f":
program bug implicit none integer get_value external get_value print *, get_value() end program integer function get_value() implicit none get_value = 0 call get_value_sub(get_value) end function get_value subroutine get_value_sub (outval) implicit none integer, intent(out) :: outval outval = 1 end
Cause is the "call get_value_sub" with the argument "get_value". The compiler pushes a wrong address to the stack.
The bug doesn't appear when compiled with 220.127.116.11 or with 18.104.22.168 without the /resursive flag.
In the moment this prevents us from upgrading the compiler.
It looks a regression in the official release of Intel Fortran 19.0 compiler. You may want to submit a support request at the Intel Online Service Center: https://supporttickets.intel.com/?lang=en-US
In the mean time, you may want to consider using the facilities in the Fortran standard starting with MODULEs and "informing" the compiler of coding instructions explicitly (!!) which may then avoid the issue e.g.,
module m implicit none contains function get_value() result( r ) integer :: r r = 0 call get_value_sub( r ) return end function get_value subroutine get_value_sub (outval) integer, intent(out) :: outval outval = 1 return end end module program bug use m, only : get_value implicit none print *, get_value() stop end program
C:\Temp>ifort /debug /recursive p.f90 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R ) 64, Version 22.214.171.124 Build 20180804 Copyright (C) 1985-2018 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.14.26433.0 Copyright (C) Microsoft Corporation. All rights reserved. -out:p.exe -debug -pdb:p.pdb -subsystem:console p.obj C:\Temp>p.exe 1 C:\Temp>
It is not clear to me what /recursive is supposed to do. It is listed under Floating-Point Options in the IFort 18 developer guide and reference. With the 18.0.3 compiler, the assembly output for the code of #1 is the same with or without the /recursive option. That is perhaps to be expected, since there is no recursion in the example code. Furthermore, the default optimization has done away with the call to GET_VALUE_SUB, and the assembly code for GET_VALUE() corresponds to
integer function get_value() implicit none get_value = 1 end function get_value
>>It is not clear to me what /recursive is supposed to do.
subroutine foo real :: a(3)
Without recursive, local arrays such as a(3) are SAVE (static)
With recursive, local arrays such as a(3) are Automatic (stack)
Similar behavior with user defined types.
Without /recursive, local arrays such as a(3) are SAVE (static)
Only if /Qsave has also been specified. By default (i.e., without /Qsave or another option with that effect), local arrays are allocated on the stack; there is an option to allocate some local variables on the heap. I suspect that these options interact, and that one of these options may override the effects of other options. The documentation does not make these interactions known.
The behavior of local arrays being save or automatic has traditionally been default save. Newer language specifications may have changed the default behavior. From IVF 2017 documentation:
SAVE Statement and Attribute: Causes the values and definition of objects to be retained after execution of a RETURN or END statement in a subprogram. The SAVE attribute can be specified in a type declaration statement or a SAVE statement, and takes one of the following forms: Type Declaration Statement: type,[att-ls,] SAVE [, att-ls] :: entity[, entity ] ... Statement: SAVE [[::]entity [, entity ] ...] type Is a data type specifier. att-ls Is an optional list of attribute specifiers. entity Is the name of an object, the name of a procedure pointer, or the name of a common block enclosed in slashes (/common-block-name/). Description In Intel® Fortran, certain variables are given the SAVE attribute, or not, by default: •The following variables are not saved by default: ◦Scalar local variables of intrinsic types INTEGER, REAL, COMPLEX, and LOGICAL without default initialization ◦Variables that are declared AUTOMATIC ◦Local variables that are allocatable arrays ◦Derived-type variables that are data initialized by default initialization of any of their components ◦RECORD variables that are data initialized by default initialization specified in its STRUCTURE declaration •The following variables are saved by default: ◦COMMON variables ◦Scalar local variables not of intrinsic types INTEGER, REAL, COMPLEX, and LOGICAL of non-recursive subprograms ◦Non-scalar local variables of non-recursive subprograms ◦Module or submodule variables ◦Data initialized by DATA statements •Local variables that are not described in the preceding two lists are saved by default. Note Certain compiler options (such as optionssave and automatic) and the use of OpenMP* features can change the defaults. To enhance portability and avoid possible compiler warning messages, Intel recommends that you use the SAVE statement to name variables whose values you want to preserve between subprogram invocations. When a SAVE statement does not explicitly contain a list, all allowable items in the scoping unit are saved. A SAVE statement cannot specify the following (their values cannot be saved): •A blank common •An object in a common block •A procedure •A dummy argument •A function result •An automatic object •A PARAMETER (named) constant Even though a common block can be included in a SAVE statement, individual variables within the common block can become undefined (or redefined) in another scoping unit. If a common block is saved in any scoping unit of a program (other than the main program), it must be saved in every scoping unit in which the common block appears. A SAVE statement has no effect in a main program.
Have just encountered this trying to update from XE2013 to XE2019.
- It's passing the address of the function itself, rather than the address of the (same-named) result variable
- Using a differently-named result variable avoids the problem
- Can reproduce in XE2013 by explicitly declaring a function as recursive (but not with the /recursive compile flag). With 2019 the /recursive flag and explicit recursive declaration are equivalent, but in 2013 the behaviour is subtly different.
It does seem a bug, although it's understandable as for a recursive function the compiler has to decide whether the function name refers to the function or the result variable. When being passed to another routine it has no way of knowing in older-style Fortran.
If the compiler is passing the function address rather than the result variable, when RESULT is omitted, that's clearly a bug and should be reported to Intel. (Was it?) I'll note that Fortran 2018 makes procedures RECURSIVE by default, but I don't think ifort has formally adopted that change. (The RECURSIVE keyword is now "advisory-only" - a new NON_RECURSIVE keyword is used to specify the older behavior.)
I disagree with Jim that "default save" has been the default. Intel Fortran, in the absence of a specification that a procedure is recursive, has chosen to make arrays static - this is not the same as SAVE. Clearly this will have to change when ifort adopts the F2018 semantics.
>>has chosen to make arrays static - this is not the same as SAVE
Correct, the SAVE attribute is missing, but otherwise they are both located in static memory areas (may have different name manglings).