2013-07-08 4 views
2

Я пытаюсь использовать инлайна ассемблерный директиву 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; 
    } 

Я получаю ошибка именно (когда я бег с БГДОМ):

 
Program received signal SIGBUS, Bus error. 
0x00000000004008e1 in main (argc=<error reading variable: Cannot access memory at address 0x400fffffffffffec>, 
    argv=<error reading variable: Cannot access memory at address 0x400fffffffffffe0>) at asm_test.cpp:28 
28  b = my_func2(&a); 

Что я делаю неправильно? В последней строке my_func2 Я указал, что память тоже сбита, я не понимаю ... Где я могу найти хорошее руководство по использованию печально известного AT & T синтаксиса?
компилировать с: g++ -g -o asm_test asm_test.cpp, г ++ версия г ++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 на Ubuntu Linux SCV 3.2.0-48-родовой # 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, есть что-то еще вы бы порекомендовали?

Спасибо,
Эма

+0

Не надо _need_ эти флаги компилировать и заставить работать. – Emanuele

ответ

2

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

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

Я все еще придерживаюсь правильных регистров?

Следующий код работает просто отлично при компиляции с 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; 
    } 
+1

'asm (" ... ":)' - это небольшой трюк, чтобы обойти тот факт, что gcc вычеркнет вашу встроенную сборку, если она написана так (используя несколько отдельных строк операторов 'asm()', которые не являются независимыми_). Вероятно, лучше переписать это. –

+0

Я знал, что это было бы так ... Вероятно, я тоже должен использовать 'volatile' (это должно заставить компилятор оставить его таким, какой он есть). Или, как вы предлагаете переписать его в одном утверждении. В любом случае использование' movupd' вместо любая _lower_/_high_ половина была проблемой _real_ ... Btw Я немного проанализировал переписывание функции 'sum' для больших двойных наборов данных, и когда я смотрел на сгенерированную _asm_, это было как и ожидалось (но я должен сказать Я инкапсулировал функции в '__attribute__ ((noinline))'). – Emanuele

0

НКУ встроенного ассемблера не особенно нравится, если у вас есть отдельные линии 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) 

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

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 
, так как компилятор распознает, что он имеет все переменные в регистре/хочет, чтобы все возвращались в регистры.

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

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

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

+0

Вопрос: как вы думаете, авария произошла не из-за того, что я должен был работать на 128 бит вместо 64? Кроме того, из некоторых тестов, вышеприведенный цикл C/C++ может быть правдой только в некоторых случаях и только тогда, когда я компилирую с помощью '-march = native'. – Emanuele

+0

В вашем конкретном примере да, вы действительно вызвали сбой, написав 128 бит в местоположение' ret ', потому что компилятор, по всей вероятности, создал mem ory в стеке, например 'struct stk stk = {double ret; void * savedfp; void * retaddr} ', и вы запустили обратный вызов вызывающего абонента. При возврате из вашей функции вызывающий, 'main', выйдет из строя при следующем обращении к памяти с обращением к камере. –

+0

Спасибо, FrankH, просто чтобы соответствовать заданному вопросу, я буду продвигать свой ответ, но я сохраню вам рекомендации. – Emanuele

 Смежные вопросы

  • Нет связанных вопросов^_^