Unexpected assembly output when passing a structure by value

Viewed 121

I'm currently researching the passing of structs by value. I have following code:

typedef struct s2{
    double d1;
    double d2;
    int a;
}S2;

void f_(S2 s);//Some external function, only used for compiling, so I can see the passing of parameters.

void f(){
    S2 s=(S2){0.5,1.0,1};
    f_(s);
}

If I compile it with -Og -S -fno-asynchronous-unwind-tables, I get following assembly output:

f:
    subq    $48, %rsp
    movq    .LC0(%rip), %rax
    movq    %rax, 8(%rsp)
    movq    .LC1(%rip), %rax
    movq    %rax, 16(%rsp)
    movl    $1, 24(%rsp)
    pushq   24(%rsp)
    pushq   24(%rsp)
    pushq   24(%rsp)
    call    f_@PLT
    addq    $72, %rsp
    ret
    .size   f, .-f
    .section    .rodata.cst8,"aM",@progbits,8
    .align 8
.LC0:
    .long   0
    .long   1071644672
    .align 8
.LC1:
    .long   0
    .long   1072693248

This assembly code, namely the passing over the stack was quite unexpected. Look into the System V Application Binary Interface. At page 17 following, the parameter passing is outlined.

The classification of the parameters is described on page 18-19.

The classification of aggregate (structures and arrays) and union types works as follows:

  1. If the size of an object is larger than four eightbytes, or it contains unaligned fields, it has class MEMORY. (S2 has the size 2*sizeof(double)+sizeof(int)=24, so less than 32 bytes.)

...Skipping 2, as it refers to C++....

  1. If the size of the aggregate exceeds a single eightbyte, each is classified separately. Each eightbyte gets initialized to class NO_CLASS. (This is true. So we have to classify each one)

Some definitions:

Arguments of types (signed and unsigned) _Bool,char,short,int,long,long long, and pointers are in the INTEGER class.

Arguments of types float,double,_Decimal32,_Decimal64 and __m64 are in class SSE.

Each member classified:

typedef struct s2{
    double d1;//SSE
    double d2;//SSE
    int a;//INTEGER
}S2;

Then how each argument should be passed:

  1. If the class is INTEGER, the next available register of the sequence %rdi,%rsi,%rdx,%rcx,%r8 and %r9 is used
  1. If the class is SSE, the next available vector register is used, the registers are taken in the order from %xmm0 to %xmm7.

So my proposal for the register allocation looks like this:

xmm0: d1
xmm1: d2
rsi:  a

This is clearly wrong, as shown by the compiler that passes the argument using the stack.

Where is my error? What detail did I miss?

0 Answers
Related