Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Changes to fastcall calling convention on x64

Tobias_Loew
Novice
1,078 Views

Hi,

I just ported mixed language code from VS 2012/IF 2013 SP1 to VS2015/IF 2016 and got strange "check-stack" errors. I'm having a static FORTRAN lib with modules which is linked into an MSVC dll. The FORTRAN lib is compiled with /iface:default in has the following interface

      module rgtafneu_module

      REAL*8 FUNCTION      RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT
     F            (TYP,E1,E2,A1,A2,GAT_SPLIT,AA_,WDT_SWT,GASTYP,REAL_GAS_CORRECTION,FNCVMETHOD,TABLE_ID,
     F             IER1) 

      IMPLICIT      NONE

      INTEGER  * 4, INTENT(IN), VALUE :: TYP     
      REAL     * 8, INTENT(IN), VALUE :: E1         
      REAL     * 8, INTENT(IN), VALUE :: E2         
      REAL     * 8, INTENT(IN), VALUE :: A1         
      REAL     * 8, INTENT(IN), VALUE :: A2         
      type(gat_type), INTENT(INOUT)        :: GAT_SPLIT    
      REAL     * 8, INTENT(IN)        :: AA_(4)           
      INTEGER  * 4, INTENT(IN), VALUE :: WDT_SWT    
      INTEGER  * 4, INTENT(IN), VALUE :: GASTYP    
      INTEGER  * 4, INTENT(IN), VALUE :: REAL_GAS_CORRECTION    
    INTEGER  * 4, INTENT(IN),VALUE  :: FNCVMETHOD 
      INTEGER  * 4, INTENT(IN), VALUE :: TABLE_ID   
      INTEGER  * 4, INTENT(INOUT)     :: IER1       

 

when this function gets called directly from C++ I get an exception/error because FORTRAN expects the parameters E1, E2 and A1 to be transferred via float-registers xmm1 to xmm3 AND rdx, r8 and r9 which is NOT the standard fastcall-convention. (see asm-dump at the end)

When I add the "BIND(C)" attribute to the function everything works fine again but I've got some questions:

- where can I find the documentation to this non-standard changing of parameter passing

- since I produce (at least I expect to) a COFF static FORTRAN library, I expect it to be fully compatible to the standard. Is there any way to enforce this (apart from adding BIND(C) to every function/procedure)

- how compatible are static FORTRAN 2016-libraries with older FORTRAN code? (Since there is no change in the name-mangling I expect not.)

best ragards

Tobias

------------------------------------------------------------------------------------------------------

asm-dump of BIND(C) version:
 

     REAL*8 FUNCTION      RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT
000000000BB991D7  push        rbp  
000000000BB991D8  sub         rsp,3F0h  
000000000BB991DF  lea         rbp,[rsp+70h]  
000000000BB991E4  mov         qword ptr [rsp],rax  
000000000BB991E8  mov         rax,3ECh  
000000000BB991EF  mov         dword ptr [rsp+rax],0CCCCCCCCh  
000000000BB991F6  sub         rax,4  
000000000BB991FA  cmp         rax,4  
000000000BB991FE  jg          RGTAFNEU_MODULE_mp_RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT+18h (0BB991EFh)  
000000000BB99200  mov         rax,qword ptr [rsp]  
000000000BB99204  mov         dword ptr [rsp],0CCCCCCCCh  
000000000BB9920B  mov         dword ptr [rsp+4],0CCCCCCCCh  
000000000BB99213  mov         qword ptr [rbp+378h],rsi  
000000000BB9921A  mov         qword ptr [rbp+370h],rbx  
000000000BB99221  mov         dword ptr [TYP],ecx  
000000000BB99227  movsd       mmword ptr [E1],xmm1                    !<<<<<<<<< float transferred via xmm
000000000BB9922F  movsd       mmword ptr [E2],xmm2                    !<<<<<<<<< float transferred via xmm
000000000BB99237  movsd       mmword ptr [A1],xmm3                    !<<<<<<<<< float transferred via xmm
000000000BB9923F  fld         qword ptr [A2]  
000000000BB99245  wait  
000000000BB99246  fstp        st(0)  
000000000BB99248  mov         byte ptr [rbp+299h],0  
000000000BB9924F  mov         byte ptr [rbp+29Ah],0  
000000000BB99256  mov         byte ptr [rbp+298h],0  
000000000BB9925D  mov         byte ptr [rbp+298h],1  
000000000BB99264  movsd       xmm0,mmword ptr [string L"ERROR : Unable to in"...+4700h (0C03EF80h)]  
000000000BB9926C  movsd       mmword ptr [RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT],xmm0  
     F            (TYP,E1,E2,A1,A2,GAT_SPLIT,AA_,WDT_SWT,GASTYP,REAL_GAS_CORRECTION,FNCVMETHOD,TABLE_ID,
     F             IER1)  BIND(C,NAME='RGTAFNEU_MODULE_mp_RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT')


