New release of fpx

There is some standardization in the sense a lot of processors copy the more generic options from the gfortran command relatively recently, so something like

<processor_command> -E -dM empty.f90 

often works, but might need “empty.F90” or might need other options such as -cpp or -fpp or -Mpostprocess and so on. It would be nice if there were a consistent way to do that. I was thinking that it would be a “reserved” name so __COMPILER__ would be appropriate. Anyone can change the Wiki, so it would be nice if those with the compiler installed added notes there.

As an example of where it would be handy, if you install the cexpl command that is a CLI interface to Command Explorer/Godbolt you can easily test short self-contained Fortran code with eight or so compilers (and many versions of each as well) but it is quite a chore to query the command line options of the commands or determine what the predefined macros are on each platform (which can change from OS to OS even for a particular compiler). If there were a standard # directive that dumped the defined macros it would make dumping the macros trivial.

1 Like

That’s a good idea. I can easily add a command to display all the predefined variables: Right now, fpx support __LINE__, __FILE__, __FILENAME__, __TIME__, __DATE__, __TIMESTAMP__, __FUNC__, and __STDF__, __FPX__ (both returning 1 as per J3 specs).
fpx also defines (or redefines) __WIN32 and __WIN64 because they are not exported by gfortran.

I am not sure I am completely following the idea behind the __COMPILER__ macro. From what I understand, this one is passed by the processor to the preprocessor (e.g. from gfortran to cpp) using -D__COMPILER__=gfortran or something like this. Therefore it does not have much to do with the preprocessor.

Fortran already has the intrinsics compiler_version() and compiler_options(). Maybe substitute something along these lines:

"Compiler: " // compiler_version() // new_line(‘a’) // "Options: " // compiler_options()

That way you get the exact information about the compiler and the options that were used at run-time. The options can certainly differ per source file of course,

Thanks @Arjen for the tip. I have used these functions in benchmark.f. But here, I do not know which piece of info you try to get from compiler_version()/compiler_options(). At best you will know with which compiler fpx has been compiled with. Since fpx is processor agnostic, it can be compiled with gfortran and used with ifort.
Know that I think about it, it could be relevant when fpx is embedded and used as a library.

My idea was that this line of code is inserted into the preprocessed source. So:

write(,) COMPILER

would then be replaced by:

write(,) "Compiler: " // compiler_version() // new_line(‘a’) // "Options: " // compiler_options()

I should have used the code style:

write(*,*) __COMPILER__

becomes:

write(*,*) "Compiler: " // compiler_version() // new_line(‘a’) // "Options: " // compiler_options()

write(,) COMPILER

1 Like

You’d need to add a block to import from the iso_fortran_env module. It can’t be done directly in a write statement though.

The point is that __COMPILER__ is meant to be a string literal however, so you can use it in #if’s and such.

1 Like

Got it now. Makes sense, but as @ivanpribec mentioned it will have some limitations.

I find it convenient to standardize some platform-specific
attributes that unfortunately are generally only readily
available via the preprocessor called or built-in to the
compiler. In my special case most of my preprocessing and
templating is done by a preprocessor that just passes through
any cpp/fpp directives and even generates them. So for the
conditional compilation my preprocessor does not perform
a header similar to the following is added at the top of
the code …

#define  __INTEL_COMP        1
#define  __GFORTRAN_COMP     2
#define  __NVIDIA_COMP       3
#define  __NAG_COMP          4
#define  __LLVM_FLANG_COMP   5
#define  __FLANG_COMP        6
#define  __UNKNOWN_COMP   9999

#define FLOAT128
#undef  HAS_DT

#ifdef __INTEL_COMPILER
#   define __COMPILER__ __INTEL_COMP
#   define HAS_DT
#elif __GFORTRAN__ == 1
#   define __COMPILER__ __GFORTRAN_COMP
#   define HAS_DT
#elif __flang__
#   undef FLOAT128
#   undef HAS_DT
#   define __COMPILER__ __LLVM_FLANG_COMP
#elif _flang_
#   undef FLOAT128
#   undef HAS_DT
#   define __COMPILER__ __FLANG_COMP
#elif __NVCOMPILER
#   undef HAS_DT
#   undef FLOAT128
#   define __COMPILER__ __NVIDIA_COMP
#else
#   undef FLOAT128
#   define __COMPILER__ __UNKNOWN_COMP
#   warning  NOTE: UNKNOWN COMPILER
#endif

