October 01, 2010
Hi,

I'm trying to understand how the inline assembler works in ldc, but I couldn't find any docs about the calling conventions. When I try to access the stack parameters, the function always segfaults, and I don't know how to fix it. I also looked at tango's BigNum asm implementation, and I tried to do the same (I didn't test though if tango's BigNum works). My platform is linux x86_64, here's what I did (or was trying to do):

import tango.io.Stdout;

/*
   In the following two functions I try to implement the identity
function that simply returns it's parameter. I don't get it to work,
though. */

uint identity_v1(uint x)
{
    asm
    {
        // D-linkage: x should be in EAX, result is returned in EAX
        // shouldn't have to do anything
        naked;
        ret;
    }
}

uint identity_v2(uint x)
{
    asm
    {
        // ok, identity_v1 doesn't work, maybe x is on the stack?
        naked;
        mov EAX, [ESP+4]; // segfaults, why?
        ret;
    }
}

// identity_v1/2 don't work, check if the result is really returned in
EAX
uint test(uint x)
{
    asm
    {
        naked;
        mov EAX, 123;
        ret;
    }
}

/* a bit more complex: returns true iff (a*b > c*d) */
bool cmpProduct(long a, long b, long c, long d)
{
    // I'm assuming d,c,b,a are in [ESP+i*8] for i=1,..,4
    // in that order
    asm
    {
        naked;
        // RBX:RCX = a*b
        mov RAX, [ESP+4*8];
        imul RAX, [ESP+3*8]; // why isn't one operand enough? bug?
        mov RBX, RDX;
        mov RCX, RAX;
        // RDX:RAX = c*d
        mov RAX, [ESP+2*8];
        imul RAX, [ESP+1*8];
        // EAX = (a*b > c*d)
        cmp RBX, RDX;
        jg AB_GREATER_CD;
        jl AB_LESS_CD;
        cmp RCX, RAX;
        xor EAX, EAX;
        setg AL;
        ret;
    AB_GREATER_CD:
        mov EAX, 1;
        ret;
    AB_LESS_CD:
        xor EAX, EAX;
        ret;
    }
}

void main()
{
    Stdout.formatln("identity_v1 = {}", identity_v1(4)); // returns 254, regardless of the parameter
    Stdout.formatln("identity_v2 = {}", identity_v2(4)); // segfaults
    Stdout.formatln("test = {}", test(4)); // returns 123, as expected
    Stdout.formatln("cmpProduct = {}", cmpProduct(1,2,3,4)); // segfaults
}

So my questions are:
(1) how do I access parameters?
(2) what are the ldc calling conventions on x86_64 platforms?
(3) what do I have to do to make identity_v1/2 and cmpProduct work?

(when (1) or (2) are sufficiently answered, question (3) of course
becomes obsolete)

October 02, 2010
The problem is mostly solved now, objdump saved me :)

It turns out ldc passes 32-bit parameters in EDI, ESI and 64-bit parameters in RDI, RSI. The result is in EAX or RAX, depending on the size. The segfaults I encountered are caused by using ESP as stackpointer, but in 64 bit mode this should of course have been RSP.

I think this solves all my practical problems for now, but it would be nice to know the detailed calling conventions, or are they maybe generated on-the-fly by llvm to get the best calling convention for each single function? I also tried to enforce the documented D calling convention with extern(D), and also tried extern(C), but that didn't change anything.
Top | Discussion index | About this forum | D home