首次上传

This commit is contained in:
TermiNexus committed 2026-08-10 03:14:17 +08:00
commit eb9216b1f1
16 files changed
+4578

No files matched your search

+665
View File
@@ -0,0 +1,665 @@
# 02 - 类型系统
Viper 的类型系统通过 `t` 模块提供,所有类型注解继承 `t.CType` 。大都以 `t.C{TypeName:PascalCase}`为类型,类型注解是 Viper 的核心机制,决定了变量的内存布局、大小和对齐方式。
## 基本类型
### 整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CChar` | `char` | 8 | 是 |
| `t.CShort` | `short` | 16 | 是 |
| `t.CInt` | `int` | 32 | 是 |
| `t.CLong` | `long` | 64 | 是 |
| `t.CUnsignedChar` | `unsigned char` | 8 | 否 |
| `t.CUnsignedShort` | `unsigned short` | 16 | 否 |
| `t.CUnsigned` / `t.CUnsignedInt` | `unsigned int` | 32 | 否 |
| `t.CUnsignedLong` | `unsigned long` | 64 | 否 |
| `t.CSignedChar` | `signed char` | 8 | 是 |
值得注意的是,t.CUnsigned 一般不单独使用,而是配合其它有符号类型,替换表示无符号,比如 `t.CUnsigned | t.CInt`,由于为了便于使用一些常用类型,则将其组合为常用的 `t.CUnsignedInt` 等。
### 固定宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CInt8T` | `int8_t` | 8 | 是 |
| `t.CInt16T` | `int16_t` | 16 | 是 |
| `t.CInt32T` | `int32_t` | 32 | 是 |
| `t.CInt64T` | `int64_t` | 64 | 是 |
| `t.CUInt8T` | `uint8_t` | 8 | 否 |
| `t.CUInt16T` | `uint16_t` | 16 | 否 |
| `t.CUInt32T` | `uint32_t` | 32 | 否 |
| `t.CUInt64T` | `uint64_t` | 64 | 否 |
### 最小宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CIntLeast8T` | `int_least8_t` | ≥8 | 是 |
| `t.CIntLeast16T` | `int_least16_t` | ≥16 | 是 |
| `t.CIntLeast32T` | `int_least32_t` | ≥32 | 是 |
| `t.CIntLeast64T` | `int_least64_t` | ≥64 | 是 |
| `t.CUIntLeast8T` | `uint_least8_t` | ≥8 | 否 |
| `t.CUIntLeast16T` | `uint_least16_t` | ≥16 | 否 |
| `t.CUIntLeast32T` | `uint_least32_t` | ≥32 | 否 |
| `t.CUIntLeast64T` | `uint_least64_t` | ≥64 | 否 |
### 最快最小宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CIntFast8T` | `int_fast8_t` | ≥8 | 是 |
| `t.CIntFast16T` | `int_fast16_t` | ≥16 | 是 |
| `t.CIntFast32T` | `int_fast32_t` | ≥32 | 是 |
| `t.CIntFast64T` | `int_fast64_t` | ≥64 | 是 |
| `t.CUIntFast8T` | `uint_fast8_t` | ≥8 | 否 |
| `t.CUIntFast16T` | `uint_fast16_t` | ≥16 | 否 |
| `t.CUIntFast32T` | `uint_fast32_t` | ≥32 | 否 |
| `t.CUIntFast64T` | `uint_fast64_t` | ≥64 | 否 |
### 最大宽度整数类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CIntMaxT` | `intmax_t` | ≥64 | 是 |
| `t.CUIntMaxT` | `uintmax_t` | ≥64 | 否 |
### stdint 短名别名
通过 `import stdint` 引入,这些短名是上述类型的 `t.CTypedef` 别名,便于在系统编程场景中快速使用。允许直接使用 `from stdint import *` 来直接使用。
#### 基本类型短名
| 短名 | 等价展开 | 说明 |
|------|---------|------|
| `stdint.INT` | `t.CInt` | - |
| `stdint.UINT` | `t.CUnsignedInt` | - |
| `stdint.BOOL` | `t.CInt` | 1 为真,0 为假 |
| `stdint.SHORT` | `t.CShort` | - |
| `stdint.USHORT` | `t.CUnsignedShort` | - |
| `stdint.LONG` | `t.CLong` | - |
| `stdint.ULONG` | `t.CUnsignedLong` | - |
| `stdint.LONGLONG` | `t.CLong \| t.CLong` | - |
| `stdint.ULONGLONG` | `t.CUnsignedLong \| t.CLong` | - |
| `stdint.FLOAT` | `t.CFloat` | - |
| `stdint.DOUBLE` | `t.CDouble` | - |
#### 固定宽度短名
| 短名 | 等价展开 |
|------|---------|
| `stdint.INT8` | `t.CInt8T` |
| `stdint.INT16` | `t.CInt16T` |
| `stdint.INT32` | `t.CInt32T` |
| `stdint.INT64` | `t.CInt64T` |
| `stdint.UINT8` | `t.CUInt8T` |
| `stdint.UINT16` | `t.CUInt16T` |
| `stdint.UINT32` | `t.CUInt32T` |
| `stdint.UINT64` | `t.CUInt64T` |
#### 字节与字短名
| 短名 | 等价展开 | 说明 |
|------|---------|------|
| `stdint.BYTE` | `t.CUnsignedChar` | 8 位无符号 |
| `stdint.WORD` | `t.CUInt16T` | 16 位无符号 |
| `stdint.DWORD` | `t.CUInt32T` | 32 位无符号 |
| `stdint.QWORD` | `t.CUInt64T` | 64 位无符号 |
#### 字符类型短名
| 短名 | 等价展开 | 说明 |
|------|---------|------|
| `stdint.TCHAR` | `t.CChar` | - |
| `stdint.CHARLIST` | `str \| t.CPtr` / `list[str, None]` | - |
| `stdint.WCHAR` | `stdint.WORD` | UTF-16 代码单元 |
| `stdint.CHAR8` | `t.CChar8T` | - |
| `stdint.CHAR16` | `t.CChar16T` | - |
| `stdint.CHAR32` | `t.CChar32T` | - |
#### 指针短名
| 短名 | 等价展开 |
|------|---------|
| `stdint.INTPTR` | `t.CInt \| t.CPtr` |
| `stdint.UINTPTR` | `t.CUnsignedInt \| t.CPtr` |
| `stdint.BYTEPTR` | `stdint.BYTE \| t.CPtr` |
| `stdint.SHORTPTR` | `t.CShort \| t.CPtr` |
| `stdint.USHORTPTR` | `t.CUnsignedShort \| t.CPtr` |
| `stdint.WCHARPTR` | `stdint.WORD \| t.CPtr` |
| `stdint.VOIDPTR` | `t.CVoid \| t.CPtr` |
| `stdint.INT8PTR` | `t.CInt8T \| t.CPtr` |
| `stdint.INT16PTR` | `t.CInt16T \| t.CPtr` |
| `stdint.INT32PTR` | `t.CInt32T \| t.CPtr` |
| `stdint.INT64PTR` | `t.CInt64T \| t.CPtr` |
| `stdint.UINT8PTR` | `t.CUInt8T \| t.CPtr` |
| `stdint.UINT16PTR` | `t.CUInt16T \| t.CPtr` |
| `stdint.UINT32PTR` | `t.CUInt32T \| t.CPtr` |
| `stdint.UINT64PTR` | `t.CUInt64T \| t.CPtr` |
| `stdint.CHAR8PTR` | `t.CChar8T \| t.CPtr` |
| `stdint.CHAR16PTR` | `t.CChar16T \| t.CPtr` |
| `stdint.CHAR32PTR` | `t.CChar32T \| t.CPtr` |
#### 平台特定短名
| 短名 | 等价展开 | 说明 |
|------|---------|------|
| `stdint.FSIZE_t` | `stdint.DWORD` | 文件大小 |
| `stdint.LBA_t` | `stdint.DWORD` | 逻辑块地址 |
### 平台相关类型
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|------------|--------|-----------|--------|
| `t.CSizeT` | `size_t` | 64 | 否 |
| `t.CIntPtrT` | `intptr_t` | 64 | 是 |
| `t.CUIntPtrT` | `uintptr_t` | 64 | 否 |
| `t.CPtrDiffT` | `ptrdiff_t` | 64 | 是 |
### stdint 短名别名
Viper 在 `stdint` 模块中提供了更简短的类型别名,通过 `t.CTypedef` 映射到对应的完整类型。使用时需 `import stdint`。
#### 固定宽度整数短名
| 短名 | 等价 Viper 类型 | C 等价 |
|------|----------------|--------|
| `INT8` | `t.CInt8T` | `int8_t` |
| `INT16` | `t.CInt16T` | `int16_t` |
| `INT32` | `t.CInt32T` | `int32_t` |
| `INT64` | `t.CInt64T` | `int64_t` |
| `UINT8` | `t.CUInt8T` | `uint8_t` |
| `UINT16` | `t.CUInt16T` | `uint16_t` |
| `UINT32` | `t.CUInt32T` | `uint32_t` |
| `UINT64` | `t.CUInt64T` | `uint64_t` |
#### 固定宽度指针短名
| 短名 | 等价 Viper 类型 | C 等价 |
|------|----------------|--------|
| `INT8PTR` | `t.CInt8T \| t.CPtr` | `int8_t*` |
| `INT16PTR` | `t.CInt16T \| t.CPtr` | `int16_t*` |
| `INT32PTR` | `t.CInt32T \| t.CPtr` | `int32_t*` |
| `INT64PTR` | `t.CInt64T \| t.CPtr` | `int64_t*` |
| `UINT8PTR` | `t.CUInt8T \| t.CPtr` | `uint8_t*` |
| `UINT16PTR` | `t.CUInt16T \| t.CPtr` | `uint16_t*` |
| `UINT32PTR` | `t.CUInt32T \| t.CPtr` | `uint32_t*` |
| `UINT64PTR` | `t.CUInt64T \| t.CPtr` | `uint64_t*` |
#### 字符类型短名
| 短名 | 等价 Viper 类型 | C 等价 |
|------|----------------|--------|
| `CHAR8` | `t.CChar8T` | `char8_t` |
| `CHAR16` | `t.CChar16T` | `char16_t` |
| `CHAR32` | `t.CChar32T` | `char32_t` |
| `CHAR8PTR` | `t.CChar8T \| t.CPtr` | `char8_t*` |
| `CHAR16PTR` | `t.CChar16T \| t.CPtr` | `char16_t*` |
| `CHAR32PTR` | `t.CChar32T \| t.CPtr` | `char32_t*` |
#### 基本类型短名
| 短名 | 等价 Viper 类型 | C 等价 |
|------|----------------|--------|
| `INT` | `t.CInt` | `int` |
| `UINT` | `t.CUnsignedInt` | `unsigned int` |
| `BOOL` | `t.CInt` | `int`(0/1) |
| `SHORT` | `t.CShort` | `short` |
| `USHORT` | `t.CUnsignedShort` | `unsigned short` |
| `LONG` | `t.CLong` | `long` |
| `ULONG` | `t.CUnsignedLong` | `unsigned long` |
| `LONGLONG` | `t.CLong \| t.CLong` | `long long` |
| `ULONGLONG` | `t.CUnsignedLong \| t.CLong` | `unsigned long long` |
| `FLOAT` | `t.CFloat` | `float` |
| `DOUBLE` | `t.CDouble` | `double` |
#### 平台 / Windows 风格短名
| 短名 | 等价 Viper 类型 | C 等价 |
|------|----------------|--------|
| `BYTE` | `t.CUnsignedChar` | `unsigned char`(8位) |
| `BYTEPTR` | `BYTE \| t.CPtr` | `unsigned char*` |
| `WORD` | `t.CUInt16T` | `uint16_t`(16位) |
| `DWORD` | `t.CUInt32T` | `uint32_t`(32位) |
| `QWORD` | `t.CUInt64T` | `uint64_t`(64位) |
| `INTPTR` | `t.CInt \| t.CPtr` | `int*` |
| `UINTPTR` | `t.CUnsignedInt \| t.CPtr` | `unsigned int*` |
| `SHORTPTR` | `t.CShort \| t.CPtr` | `short*` |
| `USHORTPTR` | `t.CUnsignedShort \| t.CPtr` | `unsigned short*` |
| `VOIDPTR` | `t.CVoid \| t.CPtr` | `void*` |
| `TCHAR` | `t.CChar` | `char` |
| `WCHAR` | `WORD` | `uint16_t`(UTF-16) |
| `WCHARPTR` | `WORD \| t.CPtr` | `uint16_t*`(UTF-16) |
| `CHARLIST` | `str \| t.CPtr` | `char*` |
| `FSIZE_t` | `DWORD` | `uint32_t` |
| `LBA_t` | `DWORD` | `uint32_t` |
### 浮点类型
| Viper 类型 | C 等价 | 大小(位) |
|------------|--------|-----------|
| `t.CFloat` | `float` | 32 |
| `t.CDouble` | `double` | 64 |
### 其他基本类型
| Viper 类型 | C 等价 | 说明 |
|------------|--------|------|
| `t.CVoid` | `void` | 空类型 |
| `t.CBool` | `bool` | 布尔类型(8位) |
| `t.CWCharT` | `wchar_t` | 宽字符(32位) |
| `t.CChar8T` | `char8_t` | UTF-8 字符(8位,无符号) |
| `t.CChar16T` | `char16_t` | UTF-16 字符(16位,无符号) |
| `t.CChar32T` | `char32_t` | UTF-32 字符(32位,无符号) |
### Python 便捷映射类型
| Python 类型| Viper 类型 | Viper 值 |
|--------|--------|------|
|`int`|`t.CInt`|-|
|`float`|`t.CFloat`|-|
|`str`|`t.CChar \| t.CPtr`|-|
|`bool`|`t.CBool`|-|
|`True`|`t.CBool`|`1`|
|`False`|`t.CBool`|`0`|
|`None`|`NULL`|`t.CIntPtr(0)`|
对于 t.py 和 c.py 的引用,必须使用 import,不能使用 from ... import ...,也不能使用 as,因为这两者都是直接以字符串识别的。
## 类型组合
Viper 使用 Python 的 `|` 来组合类型修饰符(TypeUnion),其仅在左值注解中有效,其它位置均为位或语义(至少重载前是这样的),这是 Viper 最重要的语法特性之一。
### 指针类型
使用 `t.CPtr` 表示指针,通过 `|` 与基本类型组合:
```python
x: t.CInt | t.CPtr # int* — 指向 int 的指针
p: t.CVoid | t.CPtr # void* — 通用指针
s: t.CChar | t.CPtr # char* — 字符串指针
pp: t.CInt | t.CPtr | t.CPtr # int** — 指向指针的指针
```
等价 C 代码:
```c
int* x;
void* p;
char* s;
int** pp;
```
同时,指针也可以用 `Ptr[T]` 的方法表示,但在 Viper 中不太推荐,虽然说这样做一直是类似工程的常见用法,但是如果此处 `T` 是一个多态,同时我们想要复用 Python 的高亮插件,插件无法识别 `Ptr[T]` 的属性取值,但是对于 `T | Ptr` 可以。
```python
x: t.CPtr[t.CInt]
p: t.CPtr[t.CVoid]
s: t.CPtr[t.CChar]
pp: t.CPtr[t.CPtr[t.CInt]]
pp2: t.CInt | t.CPtr[t.CPtr] # 同时也可以这么表示
```
等价 C 代码:
```c
int* x;
void* p;
char* s:
int** pp;
int** pp2;
```
### 类型限定符组合
```python
x: t.CConst | t.CInt # const int
p: t.CConst | t.CChar | t.CPtr # const char*
```
### 存储类组合
```python
x: t.CStatic | t.CInt # static int
f: t.CExtern | t.CInt # extern int(声明其存在于外部)
```
### 函数返回类型组合
```python
def foo() -> t.CInt | t.CExport: # 导出函数,返回 int
return 0
def bar() -> t.CVoid | t.CExtern: # 外部函数声明
pass
```
### `t.State` 声明性标记
`t.State` 是**声明性标记类型**,表示"仅声明不定义"(语义等价于 `t.CExport | t.CExtern`)。主要用于 FFI 外部函数声明:函数体为 `pass`,编译器只生成函数原型(declare),不生成定义(define),由链接器在外部库中解析符号。
```python
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
dwShareMode: ULONG,
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
dwCreationDisposition: ULONG,
dwFlagsAndAttributes: ULONG,
hTemplateFile: HANDLE) -> HANDLE | t.State:
pass
```
等价 C 声明:
```c
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*, ULONG, ULONG, HANDLE);
```
`t.State` 是 Viper FFI 机制的核心,无需任何特殊语法,仅靠返回类型注解即可声明外部函数。详见 [08-c-operations.md 的 FFI 章节](08-c-operations.md#ffi-外部函数声明) 和 [includes/w32](../includes/w32) 的 Win32 绑定。
## 数组类型
使用 Python 的 `list[ElementType, Size]` 语法表示固定大小数组(通常的,这用于栈上和 BSS 段上):
```python
buf: list[t.CChar, 64] # char buf[64]
ids: list[t.CInt, 10] # int ids[10]
matrix: list[t.CFloat, 16] # float matrix[16]
entries: list[idt_entry, 256] # struct idt_entry entries[256]
s: list[t.CChar, None] # 自推长度 char s[];
```
等价 C 代码:
```c
char buf[64];
int ids[10];
float matrix[16];
struct idt_entry entries[256];
```
### 数组指针和函数指针
```python
lp: list[t.CInt, 12] | t.CPtr
vp: t.Callable[[t.CInt, t.CInt], t.CInt] = x # 其中 t.Callable = typing.Callable,可以直接使用。
```
## 结构体类型
使用 `class` 定义结构体,成员通过类型注解声明:
```python
class point:
x: t.CInt
y: t.CInt
```
等价 C 代码:
```c
struct point {
int x;
int y;
};
```
### 带指针成员的结构体
```python
class node:
value: t.CInt
next: node | t.CPtr # struct node* next
```
### 匿名结构体成员(❌)
> WARNING: **警告:** 由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
继承 `t.Anonymous` 的类作为匿名成员嵌入:
```python
class header(t.Anonymous):
magic: t.CUInt32T
version: t.CUInt32T
class packet:
header # 匿名嵌入,可直接访问 packet.magic
length: t.CUInt32T
```
### 字节序标记
> WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序:
```python
class network_packet:
length: t.CUInt16T | t.BigEndian # 大端序存储
type: t.CUInt8T # 默认小端序
```
## 联合体类型
继承 `t.CUnion` 定义联合体:
```python
class value(t.CUnion):
ival: t.CInt
fval: t.CFloat
pval: t.CVoid | t.CPtr
```
等价 C 代码:
```c
union value {
int ival;
float fval;
void* pval;
};
```
## 枚举类型
继承 `t.CEnum` 定义枚举:
```python
class color(t.CEnum):
RED: t.CEnum = 0
GREEN: t.CEnum = 1
BLUE: t.CEnum = 2
```
等价 C 代码:
```c
enum color {
RED = 0,
GREEN = 1,
BLUE = 2
};
```
枚举成员可直接使用名称:
```python
c: t.CInt = color.RED
```
### REnum(标签联合体)
`t.CEnum` 是普通枚举(整数常量集合),`t.REnum` 是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。适用于少变体、无通用字段、match 分派的场景(如类型系统、IR 表示)。
```python
class LLVMType(t.REnum):
class Int:
Bits: t.CInt
class Void:
pass
match ty:
case LLVMType.Int:
snprintf(buf, size, "i%d", ty.Bits)
case LLVMType.Void:
string.strcpy(buf, "void")
```
REnum 的详细用法、与 CEnum/胖节点 AST 的对比见 [05-classes.md 的 REnum 章节](05-classes.md#renum标签联合体)。
## Typedef
使用 `t.CTypedef` 创建类型别名:
```python
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)();
```
或通过注解赋值:
```python
MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct;
```
更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 t.CTypedef 的,还是后面的注解的,而且容易触发未定义行为。
## 泛型类型(PEP 695 语法)
Viper 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。类型参数 `T` 在类体内可作为占位类型使用,实例化时用 `Name[ConcreteType]` 特化。
```python
@t.NoVTable
class GSList[T]:
Head: T | t.CPtr
Tail: T | t.CPtr
Count: t.CSizeT
lst: GSList[GNode] | t.CPtr = GSList[GNode]()
```
泛型类还支持**递归泛型继承**(如 `class GNode(GSListNode[GNode])`),实现强类型自引用结构,无需 `void*` + 转型。这是 C/C++/Rust 均无法如此简洁表达的能力。
泛型类的完整用法、递归继承、实例化示例见 [05-classes.md 的泛型类章节](05-classes.md#泛型类pep-695-语法)。
## 强制类型转换
在 Viper 中的强制类型转换有些不同,强制类型转换有两种方案,都是基于 `t.CType` 的用法,符合 Python 本意。
对于原先就继承自 `t.CType` 的类型来说,可以直接使用 call 的方法进行类型转换,比如
```python
x: t.CChar | t.CPtr = "Hello"
t.CVoid(x, t.CPtr)
```
其中用法为 `t.CType(x, ...)`,此处...可以是其它的 `t.CType` 或将要提到的 `Typedef`,将其视为组合类型对 `x` 进行强制转换。
额外补充一句,对于比较,四则运算,可以不使用强制类型转换,而是依靠隐式类型转换,但如果是严谨数学计算,推荐还是强制类型转换避免判断出错。
此外,若将一个不同类型的右值赋值到左值,则会隐式将右值转换为左值的类型。
如果你不喜欢 `t.CVoid(x, t.CPtr)` 的方法,认为这将 `void` 和 `ptr` 分开,不便于理解,那么还有一个方法,即将二者打包为一个 Typedef,即:
```python
VOIDPTR: t.CTypedef = t.CVoid | t.CPtr
```
则如果你想,你可以直接通过 call 的方式进行强转,比如:
```python
y: VOIDPTR = VOIDPTR(x)
```
## 宏定义常量
使用 `t.CDefine` 定义编译时常量。`t.CDefine` 不是数据类型,而是编译时元指令——标记为 `t.CDefine` 的常量在 LLVM IR 中不生成任何全局变量,而是在引用处直接内联展开。详见 [01-overview.md 中 t.CDefine 深度解析](01-overview.md#tcdefine-深度解析)。
```python
MAX_SIZE: t.CDefine = 1024
PAGE_SIZE: t.CDefine = 4096
FA_READ: t.CDefine = 0x01
```
语义上等价于 C 的 `#define`:
```c
#define MAX_SIZE 1024
#define PAGE_SIZE 4096
#define FA_READ 0x01
```
宏定义支持表达式:
```python
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
CPU_FEATURE_APIC: t.CDefine = (1 << 6)
```
宏定义也可与类型组合使用:
```python
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
```
## 位域
使用 `t.Bit(n)` 定义位域成员:
```python
class flags:
a: t.CUInt32T | t.Bit[1] # 此处,如果存入 2,二进制是 10,会被截断为 0
b: t.CUInt32T | t.Bit[3]
c: t.CUInt32T | t.Bit[4]
```
## LLVM IR 类型简写
Viper 提供了 LLVM IR 级别的类型简写:
```python
i1 # 1位整数
i8 # 8位有符号整数
i16 # 16位有符号整数
i32 # 32位有符号整数
i64 # 64位有符号整数
u1 # 1位无符号整数
u8 # 8位无符号整数
u16 # 16位无符号整数
u32 # 32位无符号整数
u64 # 64位无符号整数
```
仍然通过 `t.x` 的方式调用,比如 `t.i1` 等。
## `__attribute__` 属性
通过 `t.attr` 命名空间访问 GCC `__attribute__` 属性:
```python
t.attr.packed() # __attribute__((packed))
t.attr.aligned(16) # __attribute__((aligned(16)))
t.attr.section(".text.startup") # __attribute__((section(".text.startup")))
t.attr.always_inline() # __attribute__((always_inline))
t.attr.noinline() # __attribute__((noinline))
t.attr.noreturn() # __attribute__((noreturn))
t.attr.weak() # __attribute__((weak))
t.attr.unused() # __attribute__((unused))
t.attr.constructor() # __attribute__((constructor))
t.attr.destructor() # __attribute__((destructor))
t.attr.deprecated("use X") # __attribute__((deprecated("use X")))
```
### LLVM 函数属性
通过 `t.attr.llvm` 命名空间指定 LLVM 函数属性:
```python
t.attr.llvm.nobuiltin
t.attr.llvm.nounwind
t.attr.llvm.noredzone
t.attr.llvm.willreturn
t.attr.llvm.mustprogress
t.attr.llvm.optnone
t.attr.llvm.noinline
t.attr.llvm.alwaysinline
t.attr.llvm.readnone
t.attr.llvm.readonly
t.attr.llvm.writeonly
t.attr.llvm.inaccessiblememonly
t.attr.llvm.inaccessiblemem_or_argmemonly
```
在函数返回类型注解中使用:
```python
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
return 0
```
+177
View File
@@ -0,0 +1,177 @@
# 03 - 变量声明与赋值
Viper 的变量声明使用 Python 的类型注解语法,通过 `t` 模块指定 C 级别的类型信息。
## 带注解的变量声明
使用 `变量名: 类型` 语法声明变量:
```python
x: t.CInt = 0 # int x = 0;
y: t.CUInt32T = 42 # uint32_t y = 42;
name: list[t.CChar, 64] # char name[64];
ptr: t.CVoid | t.CPtr # void* ptr;
msg: t.CConst | str = "Hello" # const char* msg = "Hello";
```
### 无初始值的声明
未赋值的变量声明会生成零初始化的变量,此处是未定义行为,即在不同环境位置可能会覆盖0,也可能不会,最好预先复制或用 memset 清除:
```python
count: t.CInt # int count = 0;
buffer: list[t.CChar, 256] # char buffer[256] = {0};
```
### 延迟赋值
变量可以先声明后赋值:
```python
x: t.CInt
x = 10
```
### 指针变量
```python
p: t.CInt | t.CPtr # int* p;
fb: t.CVoid | t.CPtr # void* fb;
str_ptr: t.CConst | t.CChar | t.CPtr # const char* str_ptr;
```
### 指针变量的特殊初始化
使用 `t.CVoid(0, t.CPtr)` 初始化指针为 NULL:
```python
p: t.CVoid | t.CPtr = t.CVoid(0, t.CPtr) # void* p = NULL;
```
或使用 `None`:
```python
p: t.CVoid | t.CPtr = None # void* p = NULL;
```
## 普通赋值
```python
x: t.CInt = 0
x = 42 # x = 42;
```
## 增强赋值
支持所有 Python 增强赋值运算符:
```python
x: t.CInt = 0
x += 1 # x += 1;
x -= 1 # x -= 1;
x *= 2 # x *= 2;
x //= 3 # x /= 3; (整数除法)
x %= 5 # x %= 5;
x <<= 1 # x <<= 1;
x >>= 1 # x >>= 1;
x |= 0xFF # x |= 0xFF;
x &= 0x0F # x &= 0x0F;
x ^= 0xAA # x ^= 0xAA;
```
## 全局变量
模块顶层声明的变量即为全局变量:
```python
kbd_tid: t.CInt = -1 # 全局 int kbd_tid = -1;
BootInfo: bootinfo | t.CPtr # 全局 struct bootinfo* BootInfo;
```
### 静态全局变量
```python
x: t.CStatic | t.CInt = 0 # static int x = 0;
```
### 外部声明
```python
x: t.CExtern | t.CInt # extern int x;
```
## 常量定义
使用 `t.CDefine` 定义编译时常量。`t.CDefine` 不是数据类型,而是编译时元指令——常量在 LLVM IR 中不生成全局变量,而是在引用处直接内联展开。详见 [01-overview.md 中 t.CDefine 深度解析](01-overview.md#tcdefine-深度解析)。
```python
MAX_SIZE: t.CDefine = 1024
PAGE_SIZE: t.CDefine = 4096
FA_READ: t.CDefine = 0x01
FA_WRITE: t.CDefine = 0x02
```
## 字符串常量
Viper 中的字符串字面量编译为 C 的字符串常量(`const char*`):
```python
msg: t.CConst | str = "Hello, World!"
serial.puts(msg)
```
也可以直接传递字符串字面量:
```python
serial.puts("Hello, World!")
```
## 类型转换
使用类型构造函数进行显式类型转换:
```python
x: t.CInt = 42
y: t.CUInt32T = t.CUInt32T(x) # (uint32_t)x
f: t.CFloat = float(x) # (float)x
i: t.CInt = int(f) # (int)f
```
### 指针类型转换
```python
ptr: t.CVoid | t.CPtr = some_ptr
int_val: t.CUInt64T = t.CUInt64T(ptr) # (uint64_t)ptr — 指针转整数
```
## 结构体实例化
> 此处不理解参见 ([05-classes.md 中 结构体实例化与初始化](05-classes.md#结构体实例化与初始化))
直接使用类名作为构造函数:
```python
info: fat32_types.fat32_fileinfo
dp: fat32_types.fat32_dirobj
```
带初始化的实例化:
```python
obj: MyClass = MyClass(arg1, arg2)
```
使用 `c.Addr` 获取实例指针:
```python
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass(arg1, arg2))
```
## delete 语句
`del` 语句可用于调用对象的 `__del__` 或 `__delete__` 方法:
```python
del obj # 调用 obj.__del__() 或 obj.__delete__()
del arr[idx] # 调用 arr.__delitem__(idx)
```
+254
View File
@@ -0,0 +1,254 @@
# 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
```
在结构体中也可以指定默认值。
+274
View File
@@ -0,0 +1,274 @@
# 05 - 类与数据布局
Viper 中的 `class` 有两种用途:定义**数据布局**(结构体/联合体/枚举,本章内容),以及定义**面向对象的对象类型**(见 [06-oop.md](06-oop.md))。
## 结构体定义
普通 `class` 定义编译为 LLVM 结构体类型:
```python
class point:
x: t.CInt
y: t.CInt
```
在 C 中:
```c
struct point {
int x;
int y;
};
```
在 LLVM IR 中:
```llvm
%"<SHA1>.point" = type { i32, i32 }
```
### 带属性的结构体
```python
@c.Attribute(t.attr.packed)
class idt_entry:
base_low: t.CUInt16T
selector: t.CUInt16T
ist_attr: t.CUInt8T
type_attr: t.CUInt8T
base_middle: t.CUInt16T
base_high: t.CUInt32T
reserved1: t.CUInt32T
```
### 结构体数组
```python
idt: list[idt_entry, 256] = []
```
### 结构体指针成员
```python
class node:
value: t.CInt
next: node | t.CPtr # 指向自身的指针(有向图链表)
```
### 结构体实例化与初始化
```python
entry: idt_entry = idt_entry()
```
或带构造函数:
```python
mousepoint = Point(x=1, y=2)
```
乱序传参或带参传参对于结构体的初始化同样有效:
```python
entry: idt_entry = idt_entry(selector=0x08, base_low=0x0000)
```
也可以给结构体成员指定默认值:
```python
class config:
width: t.CInt = 640
height: t.CInt = 480
depth: t.CInt = 32
```
使用 `c.Addr` 获取实例指针:
```python
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass(arg1, arg2))
```
### 匿名结构体成员(❌)
> WARNING: **警告:** 由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
继承 `t.Anonymous` 的类作为匿名成员嵌入:
```python
class header(t.Anonymous):
magic: t.CUInt32T
version: t.CUInt32T
class packet:
header # 匿名嵌入,可直接访问 packet.magic
length: t.CUInt32T
```
### 字节序标记
> WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序:
```python
class network_packet:
length: t.CUInt16T | t.BigEndian # 大端序存储
type: t.CUInt8T # 默认小端序
```
## 联合体
继承 `t.CUnion` 定义联合体:
```python
class value(t.CUnion):
ival: t.CInt
fval: t.CFloat
pval: t.CVoid | t.CPtr
```
等价 C 代码:
```c
union value {
int ival;
float fval;
void* pval;
};
```
## 枚举
继承 `t.CEnum` 定义枚举:
```python
class color(t.CEnum):
RED: t.CEnum = 0
GREEN: t.CEnum = 1
BLUE: t.CEnum = 2
```
等价 C 代码:
```c
enum color {
RED = 0,
GREEN = 1,
BLUE = 2
};
```
枚举成员可直接使用名称:
```python
c: t.CInt = color.RED
```
## REnum(标签联合体)
`t.CEnum` 是普通枚举(整数常量集合),`t.REnum` 是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。
### 适用场景
| 类型 | 变体数 | 通用字段 | 访问模式 | 适用 |
|------|--------|---------|---------|------|
| `t.CEnum` | 任意 | 无 | 整数比较 | 状态码、opcode、token 类型 |
| `t.REnum` | 少(~10) | 无 | match 分派 | 类型系统、AST 少变体场景 |
| 胖节点(如 AST) | 多(70+) | 有 | vtype + 直接属性 | AST 节点(见 [includes/ast](../includes/ast)) |
### 定义
```python
class LLVMType(t.REnum):
class Int: # 变体 Int,携带 payload
Bits: t.CInt
class Ptr: # 变体 Ptr,携带 payload
Pointee: LLVMType | t.CPtr # 自引用用指针
class Void: # 变体 Void,无 payload
pass
```
布局为 `{ i32 __tag, <max_variant_payload> }`,所有变体共享最大变体的 payload 空间。
### match 分派
```python
match ty:
case LLVMType.Int: # 位置绑定 payload:ty.Bits 可用
snprintf(buf, size, "i%d", ty.IntBits)
case LLVMType.Ptr: # ty.PtrPointee 可用
TypePrint(buf, size, ty.PtrPointee)
case LLVMType.Void:
string.strcpy(buf, "void")
```
详见 [includes/llvmlite/__types.vp](../includes/llvmlite/__types.vp) 的 `LLVMType` 实现。
## Typedef
使用 `t.CTypedef` 创建类型别名:
```python
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)();
```
或通过注解赋值:
```python
MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct;
```
更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 `t.CTypedef` 的,还是后面的注解的,而且容易触发未定义行为。
## 泛型类(PEP 695 语法)
TransPyC 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。
### 基本泛型类
```python
@t.NoVTable
class GSListNode[T]:
Next: T | t.CPtr
@t.NoVTable
class GSList[T]:
Head: T | t.CPtr
Tail: T | t.CPtr
Count: t.CSizeT
def append(self, node: T): ...
```
### 递归泛型继承
泛型类可递归引用自身作为类型参数,实现强类型自引用结构(无需转型):
```python
@t.NoVTable
class GNode(GSListNode[GNode]): # 递归泛型继承
value: t.CInt
# 此时 GNode.Next: GNode|CPtr —— 强类型,零转型
```
这是 C(`void*` + cast)、C++(模板继承但无 `@t.NoVTable` 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。
### 实例化
```python
lst: GSList[GNode] | t.CPtr = GSList[GNode]()
node: GNode | t.CPtr = GNode()
lst.append(node) # 类型安全:append 只接受 GNode
```
详见 [includes/linkedlist.vp](../includes/linkedlist.vp) 和 [includes/vector.vp](../includes/vector.vp)。
## 位域
使用 `t.Bit(n)` 定义位域成员:
```python
class flags:
a: t.CUInt32T | t.Bit[1]
b: t.CUInt32T | t.Bit[3]
c: t.CUInt32T | t.Bit[4]
```
注意:如果存入超出位域宽度的值,会被截断。例如 `a` 为 1 位,存入 2(二进制 `10`)会被截断为 0。
+692
View File
@@ -0,0 +1,692 @@
# 06 - 面向对象与运算符重载
Viper 中的 `class` 有两种语义:**数据布局**(结构体/联合体/枚举,见 [05-classes.md](05-classes.md))和**面向对象**。面向对象特性通过 `@t.Object` 装饰器启用,多态通过 `@t.CVTable` 装饰器启用,非多态继承(C++ 风格,无 vtable 开销)通过 `@t.NoVTable` 装饰器启用。
## `@t.Object` 面向对象
使用 `@t.Object` 装饰器启用面向对象特性,类将拥有成员方法、运算符重载、属性装饰器等 OOP 支持。编译器也会自动识别:如果结构体内有函数定义,会自动使用 `@t.Object` 行为,但在实际工程中更推荐明确标记,避免未定义行为。
### 基本定义
```python
@t.Object
class ViperKernel:
BootInfo: bootinfo | t.CPtr
VGA: vga._VGAScreenDriver | t.CPtr
VGA_drv: vga._VGAScreenDriver
def __init__(self):
self.BootInfo = BootInfo
serial.init()
serial.puts("ViperOS VKernel Test\n")
```
避免在 `__init__` 中使用 `return`,虽然不会报错,但是你永远无法获得 `return` 的结果,如果需要提前结束代码流,`return` 也不失为一种办法。
### 成员方法
```python
@t.Object
class Sheet:
id: t.CInt
x: t.CInt
y: t.CInt
w: t.CInt
h: t.CInt
def __init__(self, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt):
self.x = x
self.y = y
self.w = w
self.h = h
def MoveTo(self, nx: t.CInt, ny: t.CInt):
self.x = nx
self.y = ny
def Resize(self, nw: t.CInt, nh: t.CInt):
self.w = nw
self.h = nh
```
### 方法调用
```python
sheet: Sheet = Sheet(0, 0, 640, 480)
sheet.MoveTo(100, 100)
sheet.Resize(800, 600)
```
### 属性装饰器
支持 Python 的 `@property` 和 `@xxx.setter` 装饰器:
> `@xxx.setter` 和自定义装饰器正在试验阶段,尽可能不要使用
```python
@t.Object
class MyObj:
_value: t.CInt
@property
def value(self) -> t.CInt:
return self._value
@value.setter
def value(self, v: t.CInt):
self._value = v
```
### 静态方法
```python
@staticmethod
def _yield():
pass
```
### 对象指针与解引用
`@t.Object` 的实例通常通过指针操作:
```python
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass())
obj: MyClass = c.Deref(obj_ptr)
```
### 成员访问
通过 `self` 访问成员变量和方法:
```python
def method(self) -> t.CVoid:
self.x = 10
y: t.CInt = self.y
self.DoSomething()
```
### 内嵌对象
`@t.Object` 可以包含其他对象实例(非指针):
```python
@t.Object
class Sheet:
_surf_obj: gfx.Surface
_renderer_obj: gfx.Renderer
surf: gfx.Surface | t.CPtr
renderer: gfx.Renderer | t.CPtr
def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: t.CInt, h: t.CInt):
self._surf_obj = gfx.Surface(fb, zb, w, h)
self.surf = c.Addr(self._surf_obj)
self._renderer_obj = gfx.Renderer(self.surf)
self.renderer = c.Addr(self._renderer_obj)
```
### `__before_init__` 方法
所有类(不限 `@t.Object`/`@t.CVTable`)自动生成 `__before_init__` 方法,在 `__init__` 之前**自动填充成员默认值**,并对 `@t.CVTable` 类初始化 vtable:
```llvm
define void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"* %self) {
entry:
; 对 @t.CVTable 类:存储 vtable 指针到 slot 0
; 遍历所有成员:若有默认值则 store 默认值,否则 store 零值
ret void
}
```
**职责**:
- 填充类定义中声明的成员默认值(如 `x: t.CInt = 0` 中的 `0`)
- 对无默认值的成员填充类型零值
- 对 `@t.CVTable` 类额外初始化 vtable 指针
**调用时机**(由编译器在构造时自动插入):
1. `alloca` 栈分配后调用一次
2. 若存在 `__new__` 方法且返回堆指针,对堆指针再调用一次(alloca 的默认值已随栈帧丢弃,必须重新填充)
3. `__init__` 调用前
**就地构造**:用户也可手动调用 `__before_init__` 实现就地构造(placement new 语义),典型用法见 `mpool.alloc_buf`:
```python
buf: Buf | t.CPtr = pool.alloc(Buf.__sizeof__())
buf.__before_init__() # 填充默认值
buf.__init__(args) # 用户初始化逻辑
```
跨模块导入类时,导入侧也会声明 `__before_init__`,保证跨模块构造行为一致。
## `@t.NoVTable` 非多态继承
`@t.NoVTable` 启用 C++ 风格的非多态继承(对应 `struct A : B {}` 无 virtual)。与 `@t.CVTable` 的区别:
| 特性 | `@t.NoVTable` | `@t.CVTable` |
|------|---------------|--------------|
| vtable 指针 | ❌ 无 | ✅ 自动插入 slot 0 |
| 虚派发 | ❌ 无 | ✅ 支持 |
| 方法继承 | ✅ 生成包装函数转发 | ✅ 通过 vtable |
| 字段展平 | ✅ 父类字段前置嵌入 | ✅ 父类字段前置嵌入(vtable 之后) |
| 运行时开销 | 零 | 每次虚调用一次间接跳转 |
### 基本定义
```python
@t.NoVTable
class LinkedNode:
next: "LinkedNode" | t.CPtr
prev: "LinkedNode" | t.CPtr
def append(self, node: "LinkedNode" | t.CPtr): ...
@t.NoVTable
class GNode(LinkedNode): # 非多态继承
value: t.CInt
```
子类 `GNode` 的结构体布局为 `{next, prev, value}`(父类字段前置,无 vtable 指针)。
### 方法继承机制
子类调用父类方法时,编译器通过 `_generate_inherited_method_wrappers` 自动生成包装函数:将 `self` bitcast 为父类指针后尾调用父类方法。例如 `gnode.append(child)` 会生成:
```llvm
define void @"<SHA1>.GNode.append"(%"<SHA1>.GNode"* %self, ...) {
entry:
%base = bitcast %"<SHA1>.GNode"* %self to %"<SHA1>.LinkedNode"*
call void @"<SHA1>.LinkedNode.append"(%"<SHA1>.LinkedNode"* %base, ...)
ret void
}
```
### 跨模块继承
跨模块父类(如 `linkedlist.LinkedNode`)的 `@t.NoVTable` 标记会在导入时被检测并记录,子类继承时同样跳过 vtable 插入。
### 向下转型
父类指针→子类指针需显式转型(生成 bitcast IR):
```python
root: Node = Node() # Node 继承 LinkedNode
child: LinkedNode | t.CPtr = root.child
# child 是 LinkedNode*,需要转回 Node*:
node: Node | t.CPtr = (Node | t.CPtr)(child)
```
隐式转型仅向上(子→父)。
## `@t.CVTable` 虚函数表与多态
> 尽可能避免使用 `self[0]` 来获取结构体成员,其不稳定,也不要手动操作虚表。
使用 `@t.CVTable` 装饰器可以启用虚函数表支持。启用后,结构体第一个成员自动插入 `i8*` 类型的 vtable 指针。`@t.CVTable` 的操作必须配合 `@t.Object`,但有时编译器会自动识别是否存在结构体内函数,自动使用 `@t.Object`。
### 基本定义
```python
@t.Object
@t.CVTable
class Base:
def __init__(self):
pass
def method(self) -> t.CInt:
return 0
```
在 LLVM IR 中,`@t.CVTable` 类的结构体类型自动插入 vtable 指针:
```llvm
%"<SHA1>.Base" = type { i8*, i32 }
```
同时,编译器会生成全局 vtable 变量:
```llvm
@"<SHA1>.Base_Vtable" = internal global [1 x i8*] zeroinitializer
```
### 继承与多态
虚表可以帮你使用继承语法实现多态:
```python
@t.Object
@t.CVTable
class Base:
def __init__(self):
pass
def method(self) -> t.CInt:
return 0
@t.Object
@t.CVTable
class M1(Base):
def method(self) -> t.CInt:
return 1
```
当 M1 被初始化时,VTable 会自动修改,这点和 C++ 相同,尽可能避免直接操作 VTable。
继承时,子类会自动继承父类的成员变量和方法。子类重写父类方法时,vtable 中对应位置的函数指针被子类方法替换,实现多态。
### VTable 运行时修改
如果需要临时构造一个额外的 `Base` 结构体,还可以使用:
```python
@t.Object
@t.CVTable
class Base:
def __init__(self):
pass
def method(self) -> t.CInt:
return 0
def __method1(self) -> t.CInt:
return 1
def main() -> t.CInt | t.CExport:
b: Base = Base()
b.method = __method1
```
启用 `VTable` 的表中,允许直接修改内部函数为函数指针,在底层,此操作将修改 `VTable` 来达成目的。编译器会检测当前 vtable 是否与类 vtable 相同,如果相同则创建 vtable 的副本(memcpy),避免修改影响所有实例。
### 虚方法调度机制
虚方法调用的核心流程:
1. 从对象指针 GEP 获取第 0 个 slot(vtable 指针)
2. 加载 vtable 指针
3. bitcast 为 `[N x i8*]*` 类型
4. GEP 获取方法索引处的函数指针
5. 加载函数指针
6. bitcast 为正确的函数指针类型
7. 间接调用
## 继承
### 结构体成员继承
当子类继承父类(父类在 `Gen.class_members` 中存在)时:
- 父类的成员变量被继承到子类(排在子类成员之前)
- 父类的默认值也被继承
- 父类的方法被继承(重命名为 `子类名.方法名`)
```python
@t.Object
class Animal:
name: list[t.CChar, 32]
age: t.CInt
def __init__(self, age: t.CInt):
self.age = age
def Speak(self) -> t.CVoid:
serial.puts("...\n")
@t.Object
class Dog(Animal):
breed: t.CInt
def Speak(self) -> t.CVoid:
serial.puts("Woof!\n")
```
### 异常类继承
Viper 支持异常类的继承,用于 `try/except` 的异常匹配:
```python
class MyError(Exception):
pass
class SpecificError(MyError):
pass
```
详见 [09-exceptions.md](09-exceptions.md)。
## 运算符重载
`@t.Object` 类支持运算符重载。编译器在遇到运算符时,首先检查左操作数是否为结构体类型,如果是则尝试调用对应的 dunder 方法。
### 算术运算符重载
| 运算符 | Dunder 方法 | 示例 |
|--------|------------|------|
| `+` | `__add__` | `a + b` → `a.__add__(b)` |
| `-` | `__sub__` | `a - b` → `a.__sub__(b)` |
| `*` | `__mul__` | `a * b` → `a.__mul__(b)` |
| `/` | `__truediv__` | `a / b` → `a.__truediv__(b)` |
| `//` | `__floordiv__` | `a // b` → `a.__floordiv__(b)` |
| `%` | `__mod__` | `a % b` → `a.__mod__(b)` |
| `**` | `__pow__` | `a ** b` → `a.__pow__(b)` |
示例:
```python
@t.Object
class Vec2:
x: t.CFloat
y: t.CFloat
def __init__(self, x: t.CFloat, y: t.CFloat):
self.x = x
self.y = y
def __add__(self, other: Vec2) -> Vec2:
result: Vec2 = Vec2(0.0, 0.0)
result.x = self.x + other.x
result.y = self.y + other.y
return result
def __mul__(self, scalar: t.CFloat) -> Vec2:
result: Vec2 = Vec2(0.0, 0.0)
result.x = self.x * scalar
result.y = self.y * scalar
return result
```
### 增量赋值运算符重载
| 增量运算符 | 优先 Dunder | 回退 Dunder |
|-----------|------------|------------|
| `+=` | `__iadd__` | `__add__` |
| `-=` | `__isub__` | `__sub__` |
| `*=` | `__imul__` | `__mul__` |
| `/=` | `__itruediv__` | `__truediv__` |
| `//=` | `__ifloordiv__` | `__floordiv__` |
| `%=` | `__imod__` | `__mod__` |
| `**=` | `__ipow__` | `__pow__` |
如果未定义增量版本(如 `__iadd__`),编译器自动回退到普通版本(如 `__add__`)。
### 比较运算符重载
> **实验性**:以下比较运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。定义它们不会报错,但比较运算不会自动调用。
| 运算符 | Dunder 方法 | 状态 |
|--------|------------|------|
| `==` | `__eq__` | 仅类型推断识别 |
| `!=` | `__ne__` | 仅类型推断识别 |
| `<` | `__lt__` | 仅类型推断识别 |
| `<=` | `__le__` | 仅类型推断识别 |
| `>` | `__gt__` | 仅类型推断识别 |
| `>=` | `__ge__` | 仅类型推断识别 |
### 反向运算符重载
> **实验性**:以下反向运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。
| Dunder 方法 | 预期语义 | 状态 |
|------------|---------|------|
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
| `__rsub__` | 右操作数减法 | 仅类型推断识别 |
| `__rmul__` | 右操作数乘法 | 仅类型推断识别 |
| `__rtruediv__` | 右操作数真除法 | 仅类型推断识别 |
| `__rfloordiv__` | 右操作数整除 | 仅类型推断识别 |
| `__rmod__` | 右操作数取模 | 仅类型推断识别 |
| `__rpow__` | 右操作数幂运算 | 仅类型推断识别 |
### 一元运算符重载
> **实验性**:以下一元运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。
| 运算符 | Dunder 方法 | 状态 |
|--------|------------|------|
| `-a` | `__neg__` | 仅类型推断识别 |
| `+a` | `__pos__` | 仅类型推断识别 |
## 可调用对象 `__call__`
```python
@t.Object
class Counter:
value: t.CInt
def __init__(self):
self.value = 0
def __call__(self) -> t.CInt:
self.value += 1
return self.value
c: Counter = Counter()
x: t.CInt = c() # x = 1
y: t.CInt = c() # y = 2
```
## 下标访问重载
### `__getitem__` — 读取
```python
def __getitem__(self, key: t.CInt) -> t.CInt:
return self.data[key]
```
### `__setitem__` — 赋值
```python
def __setitem__(self, key: t.CInt, value: t.CInt):
self.data[key] = value
```
### `__delitem__` — 删除
```python
def __delitem__(self, key: t.CInt):
pass
```
## 布尔转换 `__bool__`
```python
@t.Object
class Buffer:
size: t.CInt
data: list[t.CChar, 1024]
def __bool__(self) -> t.CBool:
return self.size > 0
buf: Buffer = Buffer()
if buf:
serial.puts("buffer is not empty\n")
```
## 长度 `__len__`
```python
@t.Object
class Buffer:
size: t.CInt
def __len__(self) -> t.CInt:
return self.size
buf: Buffer = Buffer()
n: t.CInt = len(buf)
```
## 字符串转换 `__str__`
```python
@t.Object
class Point:
x: t.CInt
y: t.CInt
def __str__(self) -> t.CConst | t.CChar | t.CPtr:
return "Point"
p: Point = Point(1, 2)
print(p) # 调用 p.__str__()
```
## 绝对值 `__abs__`
```python
@t.Object
class SignedValue:
val: t.CInt
def __abs__(self) -> t.CInt:
if self.val < 0:
return -self.val
return self.val
sv: SignedValue = SignedValue()
sv.val = -42
a: t.CInt = abs(sv) # a = 42
```
## 迭代器协议
Viper 的迭代器协议与 Python 相同,但 `__next__` 的实现方式通过 `StopIteration` 异常来结束。
### `__iter__` 和 `__next__`
```python
@t.Object
class IntRange:
start: t.CInt
end: t.CInt
current: t.CInt
def __init__(self, start: t.CInt, end: t.CInt):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self
def __next__(self, stop_flag) -> t.CInt:
if self.current >= self.end:
raise StopIteration
val: t.CInt = self.current
self.current += 1
return val
```
### for 循环使用
```python
r: IntRange = IntRange(0, 10)
for i in r:
print(i)
```
编译器生成的等价逻辑:
```c
IntRange r = IntRange_init(0, 10);
IntRange iter = IntRange___iter__(&r);
while (1) {
i1 stop_flag = 0;
int i = IntRange___next__(&iter, &stop_flag);
if (stop_flag == 1) break;
print_int(i);
}
```
## 上下文管理器
`with` 语句要求对象实现 `__enter__` 和 `__exit__` 方法:
```python
@t.Object
class File:
fd: t.CInt
path: t.CConst | t.CChar | t.CPtr
def __init__(self, path: t.CConst | t.CChar | t.CPtr, flags: t.CInt):
self.path = path
self.fd = -1
def __enter__(self):
self.fd = fat32.open(self.path, self.flags)
return self
def __exit__(self):
fat32.close(self.fd)
def read(self) -> t.CInt:
return fat32.read(self.fd)
```
使用方式:
```python
with File("/test.txt", FA_READ) as f:
data: t.CInt = f.read()
```
编译器会自动保证 `__exit__` 在 `with` 块结束时被调用。
## 析构 `__del__` / `__delete__`
```python
del obj # 调用 obj.__del__() 或 obj.__delete__()
del arr[idx] # 调用 arr.__delitem__(idx)
```
## Dunder 方法完整参考
### 已实现自动调度的 Dunder 方法
| Dunder 方法 | 触发语法 | 说明 |
|------------|---------|------|
| `__init__` | `ClassName(args)` | 构造函数 |
| `__before_init__` | 自动生成 | vtable 初始化,在 `__init__` 之前执行 |
| `__add__` | `a + b` | 加法 |
| `__sub__` | `a - b` | 减法 |
| `__mul__` | `a * b` | 乘法 |
| `__truediv__` | `a / b` | 真除法 |
| `__floordiv__` | `a // b` | 整除 |
| `__mod__` | `a % b` | 取模 |
| `__pow__` | `a ** b` | 幂运算 |
| `__iadd__` | `a += b` | 增量加法(回退到 `__add__`) |
| `__isub__` | `a -= b` | 增量减法(回退到 `__sub__`) |
| `__imul__` | `a *= b` | 增量乘法(回退到 `__mul__`) |
| `__itruediv__` | `a /= b` | 增量真除法(回退到 `__truediv__`) |
| `__ifloordiv__` | `a //= b` | 增量整除(回退到 `__floordiv__`) |
| `__imod__` | `a %= b` | 增量取模(回退到 `__mod__`) |
| `__ipow__` | `a **= b` | 增量幂运算(回退到 `__pow__`) |
| `__call__` | `obj(args)` | 可调用对象 |
| `__getitem__` | `obj[key]` | 下标读取 |
| `__setitem__` | `obj[key] = value` | 下标赋值 |
| `__delitem__` | `del obj[key]` | 下标删除 |
| `__bool__` | `if obj:` | 布尔转换 |
| `__len__` | `len(obj)` | 长度 |
| `__str__` | `str(obj)` / `print(obj)` | 字符串转换 |
| `__abs__` | `abs(obj)` | 绝对值 |
| `__iter__` | `for x in obj:` | 获取迭代器 |
| `__next__` | `for x in obj:` | 获取下一个值(标志位方式) |
| `__enter__` | `with obj as x:` | 上下文管理器进入 |
| `__exit__` | `with obj as x:` | 上下文管理器退出 |
| `__del__` | `del obj` | 析构 |
| `__delete__` | `del obj` | 删除描述符 |
### 声明支持但未实现自动调度的 Dunder 方法
| Dunder 方法 | 预期触发语法 | 当前状态 |
|------------|------------|---------|
| `__eq__` | `a == b` | 仅类型推断识别,比较运算不自动调度 |
| `__ne__` | `a != b` | 同上 |
| `__lt__` | `a < b` | 同上 |
| `__le__` | `a <= b` | 同上 |
| `__gt__` | `a > b` | 同上 |
| `__ge__` | `a >= b` | 同上 |
| `__neg__` | `-a` | 仅类型推断识别,一元负号不自动调度 |
| `__pos__` | `+a` | 同上 |
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
| `__rsub__` | 右操作数减法 | 同上 |
| `__rmul__` | 右操作数乘法 | 同上 |
| `__rtruediv__` | 右操作数真除法 | 同上 |
| `__rfloordiv__` | 右操作数整除 | 同上 |
| `__rmod__` | 右操作数取模 | 同上 |
| `__rpow__` | 右操作数幂运算 | 同上 |
+256
View File
@@ -0,0 +1,256 @@
# 07 - 控制流
Viper 支持 Python 风格的控制流语句,编译为 LLVM IR 的基本块和分支指令。
## 条件语句
### if / elif / else
```python
if x > 0:
y = 1
elif x == 0:
y = 0
else:
y = -1
```
等价 C 代码:
```c
if (x > 0) {
y = 1;
} else if (x == 0) {
y = 0;
} else {
y = -1;
}
```
### 条件表达式
```python
is_dir: t.CInt = 1 if (info.attr & AM_DIR) else 0
```
等价 C 代码:
```c
int is_dir = (info.attr & AM_DIR) ? 1 : 0;
```
### None 检查
```python
if BootInfo != None:
paging.init(BootInfo.MemmapAddr, BootInfo.MemmapSize)
else:
paging.init(0, 0)
```
等价 C 代码:
```c
if (BootInfo != NULL) {
paging_init(BootInfo->MemmapAddr, BootInfo->MemmapSize);
} else {
paging_init(0, 0);
}
```
## 循环语句
### while 循环
```python
i: t.CInt = 0
while i < 10:
buf[i] = 0
i += 1
```
等价 C 代码:
```c
int i = 0;
while (i < 10) {
buf[i] = 0;
i += 1;
}
```
### while-else
```python
while i < count:
if found:
break
else:
serial.puts("not found\n")
```
`else` 块在循环正常结束(非 `break` 退出)时执行。
### 无限循环
```python
while True:
sched.Scheduler._yield()
```
等价 C 代码:
```c
while (1) {
scheduler_yield();
}
```
### for 循环(range)(关于 for 循环的更多用法,参见 [06-oop.md 中 迭代器协议](06-oop.md#迭代器协议))
```python
for i in range(10):
buf[i] = 0
for i in range(5, 10):
buf[i] = 0
for i in range(0, 100, 2):
buf[i] = 0
```
等价 C 代码:
```c
for (int i = 0; i < 10; i++) {
buf[i] = 0;
}
for (int i = 5; i < 10; i++) {
buf[i] = 0;
}
for (int i = 0; i < 100; i += 2) {
buf[i] = 0;
}
```
### for 循环(迭代器)
如果类实现了 `__iter__` 和 `__next__` 方法,可以使用 `for ... in` 迭代:
```python
container: Iter = Iter()
for item in container:
process(item)
```
### for-else
```python
for i in range(count):
if items[i] == target:
break
else:
serial.puts("not found\n")
```
### break 和 continue
```python
while True:
if done:
break
if skip:
continue
process()
```
## match 语句
Viper 支持 Python 3.10+ 的 `match` 语句,编译为 C 的 `switch` 语句:
```python
match self.ctype:
case UI_LABEL:
self.RenderLabel(sh)
case UI_BUTTON:
self.RenderButton(sh)
case UI_PANEL:
self.RenderPanel(sh)
case _:
pass
```
等价 C 代码:
```c
switch (self->ctype) {
case UI_LABEL:
self_RenderLabel(self, sh);
break;
case UI_BUTTON:
self_RenderButton(self, sh);
break;
case UI_PANEL:
self_RenderPanel(self, sh);
break;
default:
break;
}
```
### match 值匹配
```python
match code:
case 0:
serial.puts("OK\n")
case 1:
serial.puts("Error\n")
case _:
serial.puts("Unknown\n")
```
### match 或模式
```python
match value:
case 1 | 2 | 3:
serial.puts("small\n")
case _:
serial.puts("other\n")
```
### fallthrough(无 break)
默认每个 `case` 自动添加 `break`。如需 fallthrough,使用 `c.NoBreak`:
```python
match value:
case 1:
do_one()
c.NoBreak
case 2:
do_two()
```
如果需要提前 break,需要使用 `c.Break()`,因为在 `Python` 中,`match` 分支不支持直接使用 `break`。
## assert 语句
> 避免使用此语句,此语句未经充分测试,在多数情况下,编译器不知道如何展示结果。
```python
assert ptr != None, "null pointer"
```
编译为条件检查和错误报告。
## with 语句
`with` 语句用于资源管理,要求对象实现 `__enter__` 和 `__exit__` 方法:
```python
with File("/test.txt", FA_READ) as f:
data: t.CInt = f.read()
```
等价 C 代码:
```c
File* f = File_enter(File_new("/test.txt", FA_READ));
int data = File_read(f);
File_exit(f);
```
+305
View File
@@ -0,0 +1,305 @@
# 08 - C 语言操作
Viper 通过 `c` 模块提供对 C 语言底层特性的访问,包括指针操作、内联汇编、预处理指令等。
## 指针操作
### 取地址 `c.Addr`
```python
x: t.CInt = 42
p: t.CInt | t.CPtr = c.Addr(x) # int* p = &x;
```
对结构体成员取地址:
```python
res: t.CInt = fat32.opendir("/", c.Addr(dp)) # res = fat32_opendir("/", &dp);
```
对数组取地址:
```python
viperlib.snprintf(c.Addr(buf), 64, "hello %d", 42) # snprintf(&buf, 64, "hello %d", 42);
```
### 解引用 `c.Deref`
```python
ptr: Sheet | t.CPtr = c.Addr(sheet_obj)
obj: Sheet = c.Deref(ptr) # struct Sheet obj = *ptr;
```
### 内存拷贝 `c.Load`
```python
c.Load(ptr, value) # *ptr = *value;
```
### 解引用赋值 `c.DerefAs`
```python
c.DerefAs(ptr, value) # *ptr = value;
```
### 赋值 `c.Set`
```python
c.Set(target, value) # target = value;
```
## 内联汇编 `c.Asm`
Viper 提供了声明式的内联汇编语法,编译为 GCC 风格的 `__asm__ __volatile__` 语句。
### 基本用法
```python
c.Asm("nop") # __asm__ __volatile__("nop");
```
### 带操作数的汇编
使用 f-string 和 `c.AsmInp` / `c.AsmOut` 标记操作数:
```python
saved_rdi: t.CUnsignedLong
c.Asm(f"mov {c.AsmOut(saved_rdi, t.ASM_DESCR.OUTPUT_REG)}, rdi",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RDI])
```
等价 C 代码:
```c
__asm__ __volatile__(
"mov %0, rdi"
: "=r"(saved_rdi)
:
: "memory", "rdi"
);
```
### 输入操作数 `c.AsmInp`
```python
msg: t.CConst | t.CChar | t.CPtr = "Hello"
c.Asm(f"mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
```
### 完整示例
```python
c.Asm(f"""mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}
call {c.AsmInp(log_info_fn, t.ASM_DESCR.REG_ANY)}""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
```
### ASM_DESCR 约束字符
#### 操作数修饰符
| 常量 | 值 | 说明 |
|------|----|------|
| `MODIFIER_OUTPUT` | `=` | 输出操作数 |
| `MODIFIER_READWRITE` | `+` | 读写操作数 |
| `MODIFIER_INPUT` | `` | 输入操作数(默认) |
| `MODIFIER_GLOBAL` | `&` | 全局操作数 |
#### 寄存器约束
| 常量 | 值 | 说明 |
|------|----|------|
| `REG_ANY` | `r` | 任何通用寄存器 |
| `REG_EAX` / `REG_RAX` | `a` | EAX/RAX |
| `REG_EBX` / `REG_RBX` | `b` | EBX/RBX |
| `REG_ECX` / `REG_RCX` | `c` | ECX/RCX |
| `REG_EDX` / `REG_RDX` | `d` | EDX/RDX |
| `REG_ESI` / `REG_RSI` | `S` | ESI/RSI |
| `REG_EDI` / `REG_RDI` | `D` | EDI/RDI |
| `REG_XMM` | `x` | XMM 寄存器 |
#### 内存与立即数
| 常量 | 值 | 说明 |
|------|----|------|
| `MEMORY` | `m` | 内存操作数 |
| `IMMEDIATE` | `i` | 立即数 |
| `ANY` | `g` | 通用寄存器/内存/立即数 |
#### 预定义组合约束
| 常量 | 值 | 说明 |
|------|----|------|
| `OUTPUT_REG` | `=r` | 输出,通用寄存器 |
| `OUTPUT_MEM` | `=m` | 输出,内存 |
| `OUTPUT_EAX` | `=a` | 输出,EAX/RAX |
| `INPUT_REG` | `r` | 输入,通用寄存器 |
| `INPUT_MEM` | `m` | 输入,内存 |
| `INPUT_IMM` | `i` | 输入,立即数 |
#### 破坏描述符
| 常量 | 说明 |
|------|------|
| `CLOBBER_EAX` / `CLOBBER_RAX` | 破坏 EAX/RAX |
| `CLOBBER_EBX` / `CLOBBER_RBX` | 破坏 EBX/RBX |
| `CLOBBER_ECX` / `CLOBBER_RCX` | 破坏 ECX/RCX |
| `CLOBBER_EDX` / `CLOBBER_RDX` | 破坏 EDX/RDX |
| `CLOBBER_ESI` / `CLOBBER_RSI` | 破坏 ESI/RSI |
| `CLOBBER_EDI` / `CLOBBER_RDI` | 破坏 EDI/RDI |
| `CLOBBER_CC` | 破坏条件码(标志寄存器) |
| `CLOBBER_MEMORY` | 破坏内存 |
| `CLOBBER_R8` ~ `CLOBBER_R15` | 破坏 R8~R15 |
## 预处理指令
Viper 通过 `c` 模块的函数调用实现 C 预处理指令。
### #define
```python
c.CDefine("MAX_SIZE", 1024) # #define MAX_SIZE 1024
```
上述方法容易引起未定义行为,至少是不便于理解,更常用的方式是使用类型注解:
```python
MAX_SIZE: t.CDefine = 1024 # #define MAX_SIZE 1024
```
### 条件编译
```python
c.CIfndef(HEADER_H) # #ifndef HEADER_H
c.CDefine(HEADER_H) # #define HEADER_H
c.CEndif() # #endif
```
```python
c.CIfdef(DEBUG) # #ifdef DEBUG
c.CEndif() # #endif
```
```python
c.CIf(VERSION > 2) # #if VERSION > 2
c.CElif(VERSION > 1) # #elif VERSION > 1
c.CElse() # #else
c.CEndif() # #endif
```
### #undef
```python
c.Undef(MACRO_NAME) # #undef MACRO_NAME
```
### #error
```python
c.CError("Platform not supported") # #error "Platform not supported"
```
### #pragma
> 此关键字已不再支持
```python
c.CPragma("GCC diagnostic push") # #pragma GCC diagnostic push
```
### ## 连接符
> 此方法可能已经不再支持
```python
c.TokenPast("PREFIX_", "NAME") # PREFIX_ ## NAME
```
由于编译器设计,以及不需要像 `C` 一样做大量字符替换,对于上述所有的条件宏都远比 `C` 差,实际工程中不推荐使用条件宏,后期将完善宏的设计,引入模板等。
## FFI 外部函数声明
Viper 的 FFI(Foreign Function Interface)**无需任何特殊语法**——仅靠返回类型注解中的 `t.State` 标记即可声明外部函数。`t.State` 是声明性标记类型,表示"仅声明不定义"(语义等价于 `t.CExport | t.CExtern`),编译器只生成 `declare` 原型,不生成 `define` 函数体,由链接器在外部库中解析符号。
### 基本声明
函数体为 `pass`,返回类型用 `RetType | t.State`:
```python
def isr0() -> t.CVoid | t.State: pass # extern void isr0(); — 来自汇编或外部
def getchar() -> t.CInt | t.State: pass # extern int getchar(); — 来自 libc
```
编译为 LLVM IR:
```llvm
declare void @"isr0"() ; 不加 SHA1 前缀(t.State 含 CExport 语义)
declare i32 @"getchar"()
```
### Win32 API 绑定示例
[includes/w32](../includes/w32) 中的 Win32 绑定全部通过 `t.State` 声明,零特殊语法:
```python
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
dwShareMode: ULONG,
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
dwCreationDisposition: ULONG,
dwFlagsAndAttributes: ULONG,
hTemplateFile: HANDLE) -> HANDLE | t.State:
pass
```
等价 C 声明:
```c
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*,
ULONG, ULONG, HANDLE);
```
链接时通过 `project.json` 指定外部库(如 `kernel32.lib`、`user32.lib`),链接器解析这些符号。
### `t.State` 与 `t.CExtern` 的区别
| 修饰 | SHA1 前缀 | 生成内容 | 用途 |
|------|----------|---------|------|
| 无修饰 | ✅ 加前缀 | `declare` + `define` | 模块私有函数 |
| `t.CExtern` | ❌ 不加 | `declare`(引用外部) | 引用外部 C 函数 |
| `t.CExport` | ❌ 不加 | `declare` + `define` | 对外暴露 API |
| `t.State` | ❌ 不加 | 仅 `declare` | FFI 外部函数声明(= `CExport \| CExtern`) |
### 声明外部全局变量
外部全局变量通过 `t.CExtern` 注解声明:
```python
errno: t.CExtern | t.CInt # extern int errno;
```
详见 [02-type-system.md 的 t.State 章节](02-type-system.md#tstate-声明性标记) 和 [includes/w32](../includes/w32) 的 Win32 绑定实现。
## LLVM IR 内联
### c.LLVMIR
直接嵌入原始 LLVM IR 文本来实现高效且跨平台的汇编操作(无返回值、不做指令解析):
```python
c.LLVMIR(f"{c.LOut(r)} = add i32 {c.LInp(a)}, {c.LInp(b)}")
```
### c.LInp / c.LOut
标记 LLVM IR 的输入/输出操作数:
```python
c.LInp(expr) # 输入操作数:替换为 expr 的 LLVM 值引用文本(%x 或常量文本)
c.LOut(expr) # 输出操作数:为 expr 变量分配唯一 SSA 寄存器 %llvmirN,
# 模板嵌入后自动追加 store 写回变量 alloca
```
## 运算符重载
`@t.Object` 类支持运算符重载,详见 [06-oop.md 中 运算符重载](06-oop.md#运算符重载)。
+186
View File
@@ -0,0 +1,186 @@
# 09 - 异常处理
Viper 支持 Python 风格的 `try/except/finally` 异常处理,编译为基于 jmp_buf 和错误码的 C 级别异常处理机制。
## 异常处理机制
Viper 的异常处理不使用 C++ 风格的零开销异常,而是通过函数参数传递错误码和错误消息实现。编译器自动为可能抛出异常的函数添加 `__eh_msg_out__` 和 `__eh_code_out__` 参数。
## try / except
```python
try:
result = risky_operation()
except ValueError:
serial.puts("ValueError occurred\n")
except OSError:
serial.puts("OSError occurred\n")
except Exception:
serial.puts("Unknown error\n")
```
### 内置异常类型
Viper 预定义了以下异常类型及其错误码:
| 异常类型 | 错误码 |
|---------|--------|
| `ValueError` | 1 |
| `TypeError` | 2 |
| `RuntimeError` | 3 |
| `ZeroDivisionError` | 4 |
| `IndexError` | 5 |
| `KeyError` | 6 |
| `IOError` | 7 |
| `OSError` | 8 |
| `AssertionError` | 9 |
| `Exception` | 99 |
### 自定义异常
```python
class DiskFullError(Exception):
pass
class ReadOnlyError(DiskFullError):
pass
```
自定义异常会自动分配递增的错误码(从 100 开始),并支持继承匹配。
### 多异常捕获
```python
try:
result = operation()
except (ValueError, TypeError):
serial.puts("Value or Type error\n")
```
### 获取异常信息
```python
try:
result = operation()
except Exception as e:
serial.puts("Error occurred\n")
```
## try / finally
```python
try:
result = operation()
finally:
cleanup()
```
`finally` 块无论是否发生异常都会执行。
## try / except / finally
```python
try:
result = operation()
except ValueError:
handle_value_error()
finally:
cleanup()
```
## raise 语句
### 抛出异常
```python
raise ValueError("invalid value")
```
### 抛出自定义异常
```python
raise MyCustomError("something went wrong")
```
### 重新抛出异常
```python
raise
```
### StopIteration
`StopIteration` 有特殊处理,用于迭代器协议:
```python
raise StopIteration
```
## 异常处理的编译实现
### 函数签名变换
可能抛出异常的函数会自动添加错误输出参数:
```python
def risky_operation() -> t.CInt:
if error:
raise ValueError("bad value")
return 0
```
编译后等价于:
```c
int risky_operation(char** __eh_msg_out__, int* __eh_code_out__) {
if (error) {
*__eh_code_out__ = 1; // ValueError code
*__eh_msg_out__ = "bad value";
return 0;
}
return 0;
}
```
### 调用点变换
```python
try:
result = risky_operation()
except ValueError:
handle_error()
```
编译后等价于:
```c
char* __eh_msg = NULL;
int __eh_code = 0;
int result = risky_operation(&__eh_msg, &__eh_code);
if (__eh_msg != NULL) {
if (__eh_code == 1) { // ValueError
handle_error();
}
}
```
## 异常继承匹配
异常匹配支持继承体系。捕获父类异常时,子类异常也会被匹配:
```python
class FileSystemError(Exception):
pass
class FileNotFoundError(FileSystemError):
pass
class PermissionError(FileSystemError):
pass
try:
operation()
except FileSystemError:
# 也会捕获 FileNotFoundError 和 PermissionError
handle_fs_error()
```
+258
View File
@@ -0,0 +1,258 @@
# 10 - 模块与导入系统
Viper 的模块系统基于 Python 的 `import` 语法,但编译时通过 TransPyC 的符号表和类型系统实现跨模块的类型解析。
## 导入语法
### 标准导入
```python
import t, c # Viper 内置类型和操作模块
import asm # 汇编辅助模块
import string # 字符串操作模块
```
### 模块导入
```python
import bootinfo
import intr
import platform.pch as pch
import drivers.serial.uart.serial as serial
```
### from 导入
```python
from stdint import * # 导入所有 stdint 类型别名
from drivers.video.vesafb.vga import _VGAScreenDriver
```
### 相对导入
```python
from . import submodule
from .. import parent_module
```
## 特殊模块
### `t` 模块
类型定义模块,提供所有 C 类型对应的 Viper 类型。每个 Viper 文件通常都需要导入:
```python
import t
```
### `c` 模块
C 语言操作模块,提供指针操作、内联汇编、预处理指令等:
```python
import c
```
### `asm` 模块
汇编辅助模块,提供常用汇编指令的封装:
```python
import asm
asm.sti() # __asm__ __volatile__("sti");
asm.hlt() # __asm__ __volatile__("hlt");
asm.cli() # __asm__ __volatile__("cli");
asm.nop() # __asm__ __volatile__("nop");
asm.BSSClean() # 清零 BSS 段
```
### `string` 模块
字符串/内存操作模块,对应 C 标准库的 `string.h`:
> string 部分使用 x86_64 专有汇编进行加速,因此某些函数在其它架构可能需要手动重新编写。
```python
import string
string.memset(c.Addr(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);
string.strcmp(a, b) # strcmp(a, b);
string.strcpy(dst, src) # strcpy(dst, src);
```
### `stdint` 模块
提供 C `stdint.h` 中的类型别名:
```python
from stdint import *
# 提供以下类型别名:
# UINT8PTR, UINT16PTR, UINT32PTR, UINT64PTR
# INT8PTR, INT16PTR, INT32PTR, INT64PTR
# 等等
```
### `viperlib` 模块
ViperOS 标准库,提供常用 C 库函数:
```python
import viperlib
viperlib.snprintf(c.Addr(buf), 64, "value=%d", 42) # snprintf(&buf, 64, "value=%d", 42);
```
### `vpsdk` 模块
> vpsdk 模块 专供于基于 Viper 开发的示例操作系统 ViperOS,不可用于其它平台,且需要特殊配置。
ViperOS 应用 SDK,提供系统调用和窗口管理:
```python
import vpsdk.window as window
import vpsdk.process as process
import vpsdk.dynlib as dynlib
import vpsdk.syscall as syscall
```
### `ast` 模块(自研解析器)
[includes/ast](../includes/ast) 是**完全自研的 Python 解析器**(词法器 + 语法器 + AST 构造,约 4176 LOC),覆盖 CPython 几乎全部语法。这是向 `TransPyV` 完全自举的关键组件——自举后编译器不再依赖 Python 运行时解析源文件。
```python
from ast import parse, parse_expression
root: AST | t.CPtr = parse(src, pool) # 解析整个源文件
expr: AST | t.CPtr = parse_expression(src, pool) # 解析单个表达式
```
AST 采用**胖节点**设计:70+ 变体共用一个 struct,用 `vtype`(`ASTVType` 枚举)区分类型,通用字段直接属性访问。详见 [13-bootstrapping.md](13-bootstrapping.md) 和 [Test/AstTest](../Test/AstTest)。
### `llvmlite` 模块(自研 IR 生成库)
[includes/llvmlite](../includes/llvmlite) 是**完全自研的 LLVM IR 文本生成库**(约 1500 LOC),封装类型/值/模块/函数/基本块/构造器的创建与打印。同样是自举的关键组件。
```python
from llvmlite import LLVMTypeCore, LLVMModuleCore
type_core: LLVMTypeCore = LLVMTypeCore(pool)
i32: LLVMType | t.CPtr = type_core.Int32()
ptr_ty: LLVMType | t.CPtr = type_core.Ptr(i32)
mod_core: LLVMModuleCore = LLVMModuleCore(pool)
mod: Module | t.CPtr = mod_core.NewModule("main")
func: Function | t.CPtr = mod_core.CreateFunction(mod, "main", ret_ty)
```
类型系统用 `t.REnum`(`LLVMType`,少变体适合 match),指令 opcode 用 `t.CEnum`(`IROp`),对象分配用普通类 + `mpool`。详见 [includes/llvmlite/README.md](../includes/llvmlite/README.md) 的选型论证。
### `linkedlist` 模块(NoVTable + PEP 695 泛型示范)
[includes/linkedlist](../includes/linkedlist.vp) 提供链表实现,是 `@t.NoVTable` 非多态继承和 PEP 695 递归泛型继承的典范用例:
```python
from linkedlist import LinkedNode, GSList
@t.NoVTable
class Node(LinkedNode): # 非多态继承,零 vtable 开销
value: t.CInt
lst: GSList[Node] | t.CPtr = GSList[Node]()
node: Node | t.CPtr = Node()
node.value = 42
lst.append(node) # 类型安全:append 只接受 Node
```
包含 `LinkedNode`/`SListNode`(双向/单向节点)和 `GSList[T]`(泛型链表,支持递归泛型继承 `class GNode(GSListNode[GNode])`)。详见 [05-classes.md 的泛型类章节](05-classes.md#泛型类pep-695-语法) 和 [06-oop.md 的 NoVTable 章节](06-oop.md#tnovtable-非多态继承)。
### `w32` 模块(Win32 FFI 绑定)
[includes/w32](../includes/w32) 提供 Win32 API 绑定,全部通过 `t.State` 的 FFI 机制声明(零特殊语法):
```python
from w32.win32file import CreateFileA, ReadFile, WriteFile
from w32.win32console import WriteConsole
from w32.win32memory import HeapAlloc, HeapFree
from w32.win32process import CreateProcess
from w32.win32sync import CreateMutex, WaitForSingleObject
handle: HANDLE = CreateFileA("test.txt", GENERIC_READ, FILE_SHARE_READ,
None, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, None)
```
子模块:`win32base`(基础类型/常量)、`win32file`(文件 IO)、`win32console`(控制台)、`win32memory`(内存)、`win32process`(进程/线程)、`win32sync`(同步对象)、`fileio`(高层文件操作封装)。详见 [08-c-operations.md 的 FFI 章节](08-c-operations.md#ffi-外部函数声明)。
## 模块解析流程
### 编译时模块加载
Viper 的 `import` 是编译时操作,不涉及运行时模块加载。完整的模块解析流程与两阶段编译紧密耦合:
1. **阶段一**:从入口文件出发,通过 `import` 语句递归发现所有可达的源文件
2. **阶段一**:对每个源文件生成 `<SHA1>.vpi` 签名存根和 `<SHA1>.stub.ll` LLVM IR 声明
3. **阶段二**:加载所有 `.vpi` 和 `.stub.ll`,构建共享符号表
4. **阶段二**:翻译源文件时,通过符号表解析跨模块的类型和函数引用
5. **阶段二**:将被引用模块的 `.stub.ll` 声明嵌入到生成的 `.ll` 文件头部
### 类型存根文件(.vpi)
`.vpi` 文件是模块的声明接口,由 `PythonToStubConverter` 在阶段一生成。存根文件的内容规则:
| 元素 | 存根表示 |
|------|---------|
| `t.CDefine` 常量 | 保留完整定义(含赋值) |
| `t.CDefine` 函数 | 保留完整函数体 |
| 普通函数 | 仅签名 + `t.State` |
| 全局变量 | 添加 `t.CExtern` |
| 类定义 | 保留成员类型注解和方法签名 |
存根文件还自动添加宏守卫(`c.CIfndef`/`c.CEndif()`),防止重复包含。
### SHA1 命名空间与导入
每个模块的函数和结构体在 LLVM IR 中自动加上 SHA1 前缀,消除跨模块符号冲突。只有标记为 `t.CExport` 的函数保持原始名称。详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。
### 符号表
所有模块的类型信息统一存储在 `SymbolTable` 中,包含:
- 结构体/联合体/枚举定义
- 函数签名
- 全局变量
- typedef 别名
- `t.CDefine` 常量
### 跨模块类型引用
```python
# 在 fat32_types.vp 中定义
class fat32_fileinfo:
fname: list[t.CChar, 13]
attr: t.CUInt8T
file_size: t.CUInt32T
# 在其他模块中引用
import fat32_types
info: fat32_types.fat32_fileinfo
```
跨模块引用的结构体在 LLVM IR 中使用 SHA1 前缀的类型名:
```llvm
%"<SHA1_of_fat32_types>.fat32_fileinfo" = type { [13 x i8], i8, i32 }
```
## 项目 includes 配置
在 `project.json` 中配置模块搜索路径:
```json
{
"includes": ["../.."],
"source_dir": "./Kernel"
}
```
编译器会在 `includes` 指定的目录中搜索导入的模块。
+204
View File
@@ -0,0 +1,204 @@
# 11 - 内置函数与运算符
Viper 支持 Python 内置函数和运算符,编译为对应的 C/LLVM 操作。
## 内置函数
### print
```python
print("Hello, World!") # 调用 puts 或 printf
print(x, y) # 多参数输出
```
### len
```python
n: t.CInt = len(array) # 获取数组长度(编译时常量)
```
### sizeof
```python
s: t.CSizeT = sizeof(t.CInt) # sizeof(int)
s: t.CSizeT = obj.__sizeof__() # sizeof(obj)
```
### abs
```python
a: t.CInt = abs(x) # abs(x)
```
### int / float / bool
```python
i: t.CInt = int(3.14) # (int)3.14
f: t.CFloat = float(42) # (float)42
b: t.CBool = bool(x) # x != 0
```
### min / max
```python
m: t.CInt = min(a, b) # a < b ? a : b
m: t.CInt = max(a, b) # a > b ? a : b
```
### chr / ord
```python
c: t.CChar = chr(65) # 'A' (整数转字符)
n: t.CInt = ord('A') # 65 (字符转整数)
```
### range
用于 `for` 循环:
```python
for i in range(10): pass # for (int i = 0; i < 10; i++)
for i in range(5, 10): pass # for (int i = 5; i < 10; i++)
for i in range(0, 10, 2): pass # for (int i = 0; i < 10; i += 2)
```
## 运算符
### 算术运算符
| Viper | C 等价 | 说明 |
|-------|--------|------|
| `a + b` | `a + b` | 加法 |
| `a - b` | `a - b` | 减法 |
| `a * b` | `a * b` | 乘法 |
| `a / b` | `a / b` | 浮点除法 |
| `a // b` | `a / b` | 整数除法 |
| `a % b` | `a % b` | 取模 |
| `a ** b` | `pow(a, b)` | 幂运算 |
| `-a` | `-a` | 取负 |
| `+a` | `+a` | 正号 |
### 位运算符
| Viper | C 等价 | 说明 |
|-------|--------|------|
| `a & b` | `a & b` | 按位与 |
| `a \| b` | `a \| b` | 按位或 |
| `a ^ b` | `a ^ b` | 按位异或 |
| `~a` | `~a` | 按位取反 |
| `a << n` | `a << n` | 左移 |
| `a >> n` | `a >> n` | 右移 |
> **注意**:`|` 运算符在类型注解中表示类型组合,在表达式中表示按位或。编译器根据上下文区分。
### 比较运算符
| Viper | C 等价 | 说明 |
|-------|--------|------|
| `a == b` | `a == b` | 等于 |
| `a != b` | `a != b` | 不等于 |
| `a < b` | `a < b` | 小于 |
| `a > b` | `a > b` | 大于 |
| `a <= b` | `a <= b` | 小于等于 |
| `a >= b` | `a >= b` | 大于等于 |
### 逻辑运算符
| Viper | C 等价 | 说明 |
|-------|--------|------|
| `a and b` | `a && b` | 逻辑与 |
| `a or b` | `a \|\| b` | 逻辑或 |
| `not a` | `!a` | 逻辑非 |
### 增强赋值运算符
| Viper | C 等价 |
|-------|--------|
| `a += b` | `a += b` |
| `a -= b` | `a -= b` |
| `a *= b` | `a *= b` |
| `a //= b` | `a /= b` |
| `a %= b` | `a %= b` |
| `a <<= b` | `a <<= b` |
| `a >>= b` | `a >>= b` |
| `a &= b` | `a &= b` |
| `a \|= b` | `a \|= b` |
| `a ^= b` | `a ^= b` |
## 类型自动转换
Viper 在算术运算中自动进行类型提升:
1. **整数宽度提升**:较窄的整数类型自动扩展为较宽的类型
- 有符号类型使用符号扩展(`sext`)
- 无符号类型使用零扩展(`zext`)
2. **整数到浮点**:整数与浮点数运算时,整数自动转换为浮点
3. **浮点精度提升**:`float` 与 `double` 运算时,`float` 提升为 `double`
## 指针算术
指针与整数的加法支持自动计算偏移:
```python
buf: t.CUInt32T | t.CPtr
buf[i] = value # buf[i] = value; (自动计算偏移)
```
## 数组下标
```python
buf: list[t.CChar, 64]
buf[0] = 'H' # buf[0] = 'H';
buf[i] = value # buf[i] = value;
info.fname[0] # info.fname[0] (结构体数组成员访问)
```
## 成员访问
```python
obj.x # obj.x (直接成员)
obj.method() # obj.method() (方法调用)
ptr.x # ptr->x (指针自动解引用)
```
## 字符串字面量
字符串字面量编译为 C 字符串常量(`const char*`),其为只读:
```python
serial.puts("Hello") # 传递 const char*
```
### 字符串索引
```python
msg: t.CConst | str = "Hello"
serial.puts(c.Addr(msg[4])) # 访问 msg[4] 的地址,输出 'o'
```
## C 标准库函数
Viper 内置支持以下 C 标准库函数的直接调用:
| 函数 | 签名 |
|------|------|
| `strlen(s)` | `size_t strlen(const char* s)` |
| `memset(s, c, n)` | `void* memset(void* s, int c, size_t n)` |
| `memcpy(d, s, n)` | `void* memcpy(void* d, const void* s, size_t n)` |
| `memmove(d, s, n)` | `void* memmove(void* d, const void* s, size_t n)` |
| `memcmp(a, b, n)` | `int memcmp(const void* a, const void* b, size_t n)` |
| `strcmp(a, b)` | `int strcmp(const char* a, const char* b)` |
| `strncmp(a, b, n)` | `int strncmp(const char* a, const char* b, size_t n)` |
| `strcpy(d, s)` | `char* strcpy(char* d, const char* s)` |
| `strncpy(d, s, n)` | `char* strncpy(char* d, const char* s, size_t n)` |
| `strcat(d, s)` | `char* strcat(char* d, const char* s)` |
| `puts(s)` | `int puts(const char* s)` |
| `atoi(s)` | `int atoi(const char* s)` |
| `atol(s)` | `long atol(const char* s)` |
| `abs(x)` | `int abs(int x)` |
| `labs(x)` | `long labs(long x)` |
部分模式比如裸机没有 libc,则不能使用这些函数,即使环境满足,Viper 标准库也可能和 C 标准库冲突。
+252
View File
@@ -0,0 +1,252 @@
# 12 - 项目配置与构建
Viper 项目使用 `project.json` 配置文件管理编译和链接参数。
## project.json 结构
```json
{
"name": "Kernel",
"version": "1.0.0",
"source_dir": "./Kernel",
"temp_dir": "./temp",
"output_dir": "./output",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "kernel.bin"
},
"includes": ["../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
```
## 配置项说明
### 基本信息字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | string | 项目名称 |
| `version` | string | 项目版本 |
| `source_dir` | string | 源代码目录(相对于 project.json 所在目录) |
| `temp_dir` | string | 临时文件目录(存放 .vpi 存根等) |
| `output_dir` | string | 输出文件目录 |
### 编译器配置
| 字段 | 类型 | 说明 |
|------|------|------|
| `compiler.cmd` | string | 编译器命令(通常为 `llc`) |
| `compiler.flags` | string[] | 编译器标志 |
常用 `llc` 标志:
- `-filetype=obj`:输出目标文件
- `-mtriple=x86_64-none-elf`:目标三元组
- `-relocation-model=static`:静态重定位
- `-O2`:优化级别
### 链接器配置
| 字段 | 类型 | 说明 |
|------|------|------|
| `linker.cmd` | string | 链接器命令(通常为 `ld.lld.exe`) |
| `linker.flags` | string[] | 链接器标志 |
| `linker.output` | string | 输出文件名 |
常用 `ld.lld` 标志:
- `-m elf_x86_64`:ELF x86_64 格式
- `-T linker.ld`:链接脚本
- `--oformat elf64-x86-64`:输出 ELF 格式
- `--oformat binary`:输出原始二进制(用于内核)
- `--strip-all`:去除所有符号信息
### 目标平台配置
| 字段 | 类型 | 说明 |
|------|------|------|
| `target.triple` | string | LLVM 目标三元组 |
| `target.datalayout` | string | LLVM 数据布局字符串 |
默认值:
- triple: `x86_64-none-elf`
- datalayout: `e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`
### 包含路径
| 字段 | 类型 | 说明 |
|------|------|------|
| `includes` | string[] | 模块搜索路径列表 |
### 编译选项
| 字段 | 类型 | 说明 |
|------|------|------|
| `options.slice_level` | int | 切片优化级别(0-3) |
| `options.target` | string | 编译目标(目前仅支持 `llvm`) |
| `options.strict_mode` | bool | 严格模式 |
## 切片优化级别
| 级别 | 名称 | 说明 |
|------|------|------|
| `0` | `no_optimize` | 无优化 |
| `1` | `conservative` | 保守优化 |
| `2` | `moderate` | 适度优化 |
| `3` | `aggressive` | 激进优化(默认) |
| `s` | `size` | 体积优化 |
## 项目结构示例
### 内核项目
```
VKernel/
├── project.json # 项目配置
├── linker.ld # 链接脚本
├── Kernel/ # 源代码目录
│ ├── main.vp # 内核入口
│ ├── bootinfo.vp
│ ├── intr/
│ │ ├── gdt.vp
│ │ ├── idt.vp
│ │ └── syscall.vp
│ ├── drivers/
│ │ ├── serial/
│ │ ├── video/
│ │ ├── storage/
│ │ └── ...
│ └── ...
├── temp/ # 临时文件(自动生成)
│ ├── <SHA1>.vpi # 签名存根(阶段一生成)
│ ├── <SHA1>.stub.ll # LLVM IR 声明(阶段一生成)
│ └── _sha1_map.txt # SHA1→源文件路径映射
└── output/ # 输出文件(自动生成)
├── kernel.bin # 内核二进制
├── *.deps.json # 依赖信息
└── linker.ld # 生成的链接脚本
```
### 应用项目
```
HelloWorld/
├── project.json # 项目配置
├── linker.ld # 链接脚本
├── main.vp # 应用入口
├── temp/ # 临时文件
└── output/ # 输出文件
└── helloworld.elf # ELF 可执行文件
```
### 库项目
```
SerialLogger/
├── project.json # 项目配置
├── linker.ld # 链接脚本
├── serial_logger.vp # 库源码
├── temp/ # 临时文件
└── output/ # 输出文件
```
## 链接脚本
ViperOS 使用自定义链接脚本控制内存布局:
```ld
ENTRY(_start)
SECTIONS
{
. = 0x100000; /* 加载地址 */
.text : {
*(.text.startup)
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
*(COMMON)
}
}
```
## 编译命令
TransPyC 编译器的基本用法(注:适用于单个文件,而不适用于工程):
```bash
python TransPyC.py -f InputFile -o OutputFile [options]
```
### 命令行参数
| 参数 | 说明 |
|------|------|
| `-f` | 输入文件路径 |
| `-o` | 输出文件路径 |
| `-wh` | 头文件列表 |
| `-debug` | 调试输出文件 |
| `-cc` | 编译命令 |
| `-cflags` | 编译标志 |
| `-run` | 编译后运行 |
| `-args` | 运行参数 |
| `-h` | 辅助文件(C 或 Python) |
工程编译使用:
```bash
python Projectrans.py xxx.json
```
即可
## 增量编译
TransPyC 支持基于 SHA1 的增量编译,与两阶段编译模型紧密耦合:
### 阶段一增量
- 每个源文件按内容计算 SHA1 哈希(16位)
- 如果 `<SHA1>.vpi` 和 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)
- 只有源文件内容变化导致 SHA1 变化时才重新生成声明接口
- `temp/_sha1_map.txt` 记录 SHA1 → 源文件路径的映射,格式为 `<sha1>:<relative_path>`
### 阶段二增量
- 阶段二加载 `temp/_sha1_map.txt` 重建 SHA1 映射
- 过滤掉不在当前 SHA1 映射中的旧 `.vpi` 和 `.stub.ll` 文件
- 共享符号表一次性构建,避免每个文件重复加载
### SHA1 命名空间与增量编译的协同
SHA1 同时服务于命名空间隔离和增量编译:
- **命名空间**:非 `t.CExport` 函数和结构体使用 SHA1 前缀,消除符号冲突
- **增量编译**:SHA1 不变则缓存命中,SHA1 变化则重新生成
详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。
+252
View File
@@ -0,0 +1,252 @@
# TransPyC 自举可行性分析
> **状态**:进行中
> **创建时间**:2026-06-26
> **最后更新**:2026-06-30
> **结论**:完全自举可行,核心障碍已被攻克,剩余工作明确
---
## 1. 背景
自举(bootstrapping)指用 TransPyC 语言重写 TransPyC 编译器自身,然后用现有编译器编译,得到新的原生编译器。本文档分析自举的可行性、进展与剩余工作。
**当前目标**:完全自举。TransPyC 的定位是独立的系统级语言,不是"Python → 原生 的工具"。自举是语言成熟度的证明,也是脱离 Python 运行时性能限制的必经之路。
---
## 2. LOC 统计
### 2.1 编译器主体(lib 目录)
| 子目录 | 文件数 | LOC | 说明 |
|--------|--------|-----|------|
| `lib/core` | 64 | 28,610 | 编译器核心 |
| ├─ `core/Handles` | 30 | 18,885 | AST 节点处理器(最大模块) |
| ├─ `core/LLVMCG` | 8 | 2,782 | LLVM 代码生成 |
| └─ `core/Translator` | 6 | 1,751 | 翻译器框架 |
| `lib/Projectrans` | 6 | 4,371 | 项目级翻译(Phase1/Phase2) |
| `lib/StubGen` | 4 | 1,187 | Stub 生成器 |
| `lib/Shell` | 4 | 630 | CLI 入口 |
| `lib/includes` | 2 | 1,609 | t.py(968) + c.py(641) 类型定义 |
| `lib/utils` | 1 | 40 | 辅助 |
| `lib/constants` | 1 | 36 | 配置 |
| **lib 总计** | **80** | **36,483** | |
### 2.2 Top 15 文件
| 文件 | LOC |
|------|-----|
| `lib/core/Handles/HandlesExprCall.py` | 3,153 |
| `lib/Projectrans/Phase2Translator.py` | 2,693 |
| `lib/core/Handles/HandlesImports.py` | 1,994 |
| `lib/core/Handles/HandlesFunctions.py` | 1,612 |
| `lib/core/Handles/HandlesAssign.py` | 1,544 |
| `lib/core/Handles/HandlesExprAttr.py` | 1,168 |
| `lib/core/Handles/HandlesClassDef.py` | 1,039 |
| `lib/core/Handles/HandlesFor.py` | 997 |
| `lib/core/Handles/HandlesTypeMerge.py` | 991 |
| `lib/includes/t.py` | 968 |
| `lib/StubGen/Converter.py` | 893 |
| `lib/core/Handles/HandlesExprBuiltin.py` | 868 |
| `lib/Projectrans/DeclarationGenerator.py` | 845 |
| `lib/core/Handles/HandlesBase.py` | 756 |
| `lib/core/stub_generator.py` | 699 |
### 2.3 运行时库(includes 目录)
| 模块 | LOC | 说明 |
|------|-----|------|
| `includes/numpy/__init__.vp` | 1,016 | ndarray n 维数组 |
| `includes/zlib/` | ~1,900 | 完整 deflate/inflate/huffman/checksum |
| `includes/ast/` | 4,176 | **自研 Python 解析器(自举组件)** |
| `includes/llvmlite/` | ~1,500 | **自研 LLVM IR 生成库(自举组件)** |
| `includes/linkedlist.vp` | ~400 | 泛型链表(@t.NoVTable + PEP 695) |
| `includes/vipermath.vp` | 450 | 数学库 |
| ...(30+ 文件) | ... | mpool/mbuddy/string/stdio/json/w32 等 |
| **includes 总计** | **~12,000** | |
### 2.4 全项目总计
```
全项目总计:~48,000 LOC
```
**自举相关核心数字:**
- 编译器主体(lib 不含 t.py/c.py):~34,874 LOC
- 自举基础组件(includes/ast + includes/llvmlite):~5,700 LOC ✅ 已完成
- 运行时库(includes 其余):~9,500 LOC
---
## 3. 自举进展(已完成的核心组件)
### 3.1 ✅ includes/ast —— Python 解析器自举
**状态**:完成,AstTest 通过(解析 CPython 几乎全部语法)
| 文件 | LOC | 功能 |
|------|-----|------|
| `__tokens.vp` | ~600 | TokenType/Keyword/TokOp 枚举 + 查找表 |
| `__nodes.vp` | ~1,400 | AST 胖节点 + ASTVType/ASTCtx/OpKind CEnum |
| `__lexer.vp` | ~800 | 词法分析器 |
| `__parser.vp` | 4,176(含表格) | 递归下降语法分析器 |
**覆盖语法**:装饰器、async/await、match(含复杂模式 `[x,*rest]`/`{'type':t,**rest}`/`Point(x,y)`/`0|1|2`)、try/except/finally/raise from、with、lambda、列表/集合/字典/生成器推导式、f-string(含 `!r`/`!s` 转换和 `:>{width}` 格式说明)、链式比较、walrus、yield from、global/nonlocal、切片、嵌套三元。
**设计要点**:
- 胖节点设计(所有节点共用一个 `AST` struct,用 `vtype` 区分)—— 避免 REnum 在 70+ 变体下的内存膨胀
- 从 `mpool.MPool` 分配,零 Python 运行时依赖
- API 与 Python `ast` 模块对齐:`parse(src, pool) -> AST`
### 3.2 ✅ includes/llvmlite —— LLVM IR 生成库自举
**状态**:完成,LLvmLiteTest 18/18 通过,TransPyV 自举实验跑通
| 文件 | 功能 |
|------|------|
| `__types.vp` | `LLVMType` REnum + 类型构造/打印 |
| `__values.vp` | Value/Constant/SSA 值表示 |
| `__function.vp` | Function + BasicBlock + 参数管理 |
| `__module.vp` | Module 容器 + 目标三元组 + 输出 .ll |
| `__builder.vp` | IRBuilder(指令发射 + SSA 命名) |
**设计要点**:
- `LLVMType` 用 `t.REnum`(tagged union)—— 少变体、无通用字段、match 分派的理想场景
- 指令 opcode 用 `t.CEnum`(IROp)—— 类型安全且零开销
- IR 文本生成用 `viperlib.snprintf` 格式化,避免手写逐字符拼接
- 对象分配用普通类 + `mpool`,不持有全局 `mbuddy`
**验证**:[TransPyV/App/main.vp](../TransPyV/App/main.vp) 10 个测试覆盖类型打印、值打印、build_add、if-else、模块打印、lli 实际执行。TransPyV 程序运行时自己生成合法 IR,被 `lli` 成功执行。
### 3.3 ✅ 基础设施库
以下 includes/ 库为自举后的编译器提供数据结构和系统调用基础:
| 库 | 自举中的作用 |
|----|-------------|
| `linkedlist.vp` | AST 节点树、符号表链、IR 指令链的容器(`@t.NoVTable` + PEP 695 递归泛型继承) |
| `mpool.vp` / `mbuddy.vp` | 编译器内存管理(替代 Python GC) |
| `string.vp` / `viperlib.vp` | 字符串处理 + snprintf(替代 Python 字符串方法) |
| `w32/` | FFI 声明,自举后编译器调用 llc/clang/linker 的基础 |
| `json/` | 符号表序列化(替代 pickle) |
---
## 4. 障碍分析(修正版)
原版本文档列出的"三大致命依赖"已被逐一攻克或降级:
### 4.1 ~~llvmlite 依赖(原:致命)~~ → ✅ 已解决
**原问题**:编译器通过 `llvmlite.ir` 生成 IR,TransPyC 无法调用 Python 库。
**解决**:`includes/llvmlite/` 用 TransPyC 自身实现了 LLVM IR 文本生成库,API 与 Python `llvmlite.ir` 对齐。采用"字符串拼 IR"路径(路径 A),不绑定 LLVM C API。18/18 测试通过,TransPyV 自举实验验证可生成可执行 IR。
### 4.2 ~~ast 模块依赖(原:致命)~~ → ✅ 已解决
**原问题**:编译器用 Python `ast` 模块解析源码,自举需自己写 Python 解析器(预计 5,000-10,000 LOC)。
**解决**:`includes/ast/` 4,176 LOC 实现完整 Python 解析器,AstTest 解析 CPython 凥乎全部语法通过。预言已兑现,且实际规模(4,176 LOC)低于预估下限(5,000 LOC)。
### 4.3 ~~动态反射 4241 处(原:致命)~~ → ⚠️ 降级为可控
**原问题**:`getattr`/`setattr`/`hasattr`/`isinstance`/`issubclass` 共 4,241 处,静态语言无法直接表达。
**修正分析**:4241 这个数字按 Python 语法 grep 统计,未区分"语义上需要反射"和"用反射偷懒"。实际分类:
| 用法 | 数量估计 | 是否真反射 | TransPyC 解法 |
|------|----------|-----------|--------------|
| `isinstance(node, ast.Xxx)` AST 节点分派 | ~2000 | ❌ 否 | 胖节点 `node.vtype == ASTVType.Xxx` 整数比较 |
| `isinstance(x, ir.IntType)` IR 类型分派 | ~500 | ❌ 否 | REnum `match x: case LLVMType.Int:` tag 比较 |
| `getattr(t, name, None)` 字符串→类型查表 | ~35 | ❌ 否 | 注册表 `t_type_registry: dict[str, t.CInt]` |
| `CTypeInfo.__getattr__` 门面路由 | ~100 | ✅ 是 | 重构为 REnum + match 显式分派 |
| `copy.deepcopy` / `pickle` | ~10 | ✅ 是 | 已解决(`__getstate__/__setstate__`) |
| `hasattr` 防御性检查 | ~1500 | ⚠️ 多数可消除 | 显式字段声明 + None 检查 |
**真实反射障碍约 200-500 处**,集中体现在 `CTypeInfo` 门面模式(FIELD_ROUTES 动态路由到 `_ts`/`_sm`)。重构方案:用 REnum + match 替换 `__getattr__` 路由,已在 `includes/llvmlite/__types.vp` 验证此范式可行。
### 4.4 Python 标准库依赖(原:严重)→ ⚠️ 部分已解决
| Python 库 | 用途 | TransPyC 现状 |
|-----------|------|--------------|
| `ast` | 源码解析 | ✅ `includes/ast/` |
| `llvmlite.ir` | LLVM IR 生成 | ✅ `includes/llvmlite/` |
| `json` | 序列化 | ✅ `includes/json/` |
| `os`/`sys` | 文件/路径 | ⚠️ `includes/os/` + `w32/`(Windows) |
| `subprocess` | 调用 llc/clang | ❌ 待实现(w32 CreateProcess 可基础) |
| `pickle` | 符号表序列化 | ❌ 待实现(json 替代) |
| `concurrent.futures` | 并行编译 | ❌ 待实现(w32 线程可基础) |
| `re` | 正则表达式 | ❌ 待实现 |
| `traceback` | 异常栈 | ❌ 待实现 |
---
## 5. 自举路径
### 5.1 路径 A:完全自举(当前目标,可行)
**已完成**:
1. ✅ `includes/ast/` Python 解析器(4,176 LOC)
2. ✅ `includes/llvmlite/` LLVM IR 生成库(~1,500 LOC)
3. ✅ 基础设施库(linkedlist/mpool/mbuddy/string/json/w32)
**剩余工作**:
1. **CTypeInfo 门面重构**(~200-500 处真反射 → REnum + match)
2. **lib/core 重写**(~20K LOC TransPyC,含 Handles/LLVMCG/Translator)
3. **lib/Projectrans 重写**(~5K LOC,Phase1/Phase2/DeclarationGenerator)
4. **lib/StubGen 重写**(~2K LOC)
5. **Python 标准库缺口补齐**(os/subprocess/re/pickle,~5K LOC)
**预计新增代码量**:30-35K LOC TransPyC。与已完成的 includes/ast(4,176 LOC)+ includes/llvmlite(~1,500 LOC)+ 其他基础库为同一量级——已经证明过两次能做这件事。
### 5.2 关键技术验证点(已完成)
以下能力已在 includes/ 库中验证,证明 TransPyC 能承载编译器复杂度:
- **复杂泛型容器**:`linkedlist.vp` 的递归泛型继承 `class GNode(GSListNode[GNode])`(C/C++/Rust 均无法如此简洁表达)
- **复杂算法**:`zlib/` 的完整 deflate/inflate/huffman(~1,900 LOC 生产级压缩)
- **n 维数组**:`numpy/__init__.vp` 的 ndarray + 运算符重载 + `__new__` 堆分配
- **平台 FFI**:`w32/` 的 Win32 API 绑定(file/memory/process/sync/console)
- **LLVM IR 生成**:`llvmlite/` 的 REnum 类型系统 + IRBuilder + ModulePrint
---
## 6. 自举的意义
1. **性能**:当前 Python 编译器翻译 6 个文件 0.6s,全量编译 36K LOC 编译器自身时 Python 解释器开销显著。自举后编译器是原生码,编译速度数量级提升。
2. **部署**:当前分发需 Python 运行时 + llvmlite + ast 依赖。自举后只需一个可执行文件 + llc/clang——系统级语言编译器该有的形态。
3. **语言成熟度证明**:能写自己的编译器是系统级语言的"成人礼"。C、C++、Rust、Go、Zig 全都走过这条路。
4. **dogfooding 闭环**:自举后每次改编译器都用编译器自己编译自己,持续暴露 bug 和性能瓶颈,形成正反馈。`includes/ast` 和 `includes/llvmlite` 的开发过程已经产出了大量深层 bug 修复(REnum 变体名冲突、跨模块继承字段展平、NoVTable OOP 方法继承等)。
---
## 7. 结论
| 维度 | 评估 |
|------|------|
| 理论可行性 | ✅ 可行(图灵完备) |
| 实践可行性 | ✅ 完全自举可行(核心障碍已攻克) |
| 当前进展 | ⚠️ 前端(ast)+ 后端(llvmlite)+ 基础库已就位,剩中间层(lib/core)重构 |
| 剩余工作量 | ~30-35K LOC TransPyC 重写 + CTypeInfo 门面重构 |
| 推荐策略 | 路径 A:完全自举,按 lib/core → lib/Projectrans → lib/StubGen 顺序推进 |
**核心判断**:原版本文档的"三大致命依赖"结论已全部失效——llvmlite 和 ast 已被 includes/ 下的自研库替代,4241 处反射中 90% 是工程偷懒而非语义需要。完全自举从"几乎不可能"变为"明确可行",剩余工作是体量问题而非技术障碍。
---
## 8. 附录:原版本文档的修正说明
本文档(2026-06-30 版)修正了 2026-06-26 原版的以下错误判断:
| 原版断言 | 修正 |
|---------|------|
| "完全自举几乎不可能" | 完全自举可行,核心障碍已攻克 |
| "llvmlite 依赖致命" | 已被 `includes/llvmlite/` 解决(18/18 测试) |
| "ast 模块依赖致命" | 已被 `includes/ast/` 解决(4,176 LOC,AstTest 通过) |
| "4241 处动态反射致命" | 90% 是工程偷懒(isinstance 节点分派/IR 类型分派),真实反射约 200-500 处 |
| "推荐路径 C:不自举" | 推荐路径 A:完全自举 |
| "预计自举后 60K-80K LOC" | 修正为 30-35K LOC(基础组件已 5,700 LOC 完成) |
+225
View File
@@ -0,0 +1,225 @@
# TPV 对象化重构方案
> 基线:TPV `f63dcd7` / TransPyC `e1cb560`(对象化前最后稳定版本,2026-08-09)
> 状态:方案讨论稿,未开始实施
## 1. 背景与目标
### 1.1 现状问题
TPV 编译器当前是"**数据对象 + 行为函数式**"架构:
- `includes/llvmlite/`:19 个数据结构类已对象化(`IRBuilder`/`LLVMModule`/`Value`/`LLVMType`…),但**所有操作是"首参传 self"的顶层函数**(`build_load(builder, ...)`、`module_add_function(mod, ...)`),与 C 风格 API 一致
- `TransPyV/App/lib/core/Handles/`:**纯函数模块**(`translate_call(pool, builder, mod, node, funcs, count, trans)` 六参连传),且存在严重膨胀:
| 文件 | 大小 | 行数 |
|---|---|---|
| HandlesExprCall.vp | 362KB | 7043 |
| HandlesExpr.vp | 181KB | 3715 |
| HandlesClassDef.vp | 172KB | 4010 |
| HandlesType.vp | 102KB | 2360 |
### 1.2 重构目标
1. **对象化**:引入 `Generator` 生成器对象(Mixin 组合),状态集中于 `self`,与 TPC 的 `Translator + LlvmCodeGenerator` 架构对齐
2. **膨胀治理**:`translate_call` 7043 行 God Function 按职责拆散为 `CallGen` 的方法簇
3. **不引入 TPC 代码**:基于 TPC 设计理念 + Viper 约束自研;TPC 的 LLVMCG 仅作参考
4. **每阶段可验证**:bootstrap 编译 + 全 Test 回归,IR 输出零变化
## 2. 现状盘点(已实测)
### 2.1 llvmlite 模块(`includes/llvmlite/`,7 文件)
| 文件 | 内容 | 对象化程度 |
|---|---|---|
| `__init__.vp` (422行) | 10 工厂函数 + `LLVMModuleCore`(27 个薄封装方法) | **已有方法化先例** |
| `__builder.vp` (1336行) | **60+ 个 `build_*` 顶层函数** + `IRBuilder`(4 字段:Pool/Func/CurBlock/Counter) | **IRBuilder 是天然状态对象,签名已按 self 优先** |
| `__types.vp` (748行) | 34 类型工厂/打印/深拷贝 + `LLVMType`(**REnum tagged union**)+ `LLVMTypeCore`(19 方法) | REnum 应保留,Core 是方法化先例 |
| `__module.vp` (565行) | 19 函数 + `LLVMModule`(12 字段容器,含 NamedType 链表) | 纯数据容器,可平移 |
| `__function.vp` (798行) | 35 函数 + `Line/BasicBlock/Param/Function`(GSListNode 链) | 纯数据结构 |
| `__values.vp` (230行) | 14 函数 + `Value`(5 字段) | 纯数据结构 |
| `__verify.vp` (399行) | 15 验证函数 + `VerifyResult` | 独立,最后迁移 |
**关键结论**:
- **IRBuilder 对象化零状态迁移成本**:4 字段已齐,60+ 顶层函数改方法只需 `self` 替代首参,签名一一对应
- **LLVMModule 对象化有先例**:`LLVMModuleCore`/`LLVMTypeCore` 已证明"方法化封装"可行(但仍为薄壳,核心 `module_add_named_type`/打印仍是顶层函数)
- **LLVMType 保持 REnum**:变体即"子类",不应转继承层次
### 2.2 演进阻力点
1. `@t.NoVTable` + 硬编码 SIZE(`BUILDER_SIZE=32`/`pool.alloc(96)`)强耦合——**方法化不改布局则无影响**;若转 `@t.CVTable` 需全量重写
2. 16 个跨模块 stub 访问器(`value_get_ty`/`paramnode_get_ty`…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(**依赖编译器缺陷修复,非本方案范围**)
3. `LLVMType` 的 `pool.alloc(__sizeof__)` + `c.DerefAs` 构造模式——保留为构造方法
## 3. 目标架构
```
includes/llvmlite/ # ① 方法化平移(P0)
├── __builder.vp # IRBuilder + 60 方法(build_load → self.load)
├── __module.vp # LLVMModule + 方法 + 命名类型注册
├── __types.vp # LLVMType(REnum) + 构造方法
├── __values.vp / __function.vp / __verify.vp
└── __init__.vp # 薄兼容层:旧顶层函数委托新方法(调用方零改动)
TransPyV/App/lib/core/
├── Gen/ # ② 生成器对象(P1-P6)
│ ├── Generator.vp # 入口:@t.CVTable,Mixin 组合,visit 分派
│ ├── BaseGenMixin.vp # 状态/Pool/Module/Builder/SymTab + 错误/trace
│ ├── TypeGenMixin.vp # 类型映射/注解解析(← HandlesType)
│ ├── StructGenMixin.vp # struct 注册/字段/继承/vtable(← HandlesStruct+ClassDef 布局)
│ ├── ExprGenMixin.vp # 表达式/二元/一元(← HandlesExpr+ExprOps)
│ ├── CallGenMixin.vp # 调用分派/方法/类型转换/c.*/stdarg(← HandlesExprCall 拆解)
│ ├── FuncGenMixin.vp # 函数/方法声明与翻译(← HandlesFunctions)
│ ├── ClassGenMixin.vp # 类定义/泛型特化(← HandlesClassDef)
│ ├── StmtGenMixin.vp # 语句(← Body/If/While/For/Assign/AnnAssign/Return/Match/Nonlocal)
│ └── ImportsGenMixin.vp # 导入(← HandlesImports)
└── Handles/ # ③ 迁移完成后:删除或保留为薄壳(P6 决定)
```
## 4. P0:llvmlite 方法化(双轨过渡)
### 4.1 原则
**纯方法化平移,不改布局、不改语义**:顶层函数 → 类方法,`self` 替代首参;类保持 `@t.NoVTable` + 硬编码 SIZE(不引入 vtable 继承)。
### 4.2 方法映射示例
```python
# 现状(__builder.vp)
def build_load(builder, ty, ptr): ...
def build_gep_struct(builder, struct_ty, field_ty, ptr, field_idx): ...
# 目标(同文件,类方法)
@t.NoVTable
class IRBuilder:
Pool, Func, CurBlock, Counter
def load(self, ty, ptr): ... # 原函数体,builder → self
def gep_struct(self, struct_ty, field_ty, ptr, field_idx): ...
# 兼容层(__init__.vp):旧函数保留为薄包装
def build_load(builder, ty, ptr):
return builder.load(ty, ptr)
```
### 4.3 覆盖清单(60+ 方法)
| 类别 | 方法 |
|---|---|
| 内存 | alloca / alloca_aligned / alloca_at_entry / load / store |
| 整数二元 | add/sub/mul/sdiv/udiv/srem/urem/and/or/xor/shl/lshr/ashr |
| 浮点 | fadd/fsub/fmul/fdiv/frem/fneg |
| 比较 | icmp / fcmp |
| 控制流 | br / cond_br / ret / ret_void / unreachable / switch / select |
| 调用 | call / call_indirect |
| 转换 | bitcast/sext/trunc/zext/ptrtoint/inttoptr/fpext/fptrunc/fp2si/si2fp/fp2ui/ui2fp |
| GEP | gep / gep_array / gep_struct |
| Phi/原子/杂项 | phi / atomicrmw / cmpxchg / va_arg / inline_asm |
### 4.4 Module 方法化
```python
def add_function(self, func): ...
def add_global(self, gv): ...
def add_named_type(self, ty): ...
def ensure_opaque_for_type(self, ty): ...
def set_target(self, triple): ...
def print_ir(self, mode): ...
```
### 4.5 验证
- bootstrap 编译 TPV(自举)通过
- 全 Test 回归:编译产物 `.text.ll` 与基线**逐字节一致**(兼容层保证行为不变)
- 兼容层在 P6 确认无调用方后删除
## 5. P1-P6:Generator 对象迁移
### 5.1 Generator 骨架(P1)
```python
# 采用 @t.CVTable(多态 Mixin 继承,与现有 ReturnHandle(HandlesBase.Mixin) 模式一致,
# 符合"多态类不用 NoVTable"约定;数据类 llvmlite 仍为 NoVTable)
@t.CVTable
class Generator(BaseGenMixin, TypeGenMixin, StructGenMixin,
ExprGenMixin, CallGenMixin, FuncGenMixin,
ClassGenMixin, StmtGenMixin, ImportsGenMixin):
pass
```
BaseGenMixin 状态字段(迁移自 HandlesTranslator):
`Pool` / `Module` / `Builder` / `SymTab` / `FuncTable` / `FuncCount` / `StructTable` / `VTableCache` / `ImportedModules` / `FromImports` / `ModuleSha1` / `CurFunc` / `CurBuilder` / `CurClassName` / 错误 trace 栈
### 5.2 Mixin 方法映射表(P2-P5)
| Mixin | 来源(现状函数) | 迁移后方法 |
|---|---|---|
| TypeGenMixin | `HandlesType.map_t_type` / `resolve_annotation_type` / CDefine 表 | `self.map_type(name)` / `self.resolve_annotation(node)` |
| StructGenMixin | `HandlesStruct.register_struct` / `find_struct` / `lookup_field`;`HandlesClassDef` 布局/vtable | `self.register_struct(...)` / `self.lookup_field(...)` / `self.gen_vtable(cls)` |
| ExprGenMixin | `HandlesExpr.translate_value` / `translate_name_value` / 属性访问;`HandlesExprOps.translate_binop` / `try_unary_operator_overload` | `self.visit_value(node)` / `self.visit_binop(node)` / `self.visit_attr(node)` |
| CallGenMixin | `HandlesExprCall.translate_call`(**拆解**)+ `_translate_method_call` / `_translate_t_type_cast` / `translate_c_addr` 等 | `self.visit_call(node)` → `self._call_method(...)` / `self._call_t_cast(...)` / `self._call_c_addr(...)` |
| FuncGenMixin | `HandlesFunctions.translate_function_def` / `_mangle_name` | `self.translate_func(node)` / `self.mangle_name(name)` |
| ClassGenMixin | `HandlesClassDef.translate_class_def` / `_specialize_generic_class` | `self.translate_class(node)` / `self.specialize_generic(...)` |
| StmtGenMixin | `HandlesBody.translate_stmt` + If/While/For/Assign/AnnAssign/Return/Match/Nonlocal 各 Handle | `self.visit_stmt(node)` |
| ImportsGenMixin | `HandlesImports` Import/ImportFrom | `self.import_module(...)` |
### 5.3 签名瘦身规则
```python
# 现状:六参连传
translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
# 目标:self 持状态
self.visit_call(node) # 内部用 self.Pool/self.Builder/self.Module/self.FuncTable
```
- 中间态:`self.visit_call(node, funcs_ptr, func_count)`(表引用局部化后去参)
- 迁移顺序**从叶子到根**(先无依赖者),每迁移一个 Mixin 立即 bootstrap
### 5.4 CallGen 拆解(P4,膨胀治理重点)
`translate_call` 7043 行按分支簇拆为方法(**纯移动,逻辑零改动**):
| 方法簇 | 对应分支 |
|---|---|
| `_call_generic_ctor(node)` | 泛型类构造 `list[int](pool, n)` |
| `_call_binop_cast(node)` | `(A|t.CPtr)(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)` |
| `_call_t_cast(node)` | `t.CInt(x)` / `t.CVoid(x, CPtr)` / 裸名 typedef |
| `_call_class_sizeof(node)` | `ClassName.__sizeof__()` |
| `_call_method(node)` | `self.field.method(...)` / `(expr).method(...)` |
| `_call_plain(node)` | 普通函数/跨模块调用 |
## 6. 迁移计划总表
| 阶段 | 内容 | 产出 | 验证 |
|---|---|---|---|
| P0 | llvmlite 方法化 + 兼容层 | Builder/Module 方法化 | bootstrap + 全回归(IR 零差异) |
| P1 | Generator 骨架 + trans 字段迁入 | `Gen/Generator.vp` 空状态对象 | bootstrap |
| P2 | TypeGen / StructGen | 2 个 Mixin 文件 | bootstrap |
| P3 | ExprGen(含 ExprOps) | 1-2 个 Mixin | bootstrap |
| P4 | CallGen(拆 7043 行) | 9 个方法簇 | bootstrap |
| P5 | FuncGen / ClassGen / StmtGen / ImportsGen | 4 个 Mixin | bootstrap |
| P6 | 删除 Handles 壳 / 兼容层 / ast visitor(可选) | 收尾 | bootstrap + 全回归 |
**每阶段验收**:bootstrap 编译通过 → 全 Test 项目编译通过 → `.text.ll` 与基线 diff 为空(或仅行号差异)。
## 7. 风险与对策
| 风险 | 对策 |
|---|---|
| `@t.NoVTable` + 硬编码 SIZE 布局 | 方法化不改布局;Generator 用 `@t.CVTable`(数据类保持 NoVTable) |
| 跨模块 stub 访问器 | 方法化后保留访问器(消除依赖编译器缺陷修复,延后) |
| Viper 循环 import | 保持现有"延迟 import"模式 |
| 迁移引入行为差异 | 每阶段 `.text.ll` 基线 diff 校验 |
| REnum LLVMType 误转继承 | 明确保持 REnum 构造模式 |
| 工作量/回归风险 | 纯移动优先(P0-P5),语义重构仅 P6(可选) |
## 8. 验收标准(最终)
1. TPV bootstrap 编译通过(三重编译流程)
2. 全部 Test 项目编译 + 运行通过
3. IR 输出与基线一致(无行为变化)
4. `HandlesExprCall.vp` 7043 行 → `CallGen` 各方法簇 < 1500 行
5. 所有 Handles 调用不再六参连传(`self` 持状态)
+134
View File
@@ -0,0 +1,134 @@
# Viper 多继承支持方案
> 背景:Viper 语法层已支持 `class X(A, B)`(`ClassDef.bases` 是列表,parser 正常解析),
> 但语义层只实现单继承(`StructEntry.ParentName` 单值,字段展平/vtable/方法查找均单父)。
> 目标:实现多继承(含字段合并、vtable 合并、方法查找、菱形去重),并同步 TPC 参考实现。
> 状态:方案稿,未实施。
## 1. 现状(已实测定位)
| 位置 | 现状 | 多继承缺口 |
|---|---|---|
| ast/stmts.vp:504 `ClassDef.bases` | 列表,parser 支持多 base | 无(语法已就绪) |
| HandlesClassDef.vp:2868 `entry.ParentName = parent_name` | 单值 | 需父类数组 |
| HandlesClassDef.vp:2618-2655 字段展平 | 只遍历单个 `parent_entry` | 需 MRO 多父合并 |
| HandlesClassDef.vp:3028+ `_collect_vtable_methods` | 单父 `parent_vt_count` | 需多父 vtable 合并 |
| HandlesStruct.vp:56 `ParentName` | 单 str 字段 | 需数组 + MRO 辅助 |
| HandlesExprCall.vp `try_operator_overload` / `_translate_method_call` | `get_parent_name` 单链 | 需多父链 |
| HandlesExpr.vp 属性访问回退链 | `get_parent_name` 单链 | 需多父链 |
## 2. MRO 规则(分阶段)
### Phase 1(本方案默认):深度优先 + 声明顺序 + 去重
- 查找顺序:**当前类 → bases 声明顺序逐个递归**
- 同名冲突:**先声明者优先**(最左优先)
- 菱形(`D(A, B)`,A/B 均继承 C):**深度优先去重**(已访问集合),C 的字段/方法只合并一次
- 理由:与 Python C3 相比实现简单,覆盖本仓库实际场景(Generator Mixin 链、无复杂菱形)
### Phase 2(可选,后续):C3 线性化
- 严格 C3 保证单调性,处理复杂菱形歧义
- 仅在 Phase 1 遇到真实需求后实施
## 3. 数据结构改造(HandlesStruct.vp)
```python
@t.NoVTable
class StructEntry:
# 现有字段不变
ParentNames: list[str] | t.CPtr # 新增:父类名数组(声明顺序,跳过 marker)
ParentCount: t.CInt # 新增
# 辅助函数(新增)
def get_parent_count(entry) -> int
def get_parent_name_at(entry, i) -> str
def get_mro_chain(entry) -> list[str] # 深度优先去重线性化(含自身)
def get_mro_parent_entries(entry) -> 父类 StructEntry 数组
```
- `ParentName` 保留(兼容现有调用,指向第一个父类),新代码优先用 `ParentNames`
- 迁移点:`get_parent_name()` 调用方逐步替换为 MRO 链遍历
## 4. 字段布局(HandlesClassDef.vp 字段展平 L2618-2655)
改造前(单父):
```
[parent_fields..., child_fields...]
```
改造后(多父):
```
[vtable?, parent1_fields..., parent2_fields..., child_fields...]
```
实现:
1. `parent_entries = get_mro_parent_entries(self_entry)`(深度优先去重)
2. 遍历每个父类字段:`__vtable__` 跳过;**字段名去重**(已存在跳过,先声明者优先)
3. 布局顺序 = MRO 父类顺序 → 子类自身字段
**影响面**:所有继承类的 `__sizeof__`/GEP 偏移变化 → 需全量回归
## 5. vtable 合并(HandlesClassDef.vp `_collect_vtable_methods` L3028+)
改造前(单父):`parent_vt_count` 遍历,full_name 用实际定义类(find_func_global 探测,已支持多层单继承)
改造后:
1. 遍历 **MRO 所有父类**的 `VTableMethods`(每个父类的 vtable 方法短名)
2. **方法短名去重**:先声明父类优先;子类覆盖优先(`child_overrides` 检查不变)
3. full_name 定位:沿用 `find_func_global("类名.方法")` 探测(多父链探测)
4. 槽位顺序:父类方法(MRO 序)→ 子类新增方法
## 6. 方法查找/运算符重载/属性访问(多父链替换单链)
| 位置 | 单链现状 | 改法 |
|---|---|---|
| HandlesExprCall.vp `try_operator_overload` L123-135 | `while cur_parent is not None` | 改为 `get_mro_chain` 遍历 |
| HandlesExprCall.vp `try_unary_operator_overload` L211-221 | 同上 | 同上 |
| HandlesExprCall.vp `_translate_method_call` 继承方法查找 | `get_parent_name` 单链 | MRO 链 |
| HandlesExpr.vp 属性访问 lookup_field 回退 | 单链 | MRO 链 |
| HandlesClassDef.vp `_find_method_defining_class`(本轮已加) | `entry.ParentName` 单链 | MRO 链 |
| HandlesType.vp / 其他 `get_parent_name` 调用 | 单链 | 逐步替换 |
## 7. 菱形继承处理
- **字段**:MRO 去重(已访问父类集合),C 字段只合并一次
- **方法/vtable**:方法短名去重,C 的 vtable 槽合并一次
- **歧义**:先声明优先;Phase 1 不报歧义错误(静默最左优先),Phase 2 C3 时再考虑警告
## 8. TPC 同步清单
TPC(Python,`lib/core/`)与 TPV 结构一致,按顺序同步:
1. `Handles/HandlesStruct.py`:StructEntry 父类数组 + MRO 辅助
2. `Handles/HandlesClassDef.py`:字段展平多父、vtable 多父合并、parent 收集
3. `Handles/HandlesExprCall.py`:方法查找/运算符重载多父链
4. `Handles/HandlesExpr.py`(若 TPC 属性访问在 HandlesExprAttr.py):多父回退
**顺序**:先 TPV(活跃编译器)实现 + 验证,再 TPC 同步(保持一致,防分叉)。
## 9. 实施计划
| 阶段 | 内容 | 验证 |
|---|---|---|
| M1 | HandlesStruct:父类数组 + MRO 辅助(新增字段/函数,不改现有逻辑) | bootstrap |
| M2 | HandlesClassDef:parent 收集多 base + 字段展平多父 | bootstrap + 单继承回归 |
| M3 | HandlesClassDef:vtable 多父合并 | bootstrap + 多继承 vtable 用例 |
| M4 | HandlesExprCall/HandlesExpr:方法/运算符/属性多父链 | bootstrap + 全用例 |
| M5 | TPC 同步(4 文件) | TPC 自测 |
| M6 | 新增 Test/MultiInheritTest(字段/vtable/菱形去重/运算符重载) | 编译 + 运行 |
## 10. 验证用例(Test/MultiInheritTest/App/main.vp)
- 多父字段合并 + 属性访问(`d = D(); d.a + d.b`)
- 多父方法调用(非虚 + 虚 vtable 分派)
- 运算符重载继承链(父类 dunder)
- 菱形去重(`D(A, B)`,A/B ← C:C 字段/方法只出现一次)
- 同名冲突最左优先
- Generator 多继承实例化(`class Generator(BaseGenMixin, TypeGenMixin, ...)`)——**本轮对象化 P2+ 的落地验证**
## 11. 风险与对策
| 风险 | 对策 |
|---|---|
| 字段布局变化破坏现有继承类 | 全量回归(所有 Test 项目) |
| vtable 合并槽位错误 | 多继承 vtable 用例 + obj 反汇编核对 |
| Viper 方法符号不转发(P1 已踩坑) | 多继承下子类仍只调自己文件定义的方法;父类方法用字段/模块函数 |
| 菱形去重遗漏 | MRO 已访问集合严格实现 |
| TPC/TPV 分叉 | TPV 先验,TPC 紧跟同步 |
+85
View File
@@ -0,0 +1,85 @@
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
### Section 1 – Definitions.
a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
### Section 2 – Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
A. reproduce and Share the Licensed Material, in whole or in part; and
B. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
3. Term. The term of this Public License is specified in Section 6(a).
4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications required to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
5. Downstream recipients.
A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this Public License.
3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
### Section 3 – License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified form):
A. You must retain the following if it is supplied by the Licensor with the Licensed Material:
i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of warranties;
v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable.
B. You must indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
C. You must indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
### Section 4 – Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
### Section 5 – Disclaimer of Warranties and Limitation of Liability.
a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, noninfringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
### Section 6 – Term and Termination.
a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
### Section 7 – Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
### Section 8 – Interpretation.
a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
Creative Commons is not a party to this Public License. Notwithstanding, Creative Commons may elect to enforce this Public License with respect to Licensed Material it licenses. The mark "Creative Commons" and the Creative Commons logos are trademarks of Creative Commons. Creative Commons does not grant trademark licenses except to the limited extent necessary to publicly indicate adherence to this Public License or as otherwise permitted by Creative Commons’ published policies.
+359
View File
@@ -0,0 +1,359 @@
# 01 - 语言概述与编译流程
> 注:本章为原本的 `01-overview.md`
## 语言定位
`Viper` 是一种 **系统级编程语言**,其语法基于 `Python`,但编译目标为 `LLVM IR`,最终生成原生机器码。`Viper` 不是 `Python` 的超集或子集,也不是"`C` 的语法糖"——它是一门拥有独立类型系统、独立编译模型、独立模块体系的语言。`Viper` 选择 `Python` 语法作为表达形式,是因为 `Python` 是人类公认的可读性最高的语言。
当前编译器 `TransPyC`(用 Python 实现)使用 Python 标准库 `ast` 解析源文件,将精力集中在语义翻译而非词法/语法分析上。但项目已在 [includes/ast](../includes/ast) 中实现**完全自研的 Python 解析器**(词法器 + 语法器 + AST 构造,约 4176 LOC,`AstTest` 综合测试通过),覆盖 CPython 几乎全部语法。这是向 `TransPyV`(用 Viper 重写编译器自身)完全自举的关键准备——自举后编译器将脱离 Python 运行时,解析、类型检查、IR 生成都由原生机器码完成。详见 [13-bootstrapping.md](13-bootstrapping.md)。
而实际上,下文中所有提到的 `C` 语言都是为了便于使用 `C` 的底层开发者理解。由于历史原因,在旧版本中我们使用 `C` 来作为中间语言,如同旧版本的 `C++` 一样,但现在 `target="c"` 的路径已经完全删除,`C` 路径永久不再受维护。
### 核心设计决策
1. **Python 语法,LLVM 语义**:源文件是完全合法的 `Python` 语法,但通过 `t` 模块的类型注解系统赋予完全不同的语义 —— `t.CInt` 不是 `Python` 的 `int`,而是 `LLVM` 的 `i32`
2. **类型注解即编译指令**:Viper 的类型注解不是可选的提示,而是编译器生成 `LLVM IR` 的决定性依据。`x: t.CInt` 生成 `i32`,`x: t.CInt | t.CPtr` 生成 `i32*`,但无注解时会自动推导,详见下文。
3. **两阶段编译**:先从源文件提取声明接口(`.vpi` + `.stub.ll`),再使用声明接口翻译源文件为含代码的 `.ll`。这是 `Viper` 区别于简单"`Python` 转 `C`"工具的关键架构
4. **`SHA1` 命名空间**:每个源文件按内容 `SHA1` 哈希命名,非导出函数和结构体自动加上 `SHA1` 前缀,从根本上消除跨模块的符号冲突
5. **万物皆数据**:为便于底层开发,实际上所有的变量和类型一般并不适用于鸭子类型,但在语义层面我们会尽可能贴近鸭子类型。
### 与 `Python` 的关系
| 特性 | `Python` | `Viper` |
|------|--------|-------|
| 类型系统 | 动态类型 | 静态类型(通过 `t` 模块注解,编译时确定) |
| 内存管理 | `GC` 自动回收 | 手动管理,无 `GC`,无运行时 |
| 运行时 | 解释执行 + 字节码 | 编译为原生机器码(通过 `LLVM`) |
| 对象模型 | 万物皆对象 | `@t.Object` 启用 OOP,`@t.CVTable` 为多态(vtable),`@t.NoVTable` 为非多态继承(C++ 风格,零运行时开销),普通 `class` 仅为结构体布局 |
| 标准库 | `Python` 标准库 | `Viper` 标准库(`includes/`)+ `ViperOS SDK` |
| 空值 | `None` | `None`(编译为 `NULL`/零值) |
| 模块系统 | 运行时 `import` | 编译时解析,插入 `.stub.ll`,`SHA1` 命名空间隔离 |
### 与 C 的关系
Viper 编译后的代码在底层等价于 `C` 编译后的机器码(都经过 LLVM),但 Viper 在语言层面提供了 C 所没有的能力:
- **SHA1 命名空间**:自动消除符号冲突,无需手动管理 `static`/命名前缀
- **类型组合语法**:`t.CConst | t.CInt | t.CPtr` 比 `const int*` 更具组合性
- **声明式内联汇编**:`c.Asm(f"mov {c.AsmOut(x, t.ASM_DESCR.OUTPUT_REG)}, rdi")` 比裸 `__asm__` 更安全,也更具可读性。
- **结构化预处理**:`c.CIfdef`/`c.CEndif()` 替代 `#ifdef`/`#endif`。
- **面向对象**:`@t.Object` + `@t.CVTable` 提供 `vtable` 支持的多态 `OOP`;`@t.NoVTable` 提供 C++ 风格的非多态继承(零 vtable 开销);PEP 695 泛型 + 递归泛型继承实现强类型自引用结构
- **存根驱动的模块系统**:`.vpi` 存根文件实现跨模块类型解析,无需头文件
- **更多内容**:额外更多的不依赖操作系统的函数和语法
## 两阶段编译流程
Viper 的编译由 `Projectrans.py` 驱动,分为两个阶段。这是 Viper 编译模型的核心,理解两阶段编译是理解 `t.CDefine`、`t.CExport`、`t.CInline` 等关键概念的前提。
```
源文件 (.vp) ──────────────────────────────────────────────────────
│ │
│ ┌─────────────── 阶段一:声明提取 ───────────────┐ │
│ │ │ │
│ │ 1. 计算源文件 SHA1 │ │
│ │ 2. 生成 <SHA1>.vpi(签名存根) │ │
│ │ 3. 构建结构体注册表 │ │
│ │ 4. 生成 <SHA1>.stub.ll(LLVM IR 声明) │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────── 阶段二:代码翻译 ───────────────┐ │
│ │ │ │
│ │ 1. 加载所有 .vpi 和 .stub.ll │ │
│ │ 2. 构建共享符号表 │ │
│ │ 3. 收集内联函数符号 │ │
│ │ 4. 翻译源文件 → <SHA1>.ll(含代码) │ │
│ │ 5. llc 编译 → <SHA1>.o(目标文件) │ │
│ │ 6. ld.lld 链接 → 可执行文件 │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
```
### 阶段一:声明提取(Phase1Generator)
阶段一的目标是从每个源文件提取**声明接口**,使其他模块在阶段二翻译时能够正确解析跨模块引用。
#### 步骤 1:可达文件发现与拓扑排序
从入口文件(`main.vp` 或 `project.json` 指定)出发,通过 `import` 语句递归遍历,找出所有可达的 `.vp` 源文件。然后对文件进行拓扑排序,确保被依赖的模块先被处理。
```
main.vp → import serial → import drivers.serial.uart.serial → ...
```
#### 步骤 2:生成 `.vpi` 签名存根
对每个源文件,计算其内容的 SHA1 哈希(16位),然后调用 `PythonToStubConverter` 生成签名存根文件 `<SHA1>.vpi`。
**SHA1 计算方式**:
```python
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 | c.State: pass` |
| 类定义 | 保留成员类型注解和方法签名 |
| 全局变量 | 添加 `t.CExtern`:`x: t.CExtern | t.CInt` |
| `c.CIfdef`/`c.CEndif()` 等预处理指令 | 保留原样 |
| `import` 语句 | 保留原样 |
**关键设计**:`t.CDefine` 常量和 `t.CDefine` 函数在存根中**保留完整定义**(包括赋值和函数体),因为它们是编译时常量/宏,其他模块引用时需要完整展开。普通函数只保留签名并标记 `c.State`(仅声明),因为函数体在阶段二生成。
#### 步骤 3:构建结构体注册表
扫描所有已生成的 `.vpi` 文件,提取:
- **结构体名称集合**:所有非枚举、非异常的 `class` 定义
- **枚举名称集合**:继承 `t.CEnum` 的类
- **异常名称集合**:继承 `Exception` 的类
- **结构体→SHA1 映射**:记录每个结构体定义在哪个模块中
这个注册表用于阶段一的声明生成和阶段二的类型解析,确保跨模块的结构体引用使用正确的 SHA1 命名空间。
#### 步骤 4:生成 `.stub.ll` LLVM IR 声明
对每个 `.vpi` 文件,由 `DeclarationGenerator` 生成对应的 LLVM IR 声明文件 `<SHA1>.stub.ll`。
**声明生成规则**:
| 元素 | 生成的 LLVM IR |
|------|---------------|
| 普通函数 | `declare void @"<SHA1>.foo"(i32)` — 非 CExport 函数加 SHA1 前缀 |
| CExport 函数 | `declare i32 @main()` — CExport 函数不加前缀,全局可见 |
| 结构体 | `%"<SHA1>.ClassName" = type { i32, i32* }` — 类型名加 SHA1 前缀 |
| 全局变量 | `@varname = external global i32` |
| `t.CDefine` 常量 | **不生成声明**(编译时常量,直接内联展开) |
| `t.CTypedef` 别名 | **不生成声明**(类型别名,在类型解析时展开) |
| 枚举 | 为每个枚举成员生成 `@__config_EnumName_member = external global i32` |
**增量编译**:如果 `<SHA1>.vpi` 或 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)。只有源文件内容变化导致 SHA1 变化时才重新生成。
### 阶段二:代码翻译(Phase2Translator)
阶段二使用阶段一生成的声明接口,将源文件翻译为包含完整代码的 LLVM IR。
#### 步骤 1:加载声明接口
加载 `temp/` 目录中所有的 `.vpi` 和 `.stub.ll` 文件,构建:
- `sig_files`:SHA1 → `.vpi` 文件路径映射
- `stub_files`:SHA1 → `.stub.ll` 文件路径映射
- `sha1_map`:SHA1 → 源文件相对路径映射
#### 步骤 2:构建共享符号表
一次性构建所有文件共享的符号表数据,避免每个文件重复加载。将所有 `.vpi` 存根和 `.stub.ll` 声明中的类型信息注册到统一的 `SymbolTable` 中。
#### 步骤 3:收集内联函数符号
扫描 `includes/` 目录中被引用的模块,收集标记为 `t.CInline` 的函数。内联函数的完整 AST body 被保存,在翻译调用点时直接展开。值得注意的是,和 C 语言不同,`t.CInline` 不是优化建议,而是强制性的。
#### 步骤 4:翻译源文件
对每个源文件,使用 `TransPyC` 翻译器将 Python AST 翻译为 LLVM IR:
1. 将对应模块的 `.stub.ll` 声明嵌入到生成的 `.ll` 文件头部
2. 所有被引用模块的 `.stub.ll` 声明也被嵌入
3. 翻译 AST 节点为 LLVM IR 指令
4. 输出 `<SHA1>.ll` 文件
#### 步骤 5:编译与链接
- `llc`:将每个 `.ll` 编译为 `.o` 目标文件
- `ld.lld`:将所有 `.o` 链接为最终可执行文件
## SHA1 命名空间机制
SHA1 命名空间是 Viper 解决跨模块符号冲突的核心机制。
### 问题
在 C 语言中,多个模块可能定义同名函数或结构体,导致链接时符号冲突。C 的解决方案是 `static`(限制可见性)和命名前缀(手动避免冲突)。
### Viper 的解决方案
Viper 使用源文件内容的 SHA1 哈希作为命名空间前缀:
```
源文件 A.vp(SHA1 = a1b2c3d4e5f6g7h8)中定义:
class Point: x: t.CInt; y: t.CInt
def draw(p: Point) -> t.CVoid: ...
源文件 B.vp(SHA1 = i9j0k1l2m3n4o5p6)中也定义:
class Point: x: t.CFloat; y: t.CFloat # 不同的结构体!
def draw(p: Point) -> t.CVoid: ...
编译后:
A.vp 的结构体 → %"a1b2c3d4e5f6g7h8.Point" = type { i32, i32 }
A.vp 的函数 → declare void @"a1b2c3d4e5f6g7h8.draw"(%"a1b2c3d4e5f6g7h8.Point"*)
B.vp 的结构体 → %"i9j0k1l2m3n4o5p6.Point" = type { float, float }
B.vp 的函数 → declare void @"i9j0k1l2m3n4o5p6.draw"(%"i9j0k1l2m3n4o5p6.Point"*)
```
**没有符号冲突**——即使两个模块定义了同名类型和函数,它们的 LLVM IR 符号也是不同的。即便有两个内容完全相同的文件也不会冲突,他们会被识别为同一个文件,然后进行单次编译。
### SHA1 前缀的豁免:`t.CExport`
标记为 `t.CExport` 的函数**不加 SHA1 前缀**,保持原始函数名。这是模块向外部暴露 API/ABI 的机制:
```python
# main.vp — 入口函数,必须全局可见
def main() -> t.CInt | t.CExport:
return 0
# 编译后:define i32 @main() — 无 SHA1 前缀
```
```python
# serial.vp — 驱动接口,对外暴露
def init() -> t.CVoid | t.CExport:
serial_puts("init\n")
# 编译后:define void @init() — 无 SHA1 前缀,其他模块可直接调用
```
### SHA1 与增量编译
SHA1 同时服务于增量编译:
- 源文件内容不变 → SHA1 不变 → `.vpi` 和 `.stub.ll` 缓存命中,跳过阶段一
- 源文件内容变化 → SHA1 变化 → 重新生成声明接口
- `temp/_sha1_map.txt` 记录 SHA1 → 源文件路径的映射,供阶段二加载
理论上,如果一个未经变动的模块的依赖路径上存在变动的模块,暂时的方法是将其简单做 SHA1 替换,暨将旧 SHA1 替换为新 SHA1,但不适用于签名改变的情况,不过签名改变这个未经变动的模块必然也需要改变,但是由于宏展开的存在,不能完全保证不会发生错误情况。且依赖路径的 SHA1 替换成功性未经完整测试,可能出现未定义行为。
## `t.CDefine` 深度解析
`t.CDefine` 是 Viper 中最特殊的类型——它不是数据类型,而是**编译时元指令**,控制编译器的代码生成行为。
### `t.CDefine` 常量
```python
MAX_SIZE: t.CDefine = 1024
PAGE_SIZE: t.CDefine = 4096
FA_READ: t.CDefine = 0x01
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
```
**编译行为**:
1. **阶段一**:存根生成器保留 `t.CDefine` 常量的完整定义(包括赋值值),因为其他模块可能引用此常量
2. **阶段一**:`.stub.ll` 声明生成器**跳过** `t.CDefine` 常量,不生成 `external global` 声明
3. **阶段二**:翻译器遇到 `t.CDefine` 常量的引用时,直接内联其值,不生成任何加载指令
这意味着 `t.CDefine` 常量在 LLVM IR 层面**不存在**——它们在编译时被完全展开,等价于 C 的 `#define` 宏。
### `t.CDefine` 函数
当函数的返回类型注解包含 `t.CDefine` 时,该函数被编译器视为**宏函数**:
```python
def MAKE_FLAG(bit) -> t.CDefine:
return (1 << bit)
def GET_PAGE_ORDER(size) -> t.CDefine:
return size // PAGE_SIZE
```
**编译行为**:
1. **阶段一**:存根生成器保留 `t.CDefine` 函数的**完整函数体**(不像普通函数那样只保留签名)
2. **阶段二**:翻译器遇到 `t.CDefine` 函数的调用时,将调用内联展开为函数体的计算结果
`t.CDefine` 函数在 LLVM IR 中**不生成函数定义**——它们是纯粹的编译时宏。
### `t.CDefine` 与类型组合
`t.CDefine` 可以与具体类型组合使用,为常量指定底层类型:
```python
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
DATA_SEG: t.CDefine | t.CUInt16T = 0x10
```
这表示常量在类型检查时被视为 `t.CUInt16T`(`uint16_t`),但在代码生成时仍然内联展开。
## `t.CExport` 深度解析
`t.CExport` 控制函数的符号可见性,是 SHA1 命名空间的豁免机制。
### 语义
| 修饰 | LLVM IR 函数名 | 链接可见性 | 用途 |
|------|---------------|-----------|------|
| 无修饰 | `@<SHA1>.funcname` | 模块内部 | 模块私有函数 |
| `t.CExport` | `@funcname` | 全局可见 | 对外暴露的 API |
| `t.CExtern` | `@funcname`(仅声明) | 外部定义 | 引用外部函数 |
### 使用场景
```python
# 入口函数 — 必须全局可见,链接器需要找到它
def _start() -> t.CInt | t.CExport:
return kernel_main()
# 驱动接口 — 其他模块/应用需要调用
def init() -> t.CVoid | t.CExport:
uart_init()
# 内部辅助函数 — 不需要全局可见,自动加 SHA1 前缀
def _helper() -> t.CInt:
return 42
```
### `t.CExport` 在存根中的表现
在 `.vpi` 存根文件中,`t.CExport` 函数与普通函数一样只保留签名(添加 `c.State`,表示单纯声明),但 `t.CExport` 标记被保留在返回类型注解中。阶段一的 `DeclarationGenerator` 检查 `t.CExport` 标记来决定是否添加 SHA1 前缀。
## `t.CInline` 深度解析
`t.CInline` 标记函数为内联函数,编译器在调用点直接展开函数体。
### 语义
```python
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
return a + b
```
**编译行为**:
1. **阶段二预收集**:`_collect_inline_symbols` 扫描 `includes/` 目录中被引用的模块,收集所有 `t.CInline` 函数的 AST body
2. **翻译时展开**:遇到内联函数调用时,将函数体的 AST 直接嵌入调用点,替换参数为实际值
3. **不生成独立函数**:内联函数不生成 LLVM IR 函数定义(除非也被非内联调用)
### `t.CInline` 与 `t.CDefine` 函数的区别
| 特性 | `t.CInline` | `t.CDefine` 函数 |
|------|------------|-----------------|
| 展开时机 | 阶段二翻译时 | 阶段二翻译时 |
| 类型检查 | 完整的参数和返回类型检查 | 返回类型为宏,类型检查较弱 |
| 存根表示 | 签名 + `c.State` | 完整函数体 |
| LLVM IR | 可能生成函数定义(如果有非内联调用) | 不生成函数定义 |
| 适用场景 | 性能关键的短函数 | 编译时常量和宏计算 |
## 目标平台
默认目标三元组:`x86_64-none-elf`
数据布局:`e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`
## Hello World 示例
```python
import t, c
def main() -> t.CInt | t.CExport:
print("Hello, ViperOS!\n")
return 0
```
编译流程:
1. **阶段一**:计算 `main.vp` 的 SHA1,生成 `.vpi` 存根(`def main() -> t.CInt | t.CExport | c.State: pass`),生成 `.stub.ll` 声明(`declare i32 @main()`,因为是 CExport 不加前缀)
2. **阶段二**:翻译 `main.vp` 为 `<SHA1>.ll`,嵌入 `print` 对应的 `puts`/`printf` 声明,生成 `define i32 @main()` 函数体
3. **编译**:`llc` 将 `.ll` 编译为 `.o`
4. **链接**:`ld.lld` 链接为可执行文件