无法在 ARM 程序集中实现 C 逻辑

Unable to implement C Logic in ARM assembly

我有一个 C 代码想用 ARM 编程语言实现。 我心目中的 C 代码是这样的:

int a;
scanf("%d",&a);
if(a == 0 || a == 1){
   a = 1;
}
else{
   a = 2;
}

我尝试过的:

//arm equivalent of taking input to reg r0

//check for first condition
cmp r0,#1
moveq r0,#1

//if false 
movne r0,#2

//check for second condition
cmp r0,#0
moveq r0,#1

这是正确的实施方式吗?

不,那行不通。

cmp r0,#1    is it a one
moveq r0,#1  yes, make it a one again?
movne r0,#2  otherwise make it a 2, what if it was a zero to start, now it is a 2
cmp r0,#0    at this point it is either a 1 or a 2 you forced it so it cannot be zero, what it started off is is now lost.
moveq r0,#1

您有正确的概念,但需要更好地订购。

尽管遵循这种思路

也许使用另一个寄存器

x = 2;
if(a==0) x = 1;
if(a==1) x = 1;
a = x;

思考这个

if(a==0) a = 1;
if(a!=1) a = 2;

或者像其他人所说的那样询问编译器。

由于 or、test OR 测试,通常它们需要单独完成第一个测试的 false 条件并不意味着您必须在声明 false 之前进行其他测试的 else 条件。但是如果为真,你需要跳过所有的东西而不是进入第二个测试,因为那可能(在这种情况下)是假的......

正如 Peter 指出的那样,您可以使用无符号小于或等于和大于条件(即使在 C 中它是一个带符号的 int,位是位)。

LS Unsigned lower or same
HI Unsigned higher

根据 ARM 指令集可以是:

        cmp     r0, #1
        movls   r0, #1
        movhi   r0, #2
        bx      lr

        cmp     r0, #1
        ite     ls
        movls   r0, #1
        movhi   r0, #2
        bx      lr

我比你聪明吗? NO 我只是简单地使用编译器来编译C代码。 https://godbolt.org/z/dqxv64Eb9

您的代码因 a=0 而损坏 - 在您的脑海中或在调试器中单步执行它,看看会发生什么。


鉴于此特定条件,它等同于 (unsigned)a <= 1U(因为负整数会转换为巨大的无符号值)。您可以执行单个 cmp 和 movls / movhi。编译器已经发现了这种优化; 以下是如何要求编译器为您制作 asm,这样您就可以学习聪明的人类编写的技巧:

int foo(int a) {
    if(a == 0 || a == 1){
       a = 1;
    }
    else{
       a = 2;
    }
    return a;
}

使用 ARM GCC10 -O3 -marm on the Godbolt compiler explorer:

foo:
        cmp     r0, #1
        movls   r0, #1
        movhi   r0, #2
        bx      lr

有关制作具有有用的 asm 输出的函数的更多信息,请参阅 。在这种情况下,r0 是调用约定中的第一个参数传递寄存器,也是 return 值寄存器。

我还包含了另一个使用 if (a <= 1U) 的 C 版本,以表明它编译为相同的 asm。 (1U 是一个无符号常量,因此 C 整数提升规则将 a 隐式转换为 unsigned,因此类型匹配 <= 运算符。您不需要显式执行(unsigned)a <= 1U.)


一般情况:不是单一范围

对于像 a==0 || a==3 这样不是单一范围检查的情况,您可以 断言第二个 cmp。 (Godbolt)

foo:
        cmp     r0, #3             # sets Z if a was 3
        cmpne   r0, #0             # leaves Z unmodified if it was already set, else sets it according to a == 0
        moveq   r0, #1
        movne   r0, #2
        bx      lr

您可以像 a==3 && b==4 一样链接 &&,或者对于像 a >= 3 && a <= 7 这样的检查,您可以使用 sub / cmp,使用与子映射后的 0 或 1 范围检查相同的无符号比较技巧 a 值到 0..n 范围内。请参阅 the Godbolt link