GCC ARM汇编预处理宏

5

我正在尝试使用一个汇编(ARM)宏来进行定点乘法:

    #define MULT(a,b) __asm__ __volatile__ ( \
        "SMULL r2, r3, %0, %1\n\t" \
        "ADD r2, r2, #0x8000\n\t" \
        "ADC r3, r3, #0\n\t" \
        "MOV %0, r2, ASR#16\n\t" \
        "ORR %0, %0, r3, ASL#16" \
        : "=r" (a) : "0"(a), "1"(b) : "r2", "r3" );

但是编译时我遇到了错误: 在asm之前期望表达式。

(如果您想节省时间,可以忽略下面的内容,但如果您能看一眼,那就太好了。这里的主要问题是如何使上述代码正常工作)

我尝试了以下代码:

    static inline GLfixed MULT(GLfixed a, GLfixed b){
       asm volatile(
        "SMULL r2, r3, %[a], %[b]\n"
        "ADD r2, r2, #0x8000\n"
        "ADC r3, r3, #0\n"
        "MOV %[a], r2, ASR#16\n"
        "ORR %[a], %[a], r3, ASL#16\n"
        : "=r" (a)
        : [a] "r" (a), [b] "r" (b)
        : "r2", "r3");
     return a; }

这段代码可以编译通过,但是当我使用常量例如:MULT(65536,65536)时它能正常工作,但是当我使用变量时好像出了问题:

GLfixed m[16];
m[0]=costab[player_ry];//1(65536 integer representation)
m[5]=costab[player_rx];//1(65536 integer representation)
m[6]=-sintab[player_rx];//0
m[8]=-sintab[player_ry];//0
LOG("%i,%i,%i",m[6],m[8],MULT(m[6],m[8]));
m[1]=MULT(m[6],m[8]);
m[2]=MULT(m[5],-m[8]);
m[9]=MULT(-m[6],m[0]);
m[10]=MULT(m[5],m[0]);
m[12]=MULT(m[0],0)+MULT(m[8],0);
m[13]=MULT(m[1],0)+MULT(m[5],0)+MULT(m[9],0);
m[14]=MULT(m[2],0)+MULT(m[6],0)+MULT(m[10],0);
m[15]=0x00010000;//1(65536 integer representation)

int i=0;
while(i<16)
{
    LOG("%i,%i,%i,%i",m[i],m[i+1],m[i+2],m[i+3]);
    i+=4;
}

上述代码将打印(LOG在此处类似于printf):
0,0,-1411346156
65536,65536,65536,440
-2134820096,65536,0,-1345274311
0,65536,22,220
65536,196608,131072,65536

当正确的结果应该是(显然以上有很多垃圾):
0,0,0
65536,0,0,0
0,65536,0,0
0,0,65536,0
0,0,0,65536
2个回答

5
第一部分相对较容易:问题在于__asm__块是一个语句,而不是表达式。
您可以使用GCC的语句表达式扩展来实现您想要的效果——类似于这样的内容。
#define MULT(a,b) \
  ({ \
    __asm__ __volatile__ ( \
      /* ... asm stuff here ... */
    ); \
    a; \
  })

第二部分是由于输入和输出操作数规范的问题。这里有两个不同版本,都是错误的。在宏版本中,您已经说:
: "=r" (a) : "0"(a), "1"(b) : "r2", "r3"

这里的约束条件包括:

  • 将输出a限制为一个寄存器(操作数0);
  • 将输入的a设置为和操作数0相同的寄存器(操作数1);
  • 将输入的b设置为和操作数1相同的寄存器,即再次是相同的寄存器(操作数2)。

在这里你需要使用"r"(b),并将其表示为%2

在内联版本中,你已经说过:

: "=r" (a) : [a] "r" (a), [b] "r" (b) : "r2", "r3"

该段代码限制了输出寄存器a和输入寄存器ab,但是:

  • 它没有声明它们之间的任何关系;
  • 汇编代码从未明确引用输出操作数(您没有为输出操作数命名,汇编代码也没有引用%0)。

您可以通过以下方式修复原始版本:

: "=r" (a) : "0" (a), "r" (b) : "r2", "r3"

请将a称作%0%1,将b称作%2

行内版本可以这样修复:

: [a] "=r" (a) : "[a]" (a), [b] "r" (b) : "r2", "r3"

并将操作数称为%[a]%[b]

如果您想在宏版本中使用名称,则需要类似以下内容的内容:

: [arg_a] "=r" (a) : "[arg_a]" (a), [arg_b] "r" (b) : "r2", "r3"

(并参考%[arg_a]%[arg_b])否则,预处理器将扩展[a][b]内部的ab

请注意,在命名参数的情况下有微妙之处:当给参数命名时(如输出中的a),您需要编写[a] - 没有引号 - 但是当您引用另一个已命名操作数的名称时(如输入中的a),您需要将其放在引号内:"[a]"


3

您是否尝试过使用简单的C代码而不是汇编语言?在我的系统上,使用GCC 4.5.3编译器生成的代码至少与您手写的汇编代码一样好:

int mul (int a, int b)
{
  long long x = ((long long)a * b + 0x8000);
  return x>>16;
}

编译成以下汇编代码:

# input: r0, r1
mov    r3, #32768
mov    r4, #0
smlal  r3, r4, r0, r1
mov    r0, r3, lsr #16
orr    r0, r0, r4, asl #16
# result in r0

(去除函数调用的后续处理和前序处理)

如果单个函数中有多次乘法运算,代码将变得更好,因为编译器会删除冗余的“mov r3,#32768”指令。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接