Встроенная сборка gcc и локальные переменные (двойные)

Я пытаюсь использовать inline как м директива gcc / g ++ (я должен сказать, что я использовал синтаксис Intel на MSVC ранее и это было ветер).
Я играю с двойной значения и следующие my_func2 кажется, сбой после выполнения:

  #include <iostream>

void my_func(const double *in, double *out) {
asm("mov %0, %%r8" : : "r"(in));
asm("movupd (%%r8), %%xmm0" :);
asm("movupd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movupd %%xmm0, (%0)" : : "r"(out) : "%r8", "%xmm0", "%xmm1");
}

double my_func2(const double *in) {
double  ret = 0.0;

asm("mov %0, %%r8" : : "r"(in));
asm("movupd (%%r8), %%xmm0" :);
asm("movupd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movupd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");

return ret;
}

int main(int argc, char *argv[]) {
const double    a = 1.0;
double      b = 0.0;
my_func(&a, &b);
std::cout << "b:" << b << std::endl;
b = my_func2(&a);
std::cout << "b:" << b << std::endl;
}

Ошибка, которую я получаю конкретно (когда я работаю с GDB):

Программа получила сигнал SIGBUS, ошибка шины.
0x00000000004008e1 в основном (argc =<ошибка чтения переменной: невозможно получить доступ к памяти по адресу 0x400fffffffffffec>,
ARGV =<ошибка чтения переменной: невозможно получить доступ к памяти по адресу 0x400fffffffffffe0>) на asm_test.cpp: 28
28 b = my_func2 (a);

Что я делаю неправильно? В последней строке my_func2 Я указал, что объем памяти тоже забита, я не понимаю …
Где я могу найти хорошее руководство, как использовать печально известный AT&Синтаксис T?
Я компилирую с: g++ -g -o asm_test asm_test.cpp, г ++ версия g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3 на Ubuntu Linux scv 3.2.0-48-generic # 74-Ubuntu SMP Чт 6 июня 19:43:26 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux.

я обнаружил http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html а также http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html , есть что-то Больше вы бы порекомендовали?

Спасибо,
Эма

2

Решение

Ошибка здесь в том, что нужно быть осторожным при использовании movupd, С помощью этой инструкции вы на самом деле копируете 128 бит памяти в а также из.

По случайности первая функция тоже может скопировать эти значения, но вторая только 64-битное пространство в ret переменная. Как и следовало ожидать, это портит стек, приводит к неопределенному поведению?
Подставляя movupd с movlpd (или же movhpd), все работает Шарм.

Я все еще бью правильные регистры?

Следующий код работает просто хорошо при компиляции с g++ -O3 -o asm_test asm_test.cpp

  void my_func(const double *in, double *out) {
asm ("mov %0, %%r8" : : "r"(in));
asm ("movhpd (%%r8), %%xmm0" :);
asm ("movhpd (%%r8), %%xmm1" :);
asm ("addpd %%xmm1, %%xmm0" :);
asm ("movhpd %%xmm0, (%0)" : : "r"(out) : "memory", "%r8", "%xmm0", "%xmm1");
}

double my_func2(const double *in) {
double  ret;

asm("mov %0, %%r8" : : "r"(in));
asm("movlpd (%%r8), %%xmm0" :);
asm("movlpd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movlpd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");

return ret;
}
2

Другие решения

Встроенная сборка GCC не особенно нравится, если у вас есть отдельный линии asm() заявления, которые на самом деле не независимый. Вы бы лучше кодировать выше, как:

#include <xmmintrin.h> // for __m128d

static  void my_func(const double *in, double *out) {
asm("movupd %1, %%xmm0\n""movupd %1, %%xmm1\n""addpd %%xmm1, %%xmm0\n""movupd %%xmm0, %0": "=rm"(*(__m128d*)out)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
}

static double my_func2(const double *in) {
double ret;
asm("movupd %1, %%xmm0\n""movupd %1, %%xmm1\n""addpd %%xmm1, %%xmm0\n""movlpd %%xmm0, %0": "=xm"(ret)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
return ret;
}

потому что это позволяет компилятору выбирать, куда помещать вещи (mem или reg). Для вашего источника это включает следующие два блока в main():

  1c: 66 0f 10 44 24 10 movupd 0x10 (% rsp),% xmm0
22: 66 0f 10 4c 24 10 movupd 0x10 (% rsp),% xmm1
28: 66 0f 58 c1 addpd% xmm1,% xmm0
2c: 66 0f 11 44 ​​24 20 movupd% xmm0,0x20 (% rsp)
[...]
63: 66 0f 10 44 24 10 movupd 0x10 (% rsp),% xmm0
69: 66 0f 10 4c 24 10 movupd 0x10 (% rsp),% xmm1
6f: 66 0f 58 c1 addpd% xmm1,% xmm0
73: 66 0f 13 44 24 08 movlpd% xmm0,0x8 (% rsp)

Это не оптимально, хотя … если вы измените его на:

static  void my_func(const double *in, double *out) {
asm volatile("movapd %1, %0\n""addpd %1, %0": "=xm"((__m128d*)out)
: "x"(*(__m128d*)in));
}

Вы оставляете это компилятору, где поместить переменные. Компилятор обнаруживает, что он может сойти с рук, не загружая и не сохраняя вообще … так как это делается просто:

  18: 66 0f 28 c1 movapd% xmm1,% xmm0
1c: 66 0f 58 c1 addpd% xmm1,% xmm0

так как компилятор распознает, что у него есть все переменные в регистрах / хочет все возвраты в регистрах.

Хотя это вовсе не обязательно делать с помощью сборки; с достойным компилятором (ваш gcc сделает) обычную версию C / C ++,

static void my_func(const double *in, double *out) {
out[0] = in[0] + in[0];
out[1] = in[1] + in[1];
}

скорее всего, будет превращен в не менее эффективный код.

0