2017-04-12 94 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的电话。