Я пытаюсь использовать 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 , есть что-то Больше вы бы порекомендовали?
Спасибо,
Эма
Ошибка здесь в том, что нужно быть осторожным при использовании 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;
}
Встроенная сборка 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];
}
скорее всего, будет превращен в не менее эффективный код.