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

stdcall calling convention for COM stuff

IanH
Honored Contributor III
695 Views

My understanding is that the stdcall calling convention specifies that returned objects of eight bytes or less in size are passed back through the EAX and EDX registers. 

However, if the function is a non-static member of a C++ class, MS VC++ appears to use a hidden pointer argument in order to return aggregate types, regardless of the size of the returned aggregate.  (Note the use of the word "appears" - I have no idea what is really supposed to happen.)

Is there a way to tell ifort that an otherwise stdcall function should always use this hidden parameter approach?  At the moment, I seem to get by by declaring the "function" as a subroutine, and explicitly including the returned thing as the second argument passed by reference.  Is this robust?

This approach also seems to work on x64.  Thoughts?

The issue has arisen when trying to write Fortran bindings for the Direct2D API.  Yes, I have run out of pills.  That API includes a handy method ID2D1RenderTarget::GetSize that returns an aggregate D2D1_SIZE_F that is simply comprised of two float's.

The Microsoft provided C interface for that particular API has the same issue as a Fortran function binding - generated code expects the size to come back in EAX and EDX and (on x86 at least) expects the called thing to execute a RET 4 instruction (the actual API appears to execute a RET 8 - four bytes for the pointer to the "this" object, four bytes for the hidden address).  I guess the number of people writing pure C interfaces to Direct2D is probably equal to the number of people writing Fortran interfaces, minus one.  And people think Fortran is dead.  While oggling around this topic I see bug reports filed against other compilers for other languages that are probably due to this difference.

 

0 Kudos
2 Replies
IanH
Honored Contributor III
695 Views

Attached (because attaching files to the first post of a thread has issues...) code that if compiled to assembly shows the equivalent difference that I'm talking about above.

Given a Fortran type:

[fortran]

TYPE, BIND(C) :: plain_old_data

  REAL(C_FLOAT) :: width

  REAL(C_FLOAT) :: height

END TYPE plain_old_data

[/fortran]

then the interface for a pointer to the C style call works out to be:

[fortran]

FUNCTION c_style_intf(not_this)

  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR

  IMPORT :: plain_old_data

  IMPLICIT NONE

  TYPE(C_PTR), INTENT(IN) :: not_this

  TYPE(plain_old_data) :: c_style_intf

  !DEC$ ATTRIBUTES STDCALL :: c_style_intf

END FUNCTION c_style_intf

[/fortran]

and the approximation for the C++ style call looks like:

[fortran]

SUBROUTINE cpp_style_intf(this, hidden)

  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR

  IMPORT :: plain_old_data

  IMPLICIT NONE

  TYPE(C_PTR), INTENT(IN) :: this

  TYPE(plain_old_data), INTENT(OUT) :: hidden

  !DEC$ ATTRIBUTES REFERENCE :: hidden

  !DEC$ ATTRIBUTES STDCALL :: cpp_style_intf

END SUBROUTINE cpp_style_intf

[/fortran]

0 Kudos
Steven_L_Intel1
Employee
695 Views

I would suggest just taking the subroutine approach here - it is robust. Unless you are using BIND(C), the Fortran compiler can choose to use or not use hidden arguments. (With BIND(C), it is disallowed from doing so.)
 

0 Kudos
Reply