asm-dump of non-BIND(C) version:

 

      REAL*8 FUNCTION      RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT
000000000BB891D7  push        rbp  
000000000BB891D8  sub         rsp,3F0h  
000000000BB891DF  lea         rbp,[rsp+70h]  
000000000BB891E4  mov         qword ptr [rsp],rax  
000000000BB891E8  mov         rax,3ECh  
000000000BB891EF  mov         dword ptr [rsp+rax],0CCCCCCCCh  
000000000BB891F6  sub         rax,4  
000000000BB891FA  cmp         rax,4  
000000000BB891FE  jg          RGTAFNEU_MODULE_mp_RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT+18h (0BB891EFh)  
000000000BB89200  mov         rax,qword ptr [rsp]  
000000000BB89204  mov         dword ptr [rsp],0CCCCCCCCh  
000000000BB8920B  mov         dword ptr [rsp+4],0CCCCCCCCh  
000000000BB89213  mov         qword ptr [rbp+378h],rsi  
000000000BB8921A  mov         qword ptr [rbp+370h],rbx  
000000000BB89221  mov         qword ptr [TYP],rcx  
000000000BB89228  mov         qword ptr [E1],rdx                    !<<<<<<<<< float expected in rcx (non-standard fastcall)
000000000BB8922F  mov         qword ptr [E2],r8                     !<<<<<<<<< float expected in r8 (non-standard fastcall) 
000000000BB89236  mov         qword ptr [A1],r9                     !<<<<<<<<< float expected in r9 (non-standard fastcall)  
000000000BB8923D  mov         byte ptr [rbp+299h],0  
000000000BB89244  mov         byte ptr [rbp+29Ah],0  
000000000BB8924B  mov         byte ptr [rbp+298h],0  
000000000BB89252  mov         byte ptr [rbp+298h],1  
000000000BB89259  movsd       xmm0,mmword ptr [string L"ERROR : Unable to in"...+4700h (0C02EF80h)]  
000000000BB89261  movsd       mmword ptr [RAUCHGASSCHNITTSTELLE_INTERNAL_INOUT],xmm0  
     F            (TYP,E1,E2,A1,A2,GAT_SPLIT,AA_,WDT_SWT,GASTYP,REAL_GAS_CORRECTION,FNCVMETHOD,TABLE_ID,
     F             IER1) 

 

0 Kudos
1 Solution
IanH
Honored Contributor II
1,078 Views

There was a change in the argument passing of VALUE dummy arguments of non-BIND(C) procedures with ifort 15.0 (see section 3.3.1.1 of the XE 2015 release notes, not sure about 2016), where the /assume:std_value option was made the default.  With this option active, for VALUE arguments of non-BIND(C) procedures, a pointer to a temporary that corresponds to the "definable anonymous data object" specified by VALUE is passed, not the value of the data object itself.

The registers used to pass the arguments then reflect that a machine pointer is being passed, not a floating point value.

If you want C calling conventions, including its version of pass by value, then you need BIND(C).

View solution in original post

0 Kudos
7 Replies
IanH
Honored Contributor II
1,079 Views

There was a change in the argument passing of VALUE dummy arguments of non-BIND(C) procedures with ifort 15.0 (see section 3.3.1.1 of the XE 2015 release notes, not sure about 2016), where the /assume:std_value option was made the default.  With this option active, for VALUE arguments of non-BIND(C) procedures, a pointer to a temporary that corresponds to the "definable anonymous data object" specified by VALUE is passed, not the value of the data object itself.

