2025-12-04 的 critique of basic-lisp 指出 basic-lisp 有三个核心问题:
- 完全没有类型信息,而优化依赖类型
- tag 处理未在 IR 中明显表示
- 限制 operand 为 variable name 导致生成的代码难看
本次重新设计需要同时支持:
- 编译 meta-lisp 这样的动态类型语言(通过
value-type和 tag 指令) - 编译类似 C 的静态类型系统编程语言(通过具体类型和内存指令)
类型
int64-type/float64-type/bool-type/void-type:原始标量类型。pointer-type:opaque pointer,不带元素类型。类型信息由load/store指令的 content-type 携带。value-type:tagged dynamic value。所有 meta-lisp 值统一用此类型。named-type:通过名称引用已定义的 struct 类型。仅用于使用位置(参数类型等),不用于定义位置。struct-type:在struct-definition中定义的结构体字段布局。字段列表为((field-name . type) ...)。名称由struct-definition层面的name字段承载。arrow-type:描述函数签名,文本语法为(-> <arg-type> ... <ret-type>)——最后一个为ret-type,之前所有为arg-types,无需额外括号包裹参数列表。例如(-> int64-t)为零参、(-> pointer-t int64-t)为一参、(-> float64-t float64-t float64-t)为二参。用于claim和knownBinaryOps/knownUnaryOps表示指令签名。
parse 与 format 时使用常见名称:int64-t、float64-t、bool-t、void-t、pointer-t、value-t;struct 类型的引用直接用其名称(如 point-t)。
Operand
var-operand:SSA 变量引用。variable 通过定义点确定类型,operand 处不需要标记类型。int64-operand/float64-operand/bool-operand:字面量常量。int64-operand/float64-operand当前暂用 meta-lisp 的int-t/float-t承载字面量。但 meta-lisp 的 value 是 3-bit tag + 61-bit payload 的 tagged value:int-t只有 61-bit 有符号范围,float-t会截断 double 的低 3 个尾数位。自举编译 meta-lisp 时无额外精度损失(end-to-end 同受 tag 限制),但无法表达完整 64-bit 整数与精确 double。- 未来扩展为 opaque 任意精度表示(类似 JS 的 BigInt),以支持完整 64-bit 整数与精确 double(编译 C-like 静态类型语言所需)。
void-operand:void 常量。仅用于return指令返回 void。address-operand:顶层符号的地址,类型为pointer-type。查符号表确定语义——若是function-definition则可用于call/tail-call;若是variable-definition则可用于load/store/padd。
Attribute
attribute 用于存放指令的编译时常量参数——类型引用、符号名、整数值、及其列表。与 operand(运行时值)分离,均通过统一的 instr-t 结构携带。
Instruction
所有指令统一为一种结构,按 op 区分操作:
id:每条指令的唯一标识。产生值的指令id即结果变量名;不产生值的指令id由编译器生成为∅.<N>(∅.1、∅.2、...)。type:本指令的结果类型。产生值的指令为具体类型(如int64-t、value-t);不产生值的指令为void-t。op:操作名。operands:运行时值——所有 SSA 变量引用和字面量。统一遍历分析 use-def。attributes:编译时常量,由:key前缀标识,值类型为attribute-t。
统一语法
(= <id> <type> (<op> <operand> ... :<key> <attribute> ...))(<op> <operand> ... :<key> <attribute> ...) 是自包含的操作形式:<operand> ... 为运行时值,:<key> <attribute> ... 为编译时常量(以 : 前缀的 key 区分),二者同处操作形式之内。(= <id> <type> ...) 外层仅负责绑定 id 与结果类型。
各 op 的 operands 与 attributes 约定
| op | operands | attributes |
|---|---|---|
iadd isub imul idiv fadd fsub fmul fdiv shl shr bitand bitor bitxor padd and or xor | [left, right] | — |
icmp-eq icmp-ne icmp-lt icmp-le icmp-gt icmp-ge fcmp-eq fcmp-ne fcmp-lt fcmp-le fcmp-gt fcmp-ge bool-eq bool-ne pointer-eq pointer-ne value-eq value-ne | [left, right] | — |
not tag-int tag-float tag-bool to-int64 to-float64 to-bool copy | [operand] | — |
load | [pointer] | — |
call | [target, ...args] | — |
apply | [target, ...args] | — |
size-of | [] | :target-type <type> |
offset-of | [] | :struct-type <type> :path (<field> ...) |
argument | [] | :index <int> |
use | [] | — |
store | [pointer, value] | :content-type <type> |
provide | [value] | :use-site <name> |
return | [value] | — |
goto | [] | :label <name> |
branch | [condition] | :then-label <name> :else-label <name> |
tail-call | [target, ...args] | — |
tail-apply | [target, ...args] | — |
Binary op 列表
| 类别 | 类型 | op name |
|---|---|---|
| 算术 | int64 | iadd isub imul idiv |
| 算术 | float64 | fadd fsub fmul fdiv |
| 位运算 | int64 | shl shr bitand bitor bitxor |
| 指针运算 | pointer | padd |
| 逻辑 | bool | and or xor |
| 比较(可排序) | int64 | icmp-eq icmp-ne icmp-lt icmp-le icmp-gt icmp-ge |
| 比较(可排序) | float64 | fcmp-eq fcmp-ne fcmp-lt fcmp-le fcmp-gt fcmp-ge |
| 比较(仅判等) | bool | bool-eq bool-ne |
| 比较(仅判等) | pointer | pointer-eq pointer-ne |
| 比较(仅判等) | value | value-eq value-ne |
Unary op 列表
| 类别 | op name |
|---|---|
| 逻辑 | not |
| Tag 包装 | tag-int tag-float tag-bool |
| Tag 解构 | to-int64 to-float64 to-bool |
| 绑定 | copy |
说明
- 比较按「类型的比较能力」分类:每个比较 op 的 operand 类型由 op 名前缀唯一确定(
icmp-*→int64、fcmp-*→float64、bool-*→bool、pointer-*→pointer、value-*→value),结果 type 恒为bool-t。cmp隐含有序比较(含lt/le/gt/ge),只给可排序的 int64 / float64;bool / pointer / value 无有序语义、只有相等性,故用-eq/-ne而不叫 cmp。value-eq/value-ne是 value 的 identity 相等(tagged 机器字相等,即 meta-lisp 的eq?);结构相等equal?是基于它的库函数,不是基础指令。
type字段:表示本指令结果(SSA 变量)的类型。算术 / 位运算 / 逻辑 op 的 type 等于 operand 类型;比较 op 的 type 恒为bool-t;terminator 类指令的 type 恒为void-t。tag-int/tag-float/tag-bool:将原始类型值包装为value-type。to-int64/to-float64/to-bool:从value-type中解构原始类型值(运行时类型检查)。copy:创建 SSA 别名。(= x int64-t (copy y))。codegen 不为copy生成代码。load:从pointer指向的地址加载值。opaque pointer 不带元素类型,type 不可省。store:将value写入pointer指向的地址。content-type为被存储值的类型。本指令 type 为void-t。call:静态调用,target为(address f)或 SSA var。apply:动态调用value-type中的函数 / 闭包,target为 SSA var。padd:指针的字节偏移加法。base为pointer-type,offset为int64-t,结果为pointer-type。等价于base + offset字节。size-of:计算target-type的字节大小。type 为int64-t。编译时常量。offset-of:沿struct-type的字段路径path逐级计算累积字节偏移。type 为int64-t。编译时常量。argument:获取函数的第index个参数(从 0 开始)。type 为参数类型。use:从合并点读取值。type 为合并点的值类型。dest 名即合并点名。provide:向合并点use-site写入value。合并点的类型由目标(use)指令的结果类型声明。本指令 type 为void-t。return:函数返回value。type 为void-t。goto:无条件跳转到 label。type 为void-t。branch:若condition为真跳转到then-label,否则跳转到else-label。condition必须为bool-t。type 为void-t。tail-call/tail-apply:尾调用 / 尾动态调用。type 为void-t。
Block
label:block 名称,跳转目标。instrs:指令序列。最后一条指令必须为 terminator 类指令(return/goto/branch/tail-call/tail-apply)。
Claim
claim 声明一个名字的类型签名。函数用 (-> ...) arrow-type:(claim add-or-sub (-> value-t value-t value-t value-t))。变量用具体类型:(claim origin point-t)。存储于 mod-t 的 claims 中,不在 definitions 中。
Definition
struct-definition:声明结构体类型及字段布局。function-definition:- 完整类型由同名
claim提供。 blocks的第一个 block 是 entry block。参数通过(argument :index N)获取。
- 完整类型由同名
variable-definition:- 完整类型由同名
claim提供。 init为(just exp)时,静态初始化为该exp描述的数据段内容。init为nothing时,等价于 BSS 段的未初始化变量。
- 完整类型由同名
不再有 define-test——test 信息由 meta-lisp 前端编码为 function-definition(零参数,返回 bool-t)。
数据表达式 (Exp)
define-variable 的 init 使用 exp-t 描述数据段内存布局, 对标 x86 汇编的 define-data。它不是指令中的运行时值(operand-t), 而是独立的数据类型:
address-exp:对已定义符号的地址引用。文本语法(address <name>), 与指令operand中的AddressOperand写法一致。int-exp/float-exp:原始整数/浮点数字面量。宽度由所在 struct 字段类型决定。string-exp:以 null-terminated bytes 存储的字符串。文本语法(string "...")。struct-exp:引用已声明 struct 类型的字段布局,字段值递归为exp-t。 文本语法(struct <name> (<field> <exp>) ...),name必选。 字段名使用hash-t(有序 Record),与StructType.fields保持一致。pointer-exp:指向内联数据的指针,数据序列化在指针之后。 文本语法(pointer <exp>)。array-exp:定长有序数组。文本语法(array <exp> ...)。
String Table 示例
Module
语法示例
示例 1:编译 meta-lisp(动态类型)
源程序:
basic-lisp IR 文本形式:
示例 2:编译 C-like 语言(静态类型)
C 程序:
long add_or_sub(int flag, long a, long b) {
if (flag) return a + b;
else return a - b;
}IR 文本形式:
示例 3:全局变量与结构体
示例 4:比较指令
int64 有序比较(icmp-*),结果为 bool-t:
动态值的 identity 判等用 value-eq / value-ne(仅判等,无有序比较):