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],这句相当于一个入栈操作。虽然程序是有问题的,但是这句话到底完成了什么操作呢。其实就是啥都没做,这里挑出关键的步骤一步一步的解释一下。

  1. str x1, [sp, #16],是将 out 入栈到 sp+16 的地址
  2. ldr x0, [sp, #24], 是读取 src 的地址到 x0 寄存器
  3. str x0, [sp, #16], 是将 x0 寄存器 入栈到 sp+16
  4. add 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

解释

  1. str x1, [sp, #16],将out入栈到 sp+16
  2. ldr x0, [sp, #16],从 sp+16取出到 x0
  3. str x0, [sp, #16],x0 入栈到 sp+16, 这里的 out 就和 前面的 入栈的 对应起来了。

输出参数列表中的变量,必须出现在输入参数列表中,不然输出变量的寻址会有问题