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

在ARM汇编中使用ADDS指令而不是ADD指令的原因是什么?,arm,cortex-m,Arm,Cortex M,我的课程笔记总是在ARM代码片段中使用ADD和SUB,而不是像我所期望的那个样使用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

我的课程笔记总是在ARM代码片段中使用ADD和SUB,而不是像我所期望的那个样使用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命令,而不是当前正在使用的ADD和SUB命令。ADDS和SUBS命令afaik将更新APSR标志NZCV,以供以后使用。但是,正如您在上面的代码片段中所看到的,没有使用更新的值。那么,该命令还有其他用途吗?

算术指令ADD、SUB等不修改状态标志,这与比较指令CMP、TEQ不同,后者默认情况下会更新条件标志。但是,将S添加到算术指令SADD、SUBS等将根据操作结果更新条件标志。这是在算术指令中使用S的唯一一点,因此如果不检查cf,就没有理由使用ADDS而不是ADD

为了实现不同的目的,有更多的代码附加到指令中,例如CC条件标志C=0,因此:

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

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

从循环的角度来看,更新条件标志与否没有区别。例如,ADDS和ADD将花费1个周期

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

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

可能会产生不同的行为

正如old_timer所指出的那样,这个问题变得非常相关。谈到统一语言,首选的语法是ADDS,而不是ADD。因此,即使是为了使用UAL组装Thumb和/或ARM,OP的代码也是绝对正确的。算术指令ADD、SUB等不会修改状态标志,这与比较指令CMP、TEQ不同,CMP、TEQ默认情况下会更新条件标志。但是,将S添加到算术指令SADD、SUBS等将根据操作结果更新条件标志。这是在算术指令中使用S的唯一一点,因此如果不检查cf,就没有理由使用ADDS而不是ADD

为了实现不同的目的,有更多的代码附加到指令中,例如CC条件标志C=0,因此:

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

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

从循环的角度来看,更新条件标志与否没有区别。例如,ADDS和ADD将花费1个周期

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

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

可能会产生不同的行为


正如old_timer所指出的那样,这个问题变得非常相关。谈到统一语言,首选的语法是ADDS,而不是ADD。因此,即使是为了使用UAL组装Thumb和/或ARM,OP的代码也绝对不错。

在某些cortex-ms上不提供不带标志更新的ADD。如果您查看ARM文档中的指令集,那么在为非通用用例编写汇编语言时,这始终是一个好主意在armv7-m cortex-m3、cortex-m4、cortex-m7上的thumb2扩展之前可用。cortex-m0和cortex-m0+以及通常使用armv4t或armv6-m的广泛兼容代码没有添加无标志选项。也许这就是原因

另一个原因可能是得到16位指令,而不是32位指令,但这是一个滑坡,因为它更多地进入汇编程序,它们的语法是由汇编程序定义的,汇编程序是处理汇编语言的程序,而不是目标。例如,非统一气体:

.thumb
add r1,r2,r3

Disassembly of section .text:

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

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


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

在某些cortex-ms上不提供无标记更新的添加。如果你看一下arm 在为通用用例编写汇编语言时,指令集的文档始终是一个好主意,在armv7-m cortex-m3、cortex-m4、cortex-m7上安装thumb2扩展之前,这些用例是不可用的。cortex-m0和cortex-m0+以及通常使用armv4t或armv6-m的广泛兼容代码没有添加无标志选项。也许这就是原因

另一个原因可能是得到16位指令,而不是32位指令,但这是一个滑坡,因为它更多地进入汇编程序,它们的语法是由汇编程序定义的,汇编程序是处理汇编语言的程序,而不是目标。例如,非统一气体:

.thumb
add r1,r2,r3

Disassembly of section .text:

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

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


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

我想你应该问问编写代码的人。可能那个人使用了一种或多种编码方式,默认情况下总是更新状态标志。@Michael,当然,我会尝试联系编写此代码的人。我想你必须询问编写此代码的人。可能那个人使用了一种或多种编码方式,默认情况下总是更新状态标志。@Michael,当然,我会尝试联系写这篇文章的人。因为您提到了ARMv6-M,所以值得一提的是,在16位thumb编码中,一些组合,如ADD r5,5是不可表达的,在full thumb 2中,指令的大小可能不同,例如ADD r5,5是32位,而ADD r5,5是16位。由于您提到了ARMv6-M,因此值得一提的是,在16位thumb编码中,某些组合(如ADD r5,5)是不可表达的,而在full thumb 2中,指令的大小可能不同,例如,添加r5,5是32位,而添加r5,5是16位。
.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