[Vm-dev] f2c/g77 and problem with FFI 64bits returning float

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Fri Jan 27 00:57:40 UTC 2017

I have strange result with 64bits FFI function returning single precision
Here is an example:

(LapackSGEMatrix rows: #((2.3))) absMax.

This matrix has a single element, 2.3 rounded to single precision float
(2.299999952316284 when printed as a double precision)

absMax is supposed to take the maximum of absolute values in the matrix.
It does so thru Lapack function slange:
*  Purpose
*  =======
*  SLANGE  returns the value of the one norm,  or the Frobenius norm, or
*  the  infinity norm,  or the  element of  largest absolute value  of a
*  real matrix A.
    <cdecl: float 'slange_'( char * long * long * float * long * float *
long )>

Unfortunately above snippet returns 3.6893488147419103e19

It correctly calls this:
            floatRet =
dispatchFunctionPointerwithwithwithwithwithwith(((float (*)(sqIntptr_t,
sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)) procAddr),

which translates into something like:

    0x10833c537 <+2615>: movq   -0x28(%rbp), %rax
    0x10833c53b <+2619>: movq   -0xe8(%rbp), %rcx
    0x10833c542 <+2626>: movq   0xd8(%rcx), %rdi
    0x10833c549 <+2633>: movq   -0xe8(%rbp), %rcx
    0x10833c550 <+2640>: movq   0xe0(%rcx), %rsi
    0x10833c557 <+2647>: movq   -0xe8(%rbp), %rcx
    0x10833c55e <+2654>: movq   0xe8(%rcx), %rdx
    0x10833c565 <+2661>: movq   -0xe8(%rbp), %rcx
    0x10833c56c <+2668>: movq   0xf0(%rcx), %rcx
    0x10833c573 <+2675>: movq   -0xe8(%rbp), %r8
    0x10833c57a <+2682>: movq   0xf8(%r8), %r8
    0x10833c581 <+2689>: movq   -0xe8(%rbp), %r9
    0x10833c588 <+2696>: movq   0x100(%r9), %r9
->  0x10833c58f <+2703>: callq  *%rax
    0x10833c591 <+2705>: cvtss2sd %xmm0, %xmm0
    0x10833c595 <+2709>: movsd  %xmm0, -0x150(%rbp)

If I print $xmm0 just after the callq, then
(lldb) nexti
(lldb) print $xmm0
(unsigned char __attribute__((ext_vector_type(16)))) $212 = (0x00, 0x00,
0x00, 0x60, 0x66, 0x66, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00)

and just after the connversion to double precision:
(lldb) nexti
(lldb) print $xmm0
(unsigned char __attribute__((ext_vector_type(16)))) $213 = (0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00)

Let's see:

tmp := #[16r00   16r00   16r00   16r60   16r66   16r66   16r02   16r40 ].
{tmp doubleAt: 1.
tmp floatAt: 1}.
 #(2.299999952316284 3.6893488147419103e19)

Bingo! that means that the value returned in xmm0 was already in double
When we convert it back to single precision (it's like interpreting the 4
LSB of the double as a single precision), then we get the incorrect value...

So why was slange result promoted to double?
I can reproduce on macosx with pre-installed veclib, and in win64 compiling
LAPACK 3.3.1 from sources (translated by f2c) with MSVC10.

Ah, Ah, f2c! Dont you promote float return values to double? YES
But why this does not happen with the 32bits VM ???
That's what drove me off the solution for a while...
It's the IA32 ABI... return value is stored in ST0 (allways promoted to
So converting it to a double again like we do is a no-op and just works in

That's going to be a problem for FORTRAN functions on 64bits.
IF compiled thru g77 or f2c conventions, then float results are promoted to
IF compiled thru gfortran, then float result just remain float results.
It means a major source of incompatibility: how to guess how this binary
was compiled? (for example vecLib...)

And how to adapt my FFI source code?
Last thing, f2c might also be non standard when returning a complex value
Big ball of mud...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20170127/3b5f9fda/attachment.html>

More information about the Vm-dev mailing list