深入解析acwj项目第29部分:编译器重构实践
acwj A Compiler Writing Journey 项目地址: https://gitcode.com/gh_mirrors/ac/acwj
前言
在编译器开发过程中,随着功能不断增加,代码结构会逐渐变得复杂。本文将详细分析acwj项目第29部分中进行的编译器重构工作,重点探讨符号表优化和类型系统改进这两个关键技术点。
符号表重构设计
现有符号表的问题
当前实现采用了类似SubC编译器的设计,使用一个数组来存储符号,全局符号和局部符号分别位于数组两端。这种设计存在几个明显问题:
- 内存空间冲突风险:需要确保全局符号和局部符号不会在数组中间相遇
- 函数参数处理复杂:需要将函数原型从全局端复制到局部端作为参数变量
- 查找效率问题:线性搜索整个符号表效率较低
计划中的改进方案
作者计划将符号表重构为多个单向链表结构:
- 全局符号链表:存储所有全局变量和函数
- 局部符号链表:存储函数内的局部变量
- 枚举值链表:未来实现枚举类型时使用
每个符号节点将包含两个指针:
next
指针:用于构建主链表param
指针:专门用于函数参数链表
这种设计将带来以下优势:
- 函数参数可以直接引用原型参数列表,无需复制
- 查找全局符号时可以跳过参数列表
- 内存使用更加灵活,没有固定大小限制
类型系统重构
原有类型编码方式
原类型系统采用简单枚举,为每种指针类型单独定义:
enum {
P_NONE, P_VOID, P_CHAR, P_INT, P_LONG,
P_VOIDPTR, P_CHARPTR, P_INTPTR, P_LONGPTR
};
这种设计限制了指针层数,且代码中需要处理大量特定类型判断。
新的类型编码方案
新方案采用位域编码方式:
- 低4位表示指针层级(0=非指针,1=一级指针,2=二级指针等)
- 高4位表示基本类型
enum {
P_NONE,
P_VOID=16,
P_CHAR=32,
P_INT=48,
P_LONG=64
};
类型操作函数
新类型系统引入了一系列辅助函数:
- 类型判断函数:
// 判断是否为整型
int inttype(int type) {
return ((type & 0xf) == 0);
}
// 判断是否为指针类型
int ptrtype(int type) {
return ((type & 0xf) != 0);
}
- 指针操作函数:
// 获取指向某类型的指针类型
int pointer_to(int type) {
return (type + 1);
}
// 获取指针指向的类型
int value_at(int type) {
return (type - 1);
}
这种设计使得类型系统更加灵活,支持最多15级指针,且代码更加简洁。
代码生成器的调整
类型系统重构后,代码生成器也需要相应调整。以cgprimsize()
函数为例:
int cgprimsize(int type) {
if (ptrtype(type)) return (8); // 所有指针类型都是8字节
switch (type) {
case P_CHAR: return (1);
case P_INT: return (4);
case P_LONG: return (8);
default: fatald("Bad type in cgprimsize:", type);
}
}
解引用操作现在可以处理任意层级的指针:
int cgderef(int r, int type) {
int newtype = value_at(type); // 获取指向的类型
int size = cgprimsize(newtype); // 获取该类型大小
// 根据大小生成不同的汇编指令
switch (size) {
case 1: // 处理char类型
case 2: // 处理short类型
case 4: // 处理int类型
case 8: // 处理long和指针类型
}
}
数据结构优化
符号表结构改进
使用匿名联合体优化了符号表结构:
struct symtable {
char *name;
int type;
int stype;
int class;
union {
int size; // 数组元素数量
int endlabel; // 函数结束标签
};
union {
int nelems; // 函数参数数量
int posn; // 局部变量栈偏移
};
};
这种设计既保持了代码清晰度,又避免了命名空间污染。
抽象语法树(AST)结构改进
同样使用匿名联合体优化了AST节点:
struct ASTnode {
int op;
int type;
int rvalue;
struct ASTnode *left, *mid, *right;
union {
int intvalue; // 字面量值
int id; // 标识符符号位置
int size; // 缩放因子大小
};
};
这使得访问节点属性更加直观,例如:
cgloadglob(n->left->id, n->op); // 替代原来的n->left->v.id
测试验证
为了验证多级指针的支持,作者创建了测试程序:
int main(int argc, char **argv) {
char *argument;
argument = *argv; // 解引用二级指针
argv = argv + 1; // 指针算术运算
}
虽然目前还不支持argv++
和argv[i]
等语法,但通过基本指针操作已经可以实现类似功能。
总结与展望
本次重构虽然没有增加新功能,但为后续开发奠定了更好的基础:
- 类型系统更加灵活,支持多级指针
- 数据结构更加清晰,减少了冗余
- 为符号表的链表重构做好了准备
在接下来的开发中,作者计划:
- 实现符号表的链表结构重构
- 添加结构体、联合体和枚举类型的支持
- 完善指针运算和数组访问功能
这些改进将使编译器能够处理更复杂的C语言特性,向着功能完整的C编译器目标又迈进了一步。
acwj A Compiler Writing Journey 项目地址: https://gitcode.com/gh_mirrors/ac/acwj
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考