ARM参数传递

时间:2016-03-11 15:17:19

标签: arm

我正在尝试编写一个ARM程序,它接受三个数字并计算判别式。它有两个源文件,driver.s& prog3.s。我理解如何找到歧视,但我如何传递值A,B和& C从主函数进入discriminim函数?我已经包含了我输入的代码....

MAIN()driver.s

avalue  .reg    r0
bvalue  .req    r1
cvalue  .req    r2
final   .req    r3
loopcount   .req    r4

readA:
.ascii  “%d”
readB:
.ascii  “%d”
readC:  
.ascii  “%d”

addressReadA:   .word   readA
addressReadB:   .word   readB
addressReadC:   .word   readC

main:
ldr avalue, addressReadA    @ load in avalue
ldr bvalue, addressReadB    @ load in bvalue
ldr cvalue, addressReadC    @ load in cvalue

DISCRIM()prog3.s

avalue  .reg    r0
bvalue  .req    r1
cvalue  .req    r2
final   .req    r3

discrim:
mul bvalue, bvalue, bvalue      @ square bvalue
mul avalue, avalue, #4      @ multiply avalue by 4
mul cvalue, avalue, cvalue      @ multiply avalue by cvalue
add final, bvalue, cvalue       @ calculated discriminant 

1 个答案:

答案 0 :(得分:1)

遵循C编译器使用的调用约定并不是一个坏主意,因为如果你从纯汇编程序转到C和asm混合,你已经有了这种经验。和/或您可能会看到所使用的调用约定中的简单性和智慧。

您如何知道编译器的调用约定是什么? 1)阅读手册/文档和谷歌。 2)试试吧。原型化一个函数,它与操作数的数量类似,操作数的类型和返回值,并将其输入实数,并查看它产生的内容。

编译为asm有时可以工作但是伪指令和汇编程序完成的其他事情我更喜欢拆解而不是编译成asm YMMV。

unsigned int fun ( unsigned int a, unsigned int b, unsigned int c );
unsigned int test ( void )
{
    return(fun(1,2,3));
}

其中gnu目前导致

00000000 <test>:
   0:   e92d4010    push    {r4, lr}
   4:   e3a02003    mov r2, #3
   8:   e3a01002    mov r1, #2
   c:   e3a00001    mov r0, #1
  10:   ebfffffe    bl  0 <fun>
  14:   e8bd4010    pop {r4, lr}
  18:   e12fff1e    bx  lr

编译器和目标的每个组合可能具有不同的调用约定,没有理由假设同一编译器的不同编译器或版本使用相同的约定。 ARM,MIPS,毫无疑问其他人试图帮助/鼓励/建议使用调用约定,而一些编译器只是遵循它,为什么不呢。

该约定中的规则有很多例外,但对于ARM,第一个最多四个寄存器值的参数,在这种情况下最多四个有符号或无符号整数或最多四个小于或等于32比特数量(浮动可以创建例外)前四个一般用途注册器使用r0作为第二个参数r1,依此类推。目前,该标准使堆栈在64位边界上保持一致。

所以我们看到第一个参数确实放在r0中r1中的第二个参数和r2中的第三个参数,显然你不必按顺序排列这三个指令,无所谓。

因为这个函数正在调用另一个函数,所以它必须在lr中保留它的返回值,以便进入堆栈,因为标准说保持堆栈在64位边界上对齐它们正在推送堆栈上的另一个寄存器r4是任意的它可以是任何寄存器,这是工具选择的那个。

因为标准说要在r0中返回,这是实现其中一个函数的代码。

    unsigned int fun ( unsigned int a, unsigned int b, unsigned int c )
    {
        return(a+b^c);
    }

00000000 <fun>:
   0:   e0800001    add r0, r0, r1
   4:   e0200002    eor r0, r0, r2
   8:   e12fff1e    bx  lr

现在非常有趣的是我看到编译器没有对调用执行尾部优化,它可能没有保存lr并且执行了分支很有趣,因为r0中的返回值是test()是什么也回到同一个寄存器。真的有点困惑,那并没有发生。

但你可以看到确实返回值保留在r0中,按照惯例,我们可以删除r0-r3,我们不必保留它们,而这些函数不是。

如果您将测试更改为

unsigned int fun ( unsigned int a, unsigned int b, unsigned int c );
unsigned int test ( void )
{
    return(fun(1,2,3)+7);
}

然后它不能进行尾部优化并且还显示返回寄存器,因此您不必创建fun()函数来查看它。

00000000 <test>:
   0:   e92d4010    push    {r4, lr}
   4:   e3a02003    mov r2, #3
   8:   e3a01002    mov r1, #2
   c:   e3a00001    mov r0, #1
  10:   ebfffffe    bl  0 <fun>
  14:   e8bd4010    pop {r4, lr}
  18:   e2800007    add r0, r0, #7
  1c:   e12fff1e    bx  lr

你可以与其他目标或其他编译器一起做这种事情,并且没有理由假设一个目标与另一个目标具有相同的约定。

Disassembly of section .text:

00000000 <fun>:
   0:   0f 5e           add r14,    r15 
   2:   0f ed           xor r13,    r15 
   4:   30 41           ret         

0000000000000000 <fun>:
   0:   8d 04 37                lea    (%rdi,%rsi,1),%eax
   3:   31 d0                   xor    %edx,%eax
   5:   c3                      retq   

这个是基于堆栈而不是基于寄存器的

Disassembly of section .text:

00000000 <_fun>:
   0:   1166            mov r5, -(sp)
   2:   1185            mov sp, r5
   4:   1d41 0004       mov 4(r5), r1
   8:   6d41 0006       add 6(r5), r1
   c:   1d40 0008       mov 10(r5), r0
  10:   7840            xor r1, r0
  12:   1585            mov (sp)+, r5
  14:   0087            rts pc

但是如果这只是一个纯粹的汇编项目而你不必与编译输出接口,那就做你想做的事情,设计项目的一部分不仅仅是每个单独的函数,而是它们如何交互,与C或Python没有区别或者您还必须在函数之间为自己定义界面。大会不会使这种特殊或不同,只是另一种语言。