Встроенная сборка GCC - переместить float в XMM0 перед вызовом

В настоящее время я пытаюсь вызвать общую функцию C из встроенной сборки GCC (я знаю, плохая идея, но мне сегодня скучно ...).

Моя операционная система - Mac OS X, 64 бита, поэтому соглашение о вызовах - System V, то есть аргументы 0-6 передаются через регистры rdi, rsi, rdx, rcx, r8 и r9. Остальные аргументы помещаются в стек.

Я знаю сигнатуру функции, поэтому могу угадать возвращаемый тип и тип аргументов. Обладая этой информацией, я могу поместить аргументы в правильные регистры.

Все отлично работает с целочисленными типами, но у меня проблема со значениями с плавающей запятой.

Значения с плавающей запятой необходимо передавать через регистры _7 _-_ 8_.

Итак, проблема в основном в следующем. У меня есть переменная C типа float. Мне нужно переместить эту переменную, скажем, в регистр xmm0, используя встроенную сборку GCC.

Представьте себе следующий код:

#include <stdio.h>

void foo( int x )
{
    printf( "X: %i\n", x );
}

int main( void )
{
    int x = 42;

    __asm__
    (
        "mov %[x], %%rdi;"
        "call _foo;"
        :
        : [ x ] "m" ( x )
    );

    return 0;
}

Вызывается функция foo с параметром 42. Оно работает...

Теперь я пробую то же самое с аргументом с плавающей запятой. Мне нужно только использовать movss вместо mov, и это работает.

Проблема возникает, когда я пытаюсь вызвать обе функции:

#include <stdio.h>

void foo( int a )
{
    printf( "A: %i\n", a );
}

void bar( float b )
{
    printf( "B: %f\n", b );
}

int main( void )
{
    int   a = 42;
    float b = 42;

    __asm__
    (
        "mov %[a], %%rdi;"
        "call _foo;"
        "movss %[b], %%xmm0;"
        "call _bar;"
        :
        : [ a ] "m" ( a ),
          [ b ] "m" ( b )
    );

    return 0;
}

Функция, принимающая аргумент с плавающей запятой, получает 0. Не понимаю почему. Я не трогаю стопку, поэтому чистки не нужно ...

Если я вызываю функции непосредственно из C, GCC выдаст следующее:

movl    $42, -4(%rbp)
movl    $0x42280000, %eax
movl    %eax, -8(%rbp)
movl    -4(%rbp), %edi
call    _foo
movss   -8(%rbp), %xmm0
call    _bar

Я не понимаю разницы ... Любая помощь будет принята с благодарностью :)

Хорошего дня всем

ИЗМЕНИТЬ

По запросу, вот вывод ASM при использовании встроенной сборки:

 movl    $42, -4(%rbp)
 movl    $0x42280000, %eax
 movl    %eax, -8(%rbp)
 mov    -4(%rbp), %rdi;
 call    _foo;
 movl    -8(%rbp), %eax;
 movl    %eax, -4(%rbp);
 movss    -4(%rbp), %xmm0;
 call    _bar;

РЕДАКТИРОВАТЬ2

Как и просили, вот вывод GDB:

0x100000e9e <main+4>:   movl   $0x2a,-0x4(%rbp)
0x100000ea5 <main+11>:  mov    $0x42280000,%eax
0x100000eaa <main+16>:  mov    %eax,-0x8(%rbp)
0x100000ead <main+19>:  mov    -0x4(%rbp),%rdi
0x100000eb1 <main+23>:  callq  0x100000e54 <foo>
0x100000eb6 <main+28>:  movss  -0x8(%rbp),%xmm0
0x100000ebb <main+33>:  callq  0x100000e75 <bar>

person Macmade    schedule 02.05.2011    source источник
comment
Разместите asm, сгенерированный gcc (используйте gcc -S <whatever>).   -  person Michael Burr    schedule 02.05.2011
comment
Важная часть уже внизу поста. Скажите, если вам нужен другой код ... :) Спасибо   -  person Macmade    schedule 02.05.2011
comment
Я думал, что сборка внизу сообщения - это то, что сгенерировал gcc при использовании C для вызова foo() и bar(). Я бы хотел увидеть, что генерирует gcc, когда вы используете встроенную сборку для вызова этих функций?   -  person Michael Burr    schedule 02.05.2011
comment
Вот так ... Немного другое, вы правы ...   -  person Macmade    schedule 02.05.2011
comment
Похоже, что встроенная сборка должна загружать 0x42280000 в xmm0 (хотя я не знаю, почему он предпринимает дополнительные шаги, чтобы сделать это через -4(%rbp)) - мне пришлось бы пройти через это с помощью отладчика, чтобы увидеть, что мне не хватает, что установка xmm0 на ноль. К сожалению, на данный момент у меня нет gcc, ориентированного на x64. Вы пробовали пройти через сборку в gdb, чтобы увидеть, где что-то не так?   -  person Michael Burr    schedule 02.05.2011
comment
Я добавил вывод GDB :)   -  person Macmade    schedule 03.05.2011


Ответы (1)


Это заняло у меня время, но я понял это. В выводе, использующем встроенную сборку, gcc использует отрицательные смещения rbp для хранения значений. Однако, поскольку он не знает о вызовах функций во встроенной сборке, он не думает, что вызывает какие-либо функции. Следовательно, он помещает переменные в красную зону и не меняет rsp, чтобы освободить место для переменных. Когда вы вызываете foo, адрес возврата помещается в стек, перезаписывая ваши сохраненные переменные и предоставляя вам неверную переменную.

Если в любой момент основной функции вне сборки вы вызвали функцию, то gcc изменит стек, чтобы сохранить переменные. Например, если вы добавите foo(-1); в начало main, это сработает.

person ughoavgfhw    schedule 02.05.2011
comment
Хорошая уловка - я предполагал, что это как-то связано с красной зоной, но недостаточно опыта с сборкой x64 и отсутствия тестового стенда, чтобы сказать наверняка (или сказать, что именно). - person Michael Burr; 03.05.2011
comment
Побил меня на пять минут - :( Вам просто нужно посмотреть в начало main, чтобы увидеть, что rsp == rbp, и поэтому аргументы перезаписываются адресом возврата. - person Gunther Piez; 03.05.2011