Программа Intrinsics (SSE) - g++ - нужна помощь

Это первый раз, когда я публикую вопрос в stackoverflow, поэтому, пожалуйста, постарайтесь не обращать внимания на любые ошибки, которые я мог допустить при форматировании моего вопроса/кода. Но, пожалуйста, укажите мне то же самое, чтобы я мог быть более осторожным.

Я пытался написать несколько простых внутренних процедур для добавления двух 128-битных (содержащих 4 переменных с плавающей запятой) чисел. Я нашел код в сети и пытался заставить его работать в моей системе. Код выглядит следующим образом:

 //this is a sample Intrinsics program to add two vectors.

    #include <iostream>  
    #include <iomanip>      
    #include <xmmintrin.h>  
    #include <stdio.h>

    using namespace std;

    struct vector4 {  
        float x, y, z, w;    };   

    //functions to operate on them.  
    vector4 set_vector(float x, float y, float z, float w = 0) {     
        vector4 temp;  
        temp.x = x;   
        temp.y = y;   
        temp.z = z;  
        temp.w = w;  
        return temp;  
    }    


    void print_vector(const vector4& v) {   
        cout << " This is the contents of vector: " << endl;  
        cout << " > vector.x = " << v.x << endl;  
        cout << " vector.y = " << v.y << endl;  
        cout << " vector.z = " << v.z << endl;  
        cout << " vector.w = " << v.w << endl;  
    }

    vector4 sse_vector4_add(const vector4&a, const vector4& b) {  
        vector4 result;  

        asm volatile (  
          "movl $a, %eax" //move operands into registers.  
          "\n\tmovl $b, %ebx"  
          "\n\tmovups  (%eax), xmm0"  //move register contents into SSE registers.  
          "\n\tmovups (%ebx), xmm1"  
          "\n\taddps xmm0, xmm1" //add the elements. addps operates on single-precision vectors.    
          "\n\t movups xmm0, result" //move result into vector4 type data.  
        );
        return result;  
    }

    int main() {     
        vector4 a, b, result;  
        a = set_vector(1.1, 2.1, 3.2, 4.5);   
        b = set_vector(2.2, 4.2, 5.6);    
        result = sse_vector4_add(a, b);    
        print_vector(a);  
        print_vector(b);    
        print_vector(result);
        return 0;
    }

Параметры g++, которые я использую:

g++ -Wall -pedantic -g -march=i386 -msse intrinsics_SSE_example.C -o h

Ошибки, которые я получаю, следующие:

intrinsics_SSE_example.C: Assembler messages:  
intrinsics_SSE_example.C:45: Error: too many memory references for movups  
intrinsics_SSE_example.C:46: Error: too many memory references for movups  
intrinsics_SSE_example.C:47: Error: too many memory references for addps  
intrinsics_SSE_example.C:48: Error: too many memory references for movups  

Я потратил много времени на отладку этих ошибок, гуглил их и так далее. Я полный нуб в Intrinsics и поэтому, возможно, упустил из виду некоторые важные вещи.

Приветствуется любая помощь,
Спасибо,
Шрирам.


person Sriram    schedule 26.05.2010    source источник
comment
Для форматирования кода используйте кнопку 1010 на панели инструментов (отступ 4 пробела). Не <blockquote> их.   -  person kennytm    schedule 26.05.2010
comment
спасибо КенниТМ. Я внес изменения.   -  person Sriram    schedule 26.05.2010


Ответы (1)


Вы используете блоки ASM, а не встроенные.

Поскольку эти xmmX являются регистрами, вы должны добавить к ним префикс %:

      "\n\tmovups  (%eax), %xmm0"
      // etc.

И ваш ASM имеет несколько ошибок.

  1. вы не должны изменять регистр ebx.
  2. $a и т. д. считается глобальным символом в ассемблере, но это не так.
  3. addps %xmm0, %xmm1 сохранит результат в xmm1. Помните, что в синтаксисе AT&T пункт назначения находится справа.

Исправленный блок ASM будет выглядеть

    asm volatile (  
      "movl %1, %%eax"
      "\n\tmovl %2, %%ecx"  
      "\n\tmovups  (%%eax), %%xmm0"
      "\n\tmovups (%%ecx), %%xmm1"  
      "\n\taddps %%xmm0, %%xmm1"
      "\n\tmovups %%xmm0, %0"
      : "=m"(result)
      : "r"(&a), "r"(&b)
      : "eax", "ecx");

По сути, %0 будет заменен адресом result, %1 и %2 будут заменены на &a и &b. См. http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html для подробного объяснения. "eax", "ecx" предотвращает использование этих двух регистров в качестве замены этих %n.

Но первые 2 movl не нужны...

    asm volatile(  
      "\n\tmovups (%1), %%xmm0"
      "\n\tmovups (%2), %%xmm1"  
      "\n\taddps %%xmm1, %%xmm0"
      "\n\tmovups %%xmm0, %0"
      : "=m"(result)
      : "r"(&a), "r"(&b));

Поскольку вы упомянули встроенный, почему бы не использовать __builtin_ia32_addps?

person kennytm    schedule 26.05.2010
comment
/tmp/ccXM3Fog.o: В функции sse_vector4_add(vector4 const&, vector4 const&)': /intrinsics_SSE_example.C:43: undefined reference to a' /intrinsics_SSE_example.C:43: неопределенная ссылка на b' /intrinsics_SSE_example.C:43: undefined reference to xmm0' /intrinsics_SSE_example.C:43: неопределенная ссылка на `result' collect2: ld вернул 1 статус выхода - person Sriram; 26.05.2010
comment
Вау! что сделал это. Я просматривал документацию, на которую вы меня направили, но, полагаю, я был недостаточно тщательным. У меня вопрос: это не то же самое, что и встроенные функции? Всякий раз, когда я читаю документ в сети по этому поводу, в нем говорится, что встроенные функции — это способ использования встроенного ассемблерного кода в программах C/C++ с использованием инструкций, которые я использовал выше. Я прав, говоря это? - person Sriram; 26.05.2010
comment
@Siriam: см. en.wikipedia.org/wiki/Intrinsic_function. Блок ASM не является функцией. - person kennytm; 26.05.2010
comment
В статье википедии для SSE en.wikipedia.org/wiki/Streaming_SIMD_Extensions упоминается пример добавления 2 вектора. Они используют те же инструкции, которые я использовал выше (в псевдокоде). Что мне нужно сделать, чтобы преобразовать этот код во встроенные функции? - person Sriram; 26.05.2010