将FPU与C内联汇编一起使用

将FPU与C内联汇编一起使用,c,gcc,x86,inline-assembly,x87,C,Gcc,X86,Inline Assembly,X87,我写了一个向量结构如下: struct vector { float x1, x2, x3, x4; }; typedef struct { float x1, x2, x3, x4; } vector; vector *adding(const vector v1[], const vector v2[], int size) { vector *vec = malloc(sizeof(vector)*size); int i; for(i = 0

我写了一个向量结构如下:

struct vector {
    float x1, x2, x3, x4;
};
typedef struct {
    float x1, x2, x3, x4;
} vector;

vector *adding(const vector v1[], const vector v2[], int size) {
    vector *vec = malloc(sizeof(vector)*size);
    int i;

    for(i = 0; i < size; i++) {
        __asm__(
            "FLDS %4 \n" //v1.x1
            "FADDS %8 \n" //v2.x1
            "FSTPS %0 \n"

            "FLDS %5 \n" //v1.x2
            "FADDS %9 \n" //v2.x2
            "FSTPS %1 \n"

            "FLDS %6 \n" //v1->x3
            "FADDS %10 \n" //v2->x3
            "FSTPS %2 \n"

            "FLDS %7 \n" //v1->x4
            "FADDS %11 \n" //v2->x4
            "FSTPS %3 \n"

            :"=m"(vec[i].x1), "=m"(vec[i].x2), "=m"(vec[i].x3), "=m"(vec[i].x4)
            :"m"(v1[i].x1), "m"(v1[i].x2), "m"(v1[i].x3), "m"(v1[i].x4),
             "m"(v2[i].x1), "m"(v2[i].x2), "m"(v2[i].x3), "m"(v2[i].x4)
            :
        );
    }

    return vec;
}
然后,我创建了一个函数,该函数使用向量对内联汇编执行一些操作:

struct vector *adding(const struct vector v1[], const struct vector v2[], int size) {
    struct vector vec[size];
    int i;
    
    for(i = 0; i < size; i++) {
        asm(
            "FLDL %4 \n" //v1.x1
            "FADDL %8 \n" //v2.x1
            "FSTL %0 \n"
            
            "FLDL %5 \n" //v1.x2
            "FADDL %9 \n" //v2.x2
            "FSTL %1 \n"
            
            "FLDL %6 \n" //v1.x3
            "FADDL %10 \n" //v2.x3
            "FSTL %2 \n"
            
            "FLDL %7 \n" //v1.x4
            "FADDL %11 \n" //v2.x4
            "FSTL %3 \n"
            
            :"=m"(vec[i].x1), "=m"(vec[i].x2), "=m"(vec[i].x3), "=m"(vec[i].x4)     //wyjscie
            :"g"(&v1[i].x1), "g"(&v1[i].x2), "g"(&v1[i].x3), "g"(&v1[i].x4), "g"(&v2[i].x1), "g"(&v2[i].x2), "g"(&v2[i].x3), "g"(&v2[i].x4) //wejscie
            :
        );
    }

    return vec;
}
struct vector*添加(常量struct vector v1[],常量struct vector v2[],int size){
结构向量向量向量[size];
int i;
对于(i=0;i
看起来一切正常,但当我尝试用GCC编译时,会出现以下错误:

错误:“fadd”的操作数类型不匹配

错误:“fld”的指令后缀无效

在XCode中的OS/X上,一切都正常工作。此代码有什么问题?

编码问题 我不想让它变得高效(如果处理器支持,我会使用SSE/SIMD)。由于这部分任务是使用FPU堆栈,因此我有一些顾虑:

您的函数声明一个基于本地堆栈的变量:

struct vector vec[size];
问题是函数返回一个
向量*
,您可以执行以下操作:

return vec;
这很糟糕。基于堆栈的变量可能在函数返回后和调用方使用数据之前被删除。一种替代方法是在堆上而不是堆栈上分配内存。您可以替换
struct vector vec[size]带有:

struct vector *vec = malloc(sizeof(struct vector)*size);
这将为
大小
向量数
的数组分配足够的空间。调用函数的人必须使用
free
在完成时从堆中释放内存


您的
向量
结构使用
浮点
,而不是
双精度
。指令FLDL、FADDL、FSTL都在双精度(64位浮点)上运行。当与内存操作数一起使用时,这些指令中的每一条都将加载和存储64位。这将导致从FPU堆栈加载/存储错误的值。您应该使用FLD、FADD、FST对32位浮点进行操作


在汇编程序模板中,对输入使用
g
约束。这意味着编译器可以自由使用任何通用寄存器、内存操作数或立即数。FLD、FADD、FST不接受立即值或通用寄存器(非FPU寄存器),因此,如果编译器尝试这样做,可能会产生类似于
错误:xxxx的操作数类型不匹配的错误

由于这些指令理解内存引用,因此使用
m
而不是
g
约束。您需要从输入操作数中删除
&
(与符号),因为
m
意味着它将处理变量/C表达式的内存地址


完成后,不会从FPU堆栈中弹出值。使用单个操作数的FST将堆栈顶部的值复制到目标。堆栈上的值保持不变。您应该存储它并使用FSTP指令将其弹出。当汇编程序模板结束时,您希望FPU堆栈为空。FPU堆栈非常有限,只有8个可用插槽。如果模板完成时FPU堆栈未清除,则在后续调用中可能会出现FPU堆栈溢出的风险。由于每次调用时在堆栈上保留4个值,因此第三次调用函数
adding
将失败


为了简化代码,我建议使用
typedef
来定义向量。按以下方式定义您的结构:

typedef struct {
    float x1, x2, x3, x4;
} vector;
struct vector
的所有引用都可以变成
vector


考虑到所有这些因素,您的代码可以如下所示:

struct vector {
    float x1, x2, x3, x4;
};
typedef struct {
    float x1, x2, x3, x4;
} vector;

vector *adding(const vector v1[], const vector v2[], int size) {
    vector *vec = malloc(sizeof(vector)*size);
    int i;

    for(i = 0; i < size; i++) {
        __asm__(
            "FLDS %4 \n" //v1.x1
            "FADDS %8 \n" //v2.x1
            "FSTPS %0 \n"

            "FLDS %5 \n" //v1.x2
            "FADDS %9 \n" //v2.x2
            "FSTPS %1 \n"

            "FLDS %6 \n" //v1->x3
            "FADDS %10 \n" //v2->x3
            "FSTPS %2 \n"

            "FLDS %7 \n" //v1->x4
            "FADDS %11 \n" //v2->x4
            "FSTPS %3 \n"

            :"=m"(vec[i].x1), "=m"(vec[i].x2), "=m"(vec[i].x3), "=m"(vec[i].x4)
            :"m"(v1[i].x1), "m"(v1[i].x2), "m"(v1[i].x3), "m"(v1[i].x4),
             "m"(v2[i].x1), "m"(v2[i].x2), "m"(v2[i].x3), "m"(v2[i].x4)
            :
        );
    }

    return vec;
}
功能如下:

vector *adding(const vector v1[], const vector v2[], int size) {
    int i, e;
    vector *vec = malloc(sizeof(vector)*size);

    for(i = 0; i < size; i++)
        for (e = 0; e < 4; e++)  {
            __asm__(
                "FADDPS\n"
                :"=t"(vec[i].x[e])
                :"0"(v1[i].x[e]), "u"(v2[i].x[e])
        );
    }

    return vec;
}
的无操作数形式执行以下操作:

将ST(0)添加到ST(1),将结果存储在ST(1)中,然后弹出寄存器堆栈

我们将这两个值传递给堆栈顶部的add,并执行一个只将结果保留在ST(0)中的操作。然后,我们可以让汇编程序模板复制堆栈顶部的值,并为我们自动弹出它

我们可以使用
=t
的输出操作数来指定要从FPU堆栈顶部移动的值
=t
还将为我们弹出(如果需要)FPU堆栈顶部的值。我们也可以使用堆栈顶部作为输入值!如果输出操作数为%0,我们可以将其引用为具有约束
0
(这意味着使用与操作数0相同的约束)的输入操作数。第二个向量值将使用
u
约束,因此它将作为第二个FPU堆栈元素(ST(1))传递

可能允许GCC优化其生成的代码的一个微小改进是在第一个输入操作数上使用
%
%
修饰符记录为:

声明此操作数与以下操作数的指令是可交换的。这意味着编译器可以交换两个操作数,如果这是使所有操作数都符合约束条件的最便宜的方法。“%”应用于所有备选方案,并且必须显示为约束中的第一个字符。只有只读操作数才能使用“%”

因为x+y和y+x产生相同的结果,我们可以告诉编译器,它可以将标记为
%
的操作数与模板中紧跟其后定义的操作数交换<代码>“0”(v1[i].x[e])
vector *adding(const vector v1[], const vector v2[], int size) { int i, e; vector *vec = malloc(sizeof(vector)*size); for(i = 0; i < size; i++) for (e = 0; e < 4; e++) { __asm__( "FADDS %2\n" :"=&t"(vec[i].x[e]) :"0"(v1[i].x[e]), "m"(v2[i].x[e]) ); } return vec; }
.L3:
        flds    (%rdi)  # MEM[base: _51, offset: 0B]
        addq    $16, %rdi       #, ivtmp.6
        addq    $16, %rcx       #, ivtmp.8
        FADDS (%rsi)    # _31->x

        fstps   -16(%rcx)     # _28->x
        addq    $16, %rsi       #, ivtmp.9
        flds    -12(%rdi)       # MEM[base: _51, offset: 4B]
        FADDS -12(%rsi) # _31->x

        fstps   -12(%rcx)     # _28->x
        flds    -8(%rdi)        # MEM[base: _51, offset: 8B]
        FADDS -8(%rsi)  # _31->x

        fstps   -8(%rcx)      # _28->x
        flds    -4(%rdi)        # MEM[base: _51, offset: 12B]
        FADDS -4(%rsi)  # _31->x

        fstps   -4(%rcx)      # _28->x
        cmpq    %rdi, %rdx      # ivtmp.6, D.2922
        jne     .L3       #,