2017-04-12 37 views
3

我在程序集x86中實現了一個函數,用於從C程序調用來添加浮點數組。函數的第一個參數是指向數組的指針,第二個參數是元素的數量。當我在linux中運行代碼時,出現了分段錯誤。我做錯了什麼?程序集中浮點數的總和

.text 
.globl floatsum 

floatsum: 
push %ebp 
movl %esp, %ebp 

movl 8(%ebp), %eax 
movl 12(%ebp), %edx 
shrl $2, %edx 

xorps %xmm0, %xmm0 
loop: 
testl %edx, %edx 
je end 
movaps (%eax), %xmm1 
addps %xmm1, %xmm0 
addl $16, %eax 
decl %edx 
jmp loop 

end: 
          #   3  2  1  0 
movaps %xmm0, %xmm1   # xmm0: w  z  y  x 
          # xmm1: z  w  x  y 
shufps $0xb1, %xmm1, %xmm1 #  10  11  00  01 = 0xb1 
addps %xmm1, %xmm0   # xmm0: w+z  z+w  y+x  x+y 
movaps %xmm0, %xmm1   # xmm1: w+z  z+w  y+x  x+y 
          # xmm1: x+y  y+x  z+w  w+z 
shufps $0x1b, %xmm1, %xmm1 #  00  01  10  11 = 0x1b 
addps %xmm1, %xmm0   # xmm0: w+z+x+y z+w+y+x y+x+z+w x+y+w+z 
          # 
#movd %xmm0, %eax 
#pushl %eax 

finst: 

flds (%esp) 
popl %eax 

movl %ebp, %esp 
popl %ebp 
ret 

// C代碼

#include <stdio.h> 
#include <stdlib.h> 


float 
floatsum(float *array, size_t number_of_items); 

float 
floatsum_c(float *array, size_t number_of_items){ 
float sum; 
size_t i; 

sum=0.0; 
for(i=0; i<number_of_items;i++){ 
    sum+=array[i]; 
} 
return sum; 
} 

float * 
create_array(size_t number_of_items){ 
float *array; 
size_t i; 

array=calloc(number_of_items, sizeof(float)); 
if(array){ 
    for(i=0; i<number_of_items; i++){ 
     array[i]=1.0+(float)i; 
    } 
    } 
    return array; 
} 

int 
main(int argc, char **argv){ 
float *a; 
float result; 
size_t number_of_items, i; 

number_of_items=8; 
a=create_array(number_of_items); 
if(a){ 
    result=floatsum_c(a, number_of_items); 
    printf("Sum (c version): %f\n", result);  
    result=floatsum(a, number_of_items); 
    printf("Sum (asm version): %f\n", result); 
    free(a); 
} 

return 0; 
} 
+2

歡迎來到Stack Overflow!這聽起來像你可能需要學習如何使用[調試器](https://en.wikipedia.org/wiki/Debugger)來遍歷你的代碼。使用一個好的調試器,您可以逐行執行您的程序,並查看它與您期望的偏離的位置。如果你打算做任何編程,這是一個重要的工具。進一步閱讀:[如何調試小程序](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)。 –

+0

崩潰發生在哪裏?我猜''movaps(%eax),%xmm1'?如果是這樣的話,這可能是一個對齊問題。 –

+0

是的,問題是由movaps(%eax)causeb,%xmm1,我該如何解決它? @PaulR – Daniele

回答

7

正如保羅提到了這很可能是一種對齊問題。從你的C代碼可以清楚你的float數組不能保證在16字節的邊界上對齊。故障是這一行:

movaps (%eax), %xmm1 

其原因是,MOVAPS有此要求:

當源或目標操作數是存儲器操作數,操作數必須在16字節對齊(128位版本)或32字節(VEX.256編碼版本)邊界或通用保護異常(#GP)將生成。

由於您正在使用128位向量寄存器,所以需要16字節的對齊方式。你有兩個選擇:

  • 變化MOVAPSMOVUPS使對齊的內存訪問可以做
  • 修改您的Ç代碼來創建一個16字節邊界上對齊float數組

首先解決方案將需要:

movaps (%eax), %xmm1 

被改爲;

movups (%eax), %xmm1 

第二個解決方案是避免使用calloc並利用一個功能,可以創建具有16字節對準的對象。如果使用C11那麼您可以使用功能aligned_allocmemset來歸零陣列。你create_array可能看起來像:

float * 
create_array(size_t number_of_items) 
{ 
    float *array = NULL; 
    size_t i; 

    array=(float *)aligned_alloc(16, number_of_items * sizeof(*array)); 
    if(array){ 
     memset (array, 0x00, number_of_items * sizeof(*array)); 
     for(i=0; i<number_of_items; i++){ 
      array[i]=1.0+(float)i; 
     } 
    } 
    return array; 
} 

如果你不使用C11可以利用POSIX功能posix_memalignmemset的在Linux上。該代碼可能看起來是這樣的:

float * 
create_array(size_t number_of_items) 
{ 
    float *array = NULL; 
    size_t i; 

    if (!posix_memalign((void **)&array, 16, number_of_items * sizeof(*array))){ 
     memset (array, 0x00, number_of_items * sizeof(*array)); 
     for(i=0; i<number_of_items; i++){ 
      array[i]=1.0+(float)i; 
     } 
    } 
    return array; 
} 

你將不得不取消註釋這些行,以及:

#movd %xmm0, %eax 
#pushl %eax 

,使它們看起來是這樣的:

movd %xmm0, %eax 
pushl %eax 

:雖然我用memset來清零浮動數組,例如calloc可能會有,因爲您之後將所有元素初始化爲特定值,所以在您的代碼中實際上並不需要它。在你的情況下,可以刪除memset的電話。