260 lines
5.8 KiB
Markdown
260 lines
5.8 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);
|
||
}
|
||
```
|
||
|
||
## 函数修饰符
|
||
|
||
通过返回类型的 `|` 组合添加函数修饰符:
|
||
|
||
### 导出函数
|
||
|
||
`export` 关键字控制函数的符号可见性——标记为 `export` 的函数不加 SHA1 前缀,裸名导出、全局可见。详见 [01-overview.md 中 t.CExport 深度解析](01-overview.md#tcexport-深度解析)。
|
||
|
||
```python
|
||
export def _start() -> t.CInt:
|
||
return 0
|
||
```
|
||
|
||
### 外部函数声明
|
||
|
||
使用 `extern` 关键字表示仅声明不定义:
|
||
|
||
```python
|
||
extern def isr0() -> t.CVoid: pass
|
||
extern def isr1() -> t.CVoid: pass
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
extern void isr0();
|
||
extern void isr1();
|
||
```
|
||
|
||
> 注明:`t.State` / `t.CExtern` / `t.CExport` 注解旧写法已废弃(`t.State` 仅 `t.CEnum` 内部临时使用)。
|
||
|
||
### 内联函数
|
||
|
||
`t.CInline` 标记函数为内联函数。当前无调用点展开实现,`t.CInline` 仅作存储类标记;实际内联用 `@c.Attribute(t.attr.always_inline())`(期望后续支持)。详见 [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
|
||
```
|
||
|
||
### 宏函数
|
||
|
||
宏函数存在两种形式:`def f(...) -> t.CDefine`,或 `define` 软关键字 `define f(...)`(自动追加 `t.CDefine` 返回注解);存根保留完整函数体;调用点按编译期内联宏展开(TPC 已实现,Test/DefineTest 实证)。详见 [01-overview.md 中 t.CDefine 函数](01-overview.md#tcdefine-函数)。
|
||
|
||
> 注明:TransPyV 端目前仅识别返回注解为 i32,调用点展开尚未对齐(期望)。
|
||
|
||
```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
|
||
```
|
||
|
||
已支持:`always_inline` / `noinline` / `noreturn` / `pure` / `const` / `nounwind` / `noredzone` / `stackrealign` / `willreturn` / `mustprogress`;示例中的 `nobuiltin` 尚未映射(会被静默丢弃,期望支持)。
|
||
|
||
## 多返回值(CReturn)
|
||
|
||
使用 `@c.CReturn` 装饰器实现多返回值,返回多个值的函数打包为 LLVM struct 返回,调用处用元组解包 `a, b = f()` 接收:
|
||
|
||
```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
|
||
|
||
q, r = divmod(a, b)
|
||
```
|
||
|
||
规则:
|
||
1. `CReturn` 中的类型数量决定返回值个数
|
||
2. 必须使用左右值(元组解包)赋值,返回值不能直接传入函数。
|
||
|
||
## 指针参数
|
||
|
||
```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
|
||
|
||
export def main() -> int:
|
||
print(add(1, 2, 3)) # (1 + 2) * 3 = 9
|
||
```
|
||
|
||
这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。
|
||
|
||
```python
|
||
# 仍然用刚才的 add 举例
|
||
export def main() -> int:
|
||
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
|
||
|
||
export def main() -> int:
|
||
print(add(1, 2)) # (1 + 2) * 1 = 3
|
||
```
|
||
|
||
在结构体中也可以指定默认值。
|