# 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）与具体 opcode（Add/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+union（Rust 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: ...
```

### 指令 opcode（CEnum）

```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 而非胖节点）
