计科210X 甘晴void 202108010XXX
(图片来源于网络,侵删)
cminus-f编译器做的事情主要如下:
在本实验中,只要完成抽象语法树到中间表示IR的生成这一步。
具体来讲,在理解cminus-f 语法与语义的基础上,参考cminusf_builder.hpp
文件以及include中的文件,补充完成 cminusf_builder.cpp
中的16个visit函数,来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序。
在自动产生 IR 的过程中,利用访问者模式自顶向下遍历抽象语法树的每一个结点,调用我们补充完成的函数对每一个抽象语法树的结点进行分析,如果程序是合法的则编译应该显示Success,否则编译不通过显示Fail。
具体步骤如下:
src/cminusfc/cminusf_builder.cpp
来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序report.md
中解释该设计,遇到的困难和解决方案待完成函数如下:
调用的函数在cminusf_builder.hpp
中被定义为了void类型的函数,故返回的结果不能通过函数本身实现,只能通过全局变量来进行传递。
此外用于Param的处理的参数指针也需要全局进行传递。
设置有效的全局变量也是实验的要点。
在void CminusfBuilder::visit(ASTVar &node)
,void CminusfBuilder::visit(ASTParam &node)
等函数中,对于数组类型都做了特殊的判断和处理,而且判断的方式有所不同。
如在前者中,使用node.num != nullptr
进行判断,在后者中使用node.expression != nullptr
进行判断。而相应的处理也是不同的。
一个变量,在放入表的时候,要考虑它的作用域,这显然是符合我们常识的。
如在实现void CminusfBuilder::visit(ASTFunDeclaration &node)
函数中创建函数之后,要进入函数的作用域再创建基本块,并在处理完函数体内的语句之后退出基本块。
同样的还有在实现void CminusfBuilder::visit(ASTCompoundStmt &node)
函数中处理复杂语句时,要先进入本基本块的作用域,并在结束之后退出这个作用域。这也是符合我们常识的,考虑下面这个场景,处理一个复杂(比如像if语句或者while语句后面的{}内部的语句块)语句块时,有可能我们会定义一些临时变量,那么显然这个临时变量的生命周期只在这个语句块内,如果不引入作用域,可能会导致溢出的后果。
对于作用域的使用也是实验的一个难点。
完成本实验需要阅读的资料实在是太多了。
需要吃透的文件至少有
对于一个变量,它的原型或者说定义,往往要跳转好几次才能找到,这个理解代价还是很大的。
实验文档提到,实验框架中提示如下:
在include/cminusf_builder.hpp
中,助教定义了一个用于存储作用域的类Scope
。它的作用是辅助我们在遍历语法树时,管理不同作用域中的变量。它提供了以下接口:
// 进入一个新的作用域
void enter();
// 退出一个作用域
void exit();
// 往当前作用域插入新的名字->值映射
bool push(std::string name, Value *val);
// 根据名字,寻找到值
Value* find(std::string name);
// 判断当前是否在全局作用域内
bool in_global();
在实验时,我们需要根据语义合理调用enter
与exit
,并且在变量声明和使用时正确调用push
与find
。在类CminusfBuilder
中,有一个Scope
类型的成员变量scope
,它在初始化时已经将input
、output
等函数加入了作用域中。因此,在进行名字查找时不需要顾虑是否需要对特殊函数进行特殊操作。
补充了几个宏,所谓宏实际上是为了避免代码重复,所以如果一段代码经常用到,就可以把它封装成宏(或者函数)。如果没有实际上关系也不大,就是把这个代码多写几遍。
1.补充了获取类型的宏,分别获取int32型的类型和float的类型。
2.补充了整型,浮点型,指针类型判断的宏,能够判断该指针的类型是否是指定的。
如下:
#define Int32Type \
Type::get_int32_type(module.get()) /* 获取int32类型 */
#define FloatType \
Type::get_float_type(module.get()) /* 获取float类型 */
#define checkInt(num) \
num->get_type()->is_integer_type() /* 整型判断 */
#define checkFloat(num) \
num->get_type()->is_float_type() /* 浮点型判断 */
#define checkPointer(num) \
num->get_type()->is_pointer_type() /* 指针类型判断 */
全局变量ret用于节点返回值。arg用于传递参数。ifAssign表示访问Var节点时,应该返回值还是变量地址。
Value *Res; /* 存储返回的结果 */
Value *arg; /* 存储参数指针,用于Param的处理 */
bool need_as_address = false; /* 标志是返回值还是返回地址 */
参考实验文档提及的有关logging,在cminus_compiler-2023-fall\Documentations\common
目录下阅读logging.md
。
助教为大家设计了一个C++简单实用的分级日志工具。该工具将日志输出信息从低到高分成四种等级:DEBUG
,INFO
,WARNING
,ERROR
。通过设定环境变量LOGV
的值,来选择输出哪些等级的日志。LOGV
的取值是0~3,分别对应到上述的4种级别(0:DEBUG
,1:INFO
,2:WARNING
,3:ERROR
)。此外输出中还会包含打印该日志的代码所在位置。
【注意】如需使用LOGGING,需要在cminusf_builder.cpp
最前面加上#include "logging.hpp"
使用方法大致如下:
#include "logging.hpp"
// 引入头文件
int main(){
LOG(DEBUG) << "This is DEBUG log item.";
// 使用关键字LOG,括号中填入要输出的日志等级
// 紧接着就是<<以及日志的具体信息,就跟使用std::cout一样
LOG(INFO) << "This is INFO log item";
LOG(WARNING) << "This is WARNING log item";
LOG(ERROR) << "This is ERROR log item";
return 0;
}
具体调试程序时使用环境变量(Linux中采取写在最前面的方式),可以调用LOG输出日志信息
LOGV=0 ./test_logging
类似于这样
如我们觉得程序已经没有问题了,不想看那么多的DEBUG信息,那么我们就可以设定环境变量LOGV=1
,选择只看级别大于等于1的日志信息。当然LOGV
值越大,日志的信息将更加简略。如果没有设定LOGV
的环境变量,将默认不输出任何信息。
我在所有函数的开头都添加了DEBUG级别的LOG,照理INFO,WARNING,ERROR等级别的LOG也是要写的,但由于时间仓促,就没有具体去一个一个分别写出来。其中有些用std::cout<<
的方式进行输出了。
LOG功能还是值得看看的。平时使用c++,如果没有用DEVC++等这些IDE而是直接手动配置gcc然后在终端输出信息的话,或者是Linux环境下的c/c++,在终端输出的编译信息,那么应该对终端弹出的ERROR和WARNING这些不陌生。或者使用Linux环境下的rust时,也能在终端看到ERROR这些信息,这些都是用类似于LOG的功能嵌在编译器内实现的。
Visit函数的实现在cminusf_builder.cpp
中完成。
如果使用VSCODE在cminus_compiler-2023-fall
文件夹下打开cminusf_builder.cpp
,配置好相关的设置,单机结构或函数,会跳转到它定义的地方,这个功能十分方便。
在ast.hpp
内定义了节点结构体的类型,包括这个节点结构体有哪些成员变量是我们可以直接调用的,同时需要弄清楚节点结构体成员变量的含义。同时每个该结构体还定义了一个纯虚函数virtual void accept(ASTVisitor &) override;
或者virtual void accept(ASTVisitor &) override final;
,在其命名空间下的实现由ast.cpp
对应命名空间的函数完成实现
在ast.cpp
内给出了ast.hpp
中纯虚函数的实现。
比对cminus_compiler-2023-fall\Documentations\common\cminusf.md
中的cminus-f的语法和语义,包括补充内容,完成cminusf_builder.cpp
。
下面是每个具体函数的实现。
该节点结构定义如下。
struct ASTProgram : ASTNode {
virtual void accept(ASTVisitor &) override final;
std::vector<std::shared_ptr<ASTDeclaration>>
declarations;
};
cminus-f语法要求:
一个程序
中至少要有一个声明
,且最后一个声明
必须是 void main(void)
形式的函数声明
。
(样例好像没有检查这个,不写检查void好像也可以过,但还是写一下)
根据要求,实现如下。
/* Program, 程序, program->declaration-list */
void CminusfBuilder::visit(ASTProgram &node)
{
// ASTProgram有一个std::vector<std::shared_ptr<ASTDeclaration>>类型的向量
// 我们要对这个向量进行遍历
LOG(DEBUG) << "Program\n";
// 语义检查
if (node.declarations.size() == 0)
{
std::cout << "ERROR: 该程序中没有声明。\n";
return;
}
if (!(node.declarations.back()->id == "main" && node.declarations.back()->type == TYPE_VOID))
{
std::cout << "ERROR: 最后一个声明不是void main(void)\n";
return;
}
for (auto decl : node.declarations) /* 遍历declaration-list子结点 */
decl->accept(*this); /* 处理每一个declaration */
return;
}
该节点结构定义如下:
struct ASTNum: ASTFactor {
virtual void accept(ASTVisitor &) override final;
CminusType type;
union {
int i_val;
float f_val;
};
};
数值节点没有子节点,直接进行处理,根据type确认数值类型,然后将值保存到全局变量中。根据语义规则,只能有整型和浮点数两个类型。分别赋值即可。
根据要求,实现如下:
/* Num,对数值进行处理 */
void CminusfBuilder::visit(ASTNum &node)
{
// ASTNum中有两个成员变量
// CminusType type; 类型决定这片空间的读取方式
// 联合体union{ int i_val; float f_val;}是这片空间保存的值
LOG(DEBUG) << "Num\n";
if (node.type == TYPE_INT) /* 若为整型 */
// 调用ConstantInt中的API
Res = ConstantInt::get(node.i_val, module.get()); /* 获取结点中存储的整型数值 */
else if (node.type == TYPE_FLOAT) /* 若为浮点型 */
// 调用ConstantFP中的API
Res = ConstantFP::get(node.f_val, module.get()); /* 获取结点中存储的浮点型数值 */
return;
}
该节点结构定义如下:
struct ASTVarDeclaration: ASTDeclaration {
virtual void accept(ASTVisitor &) override final;
CminusType type;
std::shared_ptr<ASTNum> num;
};
根据节点的定义,节点中包含一个类型和一个指针,还有继承自ASTDeclaration的id。对于变量声明节点的处理,需要产生分配空间的IR,在处理需考虑两个维度:是否全局变量,是否数组定义(根据节点的指针是否为空区分),分4类讨论。并且要把声明的变量放入当前作用域中,保证后续使用可以找到。
根据语义规则,全局变量需要初始化为0,数组变量声明时,大小应该大于0。
根据要求,实现如下:
/* Var-Declaration, 变量声明, var-declaration -> type-specifier ID | type-specifier ID [INTEGER] */
void CminusfBuilder::visit(ASTVarDeclaration &node)
{
// ASTVarDeclaration有两个成员变量:
// CminusType type;
// std::shared_ptr<ASTNum> num;
LOG(DEBUG) << "VarDeclaration\n";
Type *tmpType; /* 类型暂存变量,用于存储变量的类型,用于后续申请空间 */
if (node.type == TYPE_INT) /* 若为整型 */
tmpType = Int32Type; /* 则type为整数类型 */
else if (node.type == TYPE_FLOAT) /* 则为浮点型 */
tmpType = FloatType; /* 则type为浮点类型 */
else
std::cout << "ERROR: 在变量声明中,只有整型和浮点型可以使用\n";
// 需考虑两个维度:是否全局变量,是否数组定义,分4类讨论
if (node.num != nullptr)
{ /* 若为数组类型 */
if (node.num->i_val <= 0)
std::cout << "ERROR: 数组长度必须大于c0\n";
/* 获取需开辟的对应大小的空间的类型指针 */
auto *arrayType = ArrayType::get(tmpType, node.num->i_val); /* 获取对应的数组Type */
auto initializer = CONST_ZERO(tmpType); /* 全局变量初始化为0 */
Value *arrayAlloca; /* 存储申请到的数组空间的地址 */
if (scope.in_global()) /* 若为全局数组,则开辟全局数组 */
arrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer);
else /* 若不是全局数组,则开辟局部数组 */
arrayAlloca = builder->create_alloca(arrayType);
scope.push(node.id, arrayAlloca); /* 将获得的数组变量加入域 */
}
else
{ /* 若不是数组类型 */
auto initializer = CONST_ZERO(tmpType); /* 全局变量初始化为0 */
Value *varAlloca; /* 存储申请到的变量空间的地址 */
if (scope.in_global()) /* 若为全局变量,则申请全局空间 */
varAlloca = GlobalVariable::create(node.id, module.get(), tmpType, false, initializer);
else /* 若不是全局变量,则申请局部空间 */
varAlloca = builder->create_alloca(tmpType);
scope.push(node.id, varAlloca); /* 将获得变量加入域 */
}
}
该节点结构定义如下:
struct ASTFunDeclaration: ASTDeclaration {
virtual void accept(ASTVisitor &) override final;
std::vector<std::shared_ptr<ASTParam>> params;
std::shared_ptr<ASTCompoundStmt> compound_stmt;
};
FunDeclaration节点包含一个形参列表param和复合语句compound-stmt。需要创建的IR是创建函数和创建函数的第一个BasicBlock的指令,然后处理复合语句。在进入函数时要进入函数作用域,创建函数时要处理参数与返回值。对于每个参数,用全局变量取出实参,调用accept函数进行处理,在Param的visit函数中完成存储空间的分配,并加入到函数作用域当中。
根据要求,实现如下:
void CminusfBuilder::visit(ASTFunDeclaration &node)
{
// ASTFunDeclaration有两个成员变量:
// std::vector<std::shared_ptr<ASTParam>> params;
// std::shared_ptr<ASTCompoundStmt> compound_stmt;
LOG(DEBUG) << "FunDeclaration\n";
// 考虑函数返回类型+函数名+参数列表+复合语句(局部声明与语句列表)
Type *retType; /* 函数返回类型 */
/* 根据不同的返回类型,设置retType */
if (node.type == TYPE_INT)
{
retType = Int32Type;
}
else if (node.type == TYPE_FLOAT)
{
retType = FloatType;
}
else if (node.type == TYPE_VOID)
{
retType = Type::get_void_type(module.get());
}
/* 根据函数声明,构造形参列表(此处的形参即参数的类型) */
std::vector<Type *> paramsType; /* 参数类型列表 */
for (auto param : node.params)
{
if (param->isarray)
{ /* 若参数为数组形式,则存入首地址指针 */
if (param->type == TYPE_INT) /* 若为整型 */
paramsType.push_back(Type::get_int32_ptr_type(module.get()));
else if (param->type == TYPE_FLOAT) /* 若为浮点型 */
paramsType.push_back(Type::get_float_ptr_type(module.get()));
}
else
{ /* 若为单个变量形式,则存入对应类型 */
if (param->type == TYPE_INT) /* 若为整型 */
paramsType.push_back(Int32Type);
else if (param->type == TYPE_FLOAT) /* 若为浮点型 */
paramsType.push_back(FloatType);
}
}
auto funType = FunctionType::get(retType, paramsType); /* retType返回结构,paramsType函数形参结构 */
auto function = Function::create(funType, node.id, module.get()); /* 创建函数 */
scope.push(node.id, function); /* 将函数加入到全局作用域 */
scope.enter(); /* 进入此函数作用域 */
auto bb = BasicBlock::create(module.get(), node.id + "_entry", function); /* 创建基本块 */
builder->set_insert_point(bb); /* 将基本块加入到builder中 */
/* 函数传参,要将实参和形参进行匹配 */
std::vector<Value *> args; /* 创建vector存储实参 */
for (auto arg = function->arg_begin(); arg != function->arg_end(); arg++)
{ /* 遍历实参列表 */
args.push_back(*arg); /* 将实参加入vector */
}
for (int i = 0; i < node.params.size(); i++)
{ /* 遍历形参列表(=遍历实参列表) */
auto param = node.params[i]; /* 取出对应形参 */
arg = args[i]; /* 取出对应实参 */
param->accept(*this); /* 调用param的accept进行处理 */
}
node.compound_stmt->accept(*this); /* 处理函数体内语句compound-stmt */
// 判断返回值的类型,根据对应的返回值类型,执行ret操作
// 这里应该是实现若没有显式返回,默认返回对应类型的0或void
if (builder->get_insert_block()->get_terminator() == nullptr)
{
if (function->get_return_type()->is_void_type())
builder->create_void_ret();
else if (function->get_return_type()->is_float_type())
builder->create_ret(CONST_FP(0.0));
else
builder->create_ret(CONST_INT(0));
}
scope.exit(); /* 退出此函数作用域 */
}
该节点结构定义如下:
struct ASTParam: ASTNode {
virtual void accept(ASTVisitor &) override final;
CminusType type;
std::string id;
// true if it is array param
bool isarray;
};
在处理参数时,要为参数分配空间,使参数能够保留在函数的作用域内。在lab3中自行编写.ll文件时直接使用参数,不进行存储,直接使用就可以实现相同的逻辑。但在将cminus转换为IR时,cminus的语义规定了每次函数调用都会产生一组独立内存的参数,因此为参数分配空间,并存入作用域。
根据要求,实现如下:
/* Param, 参数 */
void CminusfBuilder::visit(ASTParam &node)
{
// ASTParam有3个成员变量
// CminusType type; 参数类型
// std::string id; 参数名
// bool isarray; 是否数组标记
LOG(DEBUG) << "Param\n";
Value *paramAlloca; // 分配该参数的存储空间
// 区分是否为数组,为整型还是浮点型,共分为4类讨论
if (node.isarray)
{ /* 若为数组 */
if (node.type == TYPE_INT) /* 若为整型数组,则开辟整型数组存储空间 */
paramAlloca = builder->create_alloca(Type::get_int32_ptr_type(module.get()));
else if (node.type == TYPE_FLOAT) /* 若为浮点数数组,则开辟浮点数数组存储空间 */
paramAlloca = builder->create_alloca(Type::get_float_ptr_type(module.get()));
}
else
{ /* 若不是数组 */
if (node.type == TYPE_INT) /* 若为整型,则开辟整型存储空间 */
paramAlloca = builder->create_alloca(Int32Type);
else if (node.type == TYPE_FLOAT) /* 若为浮点数,则开辟浮点数存储空间 */
paramAlloca = builder->create_alloca(FloatType);
}
builder->create_store(arg, paramAlloca); /* 将实参load到开辟的存储空间中 */
// 此处arg通过全局变量传递、
// 函数原型:StoreInst *create_store(Value *val, Value *ptr)
scope.push(node.id, paramAlloca); /* 将参数push到域中 */
}
该节点结构定义如下:
struct ASTCompoundStmt: ASTStatement {
virtual void accept(ASTVisitor&) override final;
std::vector<std::shared_ptr<ASTVarDeclaration>> local_declarations;
std::vector<std::shared_ptr<ASTStatement>> statement_list;
};
每个函数内部都有一个复合语句,根据ASTCompoundStmt的定义,复合语句由局部声明和一系列语句构成。只需要逐个调用相应的accept函数,不需要产生IR。
根据要求,实现如下:
void CminusfBuilder::visit(ASTCompoundStmt &node)
{
// ASTCompoundStmt有两个成员变量
// std::vector<std::shared_ptr<ASTVarDeclaration>> local_declarations;
// std::vector<std::shared_ptr<ASTStatement>> statement_list;
LOG(DEBUG) << "CompoundStmt\n";
scope.enter(); /* 进入函数体内的作用域 */
for (auto local_declaration : node.local_declarations) /* 遍历 */
local_declaration->accept(*this); /* 处理每一个局部声明 */
for (auto statement : node.statement_list) /* 遍历 */
statement->accept(*this); /* 处理每一个语句 */
scope.exit(); /* 退出作用域 */
}
该节点结构定义如下:
struct ASTExpressionStmt: ASTStatement {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTExpression> expression;
};
ExpressionStmt对应一条表达式或空,只要表达式存在,就处理该表达式,否则不需要做处理。
根据要求,实现如下:
/* ExpressionStmt, 表达式语句, expression-stmt -> expression;
* | ; */
void CminusfBuilder::visit(ASTExpressionStmt &node)
{
// ASTExpressionStmt只有一个成员变量:
// std::shared_ptr<ASTExpression> expression;
LOG(DEBUG) << "ExpressionStmt\n";
if (node.expression != nullptr) /* 若对应表达式存在 */
node.expression->accept(*this); /* 则处理该表达式 */
}
该节点结构定义如下:
struct ASTSelectionStmt: ASTStatement {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTExpression> expression;
std::shared_ptr<ASTStatement> if_statement;
// should be nullptr if no else structure exists
std::shared_ptr<ASTStatement> else_statement;
};
SelectionStmt包含一个条件表达式,一个if语句块,还有可能存在的else语句块。先处理表达式,产生条件跳转语句。
若指向else语句块的指针为空,就说明只有if语句。考虑只有if的情况,在执行到if时,应该通过br指令条件跳转到if语句块或if后的部分。
若还有else语句,则通过br指令条件跳转到if语句块或else语句块,然后从这两个语句块的结尾返回或者跳转到ifelse语句之后的部分。
在SelectionStmt的visit函数中应该至少生成三个BasicBlock,并生成br指令。根据else指针是否为空判断是否需要生成条件判断为false的BasicBlock。
根据要求,实现如下:
void CminusfBuilder::visit(ASTSelectionStmt &node)
{
// ASTSelectionStmt有3个成员变量:
// std::shared_ptr<ASTExpression> expression; 判断条件
// std::shared_ptr<ASTStatement> if_statement; if语句执行体
// std::shared_ptr<ASTStatement> else_statement; else语句执行体
// 若无else语句,else_statement == nullptr
LOG(DEBUG) << "SelectionStmt\n";
// 构建判断条件代码
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
if (resType->is_pointer_type())
{ /* 若结果为指针型,则针对指针型进行处理 */
Res = builder->create_load(Res); /* 大于0视为true */
}
else if (resType->is_integer_type())
{ /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
Res = builder->create_icmp_gt(Res, CONST_ZERO(Int32Type)); /* 大于0视为true */
}
else if (resType->is_float_type())
{ /* 若结果为浮点型,则针对浮点数进行处理 */
Res = builder->create_fcmp_gt(Res, CONST_ZERO(FloatType)); /* 大于0视为true */
}
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto trueBB = BasicBlock::create(module.get(), "true", function); /* 创建符合条件块 */
// 构建语句执行体代码
// 根据是否存在else语句分2类讨论
if (node.else_statement != nullptr)
{ /* 若存在else语句 */
auto falseBB = BasicBlock::create(module.get(), "false", function); /* 创建else块 */
builder->create_cond_br(Res, trueBB, falseBB); /* 设置跳转语句 */
// 处理trueBB
builder->set_insert_point(trueBB); /* 符合if条件的块 */
node.if_statement->accept(*this); /* 处理if语句执行体 */
auto curTrueBB = builder->get_insert_block(); /* 将块加入 */
// 处理falseBB
builder->set_insert_point(falseBB); /* else的块 */
node.else_statement->accept(*this); /* 处理else语句执行体 */
auto curFalseBB = builder->get_insert_block(); /* 将块加入 */
/* 处理返回,避免跳转到对应块后无return */
// 判断true语句中是否存在ret语句
auto trueTerm = builder->get_insert_block()->get_terminator();
// 判断else语句中是否存在ret语句
auto falseTerm = builder->get_insert_block()->get_terminator();
if (trueTerm == nullptr || falseTerm == nullptr)
{ /* 若有一方不存在return语句,则需要创建返回块 */
BasicBlock *retBB;
retBB = BasicBlock::create(module.get(), "ret", function); /* 创建return块 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
if (trueTerm == nullptr)
{ /* 若if语句执行体不存在return */
builder->set_insert_point(curTrueBB); /* 则设置跳转 */
}
else if (falseTerm == nullptr)
{ /* 若else语句执行体中不存在return */
builder->set_insert_point(curFalseBB); /* 则设置跳转 */
}
builder->create_br(retBB); /* 跳转到刚刚设置的return块 */
}
}
else
{ /* 若不存在else语句,则只需要设置true语句块和后续语句块即可 */
auto retBB = BasicBlock::create(module.get(), "ret", function); /* 后续语句块 */
builder->create_cond_br(Res, trueBB, retBB); /* 根据条件设置跳转指令 */
builder->set_insert_point(trueBB); /* true语句块 */
node.if_statement->accept(*this); /* 执行条件符合后要执行的语句 */
if (builder->get_insert_block()->get_terminator() == nullptr) /* 补充return(同上) */
builder->create_br(retBB); /* 跳转到刚刚设置的return块 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
}
}
该节点结构定义如下:
struct ASTIterationStmt: ASTStatement {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTExpression> expression;
std::shared_ptr<ASTStatement> statement;
};
while迭代语句有一个条件表达式,进行条件跳转(与if类似)。可以创建一个用于判断的ifBasicBlock,一个循环的loopBasicBlock,一个while语句后的afterWhileBasicBlock,添加相应的指令。当条件表达式为True时,进行ifBB->loopBB->ifBB的循环跳转。
根据要求,实现如下:
/* IterationStmt, while语句, iteration-stmt -> while (expression) statement */
void CminusfBuilder::visit(ASTIterationStmt &node)
{
// ASTIterationStmt有2个成员变量:
// std::shared_ptr<ASTExpression> expression; 循环判断条件
// std::shared_ptr<ASTStatement> statement; while循环执行体
LOG(DEBUG) << "IterationStmt\n";
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto conditionBB = BasicBlock::create(module.get(), "condition", function); /* 创建条件判断块 */
auto loopBB = BasicBlock::create(module.get(), "loop", function); /* 创建循环语句块 */
auto retBB = BasicBlock::create(module.get(), "ret", function); /* 创建后续语句块 */
if (builder->get_insert_block()->get_terminator() == nullptr)
builder->create_br(conditionBB); // 跳转到条件判断块
// 构建条件判断代码
builder->set_insert_point(conditionBB); /* 条件判断块 */
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
if (resType->is_pointer_type())
{ /* 若结果为指针型,则针对指针型进行处理 */
Res = builder->create_load(Res); /* 大于0视为true */
}
else if (resType->is_integer_type())
{ /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
Res = builder->create_icmp_gt(Res, CONST_ZERO(Int32Type)); /* 大于0视为true */
}
else if (resType->is_float_type())
{ /* 若结果为浮点型,则针对浮点数进行处理 */
Res = builder->create_fcmp_gt(Res, CONST_ZERO(FloatType)); /* 大于0视为true */
}
builder->create_cond_br(Res, loopBB, retBB); /* 设置条件跳转语句 */
// 构建循环语句代码
builder->set_insert_point(loopBB); /* 循环语句执行块 */
node.statement->accept(*this); /* 执行对应的语句 */
if (builder->get_insert_block()->get_terminator() == nullptr) /* 若无返回,则补充跳转 */
builder->create_br(conditionBB); /* 跳转到条件判断语句 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
}
该节点结构定义如下:
struct ASTReturnStmt: ASTStatement {
virtual void accept(ASTVisitor &) override final;
// should be nullptr if return void
std::shared_ptr<ASTExpression> expression;
};
返回语句中有一个表达式计算返回值,如果指向该返回语句的指针为空,说明没有返回值,创建一个void返回IR,否则需要调用该表达式的accept函数,并检查返回类型是否和函数的返回类型相同。
根据要求,实现如下:
/* ReturnStmt, 返回语句, return-stmt -> return;
* | return expression; */
void CminusfBuilder::visit(ASTReturnStmt &node)
{
// ASTReturnStmt只有1个成员变量
// std::shared_ptr<ASTExpression> expression; 返回语句表达式
LOG(DEBUG) << "ReturnStmt\n";
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto retType = function->get_return_type(); /* 获取返回类型 */
// 处理返回语句表达式
if (retType->is_void_type())
{
// 如果本来就是void类型的函数,需要返回void
builder->create_void_ret(); /* 则创建void返回,随后return,无需后续操作 */
return;
}
// 如果需要返回非void
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
/* 处理expression返回的结果和需要return的结果类型不匹配的问题 */
// 使用强制类型转换
if (retType->is_integer_type() && resType->is_float_type())
Res = builder->create_fptosi(Res, Int32Type);
if (retType->is_float_type() && resType->is_integer_type())
Res = builder->create_sitofp(Res, Int32Type);
builder->create_ret(Res); /* 创建return,将expression的结果进行返回 */
}
该节点结构定义如下:
struct ASTVar: ASTFactor {
virtual void accept(ASTVisitor &) override final;
std::string id;
// nullptr if var is of int type
std::shared_ptr<ASTExpression> expression;
};
根据cminus的语义说明,Var可以是整型变量,浮点变量或数组变量。如果是数组变量,需要判断下标是否为负,如果为负则添加neg_idx_except指令退出程序,否则计算对应元素的地址(gep指令)。如果是数组,则下标可能是个表达式,需要确保表达式的返回结果为整型,然后才能进行取元素的操作。
根据要求,实现如下:
void CminusfBuilder::visit(ASTVar &node)
{
// ASTVar有2个成员变量:
// std::string id; 变量名
// std::shared_ptr<ASTExpression> expression; 变量类型
// 如果变量var是int型的,这个取nullptr
LOG(DEBUG) << "Var\n";
auto var = scope.find(node.id); /* 从域中取出对应变量 */
bool if_return_lvalue = need_as_address; /* 判断是否需要返回地址(即是否由赋值语句调用) */
need_as_address = false; /* 重置全局变量need_as_address */
Value *index = CONST_INT(0); /* 初始化index */
if (node.expression != nullptr)
{ /* 若有expression,代表不是int类型的引用*/
node.expression->accept(*this); /* 处理expression,得到结果Res */
auto res = Res; /* 存储结果 */
if (checkFloat(res)) /* 判断结果是否为浮点数 */
res = builder->create_fptosi(res, Int32Type); /* 若是,则矫正为整数 */
index = res; /* 赋值给index,表示数组下标 */
/* 判断下标是否为负数。若是,则调用neg_idx_except函数进行处理 */
auto function = builder->get_insert_block()->get_parent(); /* 获取当前函数 */
auto indexTest = builder->create_icmp_lt(index, CONST_ZERO(Int32Type)); /* test是否为负数 */
auto failBB = BasicBlock::create(module.get(), node.id + "_failTest", function); /* fail块 */
auto passBB = BasicBlock::create(module.get(), node.id + "_passTest", function); /* pass块 */
builder->create_cond_br(indexTest, failBB, passBB); /* 设置跳转语句 */
// 下标为负数,调用neg_idx_except函数进行处理
builder->set_insert_point(failBB); /* fail块,即下标为负数 */
auto fail = scope.find("neg_idx_except"); /* 取出neg_idx_except函数 */
builder->create_call(static_cast<Function *>(fail), {}); /* 调用neg_idx_except函数进行处理 */
builder->create_br(passBB); /* 跳转到pass块 */
// 下标合法,
builder->set_insert_point(passBB); /* pass块 */
if (var->get_type()->get_pointer_element_type()->is_array_type()) /* 若为指向数组的指针 */
var = builder->create_gep(var, {CONST_INT(0), index}); /* 则进行两层寻址 */
else
{
if (var->get_type()->get_pointer_element_type()->is_pointer_type()) /* 若为指针 */
var = builder->create_load(var); /* 则取出指针指向的元素 */
var = builder->create_gep(var, {index}); /* 进行一层寻址(因为此时并非指向数组) */
}
if (if_return_lvalue)
{ /* 若要返回值 */
Res = var; /* 则返回var对应的地址 */
need_as_address = false; /* 并重置全局变量need_as_address */
}
else
Res = builder->create_load(var); /* 否则则进行load */
return;
}
else
{ // 处理不是数组的情况
if (if_return_lvalue)
{ /* 若要返回值 */
Res = var; /* 则返回var对应的地址 */
need_as_address = false; /* 并重置全局变量need_as_address */
}
else
{ /* 否则 */
// 数组的指针即a[]类型就返回数组的起始地址,否则load取值
if (var->get_type()->get_pointer_element_type()->is_array_type()) /* 若指向数组,需要两个偏移取地址 */
Res = builder->create_gep(var, {CONST_INT(0), CONST_INT(0)}); /* 则寻址 */
else
Res = builder->create_load(var); /* 否则则进行load */
}
}
}
该节点结构定义如下:
struct ASTAssignExpression: ASTExpression {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTVar> var;
std::shared_ptr<ASTExpression> expression;
};
对于Assign语句,将全局变need_as_address量置为true,调用子节点var的accept函数得到变量的地址,然后计算表达式的值,创建store指令将值存入地址。需要确认表达式结果是否与变量类型相同,如果不同需要将表达式结果转换为和变量相同的类型。
根据要求,实现如下:
/* AssignExpression, 赋值语句, var = expression */
void CminusfBuilder::visit(ASTAssignExpression &node)
{
// ASTAssignExpression有2个成员变量
// std::shared_ptr<ASTVar> var; 左值(被赋值的对象)
// std::shared_ptr<ASTExpression> expression; 右值(赋的值)
LOG(DEBUG) << "AssignExpression\n";
need_as_address = true; /* 设置need_as_address,表示需要返回值 */
// 获取左值,右值
node.var->accept(*this); /* 处理左var */
auto left = Res; /* 获取地址 */
node.expression->accept(*this); /* 处理右expression */
auto right = Res; /* 获得结果 */
// 处理左值,右值类型冲突问题
auto leftType = left->get_type()->get_pointer_element_type(); /* 获取var的类型 */
/* 若赋值语句左右类型不匹配,则进行匹配 */
if (leftType == FloatType && checkInt(right))
right = builder->create_sitofp(right, FloatType);
if (leftType == Int32Type && checkFloat(right))
right = builder->create_fptosi(right, Int32Type);
// 赋值
builder->create_store(right, left);
}
该节点结构定义如下:
struct ASTSimpleExpression: ASTExpression {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTAdditiveExpression> additive_expression_l;
std::shared_ptr<ASTAdditiveExpression> additive_expression_r;
RelOp op;
};
简单表达式SimpleExpression是一个加法表达式或两个加法表达式的关系运算。在节点中有两个加法表达式的指针和一个运算符类型为RelOp的运算符op,RelOp是一个枚举类型,包含了所有比较运算符。根据语义,对于该节点的处理,应该先处理加法表达式,将表达式的值保存下来,如果两个表达式指针都不为空,说明为关系运算,再比较两个运算结果,根据结果将表达式的值赋为0或1。进行比较时需要注意两个值的类型,整型和浮点型比较时要将整型转换为浮点型。
具体实现中,应该调用加法表达式的accept函数(如果指针不为空),暂存结果,对于比较运算,根据op生成icmp或fcmp的指令,最后返回的值就是指令结果。
根据要求,实现如下:
void CminusfBuilder::visit(ASTSimpleExpression &node)
{
// ASTSimpleExpression有3个成员变量
// std::shared_ptr<ASTAdditiveExpression> additive_expression_l;
// std::shared_ptr<ASTAdditiveExpression> additive_expression_r;
// RelOp op;
LOG(DEBUG) << "SimpleExpression\n";
node.additive_expression_l->accept(*this); /* 处理左边的expression */
auto lres = Res; /* 获取结果存到lres中 */
if (node.additive_expression_r == nullptr)
{
return;
} //* 若不存在右expression,则直接返回 */
node.additive_expression_r->accept(*this); /* 处理右边的expression */
auto rres = Res; /* 结果存到rres中 */
// 确保两个表达式的类型相同,若存在浮点和整型混存,全部转换为浮点型
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{
/* 根据不同的比较操作,调用icmp进行处理 */
// 比较的返回结果
// enum RelOp:
// <= 对应 OP_LE,
// < 对应 OP_LT,
// > 对应 OP_GT,
// >= 对应 OP_GE,
// == 对应 OP_EQ,
// != 对应 OP_NEQ
case OP_LE:
Res = builder->create_icmp_le(lres, rres);
break;
case OP_LT:
Res = builder->create_icmp_lt(lres, rres);
break;
case OP_GT:
Res = builder->create_icmp_gt(lres, rres);
break;
case OP_GE:
Res = builder->create_icmp_ge(lres, rres);
break;
case OP_EQ:
Res = builder->create_icmp_eq(lres, rres);
break;
case OP_NEQ:
Res = builder->create_icmp_ne(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行比较 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{
/* 根据不同的比较操作,调用fcmp进行处理 */
case OP_LE:
Res = builder->create_fcmp_le(lres, rres);
break;
case OP_LT:
Res = builder->create_fcmp_lt(lres, rres);
break;
case OP_GT:
Res = builder->create_fcmp_gt(lres, rres);
break;
case OP_GE:
Res = builder->create_fcmp_ge(lres, rres);
break;
case OP_EQ:
Res = builder->create_fcmp_eq(lres, rres);
break;
case OP_NEQ:
Res = builder->create_fcmp_ne(lres, rres);
break;
}
}
Res = builder->create_zext(Res, Int32Type); /* 将结果作为整数保存(可作为判断语句中的判断条件) */
}
该节点结构定义如下:
struct ASTAdditiveExpression: ASTNode {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTAdditiveExpression> additive_expression;
AddOp op;
std::shared_ptr<ASTTerm> term;
};
加法表达式中包含了一个乘法表达式,一个加法表达式和一个运算符。如果加法表达式指针为空,则表达式的值就是乘法表达式的值,否则分别计算两个表达式,调用相应的accept函数,然后进行根据运算符生成加或减指令。
根据要求,实现如下:
void CminusfBuilder::visit(ASTAdditiveExpression &node)
{
// ASTAdditiveExpression有3个成员变量:
// std::shared_ptr<ASTAdditiveExpression> additive_expression;
// AddOp op;
// std::shared_ptr<ASTTerm> term;
LOG(DEBUG) << "AdditiveExpression\n";
if (node.additive_expression == nullptr)
{ /* 若无加减法运算 */
node.term->accept(*this);
return; /* 则直接去做乘除法 */
}
node.additive_expression->accept(*this); /* 处理左expression */
auto lres = Res; /* 结果保存在lres中 */
node.term->accept(*this); /* 处理右term */
auto rres = Res; /* 结果保存在rres中 */
// 分为整型-整型,和存在浮点数类型,这两种情况进行讨论
// 若存在浮点数,则全部强制转换为浮点数实现
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{ /* 根据对应加法或是减法,调用iadd或是isub进行处理 */
case OP_PLUS:
Res = builder->create_iadd(lres, rres);
break;
case OP_MINUS:
Res = builder->create_isub(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{ /* 根据对应加法或是减法,调用fadd或是fsub进行处理 */
case OP_PLUS:
Res = builder->create_fadd(lres, rres);
break;
case OP_MINUS:
Res = builder->create_fsub(lres, rres);
break;
}
}
}
该节点结构定义如下:
struct ASTTerm : ASTNode {
virtual void accept(ASTVisitor &) override final;
std::shared_ptr<ASTTerm> term;
MulOp op;
std::shared_ptr<ASTFactor> factor;
};
乘法表达式由乘法表达式和因子或单独一个因子构成。与加法表达式的处理相同。
根据要求,实现如下:
void CminusfBuilder::visit(ASTTerm &node)
{
// ASTTerm有3个成员变量:
// std::shared_ptr<ASTTerm> term;
// MulOp op;
// std::shared_ptr<ASTFactor> factor;
LOG(DEBUG) << "Term\n";
if (node.term == nullptr)
{ /* 若无乘法运算 */
node.factor->accept(*this);
return; /* 则直接去处理元素 */
}
node.term->accept(*this); /* 处理左term */
auto lres = Res; /* 结果保存在lres中 */
node.factor->accept(*this); /* 处理右factor */
auto rres = Res; /* 结果保存在rres中 */
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{ /* 根据对应乘法或是除法,调用imul或是idiv进行处理 */
case OP_MUL:
Res = builder->create_imul(lres, rres);
break;
case OP_DIV:
Res = builder->create_isdiv(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{ /* 根据对应乘法或是除法,调用fmul或是fdiv进行处理 */
case OP_MUL:
Res = builder->create_fmul(lres, rres);
break;
case OP_DIV:
Res = builder->create_fdiv(lres, rres);
break;
}
}
}
该节点结构定义如下:
struct ASTCall: ASTFactor {
virtual void accept(ASTVisitor &) override final;
std::string id;
std::vector<std::shared_ptr<ASTExpression>> args;
};
call节点需要创建一条函数调用call指令,从作用域中取出函数,然后根据函数的参数将节点的实参传入,并检查类型是否与函数参数的类型一致,不一致则需要转换为函数的形参类型。创建一个参数列表,将转换好的参数存入列表来调用函数。
根据要求,实现如下:
/* Call, 函数调用, call -> ID (args) */
void CminusfBuilder::visit(ASTCall &node)
{
// ASTCall有3个成员变量:
// std::string id;
// std::vector<std::shared_ptr<ASTExpression>> args;
LOG(DEBUG) << "Call\n";
auto function = static_cast<Function *>(scope.find(node.id)); /* 获取需要调用的函数 */
auto paramType = function->get_function_type()->param_begin(); /* 获取其参数类型 */
if (function == nullptr)
std::cout << "ERROR: 函数" << node.id << "未定义\n";
// 处理参数列表
std::vector<Value *> args; /* 创建args用于存储函数参数的值,构建调用函数的参数列表 */
for (auto arg : node.args)
{ /* 遍历形参列表 */
arg->accept(*this); /* 对每一个参数进行处理,获取参数对应的值 */
if (Res->get_type()->is_pointer_type())
{ /* 若参数是指针 */
args.push_back(Res); /* 则直接将值加入到参数列表 */
}
else
{ /* 若不是指针,则需要判断形参和实参的类型是否符合。若不符合则需要类型转换 */
if (*paramType == FloatType && checkInt(Res))
Res = builder->create_sitofp(Res, FloatType);
if (*paramType == Int32Type && checkFloat(Res))
Res = builder->create_fptosi(Res, Int32Type);
args.push_back(Res);
}
paramType++; /* 查看下一个形参 */
}
Res = builder->create_call(static_cast<Function *>(function), args); /* 创建函数调用 */
}
在build文件夹下输入如下指令,使我们写的cminusf_builder.cpp文件纳入编译。
make clean
make install
编译后会产生 cminusfc
程序,它能将cminus文件输出为LLVM IR,也可以利用clang将IR编译成二进制。程序逻辑写在src/cminusfc/cminusfc.cpp
中。
需要对某个(提供的或自己写的) .cminus
文件测试时,可以这样使用:
# 假设 cminusfc 的路径在你的$PATH中
# 利用构建好的 Module 生成 test.ll
# 注意,如果调用了外部函数,如 input, output 等,则无法使用lli运行
cminusfc test.cminus -emit-llvm
# 假设libcminus_io.a的路径在$LD_LIBRARY_PATH中,clang的路径在$PATH中
# 1. 利用构建好的 Module 生成 test.ll
# 2. 调用 clang 来编译 IR 并链接上静态链接库 libcminus_io.a,生成二进制文件 test
cminusfc test.cminus
步骤如下:
如对本段代码,为保存在build文件夹内的my.cminus
void main(void){
int i;
i = 5;
while(i >= 7)
{
i = i - 2;
}
output(i);
}
在build文件夹内使用
./cminusfc my.cminus
编译my.cminus
,生成my可执行文件,然后使用
./my
运行该可执行文件,得到结果。
在 tests/lab4
目录下,输入如下指令:
./lab4_test.py
输出:
===========TEST START===========
Case 01: Success
Case 02: Success
Case 03: Success
Case 04: Success
Case 05: Success
Case 06: Success
Case 07: Success
Case 08: Success
Case 09: Success
Case 10: Success
Case 11: Success
Case 12: Success
============TEST END============
截图如下:
本次实验具有一定的难度,很考验对于实验3知识的融汇贯通。
通过本次实验,理解了从抽象语法树产生中间IR的方法,并进行了实现。在实现过程中,对于IR的指令有了进一步的熟悉与理解,掌握了使用C++接口创建不同IR指令的方法,以及在访问者模式下遍历抽象语法树,完成IR生成的过程。在完成实现时需要多次跳转,阅读相关的头文件,语义规则,原型函数定义,原型结构体定义等。
经过四次实验,结合课程所学的原理,理解了编译器的词法分析,语法分析,中间代码生成的过程,也学习了相关工具的使用并进行了实践,了解了编译器工作的每一个部分的原理和相互之间的配合。
但感觉还是没有融汇贯通,还是有很多需要学的,如果投入的时间能再多一些,感觉能学到更多。
实验难度还是有点大的,时间给的还是太少了。
而且没有给参考的代码,花了很多时间才理解实验需要做什么。前几个函数的实现参考了一些标准代码并进行研究才能稍微领悟实验书写的方式。在完成实验的过程中还是依靠研究A橙学长
和芜湖韩金轮学长
在CSDN上发的实验思路才得以完成。
总体而言,在这个实验上还是学到了很多知识,也花了很多时间,有很多收获。
https://blog.csdn.net/qq_51684393/article/details/128474127
https://blog.csdn.net/Aaron503/article/details/128325057
https://github.com/XiaoYaoYouUSTC/Cminusf-Compiler
https://github.com/kyin2905/cminus_compiler-2022-fall
#include "cminusf_builder.hpp"
#include "logging.hpp"
// use these macros to get constant value
#define CONST_INT(num) \
ConstantInt::get(num, module.get()) /* 增加一个有关整型的宏 */
// 以num值来创建常数类
#define CONST_FP(num) \
ConstantFP::get((float)num, module.get())
// 以num值来创建浮点数类
#define CONST_ZERO(type) \
ConstantZero::get(type, module.get()) /* 此处要修改为type */
// 用于全局变量初始化的常量0值
#define Int32Type \
Type::get_int32_type(module.get()) /* 获取int32类型 */
#define FloatType \
Type::get_float_type(module.get()) /* 获取float类型 */
#define checkInt(num) \
num->get_type()->is_integer_type() /* 整型判断 */
#define checkFloat(num) \
num->get_type()->is_float_type() /* 浮点型判断 */
#define checkPointer(num) \
num->get_type()->is_pointer_type() /* 指针类型判断 */
// You can define global variables here
// to store state
Value *Res; /* 存储返回的结果 */
Value *arg; /* 存储参数指针,用于Param的处理 */
bool need_as_address = false; /* 标志是返回值还是返回地址 */
/*
* use CMinusfBuilder::Scope to construct scopes
* scope.enter: enter a new scope
* scope.exit: exit current scope
* scope.push: add a new binding to current scope
* scope.find: find and return the value bound to the name
*/
// 下面的类型在include/ast.hpp有定义
// lab4和lab3的区别就是lab3是利用llvm接口来生成中间代码,而lab4直接写一个编译器将cminus转成中间代码
/* Program, 程序, program->declaration-list */
void CminusfBuilder::visit(ASTProgram &node)
{
// ASTProgram有一个std::vector<std::shared_ptr<ASTDeclaration>>类型的向量
// 我们要对这个向量进行遍历
LOG(DEBUG) << "Program\n";
// 语义检查
if (node.declarations.size() == 0)
{
std::cout << "ERROR: 该程序中没有声明。\n";
return;
}
if (!(node.declarations.back()->id == "main" && node.declarations.back()->type == TYPE_VOID))
{
std::cout << "ERROR: 最后一个声明不是void main(void)\n";
return;
}
for (auto decl : node.declarations) /* 遍历declaration-list子结点 */
decl->accept(*this); /* 处理每一个declaration */
return;
}
/* Num,对数值进行处理 */
void CminusfBuilder::visit(ASTNum &node)
{
// ASTNum中有两个成员变量
// CminusType type; 类型决定这片空间的读取方式
// 联合体union{ int i_val; float f_val;}是这片空间保存的值
LOG(DEBUG) << "Num\n";
if (node.type == TYPE_INT) /* 若为整型 */
// 调用ConstantInt中的API
Res = ConstantInt::get(node.i_val, module.get()); /* 获取结点中存储的整型数值 */
else if (node.type == TYPE_FLOAT) /* 若为浮点型 */
// 调用ConstantFP中的API
Res = ConstantFP::get(node.f_val, module.get()); /* 获取结点中存储的浮点型数值 */
return;
}
/* Var-Declaration, 变量声明, var-declaration -> type-specifier ID | type-specifier ID [INTEGER] */
void CminusfBuilder::visit(ASTVarDeclaration &node)
{
// ASTVarDeclaration有两个成员变量:
// CminusType type;
// std::shared_ptr<ASTNum> num;
LOG(DEBUG) << "VarDeclaration\n";
Type *tmpType; /* 类型暂存变量,用于存储变量的类型,用于后续申请空间 */
if (node.type == TYPE_INT) /* 若为整型 */
tmpType = Int32Type; /* 则type为整数类型 */
else if (node.type == TYPE_FLOAT) /* 则为浮点型 */
tmpType = FloatType; /* 则type为浮点类型 */
else
std::cout << "ERROR: 在变量声明中,只有整型和浮点型可以使用\n";
// 需考虑两个维度:是否全局变量,是否数组定义,分4类讨论
if (node.num != nullptr)
{ /* 若为数组类型 */
if (node.num->i_val <= 0)
std::cout << "ERROR: 数组长度必须大于c0\n";
/* 获取需开辟的对应大小的空间的类型指针 */
auto *arrayType = ArrayType::get(tmpType, node.num->i_val); /* 获取对应的数组Type */
auto initializer = CONST_ZERO(tmpType); /* 全局变量初始化为0 */
Value *arrayAlloca; /* 存储申请到的数组空间的地址 */
if (scope.in_global()) /* 若为全局数组,则开辟全局数组 */
arrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer);
else /* 若不是全局数组,则开辟局部数组 */
arrayAlloca = builder->create_alloca(arrayType);
scope.push(node.id, arrayAlloca); /* 将获得的数组变量加入域 */
}
else
{ /* 若不是数组类型 */
auto initializer = CONST_ZERO(tmpType); /* 全局变量初始化为0 */
Value *varAlloca; /* 存储申请到的变量空间的地址 */
if (scope.in_global()) /* 若为全局变量,则申请全局空间 */
varAlloca = GlobalVariable::create(node.id, module.get(), tmpType, false, initializer);
else /* 若不是全局变量,则申请局部空间 */
varAlloca = builder->create_alloca(tmpType);
scope.push(node.id, varAlloca); /* 将获得变量加入域 */
}
}
/* Fun-Declaration, 函数声明, fun-declaration -> type-specifier ID ( params ) compound-stmt */
void CminusfBuilder::visit(ASTFunDeclaration &node)
{
// ASTFunDeclaration有两个成员变量:
// std::vector<std::shared_ptr<ASTParam>> params;
// std::shared_ptr<ASTCompoundStmt> compound_stmt;
LOG(DEBUG) << "FunDeclaration\n";
// 考虑函数返回类型+函数名+参数列表+复合语句(局部声明与语句列表)
Type *retType; /* 函数返回类型 */
/* 根据不同的返回类型,设置retType */
if (node.type == TYPE_INT)
{
retType = Int32Type;
}
else if (node.type == TYPE_FLOAT)
{
retType = FloatType;
}
else if (node.type == TYPE_VOID)
{
retType = Type::get_void_type(module.get());
}
/* 根据函数声明,构造形参列表(此处的形参即参数的类型) */
std::vector<Type *> paramsType; /* 参数类型列表 */
for (auto param : node.params)
{
if (param->isarray)
{ /* 若参数为数组形式,则存入首地址指针 */
if (param->type == TYPE_INT) /* 若为整型 */
paramsType.push_back(Type::get_int32_ptr_type(module.get()));
else if (param->type == TYPE_FLOAT) /* 若为浮点型 */
paramsType.push_back(Type::get_float_ptr_type(module.get()));
}
else
{ /* 若为单个变量形式,则存入对应类型 */
if (param->type == TYPE_INT) /* 若为整型 */
paramsType.push_back(Int32Type);
else if (param->type == TYPE_FLOAT) /* 若为浮点型 */
paramsType.push_back(FloatType);
}
}
auto funType = FunctionType::get(retType, paramsType); /* retType返回结构,paramsType函数形参结构 */
auto function = Function::create(funType, node.id, module.get()); /* 创建函数 */
scope.push(node.id, function); /* 将函数加入到全局作用域 */
scope.enter(); /* 进入此函数作用域 */
auto bb = BasicBlock::create(module.get(), node.id + "_entry", function); /* 创建基本块 */
builder->set_insert_point(bb); /* 将基本块加入到builder中 */
/* 函数传参,要将实参和形参进行匹配 */
std::vector<Value *> args; /* 创建vector存储实参 */
for (auto arg = function->arg_begin(); arg != function->arg_end(); arg++)
{ /* 遍历实参列表 */
args.push_back(*arg); /* 将实参加入vector */
}
for (int i = 0; i < node.params.size(); i++)
{ /* 遍历形参列表(=遍历实参列表) */
auto param = node.params[i]; /* 取出对应形参 */
arg = args[i]; /* 取出对应实参 */
param->accept(*this); /* 调用param的accept进行处理 */
}
node.compound_stmt->accept(*this); /* 处理函数体内语句compound-stmt */
// 判断返回值的类型,根据对应的返回值类型,执行ret操作
// 这里应该是实现若没有显式返回,默认返回对应类型的0或void
if (builder->get_insert_block()->get_terminator() == nullptr)
{
if (function->get_return_type()->is_void_type())
builder->create_void_ret();
else if (function->get_return_type()->is_float_type())
builder->create_ret(CONST_FP(0.0));
else
builder->create_ret(CONST_INT(0));
}
scope.exit(); /* 退出此函数作用域 */
}
/* Param, 参数 */
void CminusfBuilder::visit(ASTParam &node)
{
// ASTParam有3个成员变量
// CminusType type; 参数类型
// std::string id; 参数名
// bool isarray; 是否数组标记
LOG(DEBUG) << "Param\n";
Value *paramAlloca; // 分配该参数的存储空间
// 区分是否为数组,为整型还是浮点型,共分为4类讨论
if (node.isarray)
{ /* 若为数组 */
if (node.type == TYPE_INT) /* 若为整型数组,则开辟整型数组存储空间 */
paramAlloca = builder->create_alloca(Type::get_int32_ptr_type(module.get()));
else if (node.type == TYPE_FLOAT) /* 若为浮点数数组,则开辟浮点数数组存储空间 */
paramAlloca = builder->create_alloca(Type::get_float_ptr_type(module.get()));
}
else
{ /* 若不是数组 */
if (node.type == TYPE_INT) /* 若为整型,则开辟整型存储空间 */
paramAlloca = builder->create_alloca(Int32Type);
else if (node.type == TYPE_FLOAT) /* 若为浮点数,则开辟浮点数存储空间 */
paramAlloca = builder->create_alloca(FloatType);
}
builder->create_store(arg, paramAlloca); /* 将实参load到开辟的存储空间中 */
// 此处arg通过全局变量传递、
// 函数原型:StoreInst *create_store(Value *val, Value *ptr)
scope.push(node.id, paramAlloca); /* 将参数push到域中 */
}
/* CompoundStmt, 函数体语句, compound-stmt -> {local-declarations statement-list} */
void CminusfBuilder::visit(ASTCompoundStmt &node)
{
// ASTCompoundStmt有两个成员变量
// std::vector<std::shared_ptr<ASTVarDeclaration>> local_declarations;
// std::vector<std::shared_ptr<ASTStatement>> statement_list;
LOG(DEBUG) << "CompoundStmt\n";
scope.enter(); /* 进入函数体内的作用域 */
for (auto local_declaration : node.local_declarations) /* 遍历 */
local_declaration->accept(*this); /* 处理每一个局部声明 */
for (auto statement : node.statement_list) /* 遍历 */
statement->accept(*this); /* 处理每一个语句 */
scope.exit(); /* 退出作用域 */
}
/* ExpressionStmt, 表达式语句, expression-stmt -> expression;
* | ; */
void CminusfBuilder::visit(ASTExpressionStmt &node)
{
// ASTExpressionStmt只有一个成员变量:
// std::shared_ptr<ASTExpression> expression;
LOG(DEBUG) << "ExpressionStmt\n";
if (node.expression != nullptr) /* 若对应表达式存在 */
node.expression->accept(*this); /* 则处理该表达式 */
}
/* SelectionStmt, if语句, selection-stmt -> if (expression) statement
* | if (expression) statement else statement */
void CminusfBuilder::visit(ASTSelectionStmt &node)
{
// ASTSelectionStmt有3个成员变量:
// std::shared_ptr<ASTExpression> expression; 判断条件
// std::shared_ptr<ASTStatement> if_statement; if语句执行体
// std::shared_ptr<ASTStatement> else_statement; else语句执行体
// 若无else语句,else_statement == nullptr
LOG(DEBUG) << "SelectionStmt\n";
// 构建判断条件代码
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
if (resType->is_pointer_type())
{ /* 若结果为指针型,则针对指针型进行处理 */
Res = builder->create_load(Res); /* 大于0视为true */
}
else if (resType->is_integer_type())
{ /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
Res = builder->create_icmp_gt(Res, CONST_ZERO(Int32Type)); /* 大于0视为true */
}
else if (resType->is_float_type())
{ /* 若结果为浮点型,则针对浮点数进行处理 */
Res = builder->create_fcmp_gt(Res, CONST_ZERO(FloatType)); /* 大于0视为true */
}
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto trueBB = BasicBlock::create(module.get(), "true", function); /* 创建符合条件块 */
// 构建语句执行体代码
// 根据是否存在else语句分2类讨论
if (node.else_statement != nullptr)
{ /* 若存在else语句 */
auto falseBB = BasicBlock::create(module.get(), "false", function); /* 创建else块 */
builder->create_cond_br(Res, trueBB, falseBB); /* 设置跳转语句 */
// 处理trueBB
builder->set_insert_point(trueBB); /* 符合if条件的块 */
node.if_statement->accept(*this); /* 处理if语句执行体 */
auto curTrueBB = builder->get_insert_block(); /* 将块加入 */
// 处理falseBB
builder->set_insert_point(falseBB); /* else的块 */
node.else_statement->accept(*this); /* 处理else语句执行体 */
auto curFalseBB = builder->get_insert_block(); /* 将块加入 */
/* 处理返回,避免跳转到对应块后无return */
// 判断true语句中是否存在ret语句
auto trueTerm = builder->get_insert_block()->get_terminator();
// 判断else语句中是否存在ret语句
auto falseTerm = builder->get_insert_block()->get_terminator();
if (trueTerm == nullptr || falseTerm == nullptr)
{ /* 若有一方不存在return语句,则需要创建返回块 */
BasicBlock *retBB;
retBB = BasicBlock::create(module.get(), "ret", function); /* 创建return块 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
if (trueTerm == nullptr)
{ /* 若if语句执行体不存在return */
builder->set_insert_point(curTrueBB); /* 则设置跳转 */
}
else if (falseTerm == nullptr)
{ /* 若else语句执行体中不存在return */
builder->set_insert_point(curFalseBB); /* 则设置跳转 */
}
builder->create_br(retBB); /* 跳转到刚刚设置的return块 */
}
}
else
{ /* 若不存在else语句,则只需要设置true语句块和后续语句块即可 */
auto retBB = BasicBlock::create(module.get(), "ret", function); /* 后续语句块 */
builder->create_cond_br(Res, trueBB, retBB); /* 根据条件设置跳转指令 */
builder->set_insert_point(trueBB); /* true语句块 */
node.if_statement->accept(*this); /* 执行条件符合后要执行的语句 */
if (builder->get_insert_block()->get_terminator() == nullptr) /* 补充return(同上) */
builder->create_br(retBB); /* 跳转到刚刚设置的return块 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
}
}
/* IterationStmt, while语句, iteration-stmt -> while (expression) statement */
void CminusfBuilder::visit(ASTIterationStmt &node)
{
// ASTIterationStmt有2个成员变量:
// std::shared_ptr<ASTExpression> expression; 循环判断条件
// std::shared_ptr<ASTStatement> statement; while循环执行体
LOG(DEBUG) << "IterationStmt\n";
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto conditionBB = BasicBlock::create(module.get(), "condition", function); /* 创建条件判断块 */
auto loopBB = BasicBlock::create(module.get(), "loop", function); /* 创建循环语句块 */
auto retBB = BasicBlock::create(module.get(), "ret", function); /* 创建后续语句块 */
if (builder->get_insert_block()->get_terminator() == nullptr)
builder->create_br(conditionBB); // 跳转到条件判断块
// 构建条件判断代码
builder->set_insert_point(conditionBB); /* 条件判断块 */
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
if (resType->is_pointer_type())
{ /* 若结果为指针型,则针对指针型进行处理 */
Res = builder->create_load(Res); /* 大于0视为true */
}
else if (resType->is_integer_type())
{ /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
Res = builder->create_icmp_gt(Res, CONST_ZERO(Int32Type)); /* 大于0视为true */
}
else if (resType->is_float_type())
{ /* 若结果为浮点型,则针对浮点数进行处理 */
Res = builder->create_fcmp_gt(Res, CONST_ZERO(FloatType)); /* 大于0视为true */
}
builder->create_cond_br(Res, loopBB, retBB); /* 设置条件跳转语句 */
// 构建循环语句代码
builder->set_insert_point(loopBB); /* 循环语句执行块 */
node.statement->accept(*this); /* 执行对应的语句 */
if (builder->get_insert_block()->get_terminator() == nullptr) /* 若无返回,则补充跳转 */
builder->create_br(conditionBB); /* 跳转到条件判断语句 */
builder->set_insert_point(retBB); /* return块(即后续语句) */
}
/* ReturnStmt, 返回语句, return-stmt -> return;
* | return expression; */
void CminusfBuilder::visit(ASTReturnStmt &node)
{
// ASTReturnStmt只有1个成员变量
// std::shared_ptr<ASTExpression> expression; 返回语句表达式
LOG(DEBUG) << "ReturnStmt\n";
auto function = builder->get_insert_block()->get_parent(); /* 获得当前所对应的函数 */
auto retType = function->get_return_type(); /* 获取返回类型 */
// 处理返回语句表达式
if (retType->is_void_type())
{
// 如果本来就是void类型的函数,需要返回void
builder->create_void_ret(); /* 则创建void返回,随后return,无需后续操作 */
return;
}
// 如果需要返回非void
node.expression->accept(*this); /* 处理条件判断对应的表达式,得到返回值存到expression中 */
auto resType = Res->get_type(); /* 获取表达式得到的结果类型 */
/* 处理expression返回的结果和需要return的结果类型不匹配的问题 */
// 使用强制类型转换
if (retType->is_integer_type() && resType->is_float_type())
Res = builder->create_fptosi(Res, Int32Type);
if (retType->is_float_type() && resType->is_integer_type())
Res = builder->create_sitofp(Res, Int32Type);
builder->create_ret(Res); /* 创建return,将expression的结果进行返回 */
}
/* Var, 变量引用, var -> ID
* | ID [expression] */
void CminusfBuilder::visit(ASTVar &node)
{
// ASTVar有2个成员变量:
// std::string id; 变量名
// std::shared_ptr<ASTExpression> expression; 变量类型
// 如果变量var是int型的,这个取nullptr
LOG(DEBUG) << "Var\n";
auto var = scope.find(node.id); /* 从域中取出对应变量 */
bool if_return_lvalue = need_as_address; /* 判断是否需要返回地址(即是否由赋值语句调用) */
need_as_address = false; /* 重置全局变量need_as_address */
Value *index = CONST_INT(0); /* 初始化index */
if (node.expression != nullptr)
{ /* 若有expression,代表不是int类型的引用*/
node.expression->accept(*this); /* 处理expression,得到结果Res */
auto res = Res; /* 存储结果 */
if (checkFloat(res)) /* 判断结果是否为浮点数 */
res = builder->create_fptosi(res, Int32Type); /* 若是,则矫正为整数 */
index = res; /* 赋值给index,表示数组下标 */
/* 判断下标是否为负数。若是,则调用neg_idx_except函数进行处理 */
auto function = builder->get_insert_block()->get_parent(); /* 获取当前函数 */
auto indexTest = builder->create_icmp_lt(index, CONST_ZERO(Int32Type)); /* test是否为负数 */
auto failBB = BasicBlock::create(module.get(), node.id + "_failTest", function); /* fail块 */
auto passBB = BasicBlock::create(module.get(), node.id + "_passTest", function); /* pass块 */
builder->create_cond_br(indexTest, failBB, passBB); /* 设置跳转语句 */
// 下标为负数,调用neg_idx_except函数进行处理
builder->set_insert_point(failBB); /* fail块,即下标为负数 */
auto fail = scope.find("neg_idx_except"); /* 取出neg_idx_except函数 */
builder->create_call(static_cast<Function *>(fail), {}); /* 调用neg_idx_except函数进行处理 */
builder->create_br(passBB); /* 跳转到pass块 */
// 下标合法,
builder->set_insert_point(passBB); /* pass块 */
if (var->get_type()->get_pointer_element_type()->is_array_type()) /* 若为指向数组的指针 */
var = builder->create_gep(var, {CONST_INT(0), index}); /* 则进行两层寻址 */
else
{
if (var->get_type()->get_pointer_element_type()->is_pointer_type()) /* 若为指针 */
var = builder->create_load(var); /* 则取出指针指向的元素 */
var = builder->create_gep(var, {index}); /* 进行一层寻址(因为此时并非指向数组) */
}
if (if_return_lvalue)
{ /* 若要返回值 */
Res = var; /* 则返回var对应的地址 */
need_as_address = false; /* 并重置全局变量need_as_address */
}
else
Res = builder->create_load(var); /* 否则则进行load */
return;
}
else
{ // 处理不是数组的情况
if (if_return_lvalue)
{ /* 若要返回值 */
Res = var; /* 则返回var对应的地址 */
need_as_address = false; /* 并重置全局变量need_as_address */
}
else
{ /* 否则 */
// 数组的指针即a[]类型就返回数组的起始地址,否则load取值
if (var->get_type()->get_pointer_element_type()->is_array_type()) /* 若指向数组,需要两个偏移取地址 */
Res = builder->create_gep(var, {CONST_INT(0), CONST_INT(0)}); /* 则寻址 */
else
Res = builder->create_load(var); /* 否则则进行load */
}
}
}
/* AssignExpression, 赋值语句, var = expression */
void CminusfBuilder::visit(ASTAssignExpression &node)
{
// ASTAssignExpression有2个成员变量
// std::shared_ptr<ASTVar> var; 左值(被赋值的对象)
// std::shared_ptr<ASTExpression> expression; 右值(赋的值)
LOG(DEBUG) << "AssignExpression\n";
need_as_address = true; /* 设置need_as_address,表示需要返回值 */
// 获取左值,右值
node.var->accept(*this); /* 处理左var */
auto left = Res; /* 获取地址 */
node.expression->accept(*this); /* 处理右expression */
auto right = Res; /* 获得结果 */
// 处理左值,右值类型冲突问题
auto leftType = left->get_type()->get_pointer_element_type(); /* 获取var的类型 */
/* 若赋值语句左右类型不匹配,则进行匹配 */
if (leftType == FloatType && checkInt(right))
right = builder->create_sitofp(right, FloatType);
if (leftType == Int32Type && checkFloat(right))
right = builder->create_fptosi(right, Int32Type);
// 赋值
builder->create_store(right, left);
}
/* SimpleExpression, 比较表达式, simple-expression -> additive-expression relop additive-expression
* | additive-expression */
void CminusfBuilder::visit(ASTSimpleExpression &node)
{
// ASTSimpleExpression有3个成员变量
// std::shared_ptr<ASTAdditiveExpression> additive_expression_l;
// std::shared_ptr<ASTAdditiveExpression> additive_expression_r;
// RelOp op;
LOG(DEBUG) << "SimpleExpression\n";
node.additive_expression_l->accept(*this); /* 处理左边的expression */
auto lres = Res; /* 获取结果存到lres中 */
if (node.additive_expression_r == nullptr)
{
return;
} //* 若不存在右expression,则直接返回 */
node.additive_expression_r->accept(*this); /* 处理右边的expression */
auto rres = Res; /* 结果存到rres中 */
// 确保两个表达式的类型相同,若存在浮点和整型混存,全部转换为浮点型
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{
/* 根据不同的比较操作,调用icmp进行处理 */
// 比较的返回结果
// enum RelOp:
// <= 对应 OP_LE,
// < 对应 OP_LT,
// > 对应 OP_GT,
// >= 对应 OP_GE,
// == 对应 OP_EQ,
// != 对应 OP_NEQ
case OP_LE:
Res = builder->create_icmp_le(lres, rres);
break;
case OP_LT:
Res = builder->create_icmp_lt(lres, rres);
break;
case OP_GT:
Res = builder->create_icmp_gt(lres, rres);
break;
case OP_GE:
Res = builder->create_icmp_ge(lres, rres);
break;
case OP_EQ:
Res = builder->create_icmp_eq(lres, rres);
break;
case OP_NEQ:
Res = builder->create_icmp_ne(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行比较 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{
/* 根据不同的比较操作,调用fcmp进行处理 */
case OP_LE:
Res = builder->create_fcmp_le(lres, rres);
break;
case OP_LT:
Res = builder->create_fcmp_lt(lres, rres);
break;
case OP_GT:
Res = builder->create_fcmp_gt(lres, rres);
break;
case OP_GE:
Res = builder->create_fcmp_ge(lres, rres);
break;
case OP_EQ:
Res = builder->create_fcmp_eq(lres, rres);
break;
case OP_NEQ:
Res = builder->create_fcmp_ne(lres, rres);
break;
}
}
Res = builder->create_zext(Res, Int32Type); /* 将结果作为整数保存(可作为判断语句中的判断条件) */
}
/* AdditiveExpression, 加法表达式, additive-expression -> additive-expression addop term
* | term */
void CminusfBuilder::visit(ASTAdditiveExpression &node)
{
// ASTAdditiveExpression有3个成员变量:
// std::shared_ptr<ASTAdditiveExpression> additive_expression;
// AddOp op;
// std::shared_ptr<ASTTerm> term;
LOG(DEBUG) << "AdditiveExpression\n";
if (node.additive_expression == nullptr)
{ /* 若无加减法运算 */
node.term->accept(*this);
return; /* 则直接去做乘除法 */
}
node.additive_expression->accept(*this); /* 处理左expression */
auto lres = Res; /* 结果保存在lres中 */
node.term->accept(*this); /* 处理右term */
auto rres = Res; /* 结果保存在rres中 */
// 分为整型-整型,和存在浮点数类型,这两种情况进行讨论
// 若存在浮点数,则全部强制转换为浮点数实现
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{ /* 根据对应加法或是减法,调用iadd或是isub进行处理 */
case OP_PLUS:
Res = builder->create_iadd(lres, rres);
break;
case OP_MINUS:
Res = builder->create_isub(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{ /* 根据对应加法或是减法,调用fadd或是fsub进行处理 */
case OP_PLUS:
Res = builder->create_fadd(lres, rres);
break;
case OP_MINUS:
Res = builder->create_fsub(lres, rres);
break;
}
}
}
/* Term, 乘除法语句, Term -> term mulop factor
* | factor */
void CminusfBuilder::visit(ASTTerm &node)
{
// ASTTerm有3个成员变量:
// std::shared_ptr<ASTTerm> term;
// MulOp op;
// std::shared_ptr<ASTFactor> factor;
LOG(DEBUG) << "Term\n";
if (node.term == nullptr)
{ /* 若无乘法运算 */
node.factor->accept(*this);
return; /* 则直接去处理元素 */
}
node.term->accept(*this); /* 处理左term */
auto lres = Res; /* 结果保存在lres中 */
node.factor->accept(*this); /* 处理右factor */
auto rres = Res; /* 结果保存在rres中 */
if (checkInt(lres) && checkInt(rres))
{ /* 确保两边都是整数 */
switch (node.op)
{ /* 根据对应乘法或是除法,调用imul或是idiv进行处理 */
case OP_MUL:
Res = builder->create_imul(lres, rres);
break;
case OP_DIV:
Res = builder->create_isdiv(lres, rres);
break;
}
}
else
{ /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
lres = builder->create_sitofp(lres, FloatType);
if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
rres = builder->create_sitofp(rres, FloatType);
switch (node.op)
{ /* 根据对应乘法或是除法,调用fmul或是fdiv进行处理 */
case OP_MUL:
Res = builder->create_fmul(lres, rres);
break;
case OP_DIV:
Res = builder->create_fdiv(lres, rres);
break;
}
}
}
/* Call, 函数调用, call -> ID (args) */
void CminusfBuilder::visit(ASTCall &node)
{
// ASTCall有3个成员变量:
// std::string id;
// std::vector<std::shared_ptr<ASTExpression>> args;
LOG(DEBUG) << "Call\n";
auto function = static_cast<Function *>(scope.find(node.id)); /* 获取需要调用的函数 */
auto paramType = function->get_function_type()->param_begin(); /* 获取其参数类型 */
if (function == nullptr)
std::cout << "ERROR: 函数" << node.id << "未定义\n";
// 处理参数列表
std::vector<Value *> args; /* 创建args用于存储函数参数的值,构建调用函数的参数列表 */
for (auto arg : node.args)
{ /* 遍历形参列表 */
arg->accept(*this); /* 对每一个参数进行处理,获取参数对应的值 */
if (Res->get_type()->is_pointer_type())
{ /* 若参数是指针 */
args.push_back(Res); /* 则直接将值加入到参数列表 */
}
else
{ /* 若不是指针,则需要判断形参和实参的类型是否符合。若不符合则需要类型转换 */
if (*paramType == FloatType && checkInt(Res))
Res = builder->create_sitofp(Res, FloatType);
if (*paramType == Int32Type && checkFloat(Res))
Res = builder->create_fptosi(Res, Int32Type);
args.push_back(Res);
}
paramType++; /* 查看下一个形参 */
}
Res = builder->create_call(static_cast<Function *>(function), args); /* 创建函数调用 */
}