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

Porting CVF Linker and @n suffix problems

Aaron_S_1
Beginner
669 Views

I am porting some legacy code from CVF to Intel Visual Fortran. I am trying to call some subroutines in a C++ DLL. I have include the C++ DLL's .lib file in my INCLUDE path but the linker isn't finding the routines. This code works in CVF. The C++ code has

[cpp]

extern "C"
{
    _declspec(dllexport) Result __declspec stdcall Foo1(long Num, long numPts, MyStruct *MyStructPtr);
    _declspec(dllexport) Result __declspec stdcall Foo2(long Num, long numPts, MyStruct *MyStructPtr);
    _declspec(dllexport) Result __declspec stdcall Foo3(long Num, double length);
};

[/cpp]

In the .lib file I can see these functions are called _Foo1@12, _Foo2@12, and _Foo3@12.

My Fortran interface looks like

[fortran]

MODULE My_Interface

  IMPLICIT NONE

  INTERFACE

    TYPE(RESULT) FUNCTION Foo1( Num, NumPts, MyStructPts )

      !DEC$ ATTRIBUTES DLLIMPORT,STDCALL :: Foo1


      !DEC$ ATTRIBUTES DECORATE, ALIAS:'Foo1' :: Foo1

      !DEC$ ATTRIBUTES VALUE :: Num, NumPts

      !DEC$ ATTRIBUTES REFERENCE :: MyStruct,

      integer(4),intent(in) :: Num

      integer(4),intent(in) :: NumPts

      type(MyStruct),intent(in) :: MystructPts(0:NumPts)

    END FUNCTION Foo1

  END INTERFACE

  INTERFACE

    TYPE(RESULT) FUNCTION Foo2( Num, NumPts, MyStructPts )

      !DEC$ ATTRIBUTES DLLIMPORT,STDCALL :: Foo2

      !DEC$ ATTRIBUTES DECORATE, ALIAS:'Foo2' :: Foo2

      !DEC$ ATTRIBUTES VALUE :: Num, NumPts

      !DEC$ ATTRIBUTES REFERENCE :: MyStruct

      integer(4),intent(in) :: Num

      integer(4),intent(in) :: NumPts

      type(MyStruct),intent(in) :: MyStructPts(0:NumPts)

    END FUNCTION Foo2

  END INTERFACE

  INTERFACE

    TYPE(RESULT) FUNCTION Foo3( Num, length )

      !DEC$ ATTRIBUTES DLLIMPORT,STDCALL :: Foo3

      !DEC$ ATTRIBUTES DECORATE, ALIAS:'Foo3' :: Foo3

      !DEC$ ATTRIBUTES VALUE :: Num, length

      integer(4),intent(in) :: Num

      real(8),intent(in) :: length

    END FUNCTION Foo3

  END INTERFACE

END MODULE My_Interface

[/fortran]

In CVF the linker will look for the routines _Foo1@12, _Foo2@12, and _Foo3@12 and the link is satisfied. In intel fortran the linker is looking for _Foo1@16, _Foo2@16 and _Foo3@16. Why is this? Is there a compiler option to coreect the problem? Does it have something to do with the return argument? I know there were changes here from CVF to IVF and I read the porting apps from CVF whitepaper and none of it seemed to apply to my situation.

Thanks in advance for any insight.

0 Kudos
10 Replies
Aaron_S_1
Beginner
669 Views

The C++ was compiled in Visual Studio 6,  and I don't have the ability to recompile the code. I have hundereds of these interfaces throughout my code base and these are the only 3 that aren't working. They are also the only 3 that are functions rather than subroutines.

0 Kudos
Steven_L_Intel1
Employee
669 Views

I suspect it has to do with the function returning a structure. Intel Fortran is passing a hidden argument for this. I will take a look at it tomorrow. What is the declaration of type RESULT?

0 Kudos
IanH
Honored Contributor III
669 Views

If the size in bytes of an object of type "RESULT" is big enough (more than 64 bits) then the compiler passes an additional hidden address to receive the function result value - hence the additional four bytes in the @nn. 

