Detection of Uninitialized Floating-Point Variables in Intel® Fortran

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
For more complete information about compiler optimizations, see our Optimization Notice.