Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

interoperable function result

20 views
Skip to first unread message

amgarching

unread,
Feb 20, 2012, 4:08:44 PM2/20/12
to

Hi, All,

I dont quite get the calling convention of Intel 11.1 for
interoperable
functions. An identity function "id" for a "short structure" (sources
below)
compiles with Gfortran to this (x86_64):

0000000000000000 <id>:
0: 48 89 f8 mov %rdi,%rax
3: c3 retq

But with Intell 11.1 the same source compiles to something that
segfaults.
I assume it is the memory access to (%rdi), location at address stored
in
a register:

0000000000000010 <id>:
10: 48 89 37 mov %rsi,(%rdi)
13: 48 89 f8 mov %rdi,%rax
16: c3 retq
17: 90 nop
18: 90 nop
19: 48 8d bf 00 00 00 00 lea 0x0(%rdi),%rdi

The structure below has just one field, and obviousely it has just one
address,
what are those two "mov"es in the Intel-generated assembly?

Quite recently I had to give op on linking Intel compiled executable
with Gfortran compiled libraries, presumably, because of the calling
conventions with functions that return (double precision) complex
numbers.
Aka. ZDOTC() "bug". I think I need to understand the options/
differences better.
A would appreciate any comments, thanks in advance.

Alexei


module test

use iso_c_binding
implicit none
private

!
! Short structs with size matching that of some integer type may be
! passed/returned in registers. That depends on the platform ABI
! convention. BIND(C) on the type(t) and relevant function
! interfaces requests the Fortran compiler to emulate the behaviour of
! (some) C compiler.
!
! http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
!
type, public, bind(c) :: t
private
integer(c_intptr_t) :: ptr
end type t

public :: id

contains

function id(i) result(o) bind(c)
implicit none
type(t), intent(in), value :: i
type(t) :: o

o = i
end function id

end module test

amgarching

unread,
Feb 20, 2012, 5:21:38 PM2/20/12
to
On Feb 20, 10:08 pm, amgarching <alexei.matv...@gmail.com> wrote:

> I assume it is the memory access to (%rdi), location at address stored
> in
> a register:
>
> 0000000000000010 <id>:
>   10:   48 89 37                mov    %rsi,(%rdi)
>   13:   48 89 f8                mov    %rdi,%rax
>   16:   c3                      retq
>   17:   90                      nop
>   18:   90                      nop
>   19:   48 8d bf 00 00 00 00    lea    0x0(%rdi),%rdi
>

This is what the ABI spec, www.x86-64.org/documentation/abi.pdf ,
says about function results:

=======
Returning of Values

The returning of values is done according to the following
algorithm:
1. Classify the return type with the classification algorithm.
2. If the type has class MEMORY, then the caller provides space for
the return
value and passes the address of this storage in %rdi as if it were the
first
argument to the function. In effect, this address becomes a “hidden”
first argument.
This storage must not overlap any data visible to the callee through
other names than this argument.
On return %rax will contain the address that has been passed in by the
caller in %rdi.
3. If the class is INTEGER, the next available register of the
sequence %rax,
%rdx is used.

...

Definitions

We first define a number of classes to classify arguments. The
classes are corresponding to AMD64 register classes and defined as:

* INTEGER This class consists of integral types that fit into one of
the general
purpose registers.

...

* MEMORY This class consists of types that will be passed and returned
in memory
via the stack.
======

Apparently there is a difference in how the structure is classified
by the two compilers. Do they really have that much freedom?

Alexei

dpb

unread,
Feb 20, 2012, 6:08:20 PM2/20/12
to
On 2/20/2012 4:21 PM, amgarching wrote:
...

> Apparently there is a difference in how the structure is classified
> by the two compilers. Do they really have that much freedom?

The Standard places no limitations on the details of the implementation
internals--only on the syntax and what the results are to be.

