Porting CVF Linker and @n suffix problems

Porting CVF Linker and @n suffix problems

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

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);
};

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

My Fortran interface looks like

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

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.

11 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.

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.

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?

Steve - Intel Developer Support

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)

Lines 11 and 29 should probably say

!DEC$ ATTRIBUTES REFERENCE :: MyStructPts

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.

Steve - Intel Developer Support

In Fortran the RESULT is defined as

TYPE RESULT
    LOGICAL(4) :: flag
    INTEGER(4) :: firstListLoc
    INTEGER(4) :: lastListLoc
    INTEGER(4) :: errorCode
END TYPE

In C++

struct Result
{
    long flag;
    long firstListLoc;
    long lastListLoc;
    long ec;
};

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

 

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.

Fichiers joints: 

Fichier attachéTaille
Télécharger example.zip16.99 Ko

Citation :

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

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.

Steve - Intel Developer Support

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.

Steve - Intel Developer Support

Laisser un commentaire

Veuillez ouvrir une session pour ajouter un commentaire. Pas encore membre ? Rejoignez-nous dès aujourd’hui