在 ARM 汇编中使用 ADDS 指令而不是 ADD 指令的原因是什么?

What would be a reason to use ADDS instruction instead of the ADD instruction in ARM assembly?

我的课程笔记总是在他们的 ARM 代码片段中使用 ADDS 和 SUBS,而不是我期望的 ADD 和 SUB。例如,这是一个这样的片段:

__asm void my_capitalize(char *str)
{
cap_loop
  LDRB r1, [r0]   // Load byte into r1 from memory pointed to by r0 (str pointer)
  CMP r1, #'a'-1  // compare it with the character before 'a'
  BLS cap_skip    // If byte is lower or same, then skip this byte
  CMP r1, #'z'    // Compare it with the 'z' character
  BHI cap_skip    // If it is higher, then skip this byte
  SUBS r1,#32     // Else subtract out difference to capitalize it
  STRB r1, [r0]   // Store the capitalized byte back in memory

cap_skip
  ADDS r0, r0, #1 // Increment str pointer
  CMP r1, #0      // Was the byte 0?
  BNE cap_loop    // If not, repeat the loop
  BX lr           // Else return from subroutine
}

这个简单的代码例如将字符串中的所有小写英文转换为大写。我在此代码中不明白的是为什么他们不使用 ADD 和 SUB 命令而不是当前使用的 ADDS 和 SUBS。 ADDS 和 SUBS 命令,afaik,更新 APSR 标志 NZCV,供以后使用。但是,正如您在上面的代码片段中看到的那样,更新后的值没有被使用。那么这个命令还有其他的用处吗?

算术指令(ADDSUB 等)不修改状态标志,这与更新条件的比较指令(CMPTEQ)不同默认情况下的标志。但是,将 S 添加到算术指令(ADDSSUBS 等)将根据运算结果更新条件标志。这是对算术指令使用 S 的唯一要点,因此如果不检查 cf,则没有理由使用 ADDS 而不是 ADD.


有更多代码附加到指令(link),以达到不同的目的,例如CC(条件标志C=0),因此:

ADDCC:如果进位状态位设置为0则执行操作

ADDCCS:如果进位状态位设置为0,则执行操作,然后更新状态标志(如果C=1,则状态标志不被覆盖)。


从循环的角度来看,是否更新条件标志没有区别。以ARMv6-M为例,ADDSADD需要1个周期。


放弃使用 ADD 可能看起来像是一个懒惰的选择,因为 ADD 在某些情况下非常有用。更进一步,考虑这些例子:

  SUBS r0, r0, #1
  ADDS r0, r0, #2
  BNE go_wherever

  SUBS r0, r0, #1
  ADD r0, r0, #2
  BNE go_wherever

可能会产生不同的行为。


正如 old_timer 指出的那样,UAL becomes quite relevant on this topic. Talking about the unified language, the preferred syntax is ADDS, instead of ADD (link)。因此,如果目的是为 Thumb and/or ARM(使用 UAL)进行组装,则 OP 的代码绝对可以(甚至推荐)。

不带标志更新的 ADD 在某些 cortex-ms 上不可用。如果您查看指令集的 arm 文档(在使用汇编语言时总是一个好主意)用于通用用例,直到 armv7-m(cortex-m3、cortex-m4、cortex-m7)上的 thumb2 扩展才可用). cortex-m0 和 cortex-m0+ 以及一般广泛的兼容性代码(将使用 armv4t 或 armv6-m)没有 add without flags 选项。所以也许这就是原因。

另一个原因可能是获得 16 位指令而不是 32 位指令,但这是一个滑坡,因为它更多地涉及汇编器及其语法(语法由汇编器定义,汇编器是处理汇编的程序语言,而不是目标)。例如不是语法统一 gas:

.thumb
add r1,r2,r3

Disassembly of section .text:

00000000 <.text>:
   0:   18d1        adds    r1, r2, r3

反汇编程序知道现实,但汇编程序不知道:

so.s: Assembler messages:
so.s:2: Error: instruction not supported in Thumb16 mode -- `adds r1,r2,r3'

但是

.syntax unified
.thumb
adds r1,r2,r3
add r1,r2,r3


Disassembly of section .text:

00000000 <.text>:
   0:   18d1        adds    r1, r2, r3
   2:   eb02 0103   add.w   r1, r2, r3

所以在这种情况下并不滑,但是使用统一语法你开始进入 blahw,blah.w,blah,键入语法并且必须回头检查以查看你想要的指令是正在生成。非统一也有自己的游戏,当然所有这些都是特定于汇编程序的。

我怀疑他们要么选择他们唯一的选择,要么使用更小、更兼容的指令,尤其是如果这是 class 或文本,越兼容越好。