255 lines
5.2 KiB
Markdown
255 lines
5.2 KiB
Markdown
# 04 - 函数定义与调用
|
||
|
||
Viper 的函数定义使用 Python 的 `def` 语法,通过类型注解指定参数类型和返回类型。
|
||
|
||
## 基本函数定义
|
||
|
||
```python
|
||
def add(a: t.CInt, b: t.CInt) -> t.CInt:
|
||
return a + b
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
int add(int a, int b) {
|
||
return a + b;
|
||
}
|
||
```
|
||
|
||
## 无返回值函数
|
||
|
||
```python
|
||
def print_msg(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
|
||
serial.puts(msg)
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
void print_msg(const char* msg) {
|
||
serial_puts(msg);
|
||
}
|
||
```
|
||
|
||
## 函数修饰符
|
||
|
||
通过返回类型的 `|` 组合添加函数修饰符:
|
||
|
||
### 导出函数
|
||
|
||
` t.CExport` 控制函数的符号可见性——标记为 `t.CExport` 的函数不加 SHA1 前缀,全局可见。详见 [01-overview.md 中 t.CExport 深度解析](01-overview.md#tcexport-深度解析)。
|
||
|
||
```python
|
||
def _start() -> t.CInt | t.CExport:
|
||
return 0
|
||
```
|
||
|
||
### 外部函数声明
|
||
|
||
使用 `c.State` 表示仅声明不定义:
|
||
|
||
```python
|
||
def isr0() -> t.CExtern | t.CVoid | c.State: pass
|
||
def isr1() -> t.CExtern | t.CVoid | c.State: pass
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
extern void isr0();
|
||
extern void isr1();
|
||
```
|
||
|
||
### 内联函数
|
||
|
||
`t.CInline` 标记函数为内联函数,编译器在调用点直接展开函数体。详见 [01-overview.md 中 t.CInline 深度解析](01-overview.md#tcinline-深度解析)。
|
||
|
||
```python
|
||
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
|
||
return a + b
|
||
```
|
||
|
||
### 宏函数
|
||
|
||
当函数返回类型注解包含 `t.CDefine` 时,该函数被视为宏函数,不生成 LLVM IR 函数定义,在调用处内联展开。详见 [01-overview.md 中 t.CDefine 函数](01-overview.md#tcdefine-函数)。
|
||
|
||
```python
|
||
def MAKE_FLAG(bit) -> t.CDefine:
|
||
return (1 << bit)
|
||
```
|
||
|
||
### 静态函数
|
||
|
||
```python
|
||
def helper() -> t.CVoid | t.CStatic:
|
||
pass
|
||
```
|
||
|
||
## 函数属性装饰器
|
||
|
||
使用 `@c.Attribute` 添加 GCC `__attribute__` 属性:
|
||
|
||
```python
|
||
@c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16))
|
||
def _start() -> t.CInt:
|
||
return 0
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
__attribute__((section(".text.startup"), aligned(16)))
|
||
int _start() {
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 常用属性组合
|
||
|
||
```python
|
||
@c.Attribute(t.attr.always_inline())
|
||
def hot_path() -> t.CInt:
|
||
return 0
|
||
|
||
@c.Attribute(t.attr.noreturn())
|
||
def panic(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
|
||
serial.puts(msg)
|
||
while True: pass
|
||
|
||
@c.Attribute(t.attr.packed)
|
||
```
|
||
|
||
## LLVM 函数属性
|
||
|
||
在返回类型注解中通过 `t.attr.llvm` 指定 LLVM 属性:
|
||
|
||
```python
|
||
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
|
||
return 0
|
||
```
|
||
|
||
## 多返回值(CReturn)
|
||
|
||
使用 `@c.CReturn` 装饰器实现多返回值,通过指针参数实现:
|
||
|
||
```python
|
||
@c.CReturn(t.CInt, t.CInt)
|
||
def divmod(a: t.CInt, b: t.CInt) -> t.CVoid:
|
||
q: t.CInt = a // b
|
||
r: t.CInt = a % b
|
||
return q, r
|
||
```
|
||
|
||
规则:
|
||
1. `CReturn` 中的类型数量决定返回值个数
|
||
2. 自动为每个返回类型添加指针参数(`t.CInt` → `t.CInt | t.CPtr`)
|
||
3. 参数名自动生成为 `__ReturnValue0__`、`__ReturnValue1__` 等
|
||
4. 调用处自动传参 `&xxx`
|
||
5. 必须使用左右值赋值,返回值不能直接传入函数。
|
||
|
||
## 指针参数
|
||
|
||
```python
|
||
def read_data(buf: t.CChar | t.CPtr, size: t.CSizeT) -> t.CInt:
|
||
pass
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
int read_data(char* buf, size_t size);
|
||
```
|
||
|
||
### 输出参数模式
|
||
|
||
使用 `c.Addr` 传递变量地址作为输出参数:
|
||
|
||
```python
|
||
res: t.CInt = fat32.opendir("/", c.Addr(dp))
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
int res = fat32_opendir("/", &dp);
|
||
```
|
||
|
||
后续会使用隐式左值获取来减少 `c.Addr` 的直接使用。
|
||
|
||
## 函数指针类型
|
||
|
||
使用 `t.Callable` 定义函数指针类型:
|
||
|
||
```python
|
||
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt]
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
typedef int (*irq_handler_t)();
|
||
```
|
||
|
||
带参数的函数指针:
|
||
|
||
```python
|
||
callback_t: t.Callable[[t.CInt, t.CInt], t.CVoid]
|
||
```
|
||
|
||
## 默认参数
|
||
|
||
Viper 支持函数默认参数值:
|
||
|
||
```python
|
||
def create_window(x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | str = "Window") -> t.CInt:
|
||
pass
|
||
```
|
||
|
||
## return 语句
|
||
|
||
```python
|
||
return 0 # return 0;
|
||
return # return; (void 函数)
|
||
return a, b # 多返回值(需配合 @c.CReturn)
|
||
```
|
||
|
||
## 全局变量声明
|
||
|
||
函数外的变量声明为全局变量,函数内通过 `global` 关键字访问:
|
||
|
||
```python
|
||
BootInfo: bootinfo | t.CPtr
|
||
|
||
def init() -> t.CVoid:
|
||
global BootInfo
|
||
BootInfo = saved_rdi
|
||
```
|
||
|
||
支持 func-in-func 嵌套函数。
|
||
|
||
## Call Func 函数调用
|
||
|
||
函数调用最好通过顺序调用来实现
|
||
|
||
```python
|
||
def add(x: int, y: int, z: int):
|
||
return (x + y) * z
|
||
|
||
def main() -> int | t.CExport:
|
||
print(add(1, 2, 3)) # (1 + 2) * 3 = 9
|
||
```
|
||
|
||
这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。
|
||
|
||
```python
|
||
# 仍然用刚才的 add 举例
|
||
def main() -> int | t.CExport:
|
||
print(add(z=3, x=1, y=2)) # (1 + 2) * 3 = 9
|
||
```
|
||
|
||
乱序传参或带参传参对于结构体的初始化同样有效。同时,你也能够给函数指定默认值。
|
||
|
||
```python
|
||
def add(x: int, y: int, z: int = 1):
|
||
return (x + y) * z
|
||
|
||
def main() -> int | t.CExport:
|
||
print(add(1, 2)) # (1 + 2) * 1 = 3
|
||
```
|
||
|
||
在结构体中也可以指定默认值。
|