rse,

When good compilers go bad

Iain Barrass Iain Barrass Follow Dec 16, 2019 · 12 mins read
When good compilers go bad
Share this

Many people rely on compilers, for languages such as C, C++ and Fortran, to create executable programs from source code. Just like our source code, compilers themselves may have bugs. In this post we look at common forms of compiler bug, with examples, and what we can do when our work is affected by such an issue.

The compilers we use to convert a program’s source code into a usable piece of software are generally very complicated programs and are themselves susceptible to containing bugs. In this post we look at the types of bugs found in Fortran compilers and what we, as users of compilers, can do about them.

What is a compiler bug?

We can broadly categorize compiler bugs by the nature of the resulting undesirable behaviour. For example, here we’ll consider the following cases:

  • reporting an internal compiler error/crashing;
  • rejecting a valid program source file;
  • failing to make a required diagnosis;
  • generating an incorrect program.

As well as these obvious bugs there are deficiencies in the user experience which we can call quality of implementation issues:

  • providing weak error detection or diagnostics;
  • producing a poorly performing program (time or accuracy);
  • taking disproportionate resources to compile.

When these latter are sufficiently severe they may also be classed as “bugs”. In this post we’ll look mostly at examples from the first class.

Internal compiler error

The most obvious of bugs in a compiler is where the compiler simply crashes or reports an internal error. We may see messages such as

Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-8/README.Bugs> for instructions.
catastrophic error: **Internal compiler error: internal abort**
Please report this error along with the circumstances in which it
occurred in a Software Problem Report. Note: File and line given
may not be explicit cause of this error.
PGF90-S-0000-Internal compiler error. get_llvm_name: bad stype for     298
Panic: polite.f90: hello oops VERY_SORRY
Internal Error -- please report this bug
Abort

or

/opt/ibm/xlf/16.1.1/bin/.orig/xlf: 1501-230 (S) Internal compiler
error; please contact your Service Representative.

A Fortran compiler may compile Fortran source to C source for a C compiler to create a program. An internal error in the Fortran compiler may instead result in C being produced which cannot be compiled:

bad_c.f90: In function ‘s_’:
bad_c.f90:1:3: error: ‘x_’ is a pointer; did you mean to use ‘->’?
 subroutine s(n)
   ^
   ->

Rejecting valid source

A compiler may determine that a source file is invalid, perhaps reporting a syntax error, even when the source is itself allowed according to the language standard. This may not necessarily be a bug: it could be that the compiler doesn’t yet understand a particular feature of the evolving Fortran language. However, if the compiler vendor claims to have support for a feature but then rejects a program, we can call that a bug. For example, given

  procedure(double precision) f
end program

we are declaring an external procedure f which is a function with result of type double precision and an implicit interface. This is a very basic form introduced in Fortran 2003. A compiler not supporting this syntax may report an error in the source, but a compiler message rejecting this source like

error #5082: Syntax error, found IDENTIFIER 'PRECISION' when expecting one of:
COMPLEX PRECISION procedure(double precision) f
-------------------^

is from a somewhat confused compiler.

Compilers may also be confused by the program

  procedure(type) a
contains
  function type()
  end function
end program

erroneously reporting a syntax error.

Equally, rejecting the program

interface
  subroutine sub
  end
end interface
end program

with a complaint that

PGF90-W-0155-END statement for internal procedure should be END SUBROUTINE

shows some difficulty in understanding the source. Here, it isn’t necessary for end subroutine [sub] to be used instead of a simple end, and the subroutine sub isn’t an internal procedure. Not only is rejecting this source a mistake, but the error message itself doesn’t apply.

Accepting invalid source

The definition of the Fortran language tells us how a Fortran processor must treat a program which conforms to the specification. If instead of providing a valid Fortran program we provide invalid source the compiler may have the ability to treat it like a valid program with extensions. For example, while the source

slksfdlfdlhd0fdlkfdhl035o53iofhd9ufgd098nhd df09fgd05te0 bfdljkfdjkld0985elk
end program

is unlikely to be viewed favourably by a compiler, the source

  integer i
  read *, i
  if (i) then
    print *, 'You gave me a true value'
  end if
end program

is not valid Fortran but can be understood by many compilers. While it can be seen as desirable for this to be rejected with a message like

Expression in IF construct is not logical

a compiler may document accepting this as an extension to the language. This wouldn’t usually be considered a bug, regardless of how unfortunate that choice may be.

