【问题标题】:Unable to implement C Logic in ARM assembly无法在 ARM 程序集中实现 C 逻辑
【发布时间】:2021-07-02 13:04:44
【问题描述】:

我脑海中有一个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

这是正确的实现方式吗?

【问题讨论】:

  • 为什么不让编译器去做呢?这是它的工作。
  • @EugeneSh。这只是一个玩具示例。我实际上想扫描用户输入的变量 a
  • 我建议将假条件移到最后。它应该在第二个条件检查之后。因为如果你输入零,那么根据你的 c 逻辑答案应该是 1,但 arm 代码会生成 2跨度>

标签: c assembly arm


【解决方案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,测试 OR 测试,通常它们需要单独完成,第一个测试的错误条件并不意味着您必须在声明错误之前执行其他测试的 else 条件。但如果为真,您需要跳过所有内容,不要陷入第二次测试,因为这可能(在这种情况下)是错误的......

正如彼得指出的那样,您可以使用无符号小于或等于和大于条件(即使在 C 中它是有符号整数,位就是位)。

LS Unsigned lower or same
HI Unsigned higher

【讨论】:

  • 给定这个特定条件,它相当于a <= 1U,所以你可以做一个单一的cmp和movls/movhi。 (编译器已经发现了这种优化:godbolt.org/z/dcevfocrh
  • 它是有符号的,所以你不能做小于或等于 1 因为一半可能的位条件会通过。我在第一次编辑时就想到了这一点。
  • 我知道,这就是我与1U 进行比较的原因,导致a 隐式转换为无符号匹配。如果你真的写(a == 0 || a==1),编译器也会做什么
【解决方案2】:

根据 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

【讨论】:

    【解决方案3】:

    您的代码因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
    

    请参阅How to remove "noise" from GCC/clang assembly output? 了解有关制作具有有用 asm 输出的函数的更多信息。在这种情况下,r0 是调用约定中的第一个参数传递寄存器,也是返回值寄存器。

    我还包含另一个使用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,使用与子映射 a 值到 0 后的 0 或 1 范围检查相同的无符号比较技巧..n 范围。请参阅the Godbolt link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多