深入解析acwj项目第29部分:编译器重构实践

深入解析acwj项目第29部分:编译器重构实践

acwj A Compiler Writing Journey acwj 项目地址: https://gitcode.com/gh_mirrors/ac/acwj

前言

在编译器开发过程中,随着功能不断增加,代码结构会逐渐变得复杂。本文将详细分析acwj项目第29部分中进行的编译器重构工作,重点探讨符号表优化和类型系统改进这两个关键技术点。

符号表重构设计

现有符号表的问题

当前实现采用了类似SubC编译器的设计,使用一个数组来存储符号,全局符号和局部符号分别位于数组两端。这种设计存在几个明显问题:

  1. 内存空间冲突风险:需要确保全局符号和局部符号不会在数组中间相遇
  2. 函数参数处理复杂:需要将函数原型从全局端复制到局部端作为参数变量
  3. 查找效率问题:线性搜索整个符号表效率较低

计划中的改进方案

作者计划将符号表重构为多个单向链表结构:

  1. 全局符号链表:存储所有全局变量和函数
  2. 局部符号链表:存储函数内的局部变量
  3. 枚举值链表:未来实现枚举类型时使用

每个符号节点将包含两个指针:

  • 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
};

类型操作函数

新类型系统引入了一系列辅助函数:

  1. 类型判断函数
// 判断是否为整型
int inttype(int type) {
  return ((type & 0xf) == 0);
}

// 判断是否为指针类型
int ptrtype(int type) {
  return ((type & 0xf) != 0);
}
  1. 指针操作函数
// 获取指向某类型的指针类型
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]等语法,但通过基本指针操作已经可以实现类似功能。

总结与展望

本次重构虽然没有增加新功能,但为后续开发奠定了更好的基础:

  1. 类型系统更加灵活,支持多级指针
  2. 数据结构更加清晰,减少了冗余
  3. 为符号表的链表重构做好了准备

在接下来的开发中,作者计划:

  1. 实现符号表的链表结构重构
  2. 添加结构体、联合体和枚举类型的支持
  3. 完善指针运算和数组访问功能

这些改进将使编译器能够处理更复杂的C语言特性,向着功能完整的C编译器目标又迈进了一步。

acwj A Compiler Writing Journey acwj 项目地址: https://gitcode.com/gh_mirrors/ac/acwj

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁姣晗Nessia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值