_HOW_ is entirely compiler-dependent. Since this is Intel-specific
question, probably the Intel forum is the best place altho S Lionel
monitors clf pretty regularly and somebody else likely has sufficient
experience w/ the Intel compiler to respond (altho I don't; only that
you can't expect these details to be consistent between platforms).

--

amgarching

unread,
Feb 20, 2012, 6:19:36 PM2/20/12
to
On Feb 21, 12:08 am, dpb <n...@non.net> wrote:
> On 2/20/2012 4:21 PM, amgarching wrote:
> ...
>
> > Apparently there is a difference in how the structure is classified
> > by the two compilers. Do they really have that much freedom?
>
> The Standard places no limitations on the details of the implementation
> internals--only on the syntax and what the results are to be.
>
> _HOW_ is entirely compiler-dependent.

Yes, but bind(c) takes most of that freedom away.

dpb

unread,
Feb 20, 2012, 6:46:18 PM2/20/12
to
I don't think so (much), anyway...it only says "interoperable" compilers
and nothing says any two randomly chosen compilers are/must be
interoperable.

--

Richard Maine

unread,
Feb 20, 2012, 7:26:12 PM2/20/12
to
And indeed it is quite plausible that there could be compiler switches
to specify which of multiple possible companion processors bind(C) is to
be interoperable with. It's been a while now and the memory of that
specific detail is a little hazy, but I'm fairly sure that was in
people's minds when writing the f2003 interoperability spec. I'm 100%
sure that the subject of multiple conventions on the same platform came
up. I think that the conclusion was that this could be handled by
compiler switches, though I'm less sure about recalling that part.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain

amgarching

unread,
Feb 21, 2012, 6:03:02 AM2/21/12
to
> > > Yes, but bind(c) takes most of that freedom away.
>
> > I don't think so (much), anyway...it only says "interoperable" compilers
> > and nothing says any two randomly chosen compilers are/must be
> > interoperable.
>
> And indeed it is quite plausible that there could be compiler switches
> to specify which of multiple possible companion processors bind(C) is to
> be interoperable with.

Ok. I think I have found an argument that does not involve
particular conventions of an "accompanying C processor".
I think it is natural to expect that two functions with the
same public interface obey the same calling conventions.
Here an example, the two functions in the text below,

id :: type(t) -> type(t)

and

jd :: type(t) -> type(t)

are declared to have identical interfaces. Both interfaces
are exposed. Both are not only exposed to the "Fortran world"
but also to a wider world of programs making use of "accompanying
C processor". In fact jd() may be implemented in *either* C
or Fortran. I think this is a sufficient argument for the two
functions to conform to the same binary interface.

An inconsistency here is fatal. Here is what Intel generates for
a call to *either* id() or jd(). The assembly of caller_1()
and caller_2() are essentially identical, here only one is
shown:

test_mp_caller_2_:
pushq %rsi #55.14
movq test_mp_a_(%rip), %rdi #58.9
call id #58.9
movq %rax, test_mp_b_(%rip) #58.5
popq %rcx #59.3
ret

Note that the (address of the) module global variable test_mp_b_,
which is where the function result should go, is not referenced
before the call to id().

An of course, if you try to really invoke caller_2() from a
Fortran program you end with a segfault. Without a single
line of C in your sources.

In summary, even if bind(c) alone does not specify what calling
conventions to use it must be enforces consistently.

Alexei

===========
module test

use iso_c_binding
implicit none
private

!
! Short structs with size matching that of some integer type may be
! passed/returned in registers. That depends on the platform ABI
! convention. BIND(C) on the type(t) and relevant function
! interfaces requests the Fortran compiler to emulate the behaviour of
! (some) C compiler.
!
! http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
!
type, public, bind(c) :: t
private
integer(c_intptr_t) :: ptr
end type t

type(t) :: a, b

public :: id

public :: caller_1
public :: caller_2

interface
function jd(x) result(y) bind(c)
! jd :: type(t) -> type(t), same as id()
import
implicit none
type(t), intent(in), value :: x
type(t) :: y
end function jd
end interface

contains

function id(i) result(o) bind(c)
! id :: type(t) -> type(t), same as jd().
implicit none
type(t), intent(in), value :: i
type(t) :: o

o = i
end function id

subroutine caller_1()
implicit none

b = jd(a)
end subroutine caller_1

subroutine caller_2()
implicit none

b = id(a)
end subroutine caller_2

end module test

program prog

use test, only: caller_2
implicit none

call caller_2()
end program prog

dpb

unread,
Feb 21, 2012, 1:17:34 PM2/21/12
to
On 2/21/2012 5:03 AM, amgarching wrote:
...

> Ok. I think I have found an argument that does not involve
> particular conventions of an "accompanying C processor".
> I think it is natural to expect that two functions with the
> same public interface obey the same calling conventions.
> Here an example, the two functions in the text below,
>
> id :: type(t) -> type(t)
>
> and
>
> jd :: type(t) -> type(t)
>
> are declared to have identical interfaces. Both interfaces
> are exposed. Both are not only exposed to the "Fortran world"
> but also to a wider world of programs making use of "accompanying
> C processor". In fact jd() may be implemented in *either* C
> or Fortran. I think this is a sufficient argument for the two
> functions to conform to the same binary interface.
...

But, I don't think it's a requirement that the interoperable Fortran
compiler generate code for Fortran that's directly callable as C. I
think it's the same thing that Fortran calling Fortran and Fortran
calling C are not necessarily compatible unless the C side is generated
consistently. As I read your example, the caller is C-aware, the called
isn't.

Unless I've missed something here, as said earlier this appears to be an
Intel-specific implementation question; you'll probably do better at the
Intel support forum.

--

amgarching

unread,
Feb 21, 2012, 1:55:59 PM2/21/12
to

> calling C are not necessarily compatible unless the C side is generated
> consistently.

I should have stressed the inconsistency I meant more clearly:

(a) when instructed to call a (C- or Fortran) function with
interoperable
interface the compiler assumes one binary interface (here passing
input in
a register, expecting output in a register)

(b) when instructed to generate code for a Fortran function with
interoperable
interface the compiler assumes a *different* binary interface (here
passing input
in a register and storage for the output by reference)

Thus code generated by the Fortran compiler in (b) cannot be used by
the very same compiler in (a).

> Unless I've missed something here, as said earlier this appears to be an
> Intel-specific implementation question; you'll probably do better at the
> Intel support forum.

I did this, I needed a confirmation that these observations are
against
the letter/spirit of the standard. For future references I'll leave a
link
here:

http://software.intel.com/en-us/forums/showthread.php?t=103223&o=a&s=lr

Alexei

James Van Buskirk

unread,
Feb 21, 2012, 7:05:36 PM2/21/12
to
"Richard Maine" <nos...@see.signature> wrote in message
news:1kfruq4.ngzh4e5fh49kN%nos...@see.signature...

> And indeed it is quite plausible that there could be compiler switches
> to specify which of multiple possible companion processors bind(C) is to
> be interoperable with. It's been a while now and the memory of that
> specific detail is a little hazy, but I'm fairly sure that was in
> people's minds when writing the f2003 interoperability spec. I'm 100%
> sure that the subject of multiple conventions on the same platform came
> up. I think that the conclusion was that this could be handled by
> compiler switches, though I'm less sure about recalling that part.

However on Linux both ifort and gfortran are documented as being
interoperable with gcc. I thought maybe the O.P. was using an old
version of ifort but Steve Lionel's post to the companion thread
in Intel's forum leads me to believe that this is not the case and
ifort likely has a bug here.

I think multiple conventions could be handled by compiler switches
but it's not implemented that way in either ifort or gfortran.
The problem lies in STDCALL interfaces to invoke Win32 API functions
or create callbacks for such functions. ifort doesn't let you mix
BIND(C) with STDCALL and gfortran has a switch -mrtd that works but
the compiler then goes ahead and generates STDCALL interfaces to
even library functions even though their interface should be
explicit to the compiler and gfortran doesn't have STDCALL library
function to invoke :(

On ifort you have to use their !DEC$ ATTRIBUTES STDCALL extension
and gfortran only works in a reasonable way if you use their
!gcc$ ATTRIBUTES STDCALL extension. Switches would be much nicer
because you could use the same source code for both without
markup like C code would use.

Functions that return user-defined types are a particular sore
point in that on 32-bit Windows the reasonable standard for anyone
to follow is Microsoft Visual C and ifort is documented as being
interoperable but gcc, hence gfortran, have an incompatibility in
that for structures that can't be returned in registers, both
MSVC++ and gcc demand a hidden first argument that is a pointer
to storage for the result variable, but gcc pops that pointer off
the stack on return whereas MSVC++ doesn't! Now, a defensively
written (in assembly language) caller can ensure that neither
behavior causes problems but you can't create guard code like
this in high level language. Really, the MinGW people should
consider this incompatibility a bug and fix it (it's not a
problem in 64-bit Windows where everyone is more civilized and
use the same calling convention.)

For more about various calling conventions, I highly recommend
Agner Fog's

http://www.agner.org/optimize/calling_conventions.pdf

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


0 new messages