Wrangling FFI for a C function that returns two floating-point values

The C ABI for x86/x64 says that two registers are to be used to return floating-point values, thus if one returns a struct

typedef struct ff {
  double x;
  double y;
} ff;

struct ff pair(double x, double y) {
    struct ff r;
    r.x = x; r.y = y;
    return r;
}

it compiles to

00000000000010f9 <pair>:
    10f9:       f3 0f 1e fa             endbr64
    10fd:       55                      push   rbp
    10fe:       48 89 e5                mov    rbp,rsp
    1101:       53                      push   rbx
    1102:       f2 0f 11 45 d8          movsd  QWORD PTR [rbp-0x28],xmm0
    1107:       f2 0f 11 4d d0          movsd  QWORD PTR [rbp-0x30],xmm1
    110c:       f2 0f 10 45 d8          movsd  xmm0,QWORD PTR [rbp-0x28]
    1111:       f2 0f 11 45 e0          movsd  QWORD PTR [rbp-0x20],xmm0
    1116:       f2 0f 10 45 d8          movsd  xmm0,QWORD PTR [rbp-0x28]
    111b:       f2 0f 11 45 e8          movsd  QWORD PTR [rbp-0x18],xmm0
    1120:       48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
    1124:       48 8b 55 e8             mov    rdx,QWORD PTR [rbp-0x18]
    1128:       48 89 c6                mov    rsi,rax
    112b:       48 89 d7                mov    rdi,rdx
    112e:       48 89 c1                mov    rcx,rax
    1131:       48 89 d3                mov    rbx,rdx
    1134:       48 89 f0                mov    rax,rsi
    1137:       66 48 0f 6e cb          movq   xmm1,rbx
    113c:       66 48 0f 6e c0          movq   xmm0,rax
    1141:       48 8b 5d f8             mov    rbx,QWORD PTR [rbp-0x8]
    1145:       c9                      leave
    1146:       c3                      ret

i.e. it passes its arguments via registers, no for pointers/allocation/etc…

However, I can’t figure out how to interface this with GHC/Haskell! Is there a way to write foreign function declarations for such structs?

I believe the only way to do it is to create a wrapper function in C that returns a pointer to the struct:

void pair_ptr(double x, double y, struct ff* out) {
    *out = pair(x, y);
}
1 Like