Please show the C and Fortran declarations of this type.

(oops, sorry for the redundancy)

0 Kudos
mecej4
Honored Contributor III
669 Views

Lines 11 and 29 should probably say

!DEC$ ATTRIBUTES REFERENCE :: MyStructPts

0 Kudos
Steven_L_Intel1
Employee
669 Views

I have tried some experiments and can't reproduce a problem. I recognize that the code posted is NOT the real code and is a paraphrase, with all of the inherent errors and omissions that introduces. The declaration of type Result in both the C++ and Fortran code is critical to understanding the issue.

As Ian says, if the size of type RESULT is 64-bits or less, then no hidden argument is passed, and my experiments with IVF show that to be the case. We need more information - ideally, a buildable example.

0 Kudos
Aaron_S_1
Beginner
669 Views

In Fortran the RESULT is defined as

[fortran]

TYPE RESULT

    LOGICAL(4) :: flag

    INTEGER(4) :: firstListLoc

    INTEGER(4) :: lastListLoc

    INTEGER(4) :: errorCode

END TYPE

[/fortran]

In C++

[cpp]

struct Result

{

    long flag;

    long firstListLoc;

    long lastListLoc;

    long ec;

};

[/cpp]

I will try to create a buildable example. Thanks for the help.

 

0 Kudos
Aaron_S_1
Beginner
669 Views

I've created an example where everything is used exactly as it would be in my code. The compiler is trying to link against _Foo@16. Unfortunately, I have no ability to create a C++ library to use in the example. I am contractually obligated to keep my computer free of any files with an extension associated with C++ or C, with the exception of header files.

0 Kudos
mecej4
Honored Contributor III
669 Views

Unfortunately, I have no ability to create a C++ library to use in the example.
Not a problem, since we can see, by compiling the sources that you provided, that the external names generated are __imp_Foo1@16, __imp_Foo2@16 and __imp_Foo3@16. Since the function return value occupies 16 bytes (in IA32 code), and therefore cannot be returned in %eax, an extra argument is pushed on the stack to receive the return value. CVF 6.6C does the same thing, but does not include this extra argument in computing the value of nn in the @nn name decoration used with Stdcall. If further checking shows that it is only the name decoration that causes a problem, and that the calling sequence is otherwise compatible with your C++ DLL, a simple fix may be possible that does not require rebuilding the DLL, if rebuilding is not feasible

0 Kudos
Steven_L_Intel1
Employee
669 Views

Ok - I can reproduce the difference, but am still trying to figure out how C++ is returning the result.  I note that MSVC gives the routine the @4 suffix ven though logic suggests it should be @8. Stay tuned.

0 Kudos
Steven_L_Intel1
Employee
669 Views

Alright, my mind is officially blown...

MSVC does something truly bizarre in this case. For a function returning a struct larger than 8 bytes, it expects the address of a place to store the return value to be passed as a "hidden" first argument, just as Fortran does.  But..  If you declare the routine with __stdcall, MSVC inexplicably omits this hidden argument when creating the @n name decoration.  In the case at hand, where there are 16 bytes pushed on the stack - and the C routine ends with a "ret 16", MSVC uses @12 as the suffix!  I can't find any documentation explaining this - it is horribly inconsistent with everything I have read.

Now something even more strange. If in CVF you explicitly give the function the STDCALL attribute, then it will also ignore the hidden argument when computing the suffix. But not if you leave it off - and remember that CVF defaults to STDCALL!

My guess is that somewhere along the way we noticed this inconsistency and fixed it. If you had a Fortran function returning a 16-byte derived type and compiled it with CVF, its name would have @16 at the end.

So, what to do?  We can't "fix" this in the compiler, because it would break calls to Fortran functions that have the STDCALL convention.  The workaround for you is to not use DECORATE and explicitly put the full name in the ALIAS, such as '_Foo1@12'. IVF will call the routine correctly and you'll work around the name mismatch.

0 Kudos
Reply