now the remaining cpp directives can use COMPILER.
Now that (most?) cpp (at least C+±compatible ones)
can handle string values this is due for an overhaul
anyway, but I guess fpx can only do that if it replaces
the default preprocessor so it gets passed those macro
definitions specific to the compiler.

Part of the reason to have the compiler dump the variables
it knows of instead of compiling is so you can get a list
of those and read them in for your cpp-compatible preprocessor.
So if you do have a command to dump the pre-defined
variables like

gfortran -cpp -E -dM empty.f90 >predefined.cfx

and then have your source do "#include “predefined.cfx”
and include the header up above you end up with COMPILER
defined. Both methods are platform specific. My method assumes
two stages of preprocessing which is best because I already
use another preprocessor. In the past I used m4() as the first
preprocessor stage. someone might use fppy() to do that as
another example. But for most people dumping the predefined
values and calling cfx or fpp or cpp first and not using the
built-it makes sense until all the vendors allow for fpx to
be used as the internal preprocessor.

f951: Fatal Error: empty.f90: No such file or directory
compilation terminated.

Example dump from gfortran

# 1 "empty.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "empty.f90"

#define __ATOMIC_ACQUIRE 2
#define __CHAR_BIT__ 8
#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __ORDER_PDP_ENDIAN__ 3412
#define __GFC_REAL_10__ 1
#define __FINITE_MATH_ONLY__ 0
#define __GNUC_PATCHLEVEL__ 1
#define __GFC_INT_2__ 1
#define __STDC_EMBED_FOUND__ 1
#define __STDC_EMBED_NOT_FOUND__ 0
#define __SIZEOF_INT__ 4
#define __SIZEOF_POINTER__ 8
#define __GFORTRAN__ 1
#define __GFC_REAL_16__ 1
#define __STDC_HOSTED__ 0
#define __NO_MATH_ERRNO__ 1
#define __STDC_EMBED_EMPTY__ 2
#define __SIZEOF_FLOAT__ 4
#define __pic__ 1
#define _LANGUAGE_FORTRAN 1
#define __SIZEOF_LONG__ 8
#define __GFC_INT_8__ 1
#define __SIZEOF_SHORT__ 2
#define __GNUC__ 16
#define __SIZEOF_LONG_DOUBLE__ 16
#define __BIGGEST_ALIGNMENT__ 16
#define __ATOMIC_RELAXED 0
#define _LP64 1
#define __GFC_INT_1__ 1
#define __ORDER_BIG_ENDIAN__ 4321
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __SIZEOF_SIZE_T__ 8
#define __PIC__ 1
#define __SIZEOF_DOUBLE__ 8
#define __ATOMIC_CONSUME 1
#define __GNUC_MINOR__ 1
#define __GFC_INT_16__ 1
#define __LP64__ 1
#define __ATOMIC_SEQ_CST 5
#define __SIZEOF_LONG_LONG__ 8
#define __ATOMIC_ACQ_REL 4
#define __ATOMIC_RELEASE 3
#define __VERSION__ "16.1.1 20260613"

So I was just wishing for a simple standard equivalent
for at least a few variables that let me know if REAL128
is available or which KINDS are supported or what compiler
is being used at the preprocessor macro stage.

But it looks like without some standardization from the
variable platform providers, which the standard is unlikely
to specify, that it is not a simple goal.

PS: to pretty-print the output from compiler_options() and compiler(v
ersion) I use the little “platform” procedure as in
the following example:

program test_id
use, intrinsic                 :: iso_fortran_env, only : int32, int64
implicit none
   call platform()
   write(*,*)
   call clockrate()
   write(*,*)
   call machineid()
contains

subroutine clockrate()
integer(kind=int64)            :: count64, count_rate64, count_max64
integer(kind=int64)            :: count64b
character(len=*), parameter    :: g = '(1x,*(g0,1x))'
integer(kind=int64)            :: i
integer(kind=int64)            :: j
   print g, huge(0_int64)
   call system_clock(count64,count_rate64,count_max64)
   print g, 'SYSTEM_CLOCK(3) (using integer(kind=int64), result may vary with argument type):'
   print g, 'COUNT_MAX(64bit)=    ', three(count_max64)
   print g, 'COUNT_RATE(64bit)=   ', three(count_rate64)
   print g, 'CURRENT COUNT(64bit)=', three(count64)

   do j=1,10
      call system_clock(count64,count_rate64,count_max64)
      do i=1,huge(0_int64)-1
         call system_clock(count64b,count_rate64,count_max64)
         if(count64b.ne.count64)then
            write(*,'(1x,g0)',advance='no')count64b-count64
            exit
         endif
      enddo
   enddo
   write(*,*)
