Skip to content

Commit 9e1402c

Browse files
committed
docs: calling convention
1 parent 33aa5a8 commit 9e1402c

1 file changed

Lines changed: 57 additions & 3 deletions

File tree

docs/new/314.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
- 延迟解析类型注解。
99
- Asyncio 支持通过进程号的方式了解 asyncio 的运行情况,展示 asyncio 中的任务依赖树,提供了类似 pstree 的功能。
1010

11+
## Free-threaded Python
12+
13+
在 Python 3.14 版本中,彻底移除了 GIL(全局解释器锁)的限制,Python 解释器现在支持多线程并发执行。在这个版本中 Free-threaded Python 的性能已经有了很大的提升。[PEP 703](https://peps.python.org/pep-0703/) 中提到的实现目前已经实现完成,实验测试 No-GIL Python 在但线程场景下只比 With-GIL 的 Python 慢 5%-10% 具体取决与使用 Python 的操作系统和编译器。
14+
1115
## 单进程多解释器
1216

1317
## Tail-Call 优化解释器
1418

15-
该解释器主要是使用 clang-19 提供的一种编译调用规约(Calling Convention)来进行解释器本身的优化。所谓调用规约就是在汇编语言层面确定调用者和被调用者之间的参数是如何的传递的一种约定。
16-
17-
### 调用规约
19+
该解释器主要是使用 clang-19 提供的一种编译调用规约(Calling Convention)来进行解释器本身的优化,在 pyperformance benchmark 上能够有 3%-5% 的性能提升。所谓调用规约就是在汇编语言层面确定调用者和被调用者之间的参数是如何的传递的一种约定。
1820

1921
以 System V AMD64 ABI 调用规约(主要使用在 Linux 、FreeBSD 等操作系统)为例子,下表表示各个通用寄存器的保存和恢复约定。
2022

@@ -99,4 +101,56 @@ gcc -c demo.c
99101

100102
如果在函数 `callee_function` 中没有保存和恢复 `rbx` 寄存器,而在调用它的函数中使用到了该寄存器,那么程序的行为将是未定义的,可能会导致程序崩溃或者结果错误,因为编译器在按照调用规约编译的时候默认 `rbx` 寄存器的值是不会被 Callee 修改的。
101103

104+
### Clang-19 preserve_none
105+
106+
在 clang-19 中提供了一种新的调用规约 `preserve_none`,该调用规约规定了所有的寄存器都是 Caller Saved 寄存器,这样被调用函数就不需要保存和恢复任何寄存器,这个调用规约在尾调用或者尾递归的时候非常好用,因为在尾调用的时候,调用者函数的栈帧已经不需要了,Caller 已经不在需要任何寄存器的值了,只需要等待函数返回时将 `rax` 寄存器(在 AMD64 Linux 中返回值都是保存在 `rax` 寄存器)的值返回即可。
107+
108+
我们现在修改一下前面的例子,然后增加一些属性来使用 `preserve_none` 调用规约和 `musttail` 属性来强制尾调用优化:
109+
110+
```c
111+
#include <stdio.h>
112+
113+
__attribute__((preserve_none))
114+
int callee_function(int a, int b) {
115+
int register c = a + b + 100;
116+
int register result = c * 2 + b * 4 + a * 8;
117+
return result;
118+
}
119+
120+
__attribute__((preserve_none))
121+
int caller_function(int x) {
122+
__attribute__((musttail))
123+
return callee_function(x, x + 1);
124+
}
125+
126+
int main() {
127+
int result = caller_function(5);
128+
printf("Result: %d\n", result);
129+
return 0;
130+
}
131+
```
132+
133+
编译之后得到的 `caller_function` 的汇编代码如下:
134+
135+
```asm
136+
0000000000000000 <callee_function>:
137+
0: 55 push %rbp
138+
1: 48 89 e5 mov %rsp,%rbp
139+
4: 89 7d fc mov %edi,-0x4(%rbp)
140+
7: 8b 45 fc mov -0x4(%rbp),%eax
141+
a: 83 c0 64 add $0x64,%eax
142+
d: 89 45 f8 mov %eax,-0x8(%rbp)
143+
10: 8b 45 f8 mov -0x8(%rbp),%eax
144+
13: d1 e0 shl %eax
145+
15: 8b 4d fc mov -0x4(%rbp),%ecx
146+
18: c1 e1 03 shl $0x3,%ecx
147+
1b: 01 c8 add %ecx,%eax
148+
1d: 89 45 f4 mov %eax,-0xc(%rbp)
149+
20: 8b 45 f4 mov -0xc(%rbp),%eax
150+
23: 5d pop %rbp
151+
24: c3 retq
152+
```
153+
154+
此时我们可以看到 `callee_function` 函数中没有任何寄存器的保存和恢复代码,包括寄存器 `rbx` ,因为所有的寄存器都是 Caller Saved 寄存器,因此被调用函数不需要保存任何寄存器的值。
155+
102156
## 外部调试接口

0 commit comments

Comments
 (0)