如何为gcc内联asm获取64位整数的低32位和高32位?(ARMV5平台)

如何为gcc内联asm获取64位整数的低32位和高32位?(ARMV5平台),c,gcc,assembly,arm,inline-assembly,C,Gcc,Assembly,Arm,Inline Assembly,我有一个关于armv5te平台的项目,我必须重写一些函数并使用汇编代码来使用增强DSP指令。 我对累加器使用了很多int64_t类型,但我不知道如何将其传递给arm指令SMULL() 如何将64个变量中较低或较高的32位传递给32位寄存器?(我知道,我可以使用中间变量int32_t,但看起来不太好) 我知道,那个编译器可以帮我完成,但我只是为一个例子编写了这个小函数 int64_t testFunc(int64_t acc, int32_t x, int32_t y) { int64_t

我有一个关于armv5te平台的项目,我必须重写一些函数并使用汇编代码来使用增强DSP指令。 我对累加器使用了很多int64_t类型,但我不知道如何将其传递给arm指令SMULL()

如何将64个变量中较低或较高的32位传递给32位寄存器?(我知道,我可以使用中间变量int32_t,但看起来不太好)

我知道,那个编译器可以帮我完成,但我只是为一个例子编写了这个小函数

int64_t testFunc(int64_t acc, int32_t x, int32_t y)
{
   int64_t tmp_acc;

   asm("SMULL %0, %1, %2, %3"
      : "=r"(tmp_acc), "=r"(tmp_acc) // no idea how to pass tmp_acc;
      : "r"(x), "r"(y)
      );

return tmp_acc + acc;
}

您不需要也不应该为此使用内联asm。编译器可以做得比SMLL更好,使用smlal与一条指令相乘:

int64_t accum(int64_t acc, int32_t x, int32_t y) {
    return acc + x * (int64_t)y;
}
它将()编译成这个asm:(ARM10E是我选择的一个ARMv5微体系结构)

作为奖励,这个纯C还可以高效地编译AArch64


如果你坚持用内联asm射击自己的脚: 或者在有其他说明的一般情况下,可能会有这样的情况

首先,请注意
smull
输出寄存器不允许与第一个输入寄存器重叠,因此您必须告诉编译器这一点。
对输出操作数的早期碰撞约束将告诉编译器这些寄存器中不能有输入。我看不到一个干净的方法来告诉编译器第二个输入可以和输出在同一个寄存器中

ARMv6及更高版本中取消了此限制(请参阅“Rn必须不同于ARMv6之前的体系结构中的RdLo和RdHi”),但为了与ARMv5兼容,您需要确保编译器在填充内联asm模板时不会违反此限制

针对32位平台,优化编译器可以优化将32位C变量组合成64位C变量的移位/移位。它们已经将64位变量存储为一对寄存器,在正常情况下,可以看出asm中没有实际的工作要做

因此您可以将64位输入或输出指定为一对32位变量。

#include <stdint.h>

int64_t testFunc(int64_t acc, int32_t x, int32_t y)
{
   uint32_t prod_lo, prod_hi;

   asm("SMULL %0, %1, %2, %3"
      : "=&r" (prod_lo), "=&r"(prod_hi)  // early clobber for pre-ARMv6
      : "r"(x), "r"(y)
      );

    int64_t prod = ((int64_t)prod_hi) << 32;
    prod |= prod_lo;        // + here won't optimize away, but | does, with gcc
    return acc + prod;
}
或者您可以使用
“=r”(prod64)
约束并使用修饰符来选择您得到的
%0
的哪一半。
不幸的是,由于某些原因,gcc和clang发出的asm效率较低,从而节省了更多寄存器(并保持8字节堆栈对齐)。gcc为2而不是1,clang为4而不是2

// using an int64_t directly with inline asm, using %Q0 and %R0 constraints
// Q is the low half, R is the high half.
int64_t testFunc2(int64_t acc, int32_t x, int32_t y)
{
   int64_t prod;    // gcc and clang seem to want more free registers this way

   asm("SMULL %Q0, %R0, %1, %2"
      : "=&r" (prod)         // early clobber for pre-ARMv6
      : "r"(x), "r"(y)
      );

    return acc + prod;
}
再次使用gcc
-O3-mcpu=arm10e
编译。(叮当声保存/恢复4个寄存器)


因此,出于某种原因,使用当前gcc和clang手动处理64位整数的一半似乎更有效。这显然是一个遗漏的优化错误。

“看起来不好”可能是一个重要的美学问题,但对内联汇编来说并不特别重要。在谷歌上找到答案大约需要30-90秒,另外还要花2-5分钟阅读gcc文档并通过谷歌搜索找到答案的原因。。你可能还想多做点谷歌搜索。我相信gcc将使用dsp指令,如果它认为它们是有价值的(和可用的)。如果您可以避免使用内联asm,您几乎肯定会更快乐。
@ gcc -O3 output with early-clobber, valid even before ARMv6
testFunc:
    str     lr, [sp, #-4]!    @,         Save return address (link register)
    SMULL ip, lr, r2, r3    @ prod_lo, prod_hi, x, y
    adds    r0, ip, r0      @, prod, acc
    adc     r1, lr, r1        @, prod, acc
    ldr     pc, [sp], #4      @          return by popping the return address into PC


@ gcc -O3 output without early-clobber (&) on output constraints:
@ valid only for ARMv6 and later
testFunc:
    SMULL r3, r2, r2, r3    @ prod_lo, prod_hi, x, y
    adds    r0, r3, r0      @, prod, acc
    adc     r1, r2, r1        @, prod, acc
    bx      lr  @
// using an int64_t directly with inline asm, using %Q0 and %R0 constraints
// Q is the low half, R is the high half.
int64_t testFunc2(int64_t acc, int32_t x, int32_t y)
{
   int64_t prod;    // gcc and clang seem to want more free registers this way

   asm("SMULL %Q0, %R0, %1, %2"
      : "=&r" (prod)         // early clobber for pre-ARMv6
      : "r"(x), "r"(y)
      );

    return acc + prod;
}
@ gcc -O3 with the early-clobber so it's safe on ARMv5
testFunc2:
    push    {r4, r5}        @
    SMULL r4, r5, r2, r3    @ prod, x, y
    adds    r0, r4, r0      @, prod, acc
    adc     r1, r5, r1        @, prod, acc
    pop     {r4, r5}  @
    bx      lr  @