Как использовать переменные C в коде ассемблера в C ++ при компиляции в GCC? - PullRequest
0 голосов
/ 28 декабря 2011

Я скомпилировал этот код, используя Visual Studio,

double callVariadicDoubleFunc(double * doubles, unsigned int numDoubles,double(*TestFunc)(double,...))

{

    // sizeof(double) must be 8!
    if (numDoubles == 0)
        return 0;
    double * lastDouble = doubles + (numDoubles - 1);
    double res;
    int temp;

    __asm mov eax, numDoubles
    __asm mov edx, lastDouble
    __asm mov temp,esp

    __asm label_loop:
    __asm sub esp, 8
    __asm fld qword ptr [edx]
    __asm fstp qword ptr [esp]
    __asm sub eax, 1
    __asm sub edx, 8
    __asm test eax, eax
    __asm jnz label_loop

    __asm call TestFunc
    __asm fstp        qword ptr res;
    __asm mov esp, temp

    return res;
}

но сейчас я пытаюсь скомпилировать его с помощью gcc, но есть ошибка, которую я не могу устранить! чтобы убрать все ошибки во время компиляции, я немного изменил этот код в этой форме:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)(...)) 
{
    int temp;
    double res;
    arguments += numDoubles;
    asm("mov eax, numDoubles"  "\n"
        "mov ecx, arguments"   "\n"
        "mov temp,esp"         "\n"

        "label_loop:"          "\n"
        "sub esp, 8"           "\n"
        "fld qword ptr [ecx]"  "\n"
        "fstp qword ptr [esp]" "\n"
        "sub eax, 1"           "\n"
        "sub ecx, 8"           "\n"
        "test eax, eax"        "\n"
        "jnz label_loop"       "\n"

        "call mFunction"       "\n"
        "fstp qword ptr res"   "\n"
        "mov esp, temp"        );
    return res;
}

, но теперь я получаю ошибки связывания:

undefined reference to `numDoubles'
undefined reference to `arguments'
undefined reference to `temp'

Есть идеи, как их разрешить?

sidenote: я компилирую свой код с такими параметрами: "- g -masm = intel -O0 -Wall"

Ответы [ 2 ]

2 голосов
/ 28 декабря 2011

Все немного сложнее.Вы должны использовать расширенную сборку GCC с ограничениями.Кроме того, использование temp для сохранения указателя стека является плохой идеей, поскольку адреса локальных переменных зависят от значения указателя стека.Лучше создать стандартный кадр стека.

Также вы забыли вычесть 1 из указателя.В качестве бонуса, в GCC нет необходимости во временной переменной для последнего указателя в массиве.

Что-то вроде:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)()) 
{
    double res;
    asm(
        /* set up the frame pointer */
        "push ebp"             "\n"
        "mov ebp, esp"         "\n"

        "label_loop:"          "\n"
        "sub esp, 8"           "\n"
        "fld qword ptr [%2]"   "\n"
        "fstp qword ptr [esp]" "\n"
        "sub %1, 1"            "\n"
        "sub %2, 8"            "\n"
        "test %1, %1"          "\n"
        "jnz label_loop"       "\n"

        "call %3"              "\n"
        "fstp qword ptr [%0]"  "\n"
        "mov esp, ebp"         "\n"
        "pop ebp"              "\n"
        : /* no output */
        :"b"(&res), "a"(numDoubles), "c"(arguments + (numDoubles - 1)), "d"(mFunction) /* input */
        :"cc" /* clobber */);
    return res;
}

Переменная res не может бытьиспользуется в качестве вывода, потому что инструкция fstp требует указатель, который является входом.Вы можете использовать ограничение m, если вы не делали смешных вещей со стеком (см. Мое исправление ниже).

ДРУГОЕ ИСПРАВЛЕНИЕ : И вы можете использовать ограничение rесли вам не нужно было перечислять EAX, ECX и EDX в закрытом списке, потому что они не сохраняются посредством вызовов функций (и вы вызываете функцию).Но вы не можете перечислить засоренный регистр, который используется в качестве ввода / вывода.

Обратите внимание, что &res использует ограничение "b", что является , сохраненным при вызове функции, и поэтомуfstp будет работать как положено.

Наконец, только «cc» указан в списке «засечек», потому что вы изменяете регистр флагов (с test) и с вызовом функции.

Запустив gcc -masm=intel -save-temps мы можем проверить сгенерированную сборку:

; Before the asm
mov     eax, DWORD PTR [ebp+12] ; numDoubles
sub     eax, 1 
sal     eax, 3 
mov     ecx, eax           
add     ecx, DWORD PTR [ebp+8]  ; arguments + 8*(numDoubles - 1)
lea     ebx, [ebp-16]           ; res
mov     eax, DWORD PTR [ebp+12] ; numDoubles
mov     edx, DWORD PTR [ebp+16] ; mFunction

; The asm
push ebp
mov ebp, esp
label_loop: 
sub esp, 8  
fld qword ptr [ecx]
fstp qword ptr [esp]
sub eax, 1
sub ecx, 8
test eax, eax
jnz label_loop
call edx         ; clobbers eax, ecx, edx and flags
fstp qword ptr [ebx]
mov esp, ebp
pop ebp

; After the asm
fld QWORD PTR [ebp-16]  ; return res

Это мне кажется наиболее правильным.

ИСПРАВЛЕНИЕ : Не совсем.Указатель на функцию и переменная res должны храниться в регистрах, потому что вы создаете стековый фрейм, о котором компилятор ничего не знает, и поэтому он не может вычислить адреса этих локальных переменных.Так что мои последние исправления хороши, но бесполезны.

Также указатель на переменную res должен быть в ECX.

Обращено.

0 голосов
/ 28 декабря 2011

При обращении к переменным во встроенной сборке им должен предшествовать $.Кроме того, gcc использует синтаксис AT & T для сборки, поэтому src и dst обратны по сравнению с Intel.

http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

Edit:

Похоже, чтоссылаясь на местных жителей по имени, а не глобальные.Поэтому, когда ассемблер видит имена, а затем пытается связать, он не может их найти.Чтобы решить эту проблему, я думаю, вам нужно использовать расширенный asm.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...