snapshot before regression test

This commit is contained in:
t
2026-07-18 19:25:40 +08:00
commit 796222a300
2295 changed files with 206453 additions and 0 deletions

301
includes/llvmlite/README.md Normal file
View File

@@ -0,0 +1,301 @@
# llvmlite 设计方案
用 TransPyC 自身实现的轻量级 LLVM IR 文本生成库,替代 Python `llvmlite` 依赖,为自举(见 `wiki/13-bootstrapping.md` 路径 B铺路。
## 1. 目标与定位
- **输入**:类型/值/指令的语义对象
- **输出**:合法的 LLVM IR 文本(`.ll`),可直接喂给 `llc`
- **不做什么**:不解析 IR、不做优化、不绑定 LLVM C API纯文本生成
- **自举约束**:本库必须能用当前 TransPyC 编译器完整编译,不得依赖任何 Python 运行时
## 2. 核心设计决策
### 2.1 类型系统用 `t.REnum`tagged union
LLVM 类型变体少、无通用字段、字段都是指针/小整数、操作主要通过 `match` 分派 —— 完全契合 REnum 的 tag+union 模型。
### 2.2 指令 opcode 用 `t.CEnum`
指令类别Binary/Unary/Cast/Memory/Terminator/Compare与具体 opcodeAdd/Sub/Load/Store/Br/Ret/...)用 `t.CEnum` 组织,类型安全且零开销。
### 2.3 IR 文本生成用 `viperlib.snprintf`
所有 IR 文本通过 `viperlib.snprintf(buf, size, fmt, *args)` 格式化写入缓冲区,避免手写逐字符拼接。`snprintf` 已支持宽度/精度/符号/`%s`/`%d`/`%x` 等,足够覆盖 IR 语法。
### 2.4 对象分配用普通类 + `mpool`
`mbuddy` 一样用普通类(无 `__new__`),由调用方注入 `mpool.MPool` 做批量分配。库内部不持有全局 `mbuddy`,遵循项目约定(`_mbuddy` 指针由 `os.py` 或调用方设置)。
---
## 3. 为什么 AST 不用 REnum而 llvmlite 用?
用户质疑REnum 是 tag+unionRust enum配合 `match` 模式匹配,按理很适合 AST。下面解释两者的适配性差异。
### 3.1 REnum 的硬限制(来自 `HandlesClassDef._EmitREnumLlvm`
1. **无通用字段共享**:每个 variant 独立声明字段,通用字段(如 `lineno`/`child`/`next`)必须在每 variant 重复声明。
2. **共享最大变体尺寸**:布局为 `{ i32 __tag, <max_variant_payload> }`,小变体浪费内存。
3. **payload 仅位置绑定**`match` 通过位置绑定 payload不能直接 `node.child` 属性访问 payload 字段。
4. **无方法**variant 不能挂方法。
### 3.2 AST 不适合 REnum 的四个原因
| 维度 | AST 需求 | REnum 限制 | 冲突 |
|------|----------|------------|------|
| 变体数量 | 70+ 节点类型 | 共享最大变体尺寸 | 内存膨胀(每节点都占最大变体空间) |
| 通用字段 | lineno/child/next/parent 等 ~8 个字段全节点共享 | 无通用字段共享 | 要么每 variant 重复声明(冗长),要么放弃统一访问 |
| 访问模式 | 频繁直接 `node.child`/`node.next`/`node.lineno` | payload 仅位置绑定,需 match 分派 | 遍历代码爆炸(每次访问都要 match |
| 字段复用 | `int_val`/`str_val`/`op` 等字段按 vtype 复用语义 | 每 variant 独立 payload | 无法复用 |
AST 采用"胖节点"设计(所有节点共用一个 `AST` struct`vtype` 区分):一次 `malloc`、统一链表遍历(`child`/`next`)、字段复用。这与 REnum"每变体独立 payload"的哲学根本冲突。
### 3.3 llvmlite 的 LLVMType 适合 REnum 的原因
| 维度 | LLVMType 需求 | REnum 特性 | 契合度 |
|------|---------------|------------|--------|
| 变体数量 | ~10 个Int/Ptr/Func/Array/Struct/Void/Float/Label/... | 共享最大变体尺寸 | 浪费可接受(最大变体 ~24 字节) |
| 通用字段 | 无(每变体字段不同) | 无通用字段共享 | 完美匹配 |
| 访问模式 | 类型操作天然通过 match 分派("是 Int 取 bits是 Ptr 取 pointee" | payload 位置绑定 + match | 天然契合 |
| 字段尺寸 | 都是指针(8B)或小整数(4B) | 共享最大变体 | 浪费小 |
**结论**REnum 是"少变体、无共性、match 分派"场景的最佳工具 —— llvmlite 类型系统正好如此AST 正好相反。
---
## 4. 文件结构
```
includes/llvmlite/
├── __init__.py # 公共导出 + 便捷工厂函数
├── __types.py # LLVMType (REnum) + 类型构造/打印
├── __values.py # Value/Constant/SSA 值表示
├── __module.py # Module 容器(函数列表 + 目标三元组 + 输出 .ll
├── __function.py # Function + BasicBlock + 参数管理
├── __builder.py # IRBuilder指令发射 + SSA 命名 + 块跳转)
└── README.md # 本文件
```
### 职责划分
- `__types.py``LLVMType` REnum 定义 + `TypePrint(buf, ty)` 将类型序列化为 IR 文本(如 `i32`/`i32*`/`{i32, i8*}`
- `__values.py``Value` 表示一个 SSA 值(`%0`/`%result`/常量),含类型指针 + 名字 + 是否常量
- `__module.py``Module` 持有函数链表 + 目标三元组 + 数据布局,`ModulePrint` 输出完整 `.ll`
- `__function.py``Function` 持有基本块链表 + 参数 + 返回类型;`BasicBlock` 持有指令文本缓冲
- `__builder.py``IRBuilder` 游标式 API`build_add`/`build_load`/`build_br`/... 发射指令到当前块
---
## 5. 类型系统设计REnum
```python
import t
from stdint import *
class LLVMType(t.REnum):
# 变体 Int整数类型
IntBits: t.CInt # 位宽 1/8/16/32/64/128
# 变体 Ptr指针类型
PtrPointee: LLVMType | t.CPtr # 指向的类型(自引用用指针)
# 变体 Func函数类型
FuncRet: LLVMType | t.CPtr # 返回类型
FuncParams: LLVMType | t.CPtr # 参数类型链表头
FuncPCount: t.CInt # 参数数量
# 变体 Array数组类型
ArrayElem: LLVMType | t.CPtr # 元素类型
ArrayCount: t.CInt # 元素数量
# 变体 Struct结构体类型
StructHead: LLVMType | t.CPtr # 字段类型链表头
StructCount: t.CInt # 字段数量
# 变体 Float浮点类型
FloatBits: t.CInt # 16/32/64/128
# 变体 Void / Label / Metadata无 payload
# REnum 允许空变体,仅靠 __tag 区分)
```
### 类型工厂(`__init__.py` 导出)
```python
def Int1() -> LLVMType | t.CPtr: ... # i1
def Int8() -> LLVMType | t.CPtr: ... # i8
def Int32() -> LLVMType | t.CPtr: ... # i32
def Int64() -> LLVMType | t.CPtr: ... # i64
def Ptr(pointee: LLVMType | t.CPtr) -> LLVMType | t.CPtr: ...
def Func(ret: LLVMType | t.CPtr, params: LLVMType | t.CPtr, count: t.CInt) -> LLVMType | t.CPtr: ...
def Void() -> LLVMType | t.CPtr: ...
```
### 类型打印match 分派)
```python
def TypePrint(buf: t.CChar | t.CPtr, size: t.CSizeT, ty: LLVMType | t.CPtr):
match ty:
case LLVMType.Int: # 位置绑定 IntBits
viperlib.snprintf(buf, size, "i%d", ty.IntBits)
case LLVMType.Ptr: # 位置绑定 PtrPointee
TypePrint(inner, size2, ty.PtrPointee)
# 拼接 "*"
case LLVMType.Func:
# 打印 ret (params)
case LLVMType.Void:
string.strcpy(buf, "void")
...
```
---
## 6. Value / SSA 设计
```python
class Value:
Ty: LLVMType | t.CPtr # 值的类型
Name: t.CChar | t.CPtr # SSA 名("%0"/"%result")或常量文本
IsConst: t.CInt # 1=常量字面量0=SSA 临时值
Next: Value | t.CPtr # 链表(函数内的值池)
```
`IRBuilder` 维护一个递增的 `%N` 计数器,每发射一条产生结果的指令就分配一个新名。
---
## 7. IRBuilder 设计
游标式 API跟踪当前 `BasicBlock`,发射指令文本到块的缓冲区。
```python
class IRBuilder:
Func: Function | t.CPtr # 所属函数
CurBlock: BasicBlock | t.CPtr # 当前插入点
Counter: t.CInt # SSA 名计数器
def build_add(self, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr:
# 1. 分配 SSA 名 %N
# 2. snprintf(buf, size, " %%%d = add %s %s, %s\n", N, ty, lhs, rhs)
# 3. 追加到 CurBlock 缓冲
# 4. 返回新 Value
def build_load(self, ty: LLVMType | t.CPtr, ptr: Value | t.CPtr) -> Value | t.CPtr: ...
def build_store(self, val: Value | t.CPtr, ptr: Value | t.CPtr): ...
def build_br(self, target: BasicBlock | t.CPtr): ...
def build_cond_br(self, cond: Value | t.CPtr, then: BasicBlock | t.CPtr, else_: BasicBlock | t.CPtr): ...
def build_ret(self, val: Value | t.CPtr): ...
def build_alloca(self, ty: LLVMType | t.CPtr) -> Value | t.CPtr: ...
def build_gep(self, ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr: ...
def build_call(self, callee: t.CChar | t.CPtr, args: Value | t.CPtr, ret_ty: LLVMType | t.CPtr) -> Value | t.CPtr: ...
```
### 指令 opcodeCEnum
```python
class IROp(t.CEnum):
# Terminator
Ret: t.CEnum = 1
Br: t.CEnum = 2
CondBr: t.CEnum = 3
Switch: t.CEnum = 4
# Binary
Add: t.CEnum = 10
Sub: t.CEnum = 11
Mul: t.CEnum = 12
SDiv: t.CEnum = 13
# Memory
Load: t.CEnum = 20
Store: t.CEnum = 21
Alloca: t.CEnum = 22
Gep: t.CEnum = 23
# Cast
BitCast: t.CEnum = 30
SExt: t.CEnum = 31
Trunc: t.CEnum = 32
# Compare
ICmp: t.CEnum = 40
# Call
Call: t.CEnum = 50
```
---
## 8. IR 文本生成snprintf 用法)
每条指令发射用 `viperlib.snprintf` 格式化到块缓冲区。示例:
```python
import viperlib
def _emit_add(block: BasicBlock | t.CPtr, name: t.CChar | t.CPtr,
ty_str: t.CChar | t.CPtr, lhs: t.CChar | t.CPtr, rhs: t.CChar | t.CPtr):
line: t.CChar | t.CPtr = mpool.alloc(128) # 行缓冲
viperlib.snprintf(line, 128, " %s = add %s %s, %s\n", name, ty_str, lhs, rhs)
_block_append(block, line)
```
### Module 输出
```python
def ModulePrint(mod: Module | t.CPtr, out_path: t.CChar | t.CPtr):
# 1. 写头部target triple / datalayout
# 2. 遍历函数,写 declare/define
# 3. 每函数:签名 + 基本块 + 指令
# 用 snprintf 拼接写到文件viperio 或 w32/win32file
```
---
## 9. 内存管理
- 库内所有对象从 `mpool.MPool` 分配(像 `ast` 库一样)
- `IRBuilder`/`Module`/`Function` 不持有 `mbuddy`,由调用方传入 `mpool`
- 文本缓冲区按行分配每指令一行128 字节起步),避免频繁 realloc
---
## 10. 基础构建范围(第一阶段)
最小可用集,能生成一个完整的小程序 IR
1. **类型**`IntType`(1/8/16/32/64) + `PointerType` + `FunctionType` + `VoidType` + `StructType`
2. **值**`Value` + 常量整数字面量
3. **Module/Function/BasicBlock**:容器与链表
4. **IRBuilder** 指令子集:
- `alloca` / `load` / `store`
- `add` / `sub` / `mul` / `sdiv`
- `icmp` (eq/ne/sgt/sge/slt/sle)
- `br` / `cond_br` / `ret`
- `call`(外部函数)
- `bitcast` / `sext` / `trunc`
- `getelementptr`
5. **ModulePrint**:输出完整 `.ll` 文件
### 后续扩展路线
- **第二阶段**:浮点指令、`phi` 节点、`switch`、字符串常量全局、属性(`nounwind`/`nocapture`
- **第三阶段**:内联汇编、`atomic` 指令、`va_arg`、精确的 SSA 验证支配边界、phi 完整性)
- **第四阶段**:对接 TransPyC 的 `LLVMCG`,替换 Python `llvmlite`,完成路径 B 自举
---
## 11. 测试计划
独立测试项目(`Test/LLvmLiteTest/`
1. **类型打印测试**:构造各 `LLVMType``TypePrint` 输出,对比预期字符串
2. **小函数生成**:用 `IRBuilder` 生成 `i32 add(i32 a, i32 b) { ret i32 %r }``ModulePrint``.ll`,用 `llc` 编译验证语法合法
3. **match 分派测试**:构造各变体 `LLVMType``match` 正确绑定 payload
4. **snprintf 格式测试**:验证 `%d`/`%s`/`%x` 在 IR 文本中的正确性
---
## 12. 与现有架构的对接点
- **`lib/core/Codegen/LLVMCG.py`**:当前用 Python `llvmlite` 生成 IR自举时改用本库
- **`includes/viperlib.py`**:提供 `snprintf` / `sprintf`
- **`includes/mpool.py`**:提供 `MPool` 内存池
- **`includes/string.py`**:提供 `strcpy`/`strlen`/`memcpy`
- **`includes/ast/`**:参考其"胖节点 + mpool + CEnum 常量"的组织方式(但类型系统用 REnum 而非胖节点)