一文搞懂 ARM 64 系列: 一文搞懂 ARM 64 系列: 函数调用传参与返回值
函数调用涉及到传参与返回值,下面就来看下ARM 64
中,参数与返回值的传递机制。
1 整数型参数传递
这里的整数型并不单指int
类型,或者NSInteger
类型,而是指任何能够使用整数表示的数据类型,包括char
、BOOL
、指针等。
对于整数型参数,需要分成参数个数<=8
个和>8
个两种情形来看。
如果参数个数 <=8
个,那么参数全部使用Xn
寄存器传递。
比如,一个函数的参数只有4
个,那么就是用X0
X1
X2
X3
寄存器传递。如果这个函数的参数为8
个,那么就使用X0
X1
X2
X3
X4
X5
X6
X7
寄存器传递。
换句话说,寄存器X0~X7
就是用来在参数个数<=8
个时,传递参数的。
// 1. 接受 4 个整型参数的函数
NSInteger add4(NSInteger zero, NSInteger one, NSInteger two, NSInteger three) {
return zero + one + two + three;
}
@implementation ViewController
- (void)viewDidLoad {
// 2. 调用函数 add4
NSInteger result = add4(0, 1, 2, 3);
NSLog(@"%ld", result);
}
@end
上面代码注释1
定义了一个接受4
个参数的函数add4
.
代码注释2
在viewDidLoad
函数中调用了函数add4
。
下面来看viewDidLoad
函数的汇编代码:
// ARMAssemble`-[ViewController viewDidLoad]:
...
0x102e142b0 <+28>: mov x1, #0x1
...
0x102e142bc <+40>: mov x0, #0x0
0x102e142c0 <+44>: mov x2, #0x2
0x102e142c4 <+48>: mov x3, #0x3
0x102e142c8 <+52>: bl 0x102e14000 ; add4 at ViewController.m:10
...
上面代码前面4
行将参数写入了对应的寄存器,最后一行调用了函数add4
。
函数add4
的汇编代码如下:
ARMAssemble`add4:
-> 0x102e14000 <+0>: sub sp, sp, #0x20
0x102e14004 <+4>: str x0, [sp, #0x18]
0x102e14008 <+8>: str x1, [sp, #0x10]
0x102e1400c <+12>: str x2, [sp, #0x8]
0x102e14010 <+16>: str x3, [sp]
...
上面代码第1
行分配栈空间,后面4
行代码就将参数值存储到了对应的栈空间。
如果参数个数 >8
个,那么寄存器X0~X7
负责传递前8
个参数,剩下的参数使用栈来传递。
// 1. 定义接受 10 个参数的函数 add10
NSInteger add10(NSInteger zero, NSInteger one, NSInteger two, NSInteger three, NSInteger four, NSInteger five, NSInteger six, NSInteger seven, NSInteger eight, NSInteger nine) {
return zero + one + two + three + four + five + six + seven + eight + nine;
}
@implementation ViewController
- (void)viewDidLoad {
// 2. 调用 add10
NSInteger result = add10(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
NSLog(@"%ld", result);
}
@end
上面代码注释1
定义了一个接受10
个参数的函数add10
。
代码注释2
在viewDidLoad
函数中调用了函数add10
。
viewDidLoad
函数的汇编代码如下:
ARMAssemble`-[ViewController viewDidLoad]:
0x10455c294 <+0>: sub sp, sp, #0x40
...
0x10455c2b0 <+28>: mov x1, #0x1
...
// 1. 存储第 9 个参数到栈中
0x10455c2bc <+40>: mov x9, sp
0x10455c2c0 <+44>: mov x8, #0x8
0x10455c2c4 <+48>: str x8, [x9]
// 2. 存储第 10 个参数到栈中
0x10455c2c8 <+52>: mov x8, #0x9
0x10455c2cc <+56>: str x8, [x9, #0x8]
0x10455c2d0 <+60>: mov x0, #0x0
0x10455c2d4 <+64>: mov x2, #0x2
0x10455c2d8 <+68>: mov x3, #0x3
0x10455c2dc <+72>: mov x4, #0x4
0x10455c2e0 <+76>: mov x5, #0x5
0x10455c2e4 <+80>: mov x6, #0x6
0x10455c2e8 <+84>: mov x7, #0x7
// 3. 调用函数 add10
0x10455c2ec <+88>: bl 0x10455c048 ; add10 at ViewController.m:14
...
上面代码注释1
后3
行代码将第9
个参数存储到栈顶。
代码注释2
后面2
行代码将第10
个参数存储到栈地址(SP + 0X8)
处。
代码注释3
调用函数add10
。
函数add10
的汇编代码如下:
ARMAssemble`add10:
-> 0x10455c048 <+0>: sub sp, sp, #0x50
// 1. 从主调函数栈中加载第 9 个参数到寄存器 X9
0x10455c04c <+4>: ldr x9, [sp, #0x50]
// 2. 从主调函数栈中加载第 10 个参数到寄存器 X8
0x10455c050 <+8>: ldr x8, [sp, #0x58]
// 3. 下面 8 条语句将对应的参数存储到对应的栈空间
0x10455c054 <+12>: str x0, [sp, #0x48]
0x10455c058 <+16>: str x1, [sp, #0x40]
0x10455c05c <+20>: str x2, [sp, #0x38]
0x10455c060 <+24>: str x3, [sp, #0x30]
0x10455c064 <+28>: str x4, [sp, #0x28]
0x10455c068 <+32>: str x5, [sp, #0x20]
0x10455c06c <+36>: str x6, [sp, #0x18]
0x10455c070 <+40>: str x7, [sp, #0x10]
// 4. 将第 9 个参数存储到栈地址 SP + 0x8 处
0x10455c074 <+44>: str x9, [sp, #0x8]
// 5. 将第 10 个参数存储到栈顶
0x10455c078 <+48>: str x8, [sp]
上面代码注释1
从主调函数栈中加载第9
个参数到寄存器X9
。
代码注释2
从主调函数栈中加载第10
个参数到寄存器X8
。
代码注释3
后面8
条语句将对应参数存储到对应栈空间。
代码注释4
将第9
个参数存储到栈地址(SP + 0x8)
处。
代码注释5
将第10
个参数存储到栈顶。
2 浮点数参数
浮点数参数的传递和整数型参数类似:
如果参数个数 <= 8
个,那么参数全部使用Dn
寄存器传递。
如果参数个数 > 8
个,那么寄存器D0~D7
负责传递前8
个参数,剩下的参数使用栈传递。
3 混合参数
混合参数是指参数中既有整数型参数,也有浮点数参数,那么参数传递规则会分别应用整数型规则和浮点数规则。
比如,如果一个函数有10
个整数型参数,10
个浮点数参数,那么参数规则应用如下:
首先应用整数型参数规则,由于参数个数超过了8
个,前8
个整数型参数由寄存器X0~X7
传递,剩余参数使用栈传递。
然后应用浮点数参数规则,由于参数个数超过了8
个,前8
个浮点数参数由浮点数寄存器D0~D7
传递,剩余参数使用栈传递。
// 1. 定义了接受 10 个整数型参数和 10 个浮点数参数的函数 hybrid
CGFloat hybrid(NSInteger zero, NSInteger one, CGFloat zerof, CGFloat onef, CGFloat twof, CGFloat threef, NSInteger two, NSInteger three, NSInteger four, NSInteger five, NSInteger six, NSInteger seven, NSInteger eight, NSInteger nine, CGFloat fourf, CGFloat fivef, CGFloat sixf, CGFloat sevenf, CGFloat eightf, CGFloat ninef) {
return zero + one + two + three + four + five + six + seven + eight + nine + zerof + onef + twof + threef + fourf + fivef + sixf + sevenf + eightf + ninef;
}
@implementation ViewController
- (void)viewDidLoad {
// 2. 调用 hybrid 函数,注意第 3 4 5 6个参数是浮点数
CGFloat result = hybrid(0, 1, 0.0f, 1.0f, 2.0f, 3.0f, 2, 3, 4, 5, 6, 7, 8, 9, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
NSLog(@"%f", result);
}
上面代码注释1
定义了一个接受10
个整数型参数和10
个浮点数参数的函数hybrid
。需要注意的是,第3
4
5
6
个参数是浮点数而不是整型数。
代码注释2
在viewDidLoad
函数中调用函数hybrid
。
viewDidLoad
函数的汇编码如下:
ARMAssemble`-[ViewController viewDidLoad]:
0x10059c294 <+0>: sub sp, sp, #0x50
...
0x10059c2b0 <+28>: mov x1, #0x1
...
// 1. 存储整数型参数中第 9 个参数到栈顶
0x10059c2bc <+40>: mov x8, sp
0x10059c2c0 <+44>: mov x9, #0x8
0x10059c2c4 <+48>: str x9, [x8]
// 2. 存储整数型参数中第 10 个参数到栈地址 SP + 0x8
0x10059c2c8 <+52>: mov x9, #0x9
0x10059c2cc <+56>: str x9, [x8, #0x8]
// 3. 存储浮点数参数中第 9 个参数到栈地址 SP + 0x10
0x10059c2d0 <+60>: fmov d0, #8.00000000
0x10059c2d4 <+64>: str d0, [x8, #0x10]
// 4. 存储浮点数参数中第 10 个参数到栈地址 SP + 0x18
0x10059c2d8 <+68>: fmov d0, #9.00000000
0x10059c2dc <+72>: str d0, [x8, #0x18]
// 5. 剩下 15 条语句存储对应参数到相应的寄存器
0x10059c2e0 <+76>: mov x0, #0x0
0x10059c2e4 <+80>: fmov d0, xzr
0x10059c2e8 <+84>: fmov d1, #1.00000000
0x10059c2ec <+88>: fmov d2, #2.00000000
0x10059c2f0 <+92>: fmov d3, #3.00000000
0x10059c2f4 <+96>: mov x2, #0x2
0x10059c2f8 <+100>: mov x3, #0x3
0x10059c2fc <+104>: mov x4, #0x4
0x10059c300 <+108>: mov x5, #0x5
0x10059c304 <+112>: mov x6, #0x6
0x10059c308 <+116>: mov x7, #0x7
0x10059c30c <+120>: fmov d4, #4.00000000
0x10059c310 <+124>: fmov d5, #5.00000000
0x10059c314 <+128>: fmov d6, #6.00000000
0x10059c318 <+132>: fmov d7, #7.00000000
// 6. 调用函数 hybrid
0x10059c31c <+136>: bl 0x10059c178 ; hybrid at ViewController.m:22
上面代码注释1
存储整数型参数中第9
个参数到栈顶。
代码注释2
存储整数型参数中第10
个参数到栈地址(SP +0x8)
处。
代码注释3
存储浮点数参数中第9
个参数到栈地址(SP + 0x10)
处。
代码注释4
存储浮点数参数中第10
个参数到站地址(SP + 0x18)
处。
代码注释5
后面的15
条语句村出纳对应参数到对应的寄存器,需要注意的是第2
条语句使用寄存器X1
传递第2
个整数型参数。其中的寄存器xzr
是全0
寄存器,也就是这个寄存器的值就是0
。
代码注释6
调用函数hybrid
。
hybrid
函数汇编码如下:
ARMAssemble`hybrid:
-> 0x10059c178 <+0>: sub sp, sp, #0xa0
// 1. 从主调函数栈中获取整数型参数中第 9 个参数到寄存器 X11
0x10059c17c <+4>: ldr x11, [sp, #0xa0]
// 2. 从主调函数栈中获取整数型参数中第 10 个参数到寄存器 X10
0x10059c180 <+8>: ldr x10, [sp, #0xa8]
// 3. 从主调函数栈中获取浮点数参数中第 9 个参数到寄存器 X9
0x10059c184 <+12>: ldr x9, [sp, #0xb0]
// 4. 从主调函数栈中获取浮点数参数中第 10 个参数到寄存器 X8
0x10059c188 <+16>: ldr x8, [sp, #0xb8]
// 5. 后面 12 条语句将对应参数存储到对应的栈地址
0x10059c18c <+20>: str x0, [sp, #0x98]
0x10059c190 <+24>: str x1, [sp, #0x90]
0x10059c194 <+28>: str d0, [sp, #0x88]
0x10059c198 <+32>: str d1, [sp, #0x80]
0x10059c19c <+36>: str d2, [sp, #0x78]
0x10059c1a0 <+40>: str d3, [sp, #0x70]
0x10059c1a4 <+44>: str x2, [sp, #0x68]
0x10059c1a8 <+48>: str x3, [sp, #0x60]
0x10059c1ac <+52>: str x4, [sp, #0x58]
0x10059c1b0 <+56>: str x5, [sp, #0x50]
0x10059c1b4 <+60>: str x6, [sp, #0x48]
0x10059c1b8 <+64>: str x7, [sp, #0x40]
// 6. 将整数型参数中第 9 个参数存储到栈地址 SP + 0x38
0x10059c1bc <+68>: str x11, [sp, #0x38]
// 7. 将整数型参数中第 10 个参数存储到栈地址 SP + 0x30
0x10059c1c0 <+72>: str x10, [sp, #0x30]
// 8. 后面 4 条语句继续存储浮点数参数到栈地址
0x10059c1c4 <+76>: str d4, [sp, #0x28]
0x10059c1c8 <+80>: str d5, [sp, #0x20]
0x10059c1cc <+84>: str d6, [sp, #0x18]
0x10059c1d0 <+88>: str d7, [sp, #0x10]
// 9. 将浮点数参数中第 9 个参数存储到栈地址 SP + 0x8
0x10059c1d4 <+92>: str x9, [sp, #0x8]
// 10. 将浮点数参数中第 10 个参数存储到栈顶
0x10059c1d8 <+96>: str x8, [sp]
...
上面代码注释1
从主调函数栈中获取整数型参数中的第9
个参数到寄存器X11
。
代码注释2
从主调函数栈中获取整数型参数中的第10
个参数到寄存器X10
。
代码注释3
从主调函数栈中获取浮点数参数中的第9
个参数到寄存器X9
。
代码注释4
从主调函数中获取浮点数参数中的第10
个参数到寄存器X8
。
代码注释5
后面12
条语句将对应参数存储到对应的栈地址。
代码注释6
将整数型参数中第9
个参数存储到栈地址(SP + 0x38)
。
代码注释7
将整数型参数中第10
个参数存储到栈地址(SP + 0x30)
。
代码注释8
后面4
条语句继续存储浮点数参数到栈地址。
代码注释9
将浮点数参数中第9
个参数存储到栈地址(SP + 0x8)
。
代码注释10
将浮点数参数中第10
个参数存储到栈顶。
从上图中可以看到,函数hybrid
中的参数和参数声明的顺序一样,越左边的参数越靠近栈中高地址。
4 结构体参数
结构体作为参数有2
种情形。
4.1 HAF 结构体
第1
种情形是HFA(Homogeneous Float-point Aggregates)
结构体。这种结构体的成员全部是浮点数类型,且成员不超过4
个。
在iOS
中,典型的就是CGRect
类型。
如果是HFA
结构体,那么其成员都是通过寄存器Dn
传递。
@implementation ViewController
- (void)viewDidLoad {
CGRect rect = CGRectMake(0.0f, 1.0f, 2.0f, 3.0f);
// 1. CGRect 作为函数 adds 的参数传递
CGFloat result = adds(rect);
NSLog(@"%f", result);
}
@end
上面代码注释1
将结构体CGRect
作为参数,传递给函数adds
.
函数viewDidLoad
的汇编码如下:
ARMAssemble`-[ViewController viewDidLoad]:
-> 0x102dd82c4 <+0>: sub sp, sp, #0x50
...
// 1. 将参数值加载到对应的 d0 d1 d2 d3 寄存器中进行传递
0x102dd830c <+72>: ldr d0, [sp, #0x10]
0x102dd8310 <+76>: ldr d1, [sp, #0x18]
0x102dd8314 <+80>: ldr d2, [sp, #0x20]
0x102dd8318 <+84>: ldr d3, [sp, #0x28]
0x102dd831c <+88>: bl 0x102dd8294 ; adds at ViewController.m:35
...
上面代码注释1
就将D0
D1
D2
D3
加载对应的参数值,进行传递。
4.2 非 HFA 结构体
第2
种情形是非HFA
结构体,也就是结构体成员不全都是浮点数类型,或者即使是浮点数类型,其成员个数也超过了4
个。
如果非HFA
结构体大小<= 16 Bytes
,那么参数使用寄存器Xn
传递。
// 1. 定义非 HFA 结构体,大小正好是 16 Bytes
typedef struct {
NSInteger one;
CGFloat onef;
} Param;
@implementation ViewController
- (void)viewDidLoad {
Param p;
p.one = 1;
p.onef = 1.0f;
// 2. 使用非 HFA 结构体作为参数,调用函数 adds
CGFloat result = adds(p);
NSLog(@"%f", result);
}
@end
上面代码注释1
定义了一个非HFA
结构体,其大小正好是16 Bytes
。
代码注释2
使用这个结构体作为参数,调用函数adds
。
函数viewDidLoad
的汇编码如下:
ARMAssemble`-[ViewController viewDidLoad]:
-> 0x1041602bc <+0>: sub sp, sp, #0x40
...
0x1041602d8 <+28>: mov x8, #0x1
...
// 1. 寄存器 X8 存储参数 1,存储到栈地址 SP + 0x10
0x1041602e4 <+40>: str x8, [sp, #0x10]
// 2. 下面 2 条指令将参数 1.0 存储到栈地址 SP + 0x18
0x1041602e8 <+44>: fmov d0, #1.00000000
0x1041602ec <+48>: str d0, [sp, #0x18]
// 3. 下面 2 条指令将参数 1 1.0 加载到寄存器 X0 X1 进行传递
0x1041602f0 <+52>: ldr x0, [sp, #0x10]
0x1041602f4 <+56>: ldr x1, [sp, #0x18]
// 4. 调用函数 adds
0x1041602f8 <+60>: bl 0x104160294 ; adds at ViewController.m:31
上面代码注释1
将参数 1
存储到栈地址(SP + 0x10)
。
代码注释2
后面2
条指令将参数1.0
存储到栈地址(SP + 0x18)
。
代码注释3
后面2
条指令将参数1
1.0
加载到寄存器X0
X1
进行传递。
代码注释4
调用函数adds
。
如果非HFA
结构体的大小> 16 Bytes
,那么主调函数会先将这个参数拷贝到一个内存区,然后将这个内存区的指针,作为参数传递。
// 1. 定义非 HFA 结构体,大小为 32 Bytes
typedef struct {
NSInteger one;
CGFloat onef;
NSInteger two;
CGFloat twof;
} Param;
// 2. 定义函数 adds,接受一个非 HFA 结构体作为参数
CGFloat adds(Param p) {
return p.one + p.onef + p.two + p.twof;
}
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
Param p;
p.one = 1;
p.onef = 1.0f;
p.two = 2;
p.twof = 2.0f;
// 3. 调用 adds 函数
CGFloat result = adds(p);
NSLog(@"%f", result);
}
@end
上面代码注释1
定义了一个非HFA
结构体Param
,大小为32 Bytes
。
代码注释2
定义了一个函数adds
,接受非HFA
结构作为参数。
代码注释3
调用adds
函数
函数viewDidLoad
的汇编码如下:
ARMAssemble`-[ViewController viewDidLoad]:
0x1025482dc <+0>: sub sp, sp, #0x80
...
// 1. 寄存器 X29 执行栈地址 SP + 0x70
0x1025482e4 <+8>: add x29, sp, #0x70
...
0x1025482f8 <+28>: mov x8, #0x1
...
// 2. 参数 1 存储到栈地址 X29 - 0x30 处
0x102548304 <+40>: stur x8, [x29, #-0x30]
// 3. 后面 2 条指令将参数 1.0 存储到栈地址 X29 - 0x28 处
0x102548308 <+44>: fmov d0, #1.00000000
0x10254830c <+48>: stur d0, [x29, #-0x28]
// 4. 后面 2 条指令将参数 2 存储到栈地址 X29 - 0x20 处
0x102548310 <+52>: mov x8, #0x2
0x102548314 <+56>: stur x8, [x29, #-0x20]
// 5. 后面 2 条指令将参数 2.0 存储到栈地址 X29 - 0x18 处
0x102548318 <+60>: fmov d0, #2.00000000
0x10254831c <+64>: stur d0, [x29, #-0x18]
// 6. 将栈地址 X29 - 0x30 处的值加载到寄存器 Q0,也就是将参数 1 1.0 加载到寄存器 Q0
0x102548320 <+68>: ldur q0, [x29, #-0x30]
// 7. 寄存器 X0 指向栈地址 SP + 0x10,它最终作为参数传递
0x102548324 <+72>: add x0, sp, #0x10
// 8. 将寄存器 Q0 的值存储到栈地址 SP + 0x10,也就是将参数 1 1.0 存入到此处
0x102548328 <+76>: str q0, [sp, #0x10]
// 9. 将栈地址 X29 - 0x20 处的值加载到寄存器 Q0,也就是将参数 2 2.0 加载到寄存器 Q0
0x10254832c <+80>: ldur q0, [x29, #-0x20]
// 10. 将寄存器 Q0 的值存储到栈地址 SP + 0x20,也就是将参数 2 2.0 存储到粗出
0x102548330 <+84>: str q0, [sp, #0x20]
// 11. 调用函数 adds
0x102548334 <+88>: bl 0x102548294 ; adds at ViewController.m:33
...
上面代码注释1
将寄存器X29
指向栈地址(SP + 0x70)
。
代码注释2
将参数1
存储到栈地址(X29 - 0x30)
。
代码注释3
后面2
条指令将参数1.0
存储到栈地址(X29 - 0x28)
。
代码注释4
后面2
条指令将参数2
存储到栈地址(X29 - 0x20)
。
代码注释5
后面2
条指令将参数2.0
存储到栈地址(X29 - 0x18)
。
代码注释2
3
4
5
本质上就是在栈空间创建结构体Param
,然后为其成员变量赋值。
从上图看到,结构体高地址成员,在栈内存中也处于高地址。
代码注释6
将栈地址(X29 - 0x30)
的值存储到寄存器Q0
。
寄存器Q0
是一个128bit
寄存器,可以存储2
个64bit
数据。换句话说,参数1
1.0
被存储到寄存器Q0
,并且参数1
在低64bit
,参数1.0
在高64bit
。
有关ARM 64
寄存器的介绍可以参看《一个搞懂 ARM 64 系列:寄存器》。
代码注释7
将X0
指向栈地址(SP + 0x10)
,此时寄存器X0
做为一块内存的指针,将作为参数传递给函数adds
。
代码注释8
将寄存器Q0
的值存储到栈地址(SP + 0x10)
。
代码注释6
7
8
的最终效果为:
代码注释9
将栈地址(X29 - 0x20)
的值存储到寄存器Q0
,也就是将参数2
2.0
存储到寄存器Q0
。
代码注释10
将寄存器Q0
的值存储到栈地址(SP + 0x20)
处。
代码注释6
8
9
10
本质上将结构体Param
进行了拷贝,拷贝到栈内存中新地址(SP + 0x10)
处,此地址内存由寄存器X0
引用。
此处使用寄存器X0
传参,是因为没有其他整型参数要传递。
代码注释11
调用函数adds
。
5 返回值
返回值的传递规则比较简单。
要确定返回值如何传递,只需要假想,如果这个返回值作为函数参数,将如何传递。参数传递的方式,决定了返回值传递的方式。
如果返回值是一个整数型,假想它作为函数参数传递,根据规则,将使用寄存器X0
传递,那么这个返回值就使用X0
返回。
如果返回值是一个浮点数,假想它作为函数参数传递,根据规则,将使用寄存器D0
传递,那么这个返回值就使用D0
返回。
如果返回值是一个HFA
结构体,假想它作为函数参数传递,根据规则,将使用寄存器Dn
传递,那么这个返回值就使用Dn
返回。
一个例子就是有函数返回iOS
中的结构体CGRect
,返回值最终使用寄存器D0
D1
D2
D3
返回。
如果返回值是一个非HFA
结构体,并且大小不超过 16 Bytes
,假想它作为参数传递,根据规则,将使用寄存器Xn
传递,那么返回值就使用Xn
返回。
如果返回值是一个非HFA
结构体,并且大小超过了16 Bytes
,假想它作为函数参数传递,根据规则,主调函数会先拷贝到一块内存区,将这块内存指针传递给被调函数,那么这个返回值也会由被调函数直接通过这个指针,写入主调函数预先开辟的内存中。只是需要注意的是,指向这块内存区域的X8
寄存器。
// 1. 定义一个非 HFA 结构体,结构体大小为 32 Bytes
typedef struct {
NSInteger one;
CGFloat onef;
NSInteger two;
CGFloat twof;
} Param;
// 2. 定义函数 adds,它返回非 HFA 结构体 Param
Param adds(void) {
Param p;
p.one = 1f;
p.onef = 1.0f;
p.two = 2;
p.twof = 2.0f;
return p;
}
@implementation ViewController
- (void)viewDidLoad {
// 3. 调用函数 adds
Param result = adds();
NSLog(@"%ld", result.one);
}
@end
代码注释1
定义一个非HFA
结构体,结构体大小32 Bytes
。
代码注释2
定义函数adds
,它返回非HFA
结构体Param
。
代码注释3
调用函数adds
。
函数viewDidLoad
的汇编码:
ARMAssemble`-[ViewController viewDidLoad]:
0x104e1c2c8 <+0>: sub sp, sp, #0x50
...
// 1. 寄存器 X8 指向栈地址 SP + 0x10,从 SP + 0x10 开始连续 4 个 64bit 区域用来接收返回值
0x104e1c2ec <+36>: add x8, sp, #0x10
0x104e1c2f0 <+40>: bl 0x104e1c294 ; adds at ViewController.m:33
...
上面代码注释1
将寄存器X8
指向栈地址(SP + 0x10)
,从(SP + 0x10)
开始连续4
个64bit
内存区域用来接收返回值。
函数adds
的汇编码:
ARMAssemble`adds:
...
0x10212c29c <+8>: mov x9, #0x1
...
// 1. 寄存器 X9 存储值 1,写入寄存器 X8 指向地址
0x10212c2a8 <+20>: str x9, [x8]
// 2. 后面 2 条指令将 1.0 写入 X8 + 0x8 地址处
0x10212c2ac <+24>: fmov d0, #1.00000000
0x10212c2b0 <+28>: str d0, [x8, #0x8]
// 3. 后面 2 条指令将 2 写入 X8 + 0x10 地址处
0x10212c2b4 <+32>: mov x9, #0x2
0x10212c2b8 <+36>: str x9, [x8, #0x10]
// 4. 后面 2 条指令将 2.0 写入 X8 + 0x18 地址处
0x10212c2bc <+40>: fmov d0, #2.00000000
0x10212c2c0 <+44>: str d0, [x8, #0x18]
0x10212c2c4 <+48>: ret
上面代码注释1
将寄存器X9
存储值1
,写入寄存器X8
指向地址。
代码注释2
后面2
条指令将1.0
写入(X8 + 0x8)
地址处。
代码注释3
后面2
条指令将2
写入(X8 + 0x10)
地址处。
代码注释4
后面2
条指令将2.0
写入(X8 + 0x18)
地址处。
一文搞懂 ARM 64 系列: 一文搞懂 ARM 64 系列: 函数调用传参与返回值的更多相关文章
- c#代码 天气接口 一分钟搞懂你的博客为什么没人看 看完python这段爬虫代码,java流泪了c#沉默了 图片二进制转换与存入数据库相关 C#7.0--引用返回值和引用局部变量 JS直接调用C#后台方法(ajax调用) Linq To Json SqlServer 递归查询
天气预报的程序.程序并不难. 看到这个需求第一个想法就是只要找到合适天气预报接口一切都是小意思,说干就干,立马跟学生沟通价格. 不过谈报价的过程中,差点没让我一口老血喷键盘上,话说我们程序猿的人 ...
- 精华推荐 | 【JVM深层系列】「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
前提介绍 很多小伙伴,都跟我反馈,说自己总是对JVM这一块的学习和认识不够扎实也不够成熟,因为JVM的一些特性以及运作机制总是混淆以及不确定,导致面试和工作实战中出现了很多的纰漏和短板,解决广大小伙伴 ...
- 【58沈剑架构系列】为什么说要搞定微服务架构,先搞定RPC框架?
第一章聊了[“为什么要进行服务化,服务化究竟解决什么问题”] 第二章聊了[“微服务的服务粒度选型”] 今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC ...
- 小胖求学系列之-文档生成利器(上)-smart-doc
最近小胖上课总是挂着黑眼圈,同桌小张问:你昨晚通宵啦?小胖有气无力的说到:最近开发的项目接口文档没写,昨晚补文档补了很久,哎,昨晚只睡了2个小时.小张说:不是有生成文档工具吗,类似swagger2.s ...
- arm汇编进入C函数分析,C函数压栈,出栈,传参,返回值
环境及代码介绍 环境和源码 由于有时候要透彻的理解C里面的一些细节问题,所有有必要看看汇编,首先这一切的开始就是从汇编代码进入C的main函数过程.这里不使用编译器自动生成的这部分汇编代码,因为编译器 ...
- C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解 ...
- (转载)Excel文档保存的时候,提示“文档未保存”
亲测,成功搞定 Excel文档保存的时候,提示“文档未保存”? 先打开你需要处理的excel,然后打开工具栏--宏--录制新宏--确定--停止录制宏--宏-宏--编辑--复制以下程序Sub 恢复保存( ...
- [转]C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
本文转自:http://www.cnblogs.com/landeanfen/p/5501487.html 阅读目录 一.void无返回值 二.IHttpActionResult 1.Json(T c ...
- 什么是API文档?--斯科特·马文
有时候,软件开发人员想要的是自己的软件被其他应用软件所应用,而不是让人来操作.API使各种应用软件互相通信成为了可能. 从事API文档写作15年,我亲眼见证了API产品的崛起.各个公司开始搭建平台,希 ...
- 【ABAP系列】SAP ABAP 实现FTP的文件上传与下载
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 实现FTP的文 ...
随机推荐
- 重新定义容器化 Serverless 应用的数据访问
简介: 本文首先聚焦到 AI 和大数据等应用 Serverless 化的最大挑战:计算和存储分离架构带来的数据访问延迟和远程拉取数据带宽巨大的挑战.尤其在 GPU 深度学习训练场景中,迭代式的远程读取 ...
- 102万行代码,1270 个问题,Flink 新版发布了什么?
阿里妹导读: Apache Flink 是公认的新一代开源大数据计算引擎,可以支持流处理.批处理和机器学习等多种计算形态,也是Apache 软件基金会和 GitHub 社区最为活跃的项目之一. 201 ...
- 记一次 JMeter 压测 HTTPS 性能问题
简介:在使用 JMeter 压测时,发现同一后端服务,在单机 500 并发下,HTTP 和 HTTPS 协议压测 RT 差距非常大.同时观测后端服务各监控指标水位都很低,因此怀疑性能瓶颈在 JMet ...
- Arthas 初探--安装初步适用
简介: 由于在项目中遇到一种情况,某段代码在进行单元测试和在 tomcat 容器中运行的性能相差数百倍,因此需要分析在不同环境下某个方法执行的具体时间,从而确定问题.Arthas 可以做到无侵入的监控 ...
- 【实践案例】Databricks 数据洞察在美的暖通与楼宇的应用实践
简介: 获取更详细的 Databricks 数据洞察相关信息,可至产品详情页查看:https://www.aliyun.com/product/bigdata/spark 作者 美的暖通与楼宇事业部 ...
- [FAQ] swagger-php 支持 Authorization Bearer token 校验的用法
@OA\SecurityScheme 可以是 Controller 层面也可以是 Action 层面. 类型 type="apiKey". in="header" ...
- [Gin] 运行模式检测和设置 (mode.go)
// 设置方式 gin.SetMode(gin.ReleaseMode) // 检测方式 if gin.Mode() == gin.DebugMode { } 更多相关信息,建议直接去看源代码. Re ...
- 6.prometheus监控--监控redis/rabbitmq/mongodb
1.监控redis 1.1 redis_exporter安装方式 1.1.1 二进制源码安装方式 参考nginx二进制安装方法 redis_exporter下载地址:https://github.co ...
- SAP Adobe Form 几种文本类型
前文: SAP Adobe Form 教程一 简单示例 SAP Adobe Form 教程二 表 SAP Adobe Form 教程三 日期,时间,floating field SAP Adobe F ...
- Solution Set - SAM
讲解一些 SAM 经典的应用.可以结合 字 符 串 全 家 桶 中 SAM 的部分食用. 洛谷P2408 求不同子串个数.在 SAM 中,所有结点是一个等价类,包含的字符串互不相同.结点 \(u\) ...