The registers used to pass the arguments then reflect that a machine pointer is being passed, not a floating point value.

If you want C calling conventions, including its version of pass by value, then you need BIND(C).

0 Kudos
Tobias_Loew
Novice
1,078 Views

Thanks a lot for the heads-up. But I've still got a question:

Assume you've got a static FORTRAN library with both, BIND(C) and non-BIND(C) functions, and this library is linked into another FORTRAN exe or dll. How does the compiler decide, which calling convention to use? Do I need explicit interfaces to solve that?

0 Kudos
mecej4
Honored Contributor III
1,078 Views

The compiler will use the default Fortran calling convention unless you tell it otherwise. Calling subprograms with dummy arguments that have the VALUE attribute or subprograms with the BIND(C) attribute are two among many cases where the standard (section 12.4.2.2) stipulates that an explicit interface must be provided.

I think that you misinterpreted how the XMM registers were being used in lines 17 to 19 of your listings. The Intel/Microsoft assembler convention is mov dest, source. Certain values are being moved from the XMM registers into memory, either to be saved across the function call or to be passed through memory as arguments.

Unless you suspect a compiler error and you know exactly the details of the ABI, it is probably not helpful to look at complex assembler listings.

0 Kudos
Tobias_Loew
Novice
1,078 Views

mecej4 wrote:

The compiler will use the default Fortran calling convention unless you tell it otherwise. Calling subprograms with dummy arguments that have the VALUE attribute or subprograms with the BIND(C) attribute are two among many cases where the standard (section 12.4.2.2) stipulates that an explicit interface must be provided.

I think that you misinterpreted how the XMM registers were being used in lines 17 to 19 of your listings. The Intel/Microsoft assembler convention is mov dest, source. Certain values are being moved from the XMM registers into memory, either to be saved across the function call or to be passed through memory as arguments.

Unless you suspect a compiler error and you know exactly the details of the ABI, it is probably not helpful to look at complex assembler listings.

I think you misunderstood the initial post: lines 17 - 19 save the transferred arguments 2-4 into the local variables (conformant to the fastcall-convention), a transfer in the opposite direction is neither mentioned nor implied.

Furthermore, an inspection of the assembler-code is indispensable to track down exceptions triggered by FORTRAN code like the "check-stack"-exception when developing mixed language programs.

0 Kudos
mecej4
Honored Contributor III
1,078 Views

Some points of confusion could be cleared up if you provided the source code from which the assembler listings in #1 were produced, and the compiler options used were listed (if other than default).

0 Kudos
JVanB
Valued Contributor II
1,078 Views

The compiler is using the FASTCALL convention in both cases. The issue is that in f2003 the standards committee decided to allow dummy arguments to have both the OPTIONAL and VALUE attributes. The reasonable way to deal with this new wrinkle, in my opinion guided by hindsight, would have been to leave f95-conformant code as it was but to copy actual arguments corresponding to OPTIONAL+VALUE dummies into a temporary memory location (using Fortran rules for copying structures) and then pass the address of that location as the (machine language) argument.

Neither ifort nor gfortran chose to do this. gfortran does leave f95-conformant code intact, but it adds a hidden argument for OPTIONAL+VALUE dummy arguments with the PRESENT status, much like the hidden LEN that gets passed along with a CHARACTER argument. When C interop starts allowing OPTIONAL dummy arguments (but without the VALUE attribute) and requires C_NULL_PTR to be passed by value for a non-PRESENT actual argument, I think gfortran may see the light.

ifort decided to change their calling convention so that a copy is always made and the address of the copy is always what gets passed. That is why in lines 17:19 without BIND(C) the parameters are passed in RDX, R8, and R9, because addresses are being passed rather than REAL(8) values. This behavior is not well documented in ifort's manual. There are several junctures where one has the ability to ask the compiler to pass by value, and you really have to write a test program to find out what is going to happen in a given case. I wrote one such and posted it, but I don't know how to find that post again just now.

There is also the /assume:nostd_value option, but the compiler doesn't document what happens when an OPTIONAL+VALUE dummy argument is passed with this switch active... another test program would be required.

 

0 Kudos
Steven_L_Intel1
Employee
1,078 Views

OPTIONAL+VALUE is not allowed.

0 Kudos
Reply