GNU ARM汇编器更改mov指令为adds指令?

5

社区!

我有一个名为first.S的单行源文件:

mov R1, R2

I generate an object file as follows:

$ arm-none-eabi-as -mcpu=cortex-m3 -march=armv7 -mthumb -c -o first.o first.S 

然后,我对它进行拆卸。

$ arm-none-eabi-objdump -d first.o

first.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   1c11        adds    r1, r2, #0

显然,在这种情况下,两个指令(movadd)具有相同的预期效果。

问题是:为什么会这样呢?

根据ARMv7-M体系结构参考手册,移动寄存器指令有多种编码方式,然而汇编器选择将其编码为一个add指令。

是否有任何文档可以描述这样的决策呢?

谢谢!


1
指令集参考描述了应该使用哪个指令。在ARMv6中,它从adds Rd,Rn,#0更改为lsls Rd,Rn,#0,现在有一个真正的不设置标志位的mov Rd,Rn,使用前一个Hi / Lo寄存器移动指令的扩展形式。 - fuz
@fuz 我成功地在2005年的ARM架构参考手册的Thumb2补充中找到了从“mov”到“add”的更改的参考。谢谢 - Alexandru N. Onea
2个回答

5

从thumb指令集开始,编码就一直存在。

0001110xxxnnnddd 

是一种

adds rd,rn,#xxx

如果您可以修改标志,那么它是更有效的编码方式。

伪指令mov rd,rn表示标志可能会改变(根据文档和gas的表现)。因此原始编码没有问题。

现在由解決器选择是否使用immed == 0时打印mov rd,rn vs adds rd,rn,#0,两种反汇编都是正确的。

现在带有一个高位寄存器和一个低位寄存器的mov表示:

Unlike the low register MOV instruction described in MOV (2) on page A7-73, this instruction does not change the flags.

现在进入了汇编语言阶段,这完全由汇编器定义,而不是目标(不是 ARM),还有可怕的统一语法等等。因此,现在它变成了一个特定于工具的事情。例如,Gnu 汇编器不喜欢使用 adds 的方式(非统一语法,我发现在 thumb 中使用它要容易得多),你执行 add 命令会得到 adds。

.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1

arm-none-eabi-as so.s -o so.o
so.s: Assembler messages:
so.s:5: Error: instruction not supported in Thumb16 mode -- `adds r0,r1,#0'

.thumb
add r0,r1,#0
mov r0,r1
movs r0,r1

00000000 <.text>:
   0:   1c08        adds    r0, r1, #0
   2:   1c08        adds    r0, r1, #0
   4:   1c08        adds    r0, r1, #0

对于Movs指令,它非常好。

0x1c08 = 0x0001110000001000,这是一个Adds Thumb指令,回到armv4t的时期,当所有这一切开始时。

.syntax unified
.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1


   0:   f101 0000   add.w   r0, r1, #0
   4:   4608        mov r0, r1
   6:   1c08        adds    r0, r1, #0
   8:   0008        movs    r0, r1

所以在这种情况下,它是一种不同的汇编语言(相同的工具不同的汇编语言)。
因此,此汇编语言尊重add vs adds和mov vs movs。
为了进行没有标志位的加法,您需要使用thumb2编码。没有标志位的mov是高寄存器mov 0x4608 0100011000001000 0x46xx。
adds仍然是原来的样子,而movs现已被编码为左移,但是它们不会反汇编成 lsl r0, r1,#0,而是反汇编成mov r0,r1,这让您更容易理解,而不仅仅是将mov反汇编为add。为什么他们不使用adds?这里还有另一个问题,如果您查看arm arm中低寄存器的mov指令,至少在旧的arm arm中,它描述了标志位的变化并显示了adds编码。但是如果您查看lsl的说明,标志位是不同的,在最长生命周期的ARM ARM(带有thumb)中描述时,lsl不能替换带标志位的mov。
好了,这很有意义,在旧版的arm arm中,它们很有帮助。如果立即数为零,则没有进位,因此它被描述为设置为零以及带符号溢出标志。
Lsl在一个文档中将进位保持不变,而在另一个文档中将其设置为零。因此,在指令的实现中可能会发生一些变化或者其中一个ARM ARM是错误的(这经常发生)。
简短地说,mov rd,rn一直是伪指令,并记录为adds,反汇编器可以选择以任何一种方式打印它,这取决于反汇编器。
汇编语言由工具定义而不是目标,因此工具确定在其语法中使用哪个标志解决方案,并且可以在adds、mov高寄存器、thumb2编码或其他编码之间进行选择。
我们不知道为什么会选择一种编码,而有多种相等编码可用时,通常会选择较短的编码(x86中的xor vs带零立即数的mov)。但是lsl vs add vs sub vs ...
在arm文档中您还会找到其他伪指令(记录了其工具的汇编语言),以及汇编器添加到其汇编语言中的伪指令,如nop。
.thumb
nop
mov r8,r8
mov r4,r4

00000000 <.text>:
   0:   46c0        nop         ; (mov r8, r8)
   2:   46c0        nop         ; (mov r8, r8)
   4:   1c24        adds    r4, r4, #0

现在的问题是为什么他们不只是将其打印出来:

   0:   1c08        mov r0,r1  ; (adds r0, r1, #0)

我也很喜欢反汇编器隐含了分号作为注释边界的功能,而汇编语言奇怪地不支持这一点(就像地球上其他大多数汇编器一样)。
当然,反汇编器不知道创建该机器码的汇编器是什么,因此对于显示伪指令的情况,同时展示两者将会很好。

0

就像在https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11.5007716中所述,移动指令的操作码也是2字节,因此使用其中任何一个都没有优势。 https://developer.arm.com/documentation/ddi0337/e/instruction-timing/processor-instruction-timings指出,这两个指令都需要1个CPU周期才能完成。

然而,编码T2 https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11.5007716将具有空字节,在某些情况下(漏洞)会产生问题。

但是,我可以想象构建汇编器会更容易,如果它产生的不同操作码越少。


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