Version Intel® Fortran Compiler version 18.0 and newer
Product Intel® Parallel Studio XE
Operating System Windows*, Linux*, macOS*
Architecture IA-32, Intel64
Reading from a variable before it has been written to can result in application errors that are not reproducible and can be difficult to debug, especially for global variables and variables passed as procedure arguments. The Intel® Fortran Compiler version 18 and newer can detect accesses to a wide variety of uninitialized floating-point variables. (The feature is also available in earlier Intel compilers with some restrictions.) This is achieved by initializing such floating-point data to a signaling NaN and then trapping the floating-point invalid exception that occurs if the data are used in a floating-point operation before they have been assigned a value by the application.
Method The application should first be compiled and run with the options /fpe:0 /traceback (Windows) or -fpe0 -traceback (Linux or macOS) to ensure there are no unexpected floating-point exceptions from other sources. If there are, these should be corrected.
Next, compile the application with the options /Qinit:snan,arrays /traceback (Windows) or -init=snan,arrays -traceback (Linux or macOS). This automatically sets /fpe:0 (Windows) or -fpe0 (Linux or macOS), which causes floating-point exceptions to be trapped. The “traceback” switch causes any such exceptions to be followed by a lightweight traceback that includes the procedure name, file name and line number where the exception occurred. If the exception results from an operation on an uninitialized floating-point variable, you will typically see the message:
forrtl: error (182): floating invalid - possible uninitialized real/complex variable.
If the “arrays” argument is omitted, uninitialized scalars will be detected, but uninitialized array elements will not.
In general, the following classes of REAL, COMPLEX, INTEGER, or LOGICAL variables of any KIND are supported:
- SAVEd (static) scalar or array variables that are not initialized in the source code
- Local scalars and arrays
- Automatic arrays
- Module variables that are not initialized in the source code
- ALLOCATABLE arrays and scalars
with the following restrictions:
- Variables in EQUIVALENCE groups
- Variables in COMMON
- Derived types, arrays of derived types and their components are not supported
- Dummy procedure arguments are not initialized to a signaling NaN locally. They may be so initialized in the calling procedure, if they meet the above criteria
- References in arguments to intrinsic procedures or I/O statements may not be detected
- Integers can be set to zero, huge or minus_huge
- In a COMPLEX variable, each of the real and imaginary part is separately initialized as REAL.
Warning /Qinit:snan (Windows) or -init=snan (Linux or macOS) unmasks floating-point exceptions so that use of SNaNs in a floating-point operation will be detected and trapped. If you build with optimization, the compiler may speculate floating-point operations, assuming the default floating-point environment in which floating-point exceptions are masked. When you add /Qinit:snan (-init=snan), this speculation may result in exceptions that now get trapped.
The simplest way to avoid this is to reduce the optimization level to /O1 or /Od (-O1 or -O0) when doing uninitialized variable detection. If you wish to maintain optimization, you should add the switch /Qfp-speculation:safe (-fp-speculation=safe) to disable speculation, if there is a possibility that the speculation may cause a floating-point exception.
Another possibility is to add the switch /fp:strict (-fp-model strict), which tells the compiler not to assume the default floating-point environment, but this is likely to have a bigger impact on performance than /Qfp-speculation:safe (-fp-speculation=safe).
Example (Source code at the end of the article)
Whilst it’s usually a good idea to compile with –O0 (Linux or macOS) or /Od (Windows) when debugging, this is not required for uninitialized variable detection. First, make sure the application runs without floating-point exceptions:
$ ifort -O0 -fpe0 -traceback uninitialized.f90; ./a.out
0.0000000E+00 -8.7806177E+13 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 1.000000 1.000000 0.0000000E+00
2.000000 0.0000000E+00 0.0000000E+00 3.000000
0.0000000E+00 0.0000000E+00 0.0000000E+00
1.1448686E+24 0.0000000E+00
No floating-point exceptions, but one or two strange-looking values.
Next, look for uninitialized scalars.
$ ifort -O0 -init=snan -traceback uninitialized.f90; ./a.out
NaN NaN 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 1.000000 1.000000 0.0000000E+00
2.000000 0.0000000E+00 0.0000000E+00 3.000000
NaN 0.0000000E+00 0.0000000E+00
1.1448686E+24 0.0000000E+00
forrtl: error (182): floating invalid - possible uninitialized real/complex variable.
Image PC Routine Line Source
a.out 0000000000405224 Unknown Unknown Unknown
libpthread-2.17.s 00007F4032803130 Unknown Unknown Unknown
a.out 0000000000403C61 sub_ 39 uninit.f90
a.out 000000000040418A MAIN__ 62 uninit.f90
a.out 0000000000403622 Unknown Unknown Unknown
libc-2.17.so 00007F4032250AF5 __libc_start_main Unknown Unknown
a.out 0000000000403529 Unknown Unknown Unknown
Aborted (core dumped)
Here we see otherwise uninitialized scalars being initialized to a NaN. The access to the module variable AM at line 39 results in an exception, diagnostic and traceback.
Next, look also for uninitialized arrays.
$ ifort -O0 -init=snan,arrays -traceback uninitialized.f90; ./a.out
NaN NaN NaN NaN NaN
NaN 1.000000 1.000000 NaN
2.000000 NaN NaN 3.000000
NaN NaN NaN
NaN NaN
forrtl: error (182): floating invalid - possible uninitialized real/complex variable.
Image PC Routine Line Source
a.out 000000000040AB64 Unknown Unknown Unknown
libpthread-2.17.s 00007F903EA5B130 Unknown Unknown Unknown
a.out 0000000000403BED sub_ 36 uninit.f90
a.out 0000000000404278 MAIN__ 62 uninit.f90
a.out 0000000000403662 Unknown Unknown Unknown
libc-2.17.so 00007F903E4A8AF5 __libc_start_main Unknown Unknown
a.out 0000000000403569 Unknown Unknown Unknown
Aborted (core dumped)
Array elements that are not explicitly initialized by the application are also being initialized to a NaN. The access to the automatic array F at line 36 results in an exception, diagnostic and traceback. Although the uninitialized variable is not identified by name, the source file and line number make it easy to find. If the application is compiled with -g (Linux or macOS) or /Zi (Windows), it also possible to use a symbolic debugger: when the debugger breaks at the floating-point exception, the variable contents may be examined directly.
Source Code
! ==============================================================
!
! SAMPLE SOURCE CODE - SUBJECT TO THE TERMS OF SAMPLE CODE LICENSE AGREEMENT,
! http://software.intel.com/en-us/articles/intel-sample-source-code-license-agreement/
!
! Copyright 2015 Intel Corporation
!
! THIS FILE IS PROVIDED "AS IS" WITH NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
! NOT LIMITED TO ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
! PURPOSE, NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS.
!
! ===============================================================
module mymod
integer, parameter :: n=100
real :: am
real, allocatable, dimension(:) :: dm
real, target, dimension(n) :: em
real, pointer, dimension(:) :: fm
end module mymod
subroutine sub(a, b, c, d, e, m)
use mymod
integer, intent(in) :: m
Real, intent(in), dimension(n) :: c
Real, intent(in), dimension(*) :: d
Real, intent(inout), dimension(*) :: e
Real, automatic, dimension(m) :: f
Real :: a, b
print *, a,b,c(2),c(n/2+1),c(n-1)
print *, d(1:n:33) ! first and last elements uninitialized
print *, e(1:n:30) ! middle two elements uninitialized
print *, am, dm(n/2), em(n/2)
print *, f(1:2) ! automatic array uninitialized
e(1) = f(1) + f(2)
em(1)= dm(1) + dm(2)
em(2)= fm(1) + fm(2)
b = 2.*am
e(2) = d(1) + d(2)
e(3) = c(1) + c(2)
a = 2.*b
end
program uninit
use mymod
implicit none
Real, save :: a
Real, automatic :: b
Real, save, target, dimension(n) :: c
Real, allocatable, dimension(:) :: d
Real, dimension(n) :: e
allocate (d (n))
allocate (dm(n))
fm => c
d(5:96) = 1.0
e(1:20) = 2.0
e(80:100) = 3.0
call sub(a,b,c,d,e(:),n/2)
deallocate(d)
deallocate(dm)
end program uninit
"