- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
OPTIONAL+VALUE is not allowed.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page