gcc中的arm内联汇编

Posted

技术标签:

【中文标题】gcc中的arm内联汇编【英文标题】:arm Inline assembly in gcc 【发布时间】:2012-02-15 22:36:48 【问题描述】:

我在处理一些内联汇编代码时遇到了一些问题。我知道应该做什么,但我错过了“如何”!

我有这个“几乎”工作的校验和函数:

static unsigned long cksum_unroll( unsigned short **w, int *mlen)

  int len;
  unsigned short *w0;
  unsigned long sum=0;

  len = *mlen;
  w0 = *w;

  while( len >= 8) 
    asm volatile (
          "ldmia %[w0]!, v1, v2\n\t"
          "adds %[sum], %[sum], v1\n\t"
          "adcs %[sum], %[sum], v2\n\t"
          "adcs %[sum], %[sum], #0"
          : [sum] "+r" (sum) : [w0] "r" (w0)
          );
    len -= 8;
  
  *mlen = len;
  *w = w0;
  return (sum);

我相信我的问题在于“: [sum] "+r" (sum) : [w0] "r" (w0)" 在第一条装配线上,ldmia 正确处理了 w0(当该线执行时,数据在 r4,r5 中,并且 w0 递增)。但是 w0 的增量值并没有保存在某个地方,当代码循环时,w0 的原始值会再次加载(参见下面的汇编代码)。 我的猜测是我应该将 w0 的值存储在 ": [sum] "+r" (sum) : [w0] "r" (w0)" 行上,但我不知道如何......

这是函数内联汇编部分的反汇编代码:

请注意:

len is stored at r11, #-16
w0 is stored at r11, #-20
sum is stored at r11, #-24

编译,如下代码:

asm volatile (
          "ldmia %[w0]!, v1, v2\n\t"
          "adds %[sum], %[sum], v1\n\t"
          "adcs %[sum], %[sum], v2\n\t"
          "adcs %[sum], %[sum], #0"
          : [sum] "+r" (sum) : [w0] "r" (w0)
);
len -= 8;

生成:

00031910:   ldr r3, [r11, #-20]
00031914:   ldr r2, [r11, #-24]
00031918:   mov r4, r2
0003191c:   ldm r3!, r4, r5
00031920:   adds r4, r4, r4
00031924:   adcs r4, r4, r5
00031928:   adcs r4, r4, #0
0003192c:   str r4, [r11, #-24]
00031930:   ldr r3, [r11, #-16]
00031934:   sub r3, r3, #8
00031938:   str r3, [r11, #-16]

如您所见,我想在第 31928 行和第 3192c 行之间添加类似“str r3, [r11, #-20]”的内容,因为当程序循环到第 31910 行时,r3 会加载初始值r3...

我认为这对于堆栈溢出社区的内联汇编专家来说是一件容易的事!

顺便说一句,我正在研究 ARM7TDMI 处理器(但这可能与这个问题无关......)

提前致谢!

编辑:

为了验证我的想法,我测试了以下内容:

asm volatile ( 
"ldmia %[w0]!, v1, v2\n\t" 
"adds %[sum], %[sum], v1\n\t" 
"adcs %[sum], %[sum], v2\n\t" 
"adcs %[sum], %[sum], #0\n\t" 
"str %[w0], [r11, #-20]" 
: [sum] "+r" (sum) : [w0] "r" (w0) 
); 

这很有效。也许这就是解决方案,但是我用什么来替换“r11,#20”,如果我修改函数可能会改变?

【问题讨论】:

为了验证我的想法,我测试了以下内容:asm volatile ( "ldmia %[w0]!, v1, v2\n\t" "adds %[sum], %[sum], v1\n\t" "adcs %[sum], %[sum], v2\n\t" "adcs %[sum], %[sum], #0\n\t" "str %[w0], [r11, #-20]" : [sum] "+r" (sum) : [w0] "r" (w0) ); 这很有效。也许这是解决方案,但是我用什么来替换“r11,#20”,如果我修改函数可能会改变? GCC 的内联汇编让我头疼(而且我已经开始了一个,因为附近的改造中正在进行一些地毯胶合),所以我不能给你任何直接的帮助......但是我可以向您指出我读过的关于如何处理 GCC 的内联汇编的最佳文档之一,以防您还没有遇到它:ethernut.de/en/documents/arm-inline-asm.html 作为奖励,该文档专门针对 ARM。跨度> 感谢您的链接。当我写这个问题时,这个网页已经打开了! 【参考方案1】:

问题似乎是您将 w0 指定为 INPUT 操作数,而实际上它应该是读写 OUTPUT 操作数,例如 sum。此外,您需要在使用这些寄存器时指定它破坏 v1 和 v2(否则,gcc 可能会将一些其他 var 放入这些 reg 中并希望它们被保留。)

所以你应该有:

asm volatile (
      "ldmia %[w0]!, v1, v2\n\t"
      "adds %[sum], %[sum], v1\n\t"
      "adcs %[sum], %[sum], v2\n\t"
      "adcs %[sum], %[sum], #0"
      : [sum] "+r" (sum) , [w0] "+r" (w0) : : "v1", "v2"
      );

也就是说,两个读写输入/输出操作数,没有独占输入操作数,以及两个寄存器clobbers

【讨论】:

哇,非常感谢,它似乎工作得很好。最初(在我开始调试这部分代码之前)该行是:[w0] "+r" (w0), [sum] "+r" (sum),除了clobbers 部分,你给出的答案,但是 sum 和 w0 的顺序相反......我试过你的解决方案没有clobbers(正如你所说我“应该”,而不是“必须”使用它;-),它的行为就像一开始一样。使用 clobbers 部分,它可以完美地工作,因为它强制编译器保存 w0 的状态! @Martin:好的,我加强了措辞。忽略clobbers是一个特别隐蔽的错误,因为它可能在您第一次尝试时工作得很好,然后当您更改一些(显然)不相关的程序部分时会破坏寄存器分配器。

以上是关于gcc中的arm内联汇编的主要内容,如果未能解决你的问题,请参考以下文章

这个 GCC 内联汇编中的参数列表有啥问题?

gcc 内联汇编中的 min

GCC 内联汇编中的标签

GCC内联汇编中的C数组?

防止 GCC LTO 删除函数

GCC 内联汇编到 IAR 内联汇编