However, certain invalid aspects of a program may require a compiler to be able to diagnose that problem. For example, if we have the source file

  integer, allocatable :: i
  allocate(i,source=0.)
end program

which is attempting sourced allocation for the integer i, we have an allocation statement which is not allowed in a Fortran program. The source here is a real constant which is not type compatible with the variable to be allocated.

If a compiler cannot detect and report such an invalid allocation statement then it does not conform to the Fortran language standards beyond Fortran 2003: the compiler vendor would likely consider this to be a bug.

Generating incorrect program

In some cases, the Fortran standard gives a precise description of the expected result of a Fortran program. As in other sections here, deviation from presenting the required result would be a non-conformance and regarded a bug. For example, the result of the program

  type t(n)
     integer, len :: n=1
     integer i(n)
  end type t

  type(t(:)), allocatable :: x
  allocate(t :: x)

  x%i = 0
  associate (y=>x%i)
    y = 1
  end associate

  print '(I1)', x%i

end program

should be the line

1

The Intel Fortran compiler version 18.0.3, however, does not give this result.

Poor compilation performance

The compilation process should use reasonable resources, proportionate for the complexity of the program to be compiled. When compilation takes much longer to complete than expected, or requires vastly more memory, this may be viewed as a bug.

For example, given the program

  type t1(n)
     integer, kind :: n
  end type t1

  type t(n)
     integer, kind :: n
     type(t1(1)) x(n)
  end type t

  type(t(1)) x

end program

gfortran 8.3.0 (but not gfortran 9.1.0) may consume vast amounts of memory.

What can we do when we find a compiler bug?

As with other software, when we find a bug in a compiler we want to find ways to avoid the problem. There may be a later version of the compiler without this bug that can be used or we can report the bug to the compiler developer and hope for a timely fix.

If you’re using the compilers installed on Apocrita and would like a newer release to be installed, then please contact us. You can also contact us for help reporting the bug to the developer, especially if using one of the commercially supported compilers such as those from Intel and NAG.

The RSE team can also help you in trimming down your large programs which upset a compiler to give smaller examples of code similar to those presented above.

Sometimes, internal compiler errors may come about when we write incorrect code. For example, the following program sees ifort 19.0.5 crash:

  type t(n)
     integer, len :: n
     real x(n)
  end type t
  type(t(k%n)) :: y
end program

This isn’t legal Fortran code (because k hasn’t been declared as of a type with component n), but it would be preferable for the compiler not to crash. In such cases we’d correct our code but we should still consider reporting the bug to the compiler developer.

Finally, it may be necessary to modify your own (correct) source code to work around the compiler bug: all releases of a compiler may have the problem or it may be necessary for you to support a particular version of a given compiler with your program.

For example, although the valid program

contains
  subroutine s
  end
end program

results in a fatal error in pgfortran 19.4 we can easily change this to a form happily accepted:

program main
contains
  subroutine s
  end
end program

With a suitable program name such a change may even be the preferred form of a program. In other cases, necessary changes may be less desirable such as trying to have ifort 19.0.5 compile one of our earlier programs:

  procedure(double precision) f
end program

We can modify this to the equally valid and meaningful

  procedure(doubleprecision) f
end program

or

  procedure(real(kind(0d0))) f
end program

which the Intel compiler accepts. Such forms may be less preferred than the original and may well require additional documentation if this is inconsistent with other double precision declarations in the source code.

Conclusion

Bugs in compilers, although hopefully rare, are sadly a feature of our life developing research software. In this post we’ve seen examples of different types of bugs in Fortran compilers and what “internal compiler error” messages may look like.

Compiler crashes are usually quite obvious and we’ve considered what we can do when we see one. Other types of bugs, such as incorrectly rejecting a valid program, may be more challenging to spot: the RSE team can help advise in cases where it isn’t clear whether it’s the compiler or the program at fault.

In yet other cases, there may be bugs which make the results of the research program incorrect. This shows us how important it is to ensure we have good testing for our software to catch not just our own errors but compiler problems, or even portability pains.

What we shouldn’t conclude is that Fortran compilers are horribly unreliable or that Fortran is a poor language to choose. Bugs are rare and compilers for other languages also have examples of bugs not shown in this post.

Please contact us for advice around issues with potential compiler bugs, software testing or portability of your research software.

Iain Barrass
Written by Iain Barrass
RSE Team Leader. He prefers Fortran, with a background in infectious diseases