docs(wiki): 全站示例统一为短名别名、T* 指针与 &/* 算子等新语法
This commit is contained in:
1 parent
71931a3f40
commit
762b3ceed4
8 files changed
+97
-92
No files matched your search
+11
-9
@@ -445,21 +445,23 @@ HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*, ULONG, ULONG, HAN
|
||||
|
||||
## 数组类型
|
||||
|
||||
固定大小数组使用 `t.CArray[ElemType, Size]`(= `rawarray`)表示(通常用于栈上和 BSS 段上)。`Size` 可为模块级 `t.CDefine` 常量或常量表达式(如 `2*MAX`);单参 `t.CArray[Elem]` 表示静态推导长度数组(退化为 `Ptr(Elem)`);也支持多维嵌套。
|
||||
固定大小数组**推荐用前缀写法 `[N]T`**(等价 `t.CArray[T, N]` = `rawarray`,软关键字形态 `array[T, N]`)(通常用于栈上和 BSS 段上)。`N` 可为模块级 `define` 常量或常量表达式(如 `2*MAX`);省略 `N`(`[]T`)表示静态推导长度数组(退化为 `T*`);也支持多维嵌套。
|
||||
|
||||
- 内建软关键字 `array[Elem, N]`(仅在类型 / 注解位置生效)与 `t.CArray[Elem, N]` 等价;裸用 `array`(无下标)、缺参 / 多参、长度非正整数常量都会 fail-fast。
|
||||
- 裸用 `array`(无下标)、缺参 / 多参、长度非正整数常量都会 fail-fast。
|
||||
- **`list[T, N]` 的双参数形态是同义的定长数组写法**(第 2 实参是元素个数,编译期正整数常量);`list[T]` 单参才是泛型容器特化。因此 `list[Elem, N]` 仍是合法的定长数组,并非“旧写法”。
|
||||
|
||||
```python
|
||||
buf: t.CArray[t.CChar, 64] # char buf[64]
|
||||
ids: t.CArray[t.CInt, 10] # int ids[10]
|
||||
matrix: t.CArray[t.CFloat, 16] # float matrix[16]
|
||||
entries: t.CArray[idt_entry, 256] # struct idt_entry entries[256];
|
||||
s: t.CArray[t.CChar] = [0x68, 0x69] # char s[] = {0x68, 0x69}; 由列表字面量推断长度 2
|
||||
grid: t.CArray[t.CArray[t.CFloat, 4], 8] # float grid[8][4]; 多维嵌套
|
||||
seg: t.CArray[t.CChar, BUF_SIZE] # char seg[BUF_SIZE]; Size 可为模块级 t.CDefine 常量
|
||||
buf: [64]char # char buf[64]
|
||||
ids: [10]int # int ids[10]
|
||||
matrix: [16]float # float matrix[16]
|
||||
entries: [256]idt_entry # struct idt_entry entries[256];
|
||||
s: []char = [0x68, 0x69] # char s[] = {0x68, 0x69}; 由列表字面量推断长度 2
|
||||
grid: [8][4]float # float grid[8][4]; 多维嵌套
|
||||
seg: [BUF_SIZE]char # char seg[BUF_SIZE]; N 可为模块级 define 常量
|
||||
```
|
||||
|
||||
> 旧写法 `t.CArray[t.CChar, 64]` / `array[char, 64]` 与 `[64]char` 完全等价,手写可用但不推荐。
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
char buf[64];
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ y: t.CStatic | t.CInt = 0 # 等价链式标记写法
|
||||
|
||||
```python
|
||||
extern errno: int # extern int errno;(前置软关键字,推荐)
|
||||
fd: t.CExtern | t.CInt # 等价链式标记写法
|
||||
fd: t.CExtern | int # 等价链式标记写法(内部标记)
|
||||
```
|
||||
|
||||
模块级 `extern` 全局生成 `@裸名 = external global T`(external linkage、无初始值、无 storage),符号由外部(C 库 / 汇编 / 其它翻译单元)提供;**不允许带初始值**。
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ __asm__("nop") # __asm__ __volatile__("nop");
|
||||
使用 f-string 和 `asmout` / `asminp` 标记操作数:
|
||||
|
||||
```python
|
||||
saved_rdi: t.CUnsignedLong
|
||||
saved_rdi: ulong
|
||||
__asm__(f"mov {asmout(saved_rdi, ASM_DESCR.OUTPUT_REG)}, rdi",
|
||||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RDI])
|
||||
```
|
||||
@@ -264,9 +264,9 @@ Viper 的 FFI(Foreign Function Interface)通过 `extern` / `export` 关键
|
||||
`extern def` 仅声明(函数体为 `pass`);`export def` 以裸名导出:
|
||||
|
||||
```python
|
||||
extern def isr0() -> t.CVoid: pass # extern void isr0(); — 来自汇编或外部
|
||||
extern def getchar() -> t.CInt: pass # extern int getchar(); — 来自 libc
|
||||
export def foo() -> t.CInt: pass # export int foo(); — 裸名导出(define)
|
||||
extern def isr0() -> void: pass # extern void isr0(); — 来自汇编或外部
|
||||
extern def getchar() -> int: pass # extern int getchar(); — 来自 libc
|
||||
export def foo() -> int: pass # export int foo(); — 裸名导出(define)
|
||||
```
|
||||
|
||||
编译为 LLVM IR:
|
||||
@@ -284,7 +284,7 @@ define i32 @"foo"() { ... } ; export:不加 SHA1 前缀,裸名 define
|
||||
```python
|
||||
extern def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
|
||||
dwShareMode: ULONG,
|
||||
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
|
||||
lpSecurityAttributes: SECURITY_ATTRIBUTES*,
|
||||
dwCreationDisposition: ULONG,
|
||||
dwFlagsAndAttributes: ULONG,
|
||||
hTemplateFile: HANDLE) -> HANDLE:
|
||||
@@ -317,8 +317,8 @@ HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*,
|
||||
外部全局变量用 `extern` 软关键字声明(推荐),或等价的 `t.CExtern` 标记:
|
||||
|
||||
```python
|
||||
extern errno: t.CInt # extern int errno;(推荐)
|
||||
fd: t.CExtern | t.CInt # 等价写法
|
||||
extern errno: int # extern int errno;(推荐)
|
||||
fd: t.CExtern | int # 等价写法(内部标记)
|
||||
```
|
||||
|
||||
模块级 `extern` 全局生成 `@裸名 = external global T`(external linkage、无初始值、无 storage),符号由外部单元提供。
|
||||
|
||||
+3
-3
@@ -72,12 +72,12 @@ asm.BSSClean() # 清零 BSS 段
|
||||
### `string` 模块
|
||||
|
||||
字符串/内存操作模块,对应 C 标准库的 `string.h`:
|
||||
> 确认:string 模块确实使用 Viper `c.Asm` 内联汇编进行加速(`memset`/`memcpy`/`memmove` 等,见 [includes/string.vp](../includes/string.vp))。因使用 x86_64 专有汇编,某些函数在其它架构可能需要手动重新编写。
|
||||
> 确认:string 模块确实使用 Viper `__asm__` 内联汇编进行加速(`memset`/`memcpy`/`memmove` 等,见 [includes/string.vp](../includes/string.vp))。因使用 x86_64 专有汇编,某些函数在其它架构可能需要手动重新编写。
|
||||
|
||||
```python
|
||||
import string
|
||||
|
||||
string.memset(c.Addr(buf), 0, 64) # memset(&buf, 0, 64);
|
||||
string.memset(&buf, 0, 64) # memset(&buf, 0, 64);
|
||||
string.memcpy(dst, src, size) # memcpy(dst, src, size);
|
||||
string.memmove(dst, src, size) # memmove(dst, src, size);
|
||||
string.memcmp(a, b, size) # memcmp(a, b, size);
|
||||
@@ -105,7 +105,7 @@ ViperOS 标准库,提供常用 C 库函数:
|
||||
```python
|
||||
import viperlib
|
||||
|
||||
viperlib.snprintf(c.Addr(buf), 64, "value=%d", 42) # snprintf(&buf, 64, "value=%d", 42);
|
||||
viperlib.snprintf(&buf, 64, "value=%d", 42) # snprintf(&buf, 64, "value=%d", 42);
|
||||
```
|
||||
|
||||
### `vpsdk` 模块
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ b: bool = bool(x) # x != 0
|
||||
|
||||
### min / max / chr / ord
|
||||
|
||||
> **实现说明**:min/max/chr/ord 定义于 `includes/builtins.vp`(`ord`: `t.CChar`→`int`;`chr`: `int`→`t.CChar`;`min`/`max`: `double` 参数,`int`/`float` 实参无损提升,返回按调用侧目标类型 coerce),编译器自动关联 builtins 模块,无需显式 import。
|
||||
> **实现说明**:min/max/chr/ord 定义于 `includes/builtins.vp`(`ord`: `char`→`int`;`chr`: `int`→`char`;`min`/`max`: `double` 参数,`int`/`float` 实参无损提升,返回按调用侧目标类型 coerce),编译器自动关联 builtins 模块,无需显式 import。
|
||||
|
||||
```python
|
||||
m: int = min(a, b) # min(a, b)(builtins.vp,double 参数,返回按目标类型 coerce)
|
||||
|
||||
+1
-1
@@ -290,7 +290,7 @@ TransPyC 支持基于 SHA1 的增量编译,与两阶段编译模型紧密耦
|
||||
### SHA1 命名空间与增量编译的协同
|
||||
|
||||
SHA1 同时服务于命名空间隔离和增量编译:
|
||||
- **命名空间**:非 `t.CExport` 函数和结构体使用 SHA1 前缀,消除符号冲突
|
||||
- **命名空间**:未 `export` 的函数(内部非 `t.CExport`)和结构体使用 SHA1 前缀,消除符号冲突
|
||||
- **增量编译**:SHA1 不变则缓存命中,SHA1 变化则重新生成
|
||||
|
||||
详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。
|
||||
+2
-2
@@ -51,7 +51,7 @@ TPV 编译器当前是"**数据对象 + 行为函数式**"架构:
|
||||
|
||||
1. `novirtual` + 硬编码 SIZE(`BUILDER_SIZE=32`/`pool.alloc(96)`)强耦合——**方法化不改布局则无影响**;若转 `virtual` 需全量重写
|
||||
2. 16 个跨模块 stub 访问器(`value_get_ty`/`paramnode_get_ty`…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(**依赖编译器缺陷修复,非本方案范围**)
|
||||
3. `LLVMType` 的 `pool.alloc(__sizeof__)` + `c.DerefAs` 构造模式——保留为构造方法
|
||||
3. `LLVMType` 的 `pool.alloc(__sizeof__)` + `*p = v`(旧 `c.DerefAs`)构造模式——保留为构造方法
|
||||
|
||||
## 3. 目标架构
|
||||
|
||||
@@ -188,7 +188,7 @@ self.visit_call(node) # 内部用 self.Pool/self.Builder/self.Module/se
|
||||
| 方法簇 | 对应分支 |
|
||||
|---|---|
|
||||
| `_call_generic_ctor(node)` | 泛型类构造 `list[int](pool, n)` |
|
||||
| `_call_binop_cast(node)` | `(A|t.CPtr)(val)` BinOp 强转 |
|
||||
| `_call_binop_cast(node)` | `(A|t.CPtr)(val)` / 新写法 `T*(val)` BinOp 强转 |
|
||||
| `_call_c_special(node)` | `c.Deref/Addr/Set/Load/Asm/LLVMIR/CIf/CIfdef` 系列 |
|
||||
| `_call_stdarg(node)` | `va_arg/arg/va_start/va_end` |
|
||||
| `_call_builtin_cast(node)` | `str/bytes/int/bool(x)` |
|
||||
|
||||
@@ -99,15 +99,15 @@ sha1 = hashlib.sha1(content.encode('utf-8')).hexdigest()[:16]
|
||||
|
||||
| 源文件元素 | 存根文件中的表示 |
|
||||
|-----------|----------------|
|
||||
| `t.CDefine` 常量 | 保留原样(含赋值):`MAX_SIZE: t.CDefine = 1024` |
|
||||
| `t.CDefine` 函数(返回 `t.CDefine` 的函数) | 保留完整函数体(宏展开需要) |
|
||||
| 普通函数 | 仅保留签名(不添加 `c.State`):`def foo(x: t.CInt) -> t.CVoid: pass` |
|
||||
| 类定义 | 保留成员类型注解和方法签名 |
|
||||
| 全局变量 | TPC 一代 `PythonToStubConverter` 会添加 `t.CExtern`(`x: t.CExtern | t.CInt`);TPV 自研 `StubGen`/`Converter.vp` 不再添加——跨模块 `external declare` 由 `VpiReader` 依 vpi 全局自动注入 |
|
||||
| `c.CIfdef`/`c.CEndif()` 等预处理指令 | 保留原样 |
|
||||
| `define` 常量(旧写法 `t.CDefine`) | 保留原样(含赋值):`define MAX_SIZE = 1024` |
|
||||
| 宏函数(`define F(...)`,或返回 `t.CDefine`) | 保留完整函数体(宏展开需要) |
|
||||
| 普通函数 | 仅保留签名:`def foo(x: int) -> void: pass` |
|
||||
| 类 / `struct` 定义 | 保留成员类型注解和方法签名 |
|
||||
| 全局变量 | TPC 一代 `PythonToStubConverter` 会添加 `t.CExtern`(`extern x: int` 的旧标记形态);TPV 自研 `StubGen`/`Converter.vp` 不再添加——跨模块 `external declare` 由 `VpiReader` 依 vpi 全局自动注入 |
|
||||
| `c.CIfdef` 等预处理指令 | 保留原样 |
|
||||
| `import` 语句 | 保留原样 |
|
||||
|
||||
**关键设计**:`t.CDefine` 常量和 `t.CDefine` 函数在存根中**保留完整定义**(包括赋值和函数体),因为它们是编译时常量/宏,其他模块引用时需要完整展开。普通函数只保留签名、不添加 `c.State`(仅声明),因为函数体在阶段二生成。
|
||||
**关键设计**:`define` 常量与宏函数在存根中**保留完整定义**(包括赋值和函数体),因为它们是编译时常量/宏,其他模块引用时需要完整展开。普通函数只保留签名(仅声明),因为函数体在阶段二生成。
|
||||
|
||||
|
||||
#### 步骤 3:构建结构体注册表
|
||||
@@ -128,12 +128,12 @@ sha1 = hashlib.sha1(content.encode('utf-8')).hexdigest()[:16]
|
||||
|
||||
| 元素 | 生成的 LLVM IR |
|
||||
|------|---------------|
|
||||
| 普通函数 | `declare void @"<SHA1>.foo"(i32)` — 非 CExport 函数加 SHA1 前缀 |
|
||||
| CExport 函数 | `declare i32 @main()` — CExport 函数不加前缀,全局可见 |
|
||||
| 普通函数 | `declare void @"<SHA1>.foo"(i32)` — 未 `export` 的函数加 SHA1 前缀 |
|
||||
| `export` 函数(内部 `t.CExport`) | `declare i32 @main()` — 不加前缀,全局可见 |
|
||||
| 结构体 | `%"<SHA1>.ClassName" = type { i32, i32* }` — 类型名加 SHA1 前缀 |
|
||||
| 全局变量 | `@varname = external global i32` |
|
||||
| `t.CDefine` 常量 | **不生成声明**(编译时常量,直接内联展开) |
|
||||
| `t.CTypedef` 别名 | **不生成声明**(类型别名,在类型解析时展开) |
|
||||
| `define` 常量(内部 `t.CDefine`) | **不生成声明**(编译时常量,直接内联展开) |
|
||||
| `typedef` 别名(内部 `t.CTypedef`) | **不生成声明**(类型别名,在类型解析时展开) |
|
||||
| 枚举 | 仅旧 Python `DeclarationGenerator` 行为:为每个枚举成员生成 `@__config_EnumName_member = external global i32`;现枚举由 `ClassGenEnum` 注册为编译期常量,不再生成 |
|
||||
|
||||
**增量编译**:如果 `<SHA1>.vpi` 或 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)。只有源文件内容变化导致 SHA1 变化时才重新生成。
|
||||
@@ -155,7 +155,7 @@ sha1 = hashlib.sha1(content.encode('utf-8')).hexdigest()[:16]
|
||||
|
||||
#### 步骤 3:收集内联函数符号
|
||||
|
||||
扫描 `includes/` 目录中被引用的模块,收集标记为 `t.CInline` 的函数。内联函数的完整 AST body 被保存,在翻译调用点时直接展开。值得注意的是,和 C 语言不同,`t.CInline` 不是优化建议,而是强制性的。
|
||||
扫描 `includes/` 目录中被引用的模块,收集标记为内联的函数(旧标记 `t.CInline`)。内联函数的完整 AST body 被保存,在翻译调用点时直接展开。值得注意的是,和 C 语言不同,这种前端内联不是优化建议,而是强制性的。
|
||||
|
||||
> 注:上述 `_collect_inline_symbols` 预收集机制属 TPC Python 侧实现;TransPyV 无对应实现——`t.CInline` 仅作为存储类标记,实际内联经 `@c.Attribute(t.attr.always_inline())`。
|
||||
|
||||
@@ -186,12 +186,12 @@ Viper 使用源文件内容的 SHA1 哈希作为命名空间前缀:
|
||||
|
||||
```
|
||||
源文件 A.vp(SHA1 = a1b2c3d4e5f6g7h8)中定义:
|
||||
class Point: x: t.CInt; y: t.CInt
|
||||
def draw(p: Point) -> t.CVoid: ...
|
||||
struct Point: x: int; y: int
|
||||
def draw(p: Point*) -> void: ...
|
||||
|
||||
源文件 B.vp(SHA1 = i9j0k1l2m3n4o5p6)中也定义:
|
||||
class Point: x: t.CFloat; y: t.CFloat # 不同的结构体!
|
||||
def draw(p: Point) -> t.CVoid: ...
|
||||
struct Point: x: float; y: float # 不同的结构体!
|
||||
def draw(p: Point*) -> void: ...
|
||||
|
||||
编译后:
|
||||
A.vp 的结构体 → %"a1b2c3d4e5f6g7h8.Point" = type { i32, i32 }
|
||||
@@ -203,13 +203,13 @@ Viper 使用源文件内容的 SHA1 哈希作为命名空间前缀:
|
||||
|
||||
**没有符号冲突**——即使两个模块定义了同名类型和函数,它们的 LLVM IR 符号也是不同的。即便有两个内容完全相同的文件也不会冲突,他们会被识别为同一个文件,然后进行单次编译。
|
||||
|
||||
### SHA1 前缀的豁免:`t.CExport`
|
||||
### SHA1 前缀的豁免:`export`
|
||||
|
||||
标记为 `t.CExport` 的函数**不加 SHA1 前缀**,保持原始函数名。这是模块向外部暴露 API/ABI 的机制:
|
||||
用 `export` 声明的函数(内部标记 `t.CExport`)**不加 SHA1 前缀**,保持原始函数名。这是模块向外部暴露 API/ABI 的机制:
|
||||
|
||||
```python
|
||||
# main.vp — 入口函数,必须全局可见
|
||||
def main() -> t.CInt | t.CExport:
|
||||
export def main() -> int:
|
||||
return 0
|
||||
|
||||
# 编译后:define i32 @main() — 无 SHA1 前缀
|
||||
@@ -217,7 +217,7 @@ def main() -> t.CInt | t.CExport:
|
||||
|
||||
```python
|
||||
# serial.vp — 驱动接口,对外暴露
|
||||
def init() -> t.CVoid | t.CExport:
|
||||
export def init() -> void:
|
||||
serial_puts("init\n")
|
||||
|
||||
# 编译后:define void @init() — 无 SHA1 前缀,其他模块可直接调用
|
||||
@@ -232,111 +232,114 @@ SHA1 同时服务于增量编译:
|
||||
|
||||
理论上,如果一个未经变动的模块的依赖路径上存在变动的模块,暂时的方法是将其简单做 SHA1 替换,暨将旧 SHA1 替换为新 SHA1,但不适用于签名改变的情况,不过签名改变这个未经变动的模块必然也需要改变,但是由于宏展开的存在,不能完全保证不会发生错误情况。且依赖路径的 SHA1 替换成功性未经完整测试,可能出现未定义行为。
|
||||
|
||||
## `t.CDefine` 深度解析
|
||||
## 编译期常量与宏(`define` / `t.CDefine`)深度解析
|
||||
|
||||
`t.CDefine` 是 Viper 中最特殊的类型——它不是数据类型,而是**编译时元指令**,控制编译器的代码生成行为。
|
||||
`t.CDefine` 是编译时元指令(**推荐用软关键字 `define` 书写**),控制编译器的代码生成行为——它不是真实的数据类型。
|
||||
|
||||
### `t.CDefine` 常量
|
||||
### `define` 常量
|
||||
|
||||
```python
|
||||
MAX_SIZE: t.CDefine = 1024
|
||||
PAGE_SIZE: t.CDefine = 4096
|
||||
FA_READ: t.CDefine = 0x01
|
||||
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
|
||||
define MAX_SIZE = 1024
|
||||
define PAGE_SIZE = 4096
|
||||
define FA_READ = 0x01
|
||||
define CPU_FEATURE_FPU = (1 << 0)
|
||||
```
|
||||
|
||||
> 推荐写法是软关键字 `define X = v`(等价旧写法 `X: t.CDefine = v`)。
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段一**:存根生成器保留 `t.CDefine` 常量的完整定义(包括赋值值),因为其他模块可能引用此常量
|
||||
2. **阶段一**:`.stub.ll` 声明生成器**跳过** `t.CDefine` 常量,不生成 `external global` 声明
|
||||
3. **阶段二**:翻译器遇到 `t.CDefine` 常量的引用时,直接内联其值,不生成任何加载指令
|
||||
1. **阶段一**:存根生成器保留 CDefine 常量的完整定义(包括赋值值),因为其他模块可能引用此常量
|
||||
2. **阶段一**:`.stub.ll` 声明生成器**跳过** CDefine 常量,不生成 `external global` 声明
|
||||
3. **阶段二**:翻译器遇到 CDefine 常量的引用时,直接内联其值,不生成任何加载指令
|
||||
|
||||
这意味着 `t.CDefine` 常量在 LLVM IR 层面**不存在**——它们在编译时被完全展开,等价于 C 的 `#define` 宏。
|
||||
这意味着 CDefine 常量在 LLVM IR 层面**不存在**——它们在编译时被完全展开,等价于 C 的 `#define` 宏。
|
||||
|
||||
### `t.CDefine` 函数
|
||||
### 宏函数(`define F(...)`)
|
||||
|
||||
当函数的返回类型注解包含 `t.CDefine` 时,该函数被编译器视为**宏函数**:
|
||||
当函数用 `define F(...)` 声明(或返回类型注解含 `t.CDefine`)时,被编译器视为**宏函数**:
|
||||
|
||||
```python
|
||||
def MAKE_FLAG(bit) -> t.CDefine:
|
||||
define MAKE_FLAG(bit):
|
||||
return (1 << bit)
|
||||
|
||||
def GET_PAGE_ORDER(size) -> t.CDefine:
|
||||
define GET_PAGE_ORDER(size):
|
||||
return size // PAGE_SIZE
|
||||
```
|
||||
|
||||
> `define F(...)` 会自动追加 `t.CDefine` 返回注解;等价旧写法 `def F(...) -> t.CDefine:`。注意与 `define X = v`(常量)区分:带 `(` 是宏函数、带 `=` 是常量。
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段一**:存根生成器保留 `t.CDefine` 函数的**完整函数体**(不像普通函数那样只保留签名)
|
||||
2. **阶段二**:翻译器遇到 `t.CDefine` 函数的调用时,将调用内联展开为函数体的计算结果
|
||||
1. **阶段一**:存根生成器保留宏函数的**完整函数体**(不像普通函数那样只保留签名)
|
||||
2. **阶段二**:翻译器遇到宏函数的调用时,将调用内联展开为函数体的计算结果
|
||||
|
||||
`t.CDefine` 函数在 LLVM IR 中**不生成函数定义**——它们是纯粹的编译时宏。
|
||||
宏函数在 LLVM IR 中**不生成函数定义**——它们是纯粹的编译时宏。
|
||||
|
||||
### `t.CDefine` 与类型组合
|
||||
### `define X: T = v` 与类型组合
|
||||
|
||||
`t.CDefine` 可以与具体类型组合使用,为常量指定底层类型:
|
||||
`define` 可以带类型注解,为常量指定底层类型(内部等价于把 `t.CDefine` 与具体类型组合):
|
||||
|
||||
```python
|
||||
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
|
||||
DATA_SEG: t.CDefine | t.CUInt16T = 0x10
|
||||
define CODE_SEG: uint16 = 0x08
|
||||
define DATA_SEG: uint16 = 0x10
|
||||
```
|
||||
|
||||
这表示常量在类型检查时被视为 `t.CUInt16T`(`uint16_t`),但在代码生成时仍然内联展开。
|
||||
这表示常量在类型检查时被视为 `uint16`(`uint16_t`),但在代码生成时仍然内联展开。
|
||||
|
||||
## `t.CExport` 深度解析
|
||||
> 旧写法 `CODE_SEG: t.CDefine | t.CUInt16T = 0x08` 等价,手写可用但不推荐。
|
||||
|
||||
`t.CExport` 控制函数的符号可见性,是 SHA1 命名空间的豁免机制。
|
||||
## `export` 深度解析(内部标记 `t.CExport`)
|
||||
|
||||
`export` 软关键字控制函数的符号可见性,是 SHA1 命名空间的豁免机制(内部改写为返回注解中的 `t.CExport` 标记)。
|
||||
|
||||
### 语义
|
||||
|
||||
| 修饰 | LLVM IR 函数名 | 链接可见性 | 用途 |
|
||||
| 写法 | LLVM IR 函数名 | 链接可见性 | 用途 |
|
||||
|------|---------------|-----------|------|
|
||||
| 无修饰 | `@<SHA1>.funcname` | 模块内部 | 模块私有函数 |
|
||||
| `t.CExport` | `@funcname` | 全局可见 | 对外暴露的 API |
|
||||
| `t.CExtern` | `@funcname`(仅声明) | 外部定义 | 引用外部函数 |
|
||||
| `export def f(...)` | `@funcname` | 全局可见 | 对外暴露的 API |
|
||||
| `extern def f(...)` | `@funcname`(仅声明) | 外部定义 | 引用外部函数 |
|
||||
|
||||
> 手写等价形态:`def f(...) -> T | t.CExport`(导出)、`def f(...) -> T | t.State`(外部声明)。
|
||||
|
||||
### 使用场景
|
||||
|
||||
```python
|
||||
# 入口函数 — 必须全局可见,链接器需要找到它
|
||||
def _start() -> t.CInt | t.CExport:
|
||||
export def _start() -> int:
|
||||
return kernel_main()
|
||||
|
||||
# 驱动接口 — 其他模块/应用需要调用
|
||||
def init() -> t.CVoid | t.CExport:
|
||||
export def init() -> void:
|
||||
uart_init()
|
||||
|
||||
# 内部辅助函数 — 不需要全局可见,自动加 SHA1 前缀
|
||||
def _helper() -> t.CInt:
|
||||
def _helper() -> int:
|
||||
return 42
|
||||
```
|
||||
|
||||
### `t.CExport` 在存根中的表现
|
||||
### `export` 在存根中的表现
|
||||
|
||||
在 `.vpi` 存根文件中,`t.CExport` 函数与普通函数一样只保留签名(不添加 `c.State`,表示单纯声明),但 `t.CExport` 标记被保留在返回类型注解中。阶段一的 `DeclarationGenerator` 检查 `t.CExport` 标记来决定是否添加 SHA1 前缀。
|
||||
在 `.vpi` 存根文件中,`export` 函数与普通函数一样只保留签名(不添加 `extern` 存储类,表示单纯声明),但内部 `t.CExport` 标记被保留在返回类型注解中。阶段一的 `DeclarationGenerator` 检查该标记来决定是否添加 SHA1 前缀。
|
||||
|
||||
## `t.CInline` 深度解析
|
||||
## `inline` 深度解析(旧标记 `t.CInline`)
|
||||
|
||||
`t.CInline` 标记函数为内联函数,编译器在调用点直接展开函数体。
|
||||
|
||||
### 语义
|
||||
推荐写法是 **`inline` 软关键字**:`inline def f(...)` 等价于给函数加 `@t.attr.always_inline`(LLVM `alwaysinline`)——这是**编译期提示落到 IR 函数属性**,由 LLVM 决定实际内联,**不是**“调用点展开”。
|
||||
|
||||
```python
|
||||
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
|
||||
inline def fast_add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
```
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段二预收集**:`_collect_inline_symbols` 扫描 `includes/` 目录中被引用的模块,收集所有 `t.CInline` 函数的 AST body
|
||||
2. **翻译时展开**:遇到内联函数调用时,将函数体的 AST 直接嵌入调用点,替换参数为实际值
|
||||
3. **不生成独立函数**:内联函数不生成 LLVM IR 函数定义(除非也被非内联调用)
|
||||
> 旧的 `-> T | t.CInline`(`t.CInline`)**不是**内联指令、也**不产生调用点展开**,它只是一个存储类标记,已不推荐;做真正内联请用 `inline`。
|
||||
|
||||
### `t.CInline` 与 `t.CDefine` 函数的区别
|
||||
### `inline` 与 `define F(...)` 的区别
|
||||
|
||||
| 特性 | `t.CInline` | `t.CDefine` 函数 |
|
||||
|------|------------|-----------------|
|
||||
| 展开时机 | 阶段二翻译时 | 阶段二翻译时 |
|
||||
| 特性 | `inline def f(...)` | `define F(...)`(宏函数) |
|
||||
|------|---------------------|--------------------------|
|
||||
| 展开时机 | LLVM 优化阶段(`alwaysinline` 属性) | 阶段二翻译时(前端内联) |
|
||||
| 类型检查 | 完整的参数和返回类型检查 | 返回类型为宏,类型检查较弱 |
|
||||
| 存根表示 | 仅签名(不添加 `c.State`) | 完整函数体 |
|
||||
| LLVM IR | 可能生成函数定义(如果有非内联调用) | 不生成函数定义 |
|
||||
| 存根表示 | 仅签名(不添加 `extern` 存储类) | 完整函数体 |
|
||||
| LLVM IR | 生成函数定义 + `alwaysinline` 属性 | 不生成函数定义 |
|
||||
| 适用场景 | 性能关键的短函数 | 编译时常量和宏计算 |
|
||||
|
||||
## 目标平台
|
||||
|
||||
Reference in new issue
Block a user