Встроенная сборка GCC - перед вызовом переместить float в XMM0 - PullRequest
6 голосов
/ 02 мая 2011

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

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

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

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

Должны быть значения с плавающей запятойпрошел через регистры xmm0 - xmm7.

Таким образом, проблема в основном заключается в следующем.У меня есть переменная 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 в качестве параметра.Это работает ...

Теперь я пытаюсь сделать то же самое с аргументом float.Мне нужно только использовать 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;
}

Функция, принимающая аргумент floatполучить 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;

EDIT2

По запросу, вот вывод 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>

1 Ответ

7 голосов
/ 03 мая 2011

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

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

...