Avoiding Potential Problems - Intel(r) Xeon Phi(tm) coprocessor predefined macros and Fortran

Consider the following code which uses the __MIC__ macro:

program f_vs_F
  implicit none
  parameter ISIZE = 100000
  real(8), dimension(ISIZE) :: a
  !dir$ attributes offload:mic::run
  !dir$ offload target(mic)
  call run(a,ISIZE)
end program f_vs_F

!dir$ attributes offload:mic::run
subroutine run(a,isize)
  use omp_lib
  implicit none
  integer isize,i
  real(8) :: a(isize)
  !dir$ if defined (__MIC__)
    PRINT *,"Using offload compiler :  Hello from the coprocessor"
  !dir$ endif
  !$omp parallel do  private(i)
  do i = 1, isize
    a(i) = i
  enddo
  !$omp end parallel do
end subroutine run

This code offloads subroutine run if possible. If the offload succeeds, the subroutine prints out a message to let you know the code ran on the coprocessor.

If we put this code in a file named ifdef_test.f90, the code compiles with the ifort command without error. However, if we put this code in a file named ifdef_test.F90, we get the following errors:

ifdef_test.F90(36): remark #5082: *MIC* Directive ignored - Syntax error, found INTEGER_CONSTANT '1' when expecting one of: <IDENTIFIER> <CHAR_CON_KIND_PARAM> <CHAR_NAM_KIND_PARAM> <CHARACTER_CONSTANT>
!dir$ if defined (1)
------------------^
ifdef_test.F90(38): remark #5169: *MIC* Misplaced conditional compilation directive or nesting too deep
!dir$ endif
------^

What is going on here?

When a file ending in F90 is compiled using the ifort command, ifort calls the fpp preprocessor before compiling the code. Other things which will cause the preprocessor to be called are file names ending in F, FOR FTN, FPP or fpp, adding -fpp on the ifort command line or calling fpp directly.

The preprocessor modifies the source code before it is handed to the compiler. But wait, I can hear you saying - there are no preprocessor directives in this source code. And indeed there are no lines starting with a #. There is, however, a macro, __MIC__. 

When the code above is placed in a file ending in F90 and compiled, several things occur: 

  1. The code is passed to the fpp preprocessor with __MIC__ undefined. Because __MIC__ is not defined and is not used in a preprocessor directive, the preprocessor does not detect that it is being used as a macro. Hence it does not change the line "!dir$ if defined (__MIC__)".
  2. The compiler compiles the code from the preprocessor, with the __MIC__ still undefined. It processes the directive "!dir$ if defined (__MIC__)" and selects the code for __MIC__ undefined. It also processes the directive "!dir$ offload target(mic)" and notes that the code must next be compiled for execution on the coprocessor.
  3. The code is passed again to the fpp preprocessor, this time with __MIC__ defined to be 1. The preprocessor notes the existence of the macro and attempts to process it by changing the line "!dir$ if defined (__MIC__)" into "!dir$ if defined (1)"
  4. The compiler compiles this modified code, this time with __MIC__ defined. However, the code no longer contains the line "!dir$ if defined (__MIC__)". Instead it contains the line "!dir$ if defined (1)", which is not a valid directive and results in the error messages shown above.

The problem is the result of trying to provide the user with the best of both worlds - the power of Fortran and the flexibility of C style directives. If the __MIC__ macro were only defined during the compile phase and not during the preprocessing phase, the problem would not exist. However, doing that would mean that preprocessor directives using __MIC__ would not behave as expected.

The simplest change for the code shown above would be to rename the file from ifdef_test.F90 to ifdef_test.f90. But now consider the following code:

#define ISIZE 100000

program f_vs_F
  implicit none
  real(8), dimension(ISIZE) :: a
  !dir$ attributes offload:mic::run
  !dir$ offload target(mic)
  call run(a)
end program f_vs_F

!dir$ attributes offload:mic::run
subroutine run(a)
  use omp_lib
  implicit none
  integer i
  real(8) :: a(isize)
  !dir$ if defined (__MIC__)
    PRINT *,"Using offload compiler :  Hello from the coprocessor"
  !dir$ endif
  !$omp parallel do  private(i)
  do i = 1, ISIZE
    a(i) = i
  enddo
  !$omp end parallel do
end subroutine run

This code relies on a preprocessor definition to set the size of the array. This code must be passed through the preprocessor. Now what do you do?

In this case, the solution is to remove the Fortran style directive and replace it with a C style preprocessor directive:

#ifdef __MIC__
    PRINT *,"Using offload compiler :  Hello from the coprocessor"
#endif

So which should you use? "!dir$ if defined (__MIC__)" or "#ifdef __MIC__"? It depends on what other restrictions you have placed on the code. If your code already uses preprocessor directives or you think you might in the future, or if your naming conventions will cause the preprocessor to be invoked, the choice is simple - use "#ifdef __MIC__". If your coding standards prohibit the use of non-Fortran style statements in your code, your choice is again simple - use "!dir$ if defined (__MIC__). In all other cases --- do what you think best.

Para obter mais informações sobre otimizações de compiladores, consulte Aviso sobre otimizações.