【问题标题】:GCC ARM Assembly Preprocessor MacroGCC ARM 汇编预处理器宏
【发布时间】:2011-09-01 08:45:03
【问题描述】:

我正在尝试使用汇编 (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; }

这可以编译,但似乎有问题,因为当我使用常量 ex: MULT(65536,65536) 时它可以工作,但是当我使用变量时,它似乎 f**k up:

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

【问题讨论】:

    标签: gcc assembly macros preprocessor arm


    【解决方案1】:

    第一部分很简单:问题是__asm__ 块是语句,而不是表达式。

    您可以使用 GCC 的 statement expressions 扩展来实现您想要的 - 像这样:

    #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 限制为寄存器,但是

    • 它没有声明它们之间的任何关系;
    • asm 从未显式引用输出操作数(您尚未为输出操作数指定名称,并且 asm 代码未引用 %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]"

    【讨论】:

      【解决方案2】:

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

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

      编译成以下 asm 代码:​​

      # 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
      

      (函数调用 epilog 和 prolog 已移除)

      如果您在单个函数中有多个乘法,代码会变得更好,因为编译器会删除多余的 mov r3, #32768 指令。

      【讨论】:

        猜你喜欢
        • 2018-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-03
        相关资源
        最近更新 更多