armv8
1. 子函数的参数,变量都会放到栈空间中
这里我们在一个子函数中写一个内联汇编函数,什么都不做。采用gcc编译之后,然后再看其汇编代码,来对函数的运行一步一步的深入。
code
void abs_assembly(float* src, float* out, int count)
{
int i = 10;
asm volatile(
""
:
:
:"cc", "memory", "v0"
);
}
然后其对应的汇编代码:
0000000000000af0 <abs_assembly>:
af0: d100c3ff sub sp, sp, #0x30
af4: f9000fe0 str x0, [sp, #24]
af8: f9000be1 str x1, [sp, #16]
afc: b9000fe2 str w2, [sp, #12]
b00: 52800140 mov w0, #0xa // #10
b04: b9002fe0 str w0, [sp, #44]
b08: d503201f nop
b0c: 9100c3ff add sp, sp, #0x30
b10: d65f03c0 ret
解释:
- 首先对x0(src), x1(out), w2(count)这三个函数的参数入栈,采用的是从左到右的顺序
mov w0, #0xa
将局部变量10放到w0寄存器,- 然后再入栈
nop
,表示什么都不做add sp, sp, #0x30
,表示清空栈空间
2. 内联汇编,给定输入参数->出栈
这里我们在上面的基础上,在内联汇编中给定输入参数。看相关的变化。
code
void abs_assembly(float* src, float* out, int count)
{
int i = 10;
asm volatile(
""
:
:"r"(src),
"r"(count),
"r"(out)
:"cc", "memory", "v0"
);
}
汇编代码
0000000000000af0 <abs_assembly>:
af0: d100c3ff sub sp, sp, #0x30
af4: f9000fe0 str x0, [sp, #24] // src
af8: f9000be1 str x1, [sp, #16] // out
afc: b9000fe2 str w2, [sp, #12] // count
b00: 52800140 mov w0, #0xa // #10
b04: b9002fe0 str w0, [sp, #44]
b08: f9400fe0 ldr x0, [sp, #24]
b0c: b9400fe1 ldr w1, [sp, #12]
b10: f9400be2 ldr x2, [sp, #16]
b14: d503201f nop
b18: 9100c3ff add sp, sp, #0x30
b1c: d65f03c0 ret
相较于前面的例子只是多了以下几句
b08: f9400fe0 ldr x0, [sp, #24] // src
b0c: b9400fe1 ldr w1, [sp, #12] // count
b10: f9400be2 ldr x2, [sp, #16] // out
解释
- 就是从栈空间中,取出参数的值,同时取的顺序和内联汇编传入参数的顺序是一样的。传入是src,count,out;ldr的顺序看上面注释。
3. 内联汇编,添加输出变量(有问题)
假设这里内联汇编要实现一个求绝对值功能的函数,src是输入,out是输出。这里我们先看一下最直接的形式,但是是有错误的格式。
code
void abs_assembly(float* src, float* out, int count)
{
int i = 10;
asm volatile(
""
:"=r"(out)
:"r"(src),
"r"(count)
:"cc", "memory", "v0"
);
}
汇编代码
0000000000000af0 <abs_assembly>:
af0: d100c3ff sub sp, sp, #0x30
af4: f9000fe0 str x0, [sp, #24]
af8: f9000be1 str x1, [sp, #16]
afc: b9000fe2 str w2, [sp, #12]
b00: 52800140 mov w0, #0xa // #10
b04: b9002fe0 str w0, [sp, #44]
b08: f9400fe0 ldr x0, [sp, #24]
b0c: b9400fe1 ldr w1, [sp, #12]
b10: f9000be0 str x0, [sp, #16]
b14: d503201f nop
b18: 9100c3ff add sp, sp, #0x30
b1c: d65f03c0 ret
可以看到这里相较于前面多了输出参数列表"=r"(out)
,汇编中多了str x0, [sp, #16]
,这句相当于一个入栈操作。虽然程序是有问题的,但是这句话到底完成了什么操作呢。其实就是啥都没做,这里挑出关键的步骤一步一步的解释一下。
str x1, [sp, #16]
,是将out
入栈到 sp+16 的地址ldr x0, [sp, #24]
, 是读取src
的地址到x0
寄存器str x0, [sp, #16]
, 是将x0
寄存器 入栈到 sp+16add sp, sp, #0x30
,清除栈空间。
但是这里注意,输出参数列表中"=r"(out) ,是src存到栈 sp+16 地址 ,并不是out的地址
4. 输出参数列表中的变量,必须出现在输入参数列表中。
修改为:
void abs_assembly(float* src, float* out, int count)
{
int i = 10;
asm volatile(
""
:"=r"(out)
:"r"(src),
"r"(count),
"0"(out)
:"cc", "memory", "v0"
);
}
相应的汇编代码
0000000000000af0 <abs_assembly>:
af0: d100c3ff sub sp, sp, #0x30
af4: f9000fe0 str x0, [sp, #24]
af8: f9000be1 str x1, [sp, #16]
afc: b9000fe2 str w2, [sp, #12]
b00: 52800140 mov w0, #0xa // #10
b04: b9002fe0 str w0, [sp, #44]
b08: f9400fe1 ldr x1, [sp, #24]
b0c: b9400fe2 ldr w2, [sp, #12]
b10: f9400be0 ldr x0, [sp, #16]
b14: f9000be0 str x0, [sp, #16]
b18: d503201f nop
b1c: 9100c3ff add sp, sp, #0x30
b20: d65f03c0 ret
解释
str x1, [sp, #16]
,将out入栈到 sp+16ldr x0, [sp, #16]
,从 sp+16取出到 x0str x0, [sp, #16]
,x0 入栈到 sp+16, 这里的 out 就和 前面的 入栈的 对应起来了。
输出参数列表中的变量,必须出现在输入参数列表中,不然输出变量的寻址会有问题