gcc 将ASM与输入/输出操作数内联以更新指针并更新它所指向的内容

ukqbszuj  于 2023-04-12  发布在  其他
关注(0)|答案(1)|浏览(161)

我正在使用RISC-V汇编语言实现33矩阵和31矩阵乘法,使用GNU C内联asm。

// description: matrix multiply with two-level for loop

#include<stdio.h>
int main()
{
        int f,i=0;
        int h[9]={0}, x[3]={0}, y[3]={0};
        FILE *input = fopen("../input/3.txt","r");
        for(i = 0; i<9; i++) fscanf(input, "%d", &h[i]);
        for(i = 0; i<3; i++) fscanf(input, "%d", &x[i]);
        for(i = 0; i<3; i++) fscanf(input, "%d", &y[i]);
        fclose(input);
        int *p_x = &x[0] ;
        int *p_h = &h[0] ;
        int *p_y = &y[0] ;

        for (i = 0 ; i < 3; i++)
        {

                p_x = &x[0] ;
        /*
         for (f = 0 ; f < 3; f++)
             *p_y += *p_h++ * *p_x++ ;
        */

                for (f = 0 ; f < 3; f++){
                   asm volatile (
                    "addi t0, zero, 2\n\t"
                    "bne t0, %[f], Multi\n\t"
                    "mul t1, %[sp_h], %[sp_x]\n\t"
                    "add %[sp_y], %[sp_y], t1\n\t"
                    "addi %[p_h], %[p_h], 4\n\t"
                    "addi %[p_x], %[p_x], 4\n\t"
                    "addi %[p_y], %[p_y], 4\n\t"
                    "beq zero, zero, Exit\n\t"
                    "Multi: \n\t"
                    "mul t1, %[sp_h], %[sp_x]\n\t"
                    "add %[sp_y], %[sp_y], t1\n\t"
                    "addi %[p_h], %[p_h], 4\n\t"
                    "addi %[p_x], %[p_x], 4\n\t"
                    "Exit: \n\t"
                    :[p_y] "+&r"(p_y),
                     [p_x] "+&r"(p_x),
                     [p_h] "+&r"(p_h),
                     [sp_y] "+&r"(*p_y)
                    :[sp_h] "r"(*p_h),
                     [sp_x] "r"(*p_x),
                     [f] "r"(f)
                    :"t0","t1"

                );
            printf("x value=%d, h value=%d, y value=%d, y address=%d\n", *p_x, *p_h, *p_y, p_y);
        }

        }

        p_y = &y[0];

        for(i = 0; i<3; i++)
                printf("%d \n", *p_y++);

        return(0) ;

}

我想把这个评论转过来

for (f = 0 ; f < 3; f++) 
    *p_y += *p_h++ * *p_x++ ;
p_y++;

到asm volatile(...),但是在asm上面的代码中我遇到了这个问题:my problem看起来p_y地址加对了,我的乘法是对的,但是我的值存储到内存中是错误的。它会存储到内存中太快,现在我的答案加上前面的答案在一起。上面的代码答案是5 28 69有人能帮我吗?我已经编辑了"+r""+&r",并添加了clobbers到代码中,但它不起作用
顺便说一下,我从这段代码中得到了正确的答案:

for (f = 0 ; f < 3; f++)
                    asm volatile ("mul %[tmp], %[sp_h], %[sp_x]\n\t"
                                  "add %[sp_y], %[sp_y], %[tmp]\n\t"
                                  "addi %[p_x], %[p_x], 4\n\t"
                                  "addi %[p_h], %[p_h], 4\n\t"
                                  :[tmp] "=r"(tmp),
                                   [p_x] "+r"(p_x),
                                   [p_h] "+r"(p_h),
                                   [sp_y] "+r"(*p_y)
                                  :[sp_h] "r"(*p_h),
                                   [sp_x] "r"(*p_x)
                                );
            p_y++;

我希望

14 
32 
50

答案。这里是输入数据:

8wigbo56

8wigbo561#

你的[sp_y] "+&r"(*p_y)输入/输出使用原始的p_y来读取输入,但它使用asm语句更新的p_y(在f==2的情况下)来存储结果。编译器从gcc -O1 -Wall(在Godbolt上)生成的asm很好地展示了这一点:

# top of inner loop
.L4:
        lw      a1,0(s3)
        lw      a0,0(s2)
        lw      a3,0(s1)      # "+r"(*p_y) in/out operand in a3
        mv      a4,s1         # "+r"(p_y)  in a4
        mv      a5,s2
        mv      a2,s3
# your asm starts here
        addi t0, zero, 2
        bne t0, s0, Multi
        mul t1, a1, a0
        add a3, a3, t1        # sum += stuff
        addi a2, a2, 4
        addi a5, a5, 4
        addi a4, a4, 4
        beq zero, zero, Exit
Multi: 
        mul t1, a1, a0
        add a3, a3, t1       # sum += stuff in the other branch
        addi a2, a2, 4
        addi a5, a5, 4
Exit: 

# your asm ends here, compiler now has to move regs to C objects
# (with more optimization, it might have just used s1 as [p_y] avoiding two mv)
        mv      s1,a4
        mv      s2,a5
        mv      s3,a2
        sw      a3,0(a4)    # store new sum using the new p_y
        lw      a2,0(a2)
        lw      a1,0(a5)
        addi    a0,s5,%lo(.LC3)
        call    printf

GCC's docs没有提到当一个输出修改一个C变量时会发生什么,这个C变量也是另一个输出的左值表达式的一部分,至少我没有注意到。**我假设输出是无序的,**在这种情况下,它是类似于x = ++x + ++x;的未定义行为,或者至少没有具体说明会发生什么。

不要在内部循环中有条件地递增p_y,所以根本不要将p_y作为内部循环asm语句的输出(或输入)。

我不认为有任何方法可以使其完全良好定义,尽管"=r"(p_y[f == 2 ? -1 : 0])"0"(*p_y)这样的匹配约束可能总是使用来自另一个输出寄存器的更新后的p_y。但是您需要在C中手动执行偏移量以平衡asm的操作,完全违背了目的。所以在内部循环外使用一个单独的asm语句来在外部循环中执行指针递增。
一个单独的int sum = *p_y;变量对于这类事情来说是正常的,但是在递增p_y之前,你仍然需要赋值*p_y = sum;,所以你仍然不能在内部循环asm语句中这样做。你不希望编译器在内部循环中对sw/lw求和,就像你鼓励它对*p_y做的那样。
这样看起来就像

int sum = *p_y;      // asm lw
  for (int f = 0 ; f < 3; f++) {
      asm("..." 
          : "+&r"(sum), ...  // not including p_y
          : [sp_h] "r"(*p_h),  [sp_x] "r"(*p_x)  // same as before
          : // no clobbers, you don't need to compare/branch inside the asm
      );
  }
  *p_y = sum;          // asm sw
  asm("addi %0, %0, 4" : "+r"(p_y));  // p_y++;

或者在asm中编写整个外循环体(包括内循环和p_y++),包括您自己的lwsw,这样您就可以手动将addi %[p_y], %[p_y], 4 * 放在使用%[p_y]sw之后。
(Don对于一个带有指针输入的asm语句,不要忘记a "memory" clobber,它读/写不是"m"约束的内存。)

相关问题