Skip to content

编译原理实验报告

:material-circle-edit-outline: 约 1104 个字 :material-clock-time-two-outline: 预计阅读时间 4 分钟

实验三:中间代码生成 2025 年 4 月 21 日

姓名:俞仲炜 学号:3220104929

功能清单

本次实验的核心任务是将抽象语法树翻译为中间代码,我选择使用的中间代码为 Zero IR。

我们翻译 AST 为 IR tree 的逻辑是,将 AST 各个结点转化为 IR tree 结点,后者负责存储原程序的一个逻辑片段,并且可以输出对应的 IR 代码。如此我们只需要先定义各个 IR tree 结点类,再将 AST 翻译为 IR tree,便可利用 IR tree 输出原程序对应的中间代码。

IR 树结点定义

ir/ir.hpp 中包含了所有 IR 树结点类的定义。每个结点负责存储一个中间代码语句的信息,并按照 zero IR 的定义打印对应的中间代码语句。定义的具体实现重复且相对容易,故不在此赘述。

至此,我们定义好了 IR tree 的结点。

翻译 AST 为 IR tree

将 AST 翻译为 IR tree 的相关函数主要位于 /ir/ir_translator.hpp ,是为本次实验的核心部分。整体思路是递归遍历 AST,逐个处理每一个 AST 结点,将其转化为 IR tree 结点并存储。

每个翻译函数的大致思路即从 AST 结点提取出必要的信息,根据运行逻辑翻译为对应的 IR tree 结点(组)。大部分翻译函数均按图索骥地实现即可,此处选取部分简要说明,其余不再赘述。

translateVarDef 负责翻译 AST 的 VarDef 结点,需要对全局变量和局部变量进行分类处理,若是数组又需要单独讨论,若进行了初始化又需要额外处理,故实现上采用了连续、嵌套的 if-else 语句进行分类处理。特别的,我给 VarDef AST 结点补充了一个变量用于线性存储最终的初始化值,并在类型检查时顺便完成该变量的构造,最终在翻译为中间代码时能够直接使用。

类似的,translateAssignStmttranslateLVal 也需要一定的分类处理。值得一提的是,左值需要检查其类型来判断其内容是地址还是数值。

IFWHILE 的实现借助了 translateCond 函数专门负责处理条件语句,并实现了短路,具体实现逻辑采用实验文档所提供的。此外,我设置了一个 new_label 函数,与 new_temp 类似,负责提供不重复的 label 名。值得一提的是,zero IR 不支持独立进行关系运算,其只能附属在条件语句中。

至此,我们能够将 AST 翻译为 IR tree 了。

基本块划分

analysis/control_flow.hpp 中定义了 ModuleFunctionBasicBlock 类,用于表示模块、函数和基本块。我在 Module 类中添加了一个 vector 用于存储全局变量的 IR tree 结点,并在输出中间代码时优先输出全局变量。

cfg_builder 中我们对翻译得到的 IR tree 结点进行了整理,将全局变量单独存储,然后根据函数定义切割为一个一个函数进行存储,函数内又根据 Label 进一步分割为一个一个基本块进行存储。最后在输出中间代码时以基本块为单位进行输出。

至此,我们能够读取原程序文件,输出对应的中间代码文件,并能够使用解释器正确运行得到的中间代码。

亮点摘选

使用数组时计算地址偏移

IRTranslator::get_array_offset 函数专门负责根据下标计算地址偏移。地址偏移需要从符号表得到左值的维度信息,再结合下标信息便可计算偏移量。

AI 使用情况

ir/ir.hpp 实现 IR tree 结点时类,我参考/使用了 AI 生成了部分结点类的定义,因此处的定义代码存在大量重复且相对简单。AI 生成的类定义包括 class Ifclass Paramclass Decclass GetGloAddr。其余的类定义相对复杂,为人工实现。

image-20250422144815411

在排查编译错误以及调试时,有借助 AI 解析报错信息,和实验代码实现无直接关联。

image-20250422145019225

以及,继续向 AI 询问 C++编程相关问题。

image-20250422145308574