end subroutine clockrate

subroutine platform()
use, intrinsic                 :: iso_fortran_env, only : compiler_version
use, intrinsic                 :: iso_fortran_env, only : compiler_options
implicit none
character(len=:),allocatable   :: version, options
character(len=*),parameter     :: nl=new_line('a')
integer                        :: where, start, break, i, last, col
   version=compiler_version()//' '
   options=' '//compiler_options()
   start=1
   do
      where=index(options(start:),' -')
      if(where.eq.0)exit
      break=where+start-1
      options(break:break)=nl
      start=where
   enddo
   if(start.eq.1)then
      do
         where=index(options(start:),' /')
         if(where.eq.0)exit
         break=where+start-1
         options(break:break)=nl
         start=where
      enddo
   endif
   last=len_trim(version)+1
   col=0
   do i=1,len_trim(version)
    col=col+1
    if(version(i:i).eq.' ')last=i
    if(col.gt.76)then
       version(last:last)=nl
       col=0
    endif
   enddo
   print '(a,/,3x,*(a))', 'This file was compiled by :', inset(version)
   if(options.ne.'')then
      print '(*(a))', 'using the options :', inset(options)
   endif
end subroutine platform

function inset(string) result(longer)
character(len=*),intent(in)    :: string
character(len=:),allocatable   :: longer
character(len=*),parameter     :: nl=new_line('a')
integer                        :: i
   longer=''
   do i=1,len(string)
      longer=longer//string(i:i)
      if(string(i:i).eq.nl)then
         longer=longer//'   '
      endif
   enddo
end function inset

function three(in) result(out)
integer(kind=int64),intent(in) :: in
character(len=:),allocatable   :: temp
character(len=:),allocatable   :: out
integer                        :: i
character(len=80)              :: line
   write(line,*)abs(in)
   temp='  '//trim(adjustl(line))
   out=''
   do i=len(temp),3,-3
      out=','//temp(i-2:i)//out
   enddo
   out=trim(adjustl(out(2:)))
   out=merge(' ','-',in>0)//out
end function three

subroutine machineid()
! OS code for machine, not real hardware ID.
! should look something like 566f9e9774c46d993b0k4f5f57f6a8
integer            :: lun
integer            :: iostat
character(len=256) :: line
   open(newunit=lun,file='/var/lib/dbus/machine-id',iostat=iostat)
   if(iostat.eq.0)then
      read(lun,'(a)',iostat=iostat)line
      if(iostat.eq.0)then
         write(*,'(*(g0))')'pseudo machine-id : ',trim(line)
      endif
   endif
end subroutine machineid

end program test_id
```
where the output has some linebreaks added to make it a bit neater:
```text
This file was compiled by :
   GCC version 16.1.1 20260613 
using the options :
   -I build/gfortran_87E2AE0597D39913
   -mtune=generic
   -march=x86-64
   -g
   -Wall
   -Wextra
   -Werror=implicit-interface
   -fPIC
   -fmax-errors=1
   -fbounds-check
   -fcheck=array-temps
   -fbacktrace
   -fcoarray=single
   -fimplicit-none
   -ffree-form
   -J build/gfortran_87E2AE0597D39913

 9223372036854775807
 SYSTEM_CLOCK(3) (using integer(kind=int64), result may vary with argument type):
 COUNT_MAX(64bit)=      9,223,372,036,854,775,807
 COUNT_RATE(64bit)=     1,000,000,000
 CURRENT COUNT(64bit)=  11,919,876,060,600
 100 100 200 100 200 200 100 100 200 200

