There is a large, common subset.
Certainly. There's even a large common subset with CFront 2.1
or Zortech 1.x---I have some code originally written for the old
Zortech which still compiles today.
The problem is knowing the limits of that subset in advance, for
all of the compilers you have to target now, and may have to
target in the future. With a reasonable degree of accuracy; you
can't possibly get it perfect, because you can't know in advance
what errors might be present in some future version.
Incidentally, I wrote some code last week that compiled and
worked as expected with one GCC 3.x, but not with a later 3.x.
The issue was that I moved a template definition up in its
translation unit, before the declaration of one of the
non-dependent names in the definition. The older GCC 3 failed
to notice that the name was not yet in scope when the template
was defined, but found the intended name when the template was
instantiated. I wonder how much processing of template
definitions the various compilers actually do.
Variations in the lookup of non-dependent names are probably the
biggest portability problem today. Curiously enough,
portability seems work best using the reverse of normal
procedure: code which compiles and works with the most recent
compiler will usually work with older versions. (Usually, it's
the opposite: code which compiles and works with older compilers
continues to work, but the fact that some code compiles and
works with a newer version doesn't mean it will do so with an
older one.)
[...]
Assembler varies wildly from one assembler to the next. (I
don't know Fortran, so no comment.)
Assembler varies from one machine to the next, but I've rarely
encountered problems with upgrading the version of the assembler
for the same machine.
Yes, thank you for the correction.
Even more insidious are instructions that still "work," but
very slowly.
That's one advantage JIT compilers have; they know the exact
machine they're running on, and can tune for it.