assembly 比较有符号整数并在Thumb 2程序集中返回0或-1

xkrw2x1b  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(79)

在thumb 2汇编中,当r 0和r1有有符号整数时,我希望r1=-1(i.即0xffffffff),否则r1=0。
我可以简单地写代码:

4288            cmp     r0, r1
bfb4            ite     lt
f04f 31ff       movlt.w r1, #-1
2100            movge   r1, #0

但我想知道是否有更优化的方式,无论是在周期或空间。
如果它是一个unsigned比较,我可以使用进位标志:

4288            cmp     r0, r1
4189            sbcs    r1, r1

在ARM 64中,cset r1, lt会返回0或1,但我喜欢在thumb 2汇编中编写代码。

tquggr8v

tquggr8v1#

**如果你的输入范围有限,所以减法不会有符号溢出,**你可以使用Jester的建议,使用减法的符号位:

@@ With limited-range inputs
   rsb r1, r0          @ must not overflow, oVerflow flag must be 0 after this
   asr r1, #31         @ copy the sign bit of r0-r1 to all other bits

只要r0-r1不溢出2的补码有符号整数,这就可以工作。那么,当r0 < r1时,结果的符号确实是负的。
失败的情况包括-10 < INT_MAX,数学结果是-2147483657,但截断为32位,我们得到0x7ffffff7+2147483639)。V标志将被设置,指示有符号的overflow,并且N标志(符号位)将被清除,因为截断结果不是负的,与数学非截断结果的符号相反。
这就是为什么像lt这样的signed compare conditions检查N != V,而不仅仅是N,所以例如cmp/blt可以正确地使用这些输入。

**如果你的代码必须正确地处理任意输入(全范围),我不认为有任何改进的空间,甚至在代码大小上也是如此。**使用lt条件,分支或it预测,似乎是唯一合理的选择。手动模拟2的补码比较不会比这更短。

即使在IT块之外,ARM Thumb 2也没有用于将寄存器设置为-1的2字节指令。(至少编译器不知道或使用。)movs不对其立即数进行符号扩展,而mvn/mvns-immediate是一条4字节指令。orrs r0, #-1也是如此,并不是说您无论如何都希望这种虚假的依赖关系来提高性能。因此,即使我们可以在与任何输入不同的寄存器中产生结果,也没有节省。
当前的GCC和clang(Godbolt)更喜欢无条件地设置一个寄存器,然后Assert一个mov-immediate来覆盖它。但这可能只是Thumb模式的启发式,如果其中一个常量允许IT块外部的更短指令,或者在填充IT块并需要另一个IT或无法合并成一个ITE的情况下,预测更少的指令。这可能发生在一个更大的函数中,或者如果其他事情是在相同的条件下预测的,但在这里不是问题。
ARM模式下(-marm),GCC首选cmp;movge r0, #0 ;mvnlt r0, #0对于我看过的所有-mcpu=(cortex-a8,cortex-a53,cortex-a76和unset)。(我正在看一个函数,所以它返回r 0,但输入是r 0和r1,所以它仍然是你的情况一样。)
所以这和你的拇指模式的策略完全一样。除非IT块内部的指令比外部的指令慢,否则最好还是做你正在做的事情。

@ GCC -O3, probably no better than yours, but same size
foo:
 cmp    r1, r0
 mov.w  r0, #4294967295 ; 0xffffffff        @ 4-byte instruction
 it ge
 movge  r0, #0                              @ 2-byte instruction
@ r0 and r1 are swapped vs. your version since functions return in r0

相关问题