pseudo machine-id : 566f9e977c3c4c46d993b04f5f57f6a8
```

May I ask what’s the purpose of doubling down on #defines?

I mean, you could just use the macros defined by the compiler whenever you’re using __COMPILER__ right now, without having to bother about the actual value:

    interface mygeneric
        module procedure mygeneric32, mygeneric64
#if defined(__INTEL_COMPILER) || defined(__GFORTRAN__)
        module procedure mygeneric128
#endif
    end interface

contains
...
#if defined(__INTEL_COMPILER) || defined(__GFORTRAN__)
    subroutine mygeneric128(arg, ...)
    ...
    end subroutine
#endif

Also, using the defined operator is better than relying on a macro being 1, e.g.:

#elif defined(__GFORTRAN__)

The predefined values are only available using the supplied preprocessors which all vary. In some cases you can specify your own preprocessor but typically a custom version built into the compiler that is typically not well documented with subtle differences in behavior is used (if any is supported at all). Some use cpp directly, which is not designed for Fortran but for C/C++. A standardized preprocessor such as fpx solves many of those issues but then we are still left with the problem that each compiler supports its own predefined macros. Some of the most common ones such as compiler IDs go by different names so you have to look them up for each compiler. Some compilers have not even provided a reliable variable for that purpose. Those names have also changed in the past. Some did not use the __ prefix but defined variables like “linux” that already occurred in code, causing inadvertent substitutions. You can avoid this current state of affairs by using fpx and preprocessing the data yourself. That leaves one piece missing in most cases – use of the predefined variables you mention (and their subsequent non-standarization as well). So to get to those you can in some cases dump the predefined variables and read them into fpx or other cpp-like processors. (If that ability exists for the compiler). Those variables are not standardized, which reduces their value. The simplest and most common variable I want is one that tells me which compiler (and hopefully which version) is being used. So what is the command to dump the variables for a Cray, IBM, gfortran, lfortran, ifx, nvfortran, nagfor, flang, … compiler. Just this abbreviated list varies as to whether real128 is supported, whether C-escapes are expanded in strings by default, essentially all have a method for accessing common system functions that are unique to each compiler. You list the macros for two compilers. What are the variables for the rest?

If it is simple to do, it would be nice for fpx to provide some predefined standard-named variables to help with that. Unfortunately it does not appear to be a simple thing to do. So when the standard committee finishes specifying the preprocessor will they also include some macro names to be required for such features? Almost certainly not. So If you know the macro name for all the compilers it might be nice if you added them to the Fortran Wiki page along with the command that lets you dump the macro for use by fpx or other preprocessors.

Not having access to the predefined macros was a major hindrance to coco, a previous optional but standard-specified preprocessor. Here we are just pondering what it would take for fpx to avoid the same issue. One advantage fpx has on this front is that it is cpp-like as are most of the pre-processors supplied by various compilers. So IF the compiler supplies a command to dump its macros in a format fpx can read that helps (although that command, if it exists, is also not standarized). Even though that solves many issues there is still the issue that there is no standard for the predefined macros. It would be nice if the standard specified some standard macros as well that would be a list of kinds available for intrinsics, a compiler version/name, .Some thoughts

  • it would be nice if fpm added a -D__COMPILER__= to compiler calls. One issue with this is at least one preprocessor in the past only allowed macro names that started with an ASCII alphameric so the leading underscore might be problematic.
  • it would be nice to have a Wiki page describing how to use fpx as the compiler-invoked preprocessor. Intel describes how to provide your own preprocessor, but it had several trouble tickets open on the process last I looked. In some cases just renaming fpx to cpp or fpp and putting it early in your command search path might work, but not always; or fpm could just try to do that by default as well.
  • In the long term if all compilers are required to call an fpx-compatible preprocessor with their predefined variables and all accept the syntax -D NAME=VALUE things will be much better.

I really like the idea, and I will see what I can do.
It is fairly easy to define macros about the OS. fpx contains a slighly modified version of the ‘os’ module from fpm. I already use it to redefine Windows related variables. I can extend it to include all supported os types.

For the compiler, this is another story since I do not know which processor is calling. Tighter integration with fpm could solve that issue but it would be limited to the use of fpm as a build system.

I recall that coco had a side program (env2inc ?) to retrieve some environmental variables. There might be something to look at.

There might be the possibility to read it from the environmental variable ‘FC’. That would work with fpm, Cmake,… but not from IDE like visual studio.

Hm, I had not thought of that - I was thinking of the typical use of LINE and such :innocent:

I may have found a solution to provide the __COMPILER__ macro. It’s a bit hacky though. The idea is to proceed as follows:

  1. The macro __COMPILER__ is expliciely provided;
  2. The FC environmental variable is defined
  3. Get the parent pid and the name using combinations of reading /proc/self/status and /proc/{pid}/comm (on Linux), or using the command ps -p {pid} -o comm= (on other Unix systems), and CreateToolhelp32Snapshot and CreateToolhelp32Snapshot on Windows.

The third step is the last resort option and should probably be recursive until finding a compiler name (or hitting 1). I also thought of bypassing this and using compiler_version if fpx is embedded rather than used as a program.

Does anyone have a better idea, especially on Unix systems?