- [[#实验框架|实验框架]]
- [[#实验过程|实验过程]]
- [[#实验过程#全局变量的设计|全局变量的设计]]
- [[#实验过程#1ASTProgram|1ASTProgram]]
- [[#实验过程#2ASTNum|2ASTNum]]
- [[#实验过程#3ASTVarDeclaration|3ASTVarDeclaration]]
- [[#实验过程#4ASTFunDeclaration|4ASTFunDeclaration]]
- [[#实验过程#5ASTParam|5ASTParam]]
- [[#实验过程#6ASTCompoundStmt|6ASTCompoundStmt]]
- [[#实验过程#7ASTExpressionStmt|7ASTExpressionStmt]]
- [[#实验过程#8ASTSelectionStmt|8ASTSelectionStmt]]
- [[#实验过程#9ASTIterationStmt|9ASTIterationStmt]]
- [[#实验过程#10ASTReturnStmt|10ASTReturnStmt]]
- [[#实验过程#11ASTVar|11ASTVar]]
- [[#实验过程#12ASTAssignExpression|12ASTAssignExpression]]
- [[#实验过程#13ASTSimpleExpression|13ASTSimpleExpression]]
- [[#实验过程#14ASTAdditiveExpression|14ASTAdditiveExpression]]
- [[#实验过程#15ASTTerm|15ASTTerm]]
- [[#实验过程#16ASTCall|16ASTCall]]
- [[#编译|编译]]
- [[#编译#结果验证|结果验证]]
- [[#实验总结|实验总结]]
- [[#参考|参考]]
实验要求
需要使用 LightIR
框架自动产生 cminus-f
语言的LLVM IR。
要编写的代码是:修改 src/cminusfc/cminusf_builder.cpp
,其内含各个 visit 函数,我们需要补全这些 visit 的函数。来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序
要掌握 logging 工具,仔细看 Light IR 文档(主要看 C++ APIs 部分),仔细看cminus-f 的语法与语义
实验难点
- 函数没有返回值,都是void类型,对于一些有计算结果的代码不好处理。但是实验有规定不能修改实验代码,用全局变量来存储返回值也是一个不错的选择,关键在于很正确的调用这些函数并且逻辑不出现错误,这样才能正确的传递信息
- 在生成代码的时候需要对不符合要求的数值进行数据转换。
实验框架
本次实验使用了由C++编写的 LightIR 来生成 LLVM IR。为了便于大家进行实验,该框架自动完成了语法树到 C++ 上的抽象语法树的转换。 我们可以使用访问者模式来设计抽象语法树中的算法。大家可以参考打印抽象语法树的算法, 以及运行 test_ast 来理解访问者模式下算法的执行流程。
在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等函数加入了作用域中。因此,你们在进行名字查找时不需要顾虑是否需要对特殊函数进行特殊操作。
实验过程
将实验仓库克隆到本地
打开本地的工作目录,在命令行中输入
git clone https://gitee.com/你的gitee用户名/cminus_compiler-2022-fall.git
全局变量的设计
#include "cminusf_builder.hpp"
#include "logging.hpp"
// use these macros to get constant value
// 得到常数值的表示,方便后面多次用到
#define CONST_INT(num) \
ConstantInt::get(num, module.get())
#define CONST_FP(num) \
ConstantFP::get(num, module.get())
// You can define global variables here
// to store state
Value *ret; //存储返回值
std::vector<Type *> Ints; //储存参数的类型,以确定函数的类型
int return_flag = 0; //全局变量标识当前模块是否已经有return语句
1ASTProgram
ASTProgram 语义规则为program -> declaration-list,遍历program下层的declaration-list中的声明即可
void CminusfBuilder::visit(ASTProgram &node) //遍历program下层的declaration-list中的声明
//program -> declaration-list
{
for (auto decl : node.declarations)
{
decl->accept(*this);
}
}
2ASTNum
ASTNum判断node的类型(整型或浮点型),将将相应的value值存入,并将结果赋给ret,这里的ret是一个 全局变量。
void CminusfBuilder::visit(ASTNum &node) //判断node的类型(整型或浮点型),将将相应的value值存入,并将地址赋给ret
{
if(return_flag)
return;
if (node.type == TYPE_INT)
{
ret = CONST_INT(node.i_val);
}
else if (node.type == TYPE_FLOAT)
{
ret = CONST_FP(node.f_val);
}
}
3ASTVarDeclaration
语义规则为var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ]
,变量要么是整型或浮点型,要么是数组。 首先使用scope.in_global判断是否为全局变量,然后根据node.num是否为空判断是否为数组变量 (若不为空则为数组变量),接着根据node.type判断是整型还是浮点型,最后将变量名和变量值 压入scope中。
void CminusfBuilder::visit(ASTVarDeclaration &node) //变量声明
//var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ]
{
if(return_flag)
return;
auto TyInt32 = Type::get_int32_type(module.get());
auto TyFloat = Type::get_float_type(module.get());
if (!scope.in_global()) //局部
{
if (node.num != nullptr) //数组(指针非空
{
if(!node.num->i_val)
{
Value * call_error = scope.find("neg_idx_except");//数组定义是大小为零时,打印报错信息
builder->create_call(call_error,{});
}
if (node.type == TYPE_INT) //整型数组
{
auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);
auto Local_IntArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间
scope.push(node.id, Local_IntArrayAlloca);
}
else if (node.type == TYPE_FLOAT) //浮点型数组
{
auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);
auto Local_FloatArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间
scope.push(node.id, Local_FloatArrayAlloca);
}
}
else //变量
{
if (node.type == TYPE_INT) //整型变量
{
auto Local_IntAlloca = builder->create_alloca(TyInt32); //为变量分配空间
scope.push(node.id, Local_IntAlloca);
}
else if (node.type == TYPE_FLOAT) //浮点型变量
{
auto Local_FloatAlloca = builder->create_alloca(TyFloat); //为变量分配空间
scope.push(node.id, Local_FloatAlloca);
}
}
}
else //全局
{
if (node.num != nullptr ) //数组(指针非空
{
if(!node.num->i_val)
{
Value * call_error = scope.find("neg_idx_except");
builder->create_call(call_error,{});
}
if (node.type == TYPE_INT) //整型数组
{
auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);
auto initializer = ConstantZero::get(arrayType, module.get());
auto Globle_IntArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间
scope.push(node.id, Globle_IntArrayAlloca);
}
else if (node.type == TYPE_FLOAT) //浮点型数组
{
auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);
auto initializer = ConstantZero::get(arrayType, module.get()); //初始值赋为零
auto Globle_FloatArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间
scope.push(node.id, Globle_FloatArrayAlloca);
}
}
else //变量
{
if (node.type == TYPE_INT) //整型变量
{
auto initializer = ConstantZero::get(TyInt32, module.get());
auto Globle_IntAlloca = GlobalVariable::create(node.id, module.get(), TyInt32, false, initializer); //为变量分配空间
scope.push(node.id, Globle_IntAlloca);
}
else if (node.type == TYPE_FLOAT) //浮点型变量
{
auto initializer = ConstantZero::get(TyFloat, module.get());
auto Globle_FloatAlloca = GlobalVariable::create(node.id, module.get(), TyFloat, false, initializer); //为变量分配空间
scope.push(node.id, Globle_FloatAlloca);
}
}
}
}
4ASTFunDeclaration
语义规则为fun-declaration → type-specifier ID ( params ) compound-stmt
首先要用create构造函数,那么就要得到函数的类型和参数列表,根据node.type得到函数类型, 然后遍历node.params,对于每一个param,得到其参数类型。若param为数组,则开辟数组空间,若param为整型或浮点型变量,则开辟单个变量的空间,然后把参数store下来。最后处理 compound_stmt。
void CminusfBuilder::visit(ASTFunDeclaration &node) //函数声明
//fun-declaration → type-specifier ID ( params ) compound-stmt
{
scope.enter(); //进入函数的作用域
Type *TYPE32 = Type::get_int32_type(module.get());
Type *TyFloat = Type::get_float_type(module.get());
Type *TYPEV = Type::get_void_type(module.get());
Type *TYPEARRAY_32 = PointerType::get_int32_ptr_type(module.get());
Type *funType;
Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());
Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());
//判断新声明的function的返回值类型
if (node.type == TYPE_FLOAT)
funType = TyFloat;
else if(node.type == TYPE_VOID)
{
funType = TYPEV;
}
else
{
funType = TYPE32;
}
// 函数参数的vector
std::vector<Value *> args;
if (node.params.size() > 0) //参数列表非空
{
for (auto param : node.params) //遍历Fundeclaration下的param节点,得到参数类型
{
param->accept(*this); //得到参数类型
}
auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数
auto bb = BasicBlock::create(module.get(), "entry", fun); // BB的名字在生成中无所谓,但是可以方便阅读
builder->set_insert_point(bb);
scope.exit(); //先退出当前作用域
scope.push(node.id, fun); //函数名放进作用域
scope.enter(); //再次进入函数的作用域
for (auto param : node.params) //遍历Fundeclaration下的param节点,根据参数的类型分配空间
{
if (param->isarray) //数组
{
if (param->type == TYPE_INT) //整型数组
{
auto pAlloca = builder->create_alloca(TYPEARRAY_INT_32); //在内存中分配空间
scope.push(param->id, pAlloca);
}
else if (param->type == TYPE_FLOAT) //浮点型数组
{
auto pAlloca = builder->create_alloca(TYPEARRAY_FLOAT_32);
scope.push(param->id, pAlloca);
}
}
else if (param->type == TYPE_INT) //整型
{
auto pAlloca = builder->create_alloca(TYPE32);
scope.push(param->id, pAlloca);
}
else if (param->type == TYPE_FLOAT) //浮点型
{
auto pAlloca = builder->create_alloca(TyFloat);
scope.push(param->id, pAlloca);
}
}
for (auto arg = fun->arg_begin(); arg != fun->arg_end(); arg++) //获取形参
{
args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素
}
int i = 0;
for (auto param : node.params) //将参数store下来
{
auto pAlloca = scope.find(param->id);
if (pAlloca == nullptr)
exit(0);
else
builder->create_store(args[i++], pAlloca);
Ints.pop_back(); //清空向量
}
}
else //参数列表为空
{
auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数
auto bb = BasicBlock::create(module.get(), "entry", fun);// BB的名字在生成中无所谓,但是可以方便阅读
builder->set_insert_point(bb);
scope.exit(); //先退出当前作用域
scope.push(node.id, fun); //函数名放进作用域
scope.enter(); //再次进入函数的作用域
}
node.compound_stmt->accept(*this); //执行compound-stmt
if(return_flag == 0) //当前模块没有return
{
auto return_type = builder->get_insert_block()->get_parent()->get_return_type();
if(return_type->is_void_type())
builder->create_void_ret();
else if(return_type->is_integer_type())
builder->create_ret(CONST_INT(0));
else
builder->create_ret(CONST_FP(0));
}
return_flag = 0;
scope.exit();
}
5ASTParam
语义规则为param -> type-specifier ID | type-specifier ID []
,对于每一个传进来的参数node,将 其类型存入到全局变量Ints中。 若为数组类型则将数组类型push进Ints(参数列表)中,若为变量类型则将变量类型push进Ints (参数列表)中。
void CminusfBuilder::visit(ASTParam &node) //对于每一个传进来的参数node,将其类型存入到全局变量Ints中 //参数
//param -> type-specifier ID | type-specifier ID []
{
Type *TYPE32 = Type::get_int32_type(module.get());
Type *TyFloat = Type::get_float_type(module.get());
Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());
Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());
//返回参数类型并分配空间
if (node.isarray) //数组参数
{
if (node.type == TYPE_INT)
{
Ints.push_back(TYPEARRAY_INT_32);
}
else if (node.type == TYPE_FLOAT)
{
Ints.push_back(TYPEARRAY_FLOAT_32);
}
}
else if (node.type == TYPE_INT) //整型
{
Ints.push_back(TYPE32);
}
else if (node.type == TYPE_FLOAT) //浮点型
{
Ints.push_back(TyFloat);
}
return;
}
6ASTCompoundStmt
语义规则为compound-stmt -> { local-declarations statement-list }
。 处理每个局部声明和每个语句,然后退出作用域
void CminusfBuilder::visit(ASTCompoundStmt &node) //复合语句
// compound-stmt -> { local-declarations statement-list }
{
if(return_flag)
return;
scope.enter(); //先进入到新的作用域,可能有局部变量
for (auto loc_decl : node.local_declarations) //遍历所有local_declarations
{
loc_decl->accept(*this);
}
for (auto stmt : node.statement_list) //遍历所有statement-list
{
stmt->accept(*this);
}
scope.exit();
}
7ASTExpressionStmt
语义规则为expression-stmt→expression ; ∣ ;expression→assign-expression ∣ simple expression
若expression存在(即不为空),则处理。
void CminusfBuilder::visit(ASTExpressionStmt &node) //对expression结点调用accept //表达式语句
//expression-stmt→expression ; ∣ ;
//expression→assign-expression ∣ simple-expression
{
if(return_flag)
return;
if (node.expression != nullptr)
{
node.expression->accept(*this);
}
}
8ASTSelectionStmt
首先accpet判断语句,若为float则用fcmp与0比较,若为int则用icmp与0比较,若为指针则将地址 中的值load出来赋给ret后再进行比较。 然后判断是否存在else语句,若存在则创建true块和false块,若在块中不存在return,则都去到 next块,若不存在则只创建true块和next块,如果true块中没有return则去到next块。
void CminusfBuilder::visit(ASTSelectionStmt &node) //if
//selection-stmt→ if ( expression ) statement∣ if ( expression ) statement else statement
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());
node.expression->accept(*this);
if (ret->get_type()->is_pointer_type()) //指针
ret = builder->create_load(ret);
if (ret->get_type()->is_float_type())
ret = builder->create_fcmp_ne(ret, CONST_FP(0));
else if (ret->get_type() == TYPE32)
ret = builder->create_icmp_ne(ret, CONST_INT(0));
//currentFunction
auto currentFunc = builder->get_insert_block()->get_parent();
auto trueBB = BasicBlock::create(module.get(), "", currentFunc); //创建true分支
BasicBlock *falseBB;
BasicBlock *nextBB;
BranchInst *br;
int insertedflag = 0;
if (node.else_statement != nullptr) //有else
{
falseBB = BasicBlock::create(module.get(), "", currentFunc);
br = builder->create_cond_br(ret, trueBB, falseBB);
//falseBB
builder->set_insert_point(falseBB);
node.else_statement->accept(*this);
if (builder->get_insert_block()->get_terminator() == nullptr)
{ // no return inside the block
insertedflag = 1;
nextBB = BasicBlock::create(module.get(), "", currentFunc);
builder->create_br(nextBB);
}
return_flag = 0;
//tureBB
builder->set_insert_point(trueBB);
node.if_statement->accept(*this);
if (builder->get_insert_block()->get_terminator() == nullptr)
{ // no return inside the block
if (insertedflag == 0)
{
insertedflag = 1;
nextBB = BasicBlock::create(module.get(), "", currentFunc);
}
builder->create_br(nextBB);
}
return_flag = !insertedflag;
//nextBB
if (insertedflag == 1)
{
builder->set_insert_point(nextBB);
}
}
else //无else
{
//tureBB
nextBB = BasicBlock::create(module.get(), "", currentFunc);
br = builder->create_cond_br(ret, trueBB, nextBB);
builder->set_insert_point(trueBB);
node.if_statement->accept(*this);
if (return_flag == 0)
{
builder->create_br(nextBB);
}
return_flag = 0;
//nextBB
builder->set_insert_point(nextBB);
}
}
9ASTIterationStmt
语义规则为iteration-stmt→while ( expression ) statement
首先创建loopJudge, loopBody和out块, 然后进入loopJudge块,accept判断语句,获得判断语句 的结果,若判断结果为int,则用icmp与0比较,若判断结果为float,则用fcmp与0比较,若为真, 则进入loopBody块,如果loopBody块中没有return,就再次进入loopJudge块;若为假,就进入 out块。
void CminusfBuilder::visit(ASTIterationStmt &node)//迭代语句 while
//iteration-stmt→while ( expression ) statement
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());
//currentFunction
auto currentFunc = builder->get_insert_block()->get_parent();
auto loopJudge = BasicBlock::create(module.get(), "", currentFunc);
auto loopBody = BasicBlock::create(module.get(), "", currentFunc);
auto out = BasicBlock::create(module.get(), "", currentFunc);
if (builder->get_insert_block()->get_terminator() == nullptr)
builder->create_br(loopJudge);
//loopJudge BB
builder->set_insert_point(loopJudge);
node.expression->accept(*this);
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
if (ret->get_type()->is_float_type())
ret = builder->create_fcmp_ne(ret, CONST_FP(0));
else if (ret->get_type() == TYPE32)
ret = builder->create_icmp_ne(ret, CONST_INT(0));
auto br = builder->create_cond_br(ret, loopBody, out);
//loopBody BB
builder->set_insert_point(loopBody);
node.statement->accept(*this);
if (builder->get_insert_block()->get_terminator() == nullptr)
builder->create_br(loopJudge);
return_flag = 0;
//outloop BB
builder->set_insert_point(out);
}
10ASTReturnStmt
语义规则为return-stmt→return ; ∣ return expression ;
首先得到返回值类型,若为void则创建void返回,若为指针则将地址中的值load出来然后返回,若 为整型或浮点型则做类型匹配,最后用create_ret对返回值进行返回。
void CminusfBuilder::visit(ASTReturnStmt &node) //return语句 //强制转换
//return-stmt→return ; ∣ return expression ;
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());
Type *TYPE1 = Type::get_int1_type(module.get());
Type *TyFloat = Type::get_float_type(module.get());
auto return_type = builder->get_insert_block()->get_parent()->get_return_type();
if (node.expression == nullptr) //空指针
{
if (!return_type->is_void_type())
printf("return_type is not void, but expression is empty\n");
builder->create_void_ret();
}
else
{
node.expression->accept(*this);
if (return_type->is_void_type()) //void型
{
printf("return_type is void, but expression is not empty\n");
builder->create_void_ret();
return;
}
if (ret->get_type()->is_pointer_type()) //ret为指针型
ret = builder->create_load(ret);
if (return_type == TYPE32) //转化为int型
{
if (ret->get_type() == TYPE1) //布尔型
ret = builder->create_zext(ret, TYPE32);
else if (ret->get_type() == TyFloat)
ret = builder->create_fptosi(ret, TYPE32);
}
if (return_type == TyFloat) //转化为float型
{
if (ret->get_type()->is_integer_type())
ret = builder->create_sitofp(ret, TyFloat);
}
builder->create_ret(ret);
}
return_flag = 1;
}
11ASTVar
语义规则为var→ID ∣ ID [ expression] var
在cminus-f中比较简单,既可以是整型也可以是取了下标后的数组。
void CminusfBuilder::visit(ASTVar &node) //变量
//var→ID ∣ ID [ expression]
{
if(return_flag)
return;
Type *FloatPtrType = Type::get_float_ptr_type(module.get());
Type *Int32PtrType = Type::get_int32_ptr_type(module.get());
Type *TYPE32 = Type::get_int32_type(module.get());
Type *TYPE1 = Type::get_int1_type(module.get());
//currentFunction
auto currentFunc = builder->get_insert_block()->get_parent();
auto var = scope.find(node.id);
if (var)
{
if (node.expression != nullptr) //数组
{
node.expression->accept(*this);
Value *num = ret;
//转化为int
if (num->get_type()->is_pointer_type())
num = builder->create_load(num);
if (num->get_type() == TYPE1)
num = builder->create_zext(num, TYPE32);
else if (num->get_type()->is_float_type())
num = builder->create_fptosi(num, TYPE32);
//if num < 0; enter exphandBB
auto exphandBB = BasicBlock::create(module.get(), "", currentFunc);
auto normalBB = BasicBlock::create(module.get(), "", currentFunc);
auto outBB = BasicBlock::create(module.get(), "", currentFunc);
auto flagnum = builder->create_icmp_ge(num, CONST_INT(0));
auto br = builder->create_cond_br(flagnum, normalBB, exphandBB);
//normalBB
builder->set_insert_point(normalBB);
if (var->get_type()->get_pointer_element_type()->is_pointer_type())
{
//var is an array that sub func get from main func
auto var_load = builder->create_load(var);
var = builder->create_gep(var_load, {num});
}
else if (var->get_type()->get_pointer_element_type()->is_array_type())
{
//var is an id of array,get address of id[num]
var = builder->create_gep(var, {CONST_INT(0), num});
}
else
{
printf("id is a float or int, but expression is not empty\n");
}
ret = var;
builder->create_br(outBB);
//exphandBB
builder->set_insert_point(exphandBB);
Value * call_error = scope.find("neg_idx_except");
builder->create_call(call_error, {});
builder->create_br(outBB);
//outBB
builder->set_insert_point(outBB);
}
else
{
if (!(var->get_type()->get_pointer_element_type()->is_float_type()) && !(var->get_type()->get_pointer_element_type()->is_integer_type()) && !(var->get_type()->get_pointer_element_type()->is_array_type()))
{
var = builder->create_load(var);
}
ret = var;
}
}
else
{
printf("cannot find the var\n");
return;
}
}
12ASTAssignExpression
语义规则为assign-expression→var = expression
首先accept拿到表达式左边var的地址,然后accept拿到表达式右边expression的值,匹配左值和 右值的类型,把左值store到右值的地址中。
void CminusfBuilder::visit(ASTAssignExpression &node) //赋值 //强制转换
//assign-expression→var = expression
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());
Type *TYPE1 = Type::get_int1_type(module.get());
Type *TYPEFLOAT = Type::get_float_type(module.get());
node.var.get()->accept(*this);
Value *var = ret;
node.expression.get()->accept(*this);
if (var->get_type()->get_pointer_element_type()->is_float_type()) //指向的是float型
{
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
if (ret->get_type()->is_integer_type())
ret = builder->create_sitofp(ret, TYPEFLOAT);
builder->create_store(ret, var);
}
else //指向的是int型
{
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
if (ret->get_type() == TYPE1)
ret = builder->create_zext(ret, TYPE32);
else if (ret->get_type()->is_float_type())
ret = builder->create_fptosi(ret, TYPE32);
builder->create_store(ret, var);
}
}
13ASTSimpleExpression
语义规则为simple-expression -> additive-expression relop additive- expression | additive expression
首先accept左边,再accept右边,然后判断左右两边的类型,若至少有一个为float,则把两个都转 成float,再判断op类型,左边和右边进行比较,将结果作为整数保存。
void CminusfBuilder::visit(ASTSimpleExpression &node) //简单表达式
//simple-expression -> additive-expression relop additive- expression | additive-expression
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());
Type *FloatType = Type::get_float_type(module.get());
Type *Int1Type = Type::get_int1_type(module.get());
//simple-expression -> additive-expression
if (!node.additive_expression_r) //简单加法表达式,通过accept调用下一层级
{
node.additive_expression_l->accept(*this);
}
//simple-expression -> additive-expression relop additive- expression
else //关系表达式,运算结果为整型1 或者 0
{
//获取左值和右值
Value *AdditiveLoad_l;
Value *AdditiveLoad_r;
Value *icmp;
node.additive_expression_l->accept(*this);
if (ret->get_type()->is_pointer_type())
AdditiveLoad_l = builder->create_load(ret);
else
AdditiveLoad_l = ret;
node.additive_expression_r->accept(*this);
if (ret->get_type()->is_pointer_type())
AdditiveLoad_r = builder->create_load(ret);
else
AdditiveLoad_r = ret;
int flag = 0; //标志是否为浮点数
if (AdditiveLoad_l->get_type()->is_float_type()) //l是浮点数
{
flag = 1;
if (AdditiveLoad_r->get_type()->is_integer_type())
AdditiveLoad_r = builder->create_sitofp(AdditiveLoad_r, FloatType);
}
else
{
if (AdditiveLoad_r->get_type()->is_float_type()) //r是浮点数
{
flag = 1;
AdditiveLoad_l = builder->create_sitofp(AdditiveLoad_l, FloatType);
}
else
{
flag = 0;
if (AdditiveLoad_l->get_type() == Int1Type)
AdditiveLoad_l = builder->create_zext(AdditiveLoad_l, Int32Type);
if (AdditiveLoad_r->get_type() == Int1Type)
AdditiveLoad_r = builder->create_zext(AdditiveLoad_r, Int32Type);
}
}
if (flag == 1) //float型
{
switch (node.op)
{
case OP_GE:
icmp = builder->create_fcmp_ge(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_GT:
icmp = builder->create_fcmp_gt(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_LE:
icmp = builder->create_fcmp_le(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_LT:
icmp = builder->create_fcmp_lt(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_EQ:
icmp = builder->create_fcmp_eq(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_NEQ:
icmp = builder->create_fcmp_ne(AdditiveLoad_l, AdditiveLoad_r);
break;
default:
break;
}
}
else //int型
{
switch (node.op)
{
case OP_GE:
icmp = builder->create_icmp_ge(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_GT:
icmp = builder->create_icmp_gt(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_LE:
icmp = builder->create_icmp_le(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_LT:
icmp = builder->create_icmp_lt(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_EQ:
icmp = builder->create_icmp_eq(AdditiveLoad_l, AdditiveLoad_r);
break;
case OP_NEQ:
icmp = builder->create_icmp_ne(AdditiveLoad_l, AdditiveLoad_r);
break;
default:
break;
}
}
ret = icmp;
}
}
14ASTAdditiveExpression
语义规则为additive-expression -> additive-expression addop term | term
首先accept additive_expression和term,然后判断二者类型,若至少有一个为float,则将两个都 转成float,然后进行计算
void CminusfBuilder::visit(ASTAdditiveExpression &node)
//additive-expression -> additive-expression addop term | term
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());
Type *Int1Type = Type::get_int1_type(module.get());
Type *FloatType = Type::get_float_type(module.get());
Value *AdditiveExpression;
Value *Term;
Value *icmp;
//additive-expression -> term
if (node.additive_expression == nullptr) //如果只是简单的项,转到下一层
{
node.term->accept(*this);
}
//additive-expression -> additive-expression addop term
else
{
node.additive_expression->accept(*this);
if (ret->get_type()->is_pointer_type())
AdditiveExpression = builder->create_load(ret);
else
AdditiveExpression = ret;
node.term->accept(*this);
if (ret->get_type()->is_pointer_type())
Term = builder->create_load(ret);
else
Term = ret;
int flag = 0;
if (AdditiveExpression->get_type()->is_float_type()) //如果是浮点数相加
{
flag = 1;
if (Term->get_type()->is_integer_type())
Term = builder->create_sitofp(Term, FloatType);
}
else
{
if (Term->get_type()->is_float_type()) //term为float型
{
flag = 1;
AdditiveExpression = builder->create_sitofp(AdditiveExpression, FloatType);
}
else
{
flag = 0;
if (AdditiveExpression->get_type() == Int1Type)
AdditiveExpression = builder->create_zext(AdditiveExpression, Int32Type);
if (Term->get_type() == Int1Type)
Term = builder->create_zext(Term, Int32Type);
}
}
if (flag == 1) //float型
{
if (node.op == OP_PLUS)
{
icmp = builder->create_fadd(AdditiveExpression, Term);
}
else
{
icmp = builder->create_fsub(AdditiveExpression, Term);
}
}
else //int型
{
if (node.op == OP_PLUS)
{
icmp = builder->create_iadd(AdditiveExpression, Term);
}
else
{
icmp = builder->create_isub(AdditiveExpression, Term);
}
}
ret = icmp;
}
}
15ASTTerm
语义规则为term -> term mulop factor | factor
大致和ASTAdditiveExpression差不多。
void CminusfBuilder::visit(ASTTerm &node)
//term -> term mulop factor | factor
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());
Type *Int1Type = Type::get_int1_type(module.get());
Type *FloatType = Type::get_float_type(module.get());
Value *Term;
Value *Factor;
Value *icmp;
//term -> factor
if (!node.term)
{
node.factor->accept(*this);
}
//term -> term mulop factor
else
{
node.term->accept(*this);
if (ret->get_type()->is_pointer_type())
Term = builder->create_load(ret);
else
Term = ret;
node.factor->accept(*this);
if (ret->get_type()->is_pointer_type())
Factor = builder->create_load(ret);
else
Factor = ret;
int flag = 0;
if (Term->get_type()->is_float_type())
{
flag = 1;
if (Factor->get_type()->is_integer_type())
Factor = builder->create_sitofp(Factor, FloatType);
}
else
{
if (Factor->get_type()->is_float_type())
{
flag = 1;
Term = builder->create_sitofp(Term, FloatType);
}
else
{
flag = 0;
if (Factor->get_type() == Int1Type)
Factor = builder->create_zext(Factor, Int32Type);
if (Term->get_type() == Int1Type)
Term = builder->create_zext(Term, Int32Type);
}
}
if (flag == 1)
{
if (node.op == OP_MUL)
{
icmp = builder->create_fmul(Term, Factor);
}
else
{
icmp = builder->create_fdiv(Term, Factor);
}
}
else
{
if (node.op == OP_MUL)
{
icmp = builder->create_imul(Term, Factor);
}
else
{
icmp = builder->create_isdiv(Term, Factor);
}
}
ret = icmp;
}
}
16ASTCall
首先scope.find找到对应的函数,get_type获取函数类型,然后遍历node.args,检查参数类型是 否匹配,若形参为int或float且形参与实参类型不匹配,则将实参转换为形参类型,压入function (参数列表)。
void CminusfBuilder::visit(ASTCall &node)
{
//根据名字寻找到对应的值
if(return_flag)
return;
Value *value;
value = scope.find(node.id); //调用scope.find()找ID对应的值
if (value == nullptr) //是不是函数名
{
printf("cannot find the fun\n");
return;
}
auto fun = value->get_type();
if (!fun->is_function_type()) //检查函数类型
return;
auto callfun = static_cast<FunctionType *>(fun);
Value *value_args;
int i = 0;
std::vector<Value *> function;
Type *Int32Type = Type::get_int32_type(module.get());
Type *FloatType = Type::get_float_type(module.get());
Type *Int32PtrType = Type::get_int32_ptr_type(module.get());
Type *Int1Type = Type::get_int1_type(module.get());
for (auto Args : node.args) //检查参数类型是否匹配
{
auto arg_type = callfun->get_param_type(i);
i++;
Args->accept(*this);
if (ret->get_type() == Int1Type) //如果ret是布尔型,ret先转换成32位整型
{
ret = builder->create_zext(ret, Int32Type);
}
if (arg_type == Int32Type) //要求的参数为整型
{
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
else if (ret->get_type() == FloatType)
ret = builder->create_fptosi(ret, Int32Type);
value_args = ret;
}
else if (arg_type == FloatType) //要求的参数为浮点数
{
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
else if (ret->get_type() == Int32Type)
ret = builder->create_sitofp(ret, FloatType);
value_args = ret;
}
else //要求的参数为指针
{
if (ret->get_type() == Int32Type || ret->get_type() == FloatType)
return;
value_args = ret;
}
function.push_back(value_args);
}
if (i != callfun->get_num_of_args())
{
printf("\t the num of arg error\n");
return;
}
//call,get into sub func
ret = builder->create_call(value, function);
}
编译
mkdir build
cd build
cmake … -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ mkdir build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ cd build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found FLEX: /usr/bin/flex (found version "2.6.4")
-- Found BISON: /usr/bin/bison (found version "3.5.1")
-- Found LLVM 10.0.0
-- Using LLVMConfig.cmake in: /usr/lib/llvm-10/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sunny2004/lab1/cminus_compiler-2023-fall/build
make -j
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ make -j
[ 1%] [FLEX][lex] Building scanner with flex 2.6.4
Scanning dependencies of target cminus_io
Scanning dependencies of target OP_lib
Scanning dependencies of target common
[ 3%] [BISON][syntax] Building parser with bison 3.5.1
Scanning dependencies of target IR_lib
[ 5%] [FLEX][lex] Building scanner with flex 2.6.4
lexical_analyzer.l:60: warning, 无法匹配规则
syntax_analyzer.y[ 7%] Building C object src/io/CMakeFiles/cminus_io.dir/io.c.o
lexical_analyzer.l:53: warning, 无法匹配规则
: 警告: 1 项偏移/归约冲突 [-Wconflicts-sr]
[ 18%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ConstPropagation.cpp.o
[ 9%] Building C object src/common/CMakeFiles/common.dir/syntax_tree.c.o
[ 11%] Building CXX object src/common/CMakeFiles/common.dir/logging.cpp.o
[ 13%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Dominators.cpp.o
Scanning dependencies of target flex
[ 15%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Value.cpp.o
[ 16%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/BasicBlock.cpp.o
[ 20%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Constant.cpp.o
[ 22%] Building CXX object src/common/CMakeFiles/common.dir/ast.cpp.o
[ 24%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Function.cpp.o
[ 26%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Type.cpp.o
[ 28%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Mem2Reg.cpp.o
[ 30%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/User.cpp.o
[ 32%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopSearch.cpp.o
[ 33%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ActiveVars.cpp.o
[ 35%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopInvHoist.cpp.o
[ 37%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/GlobalVariable.cpp.o
[ 39%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Module.cpp.o
[ 41%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Instruction.cpp.o
[ 43%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/IRprinter.cpp.o
[ 45%] Building C object src/lexer/CMakeFiles/flex.dir/lex.yy.c.o
[ 47%] Linking C static library ../../libcminus_io.a
lexical_analyzer.l: In function ‘analyzer’:
lexical_analyzer.l:92:5: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
lexical_analyzer.l: At top level:
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1320:17: warning: ‘yyunput’ defined but not used [-Wunused-function]
static void yyunput (int c, char * yy_bp )
^
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1363:16: warning: ‘input’ defined but not used [-Wunused-function]
static int input (void)
^
Scanning dependencies of target syntax
[ 47%] Built target cminus_io
[ 49%] Building C object src/parser/CMakeFiles/syntax.dir/syntax_analyzer.c.o
[ 50%] Building C object src/parser/CMakeFiles/syntax.dir/lexical_analyzer.c.o
[ 52%] Linking C static library ../../libflex.a
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1301:17: warning: ‘yyunput’ defined but not used [-Wunused-function]
static void yyunput (int c, char * yy_bp )
^
syntax_analyzer.y: In function ‘parse’:
syntax_analyzer.y:180:5: warning: implicit declaration of function ‘yyrestart’ [-Wimplicit-function-declaration]
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1344:16: warning: ‘input’ defined but not used [-Wunused-function]
static int input (void)
^
[ 52%] Built target flex
Scanning dependencies of target lexer
[ 54%] Linking C static library ../../libsyntax.a
[ 56%] Building C object tests/lab1/CMakeFiles/lexer.dir/main.c.o
[ 56%] Built target syntax
[ 58%] Linking C executable ../../lexer
[ 58%] Built target lexer
[ 60%] Linking CXX static library ../../libIR_lib.a
[ 62%] Linking CXX static library ../../libcommon.a
[ 62%] Built target IR_lib
Scanning dependencies of target stu_while_generator
Scanning dependencies of target stu_if_generator
Scanning dependencies of target stu_assign_generator
Scanning dependencies of target stu_fun_generator
Scanning dependencies of target gcd_array_generator
[ 64%] Building CXX object tests/lab3/CMakeFiles/stu_while_generator.dir/stu_cpp/while_generator.cpp.o
[ 66%] Building CXX object tests/lab3/CMakeFiles/stu_if_generator.dir/stu_cpp/if_generator.cpp.o
[ 69%] Building CXX object tests/lab3/CMakeFiles/stu_fun_generator.dir/stu_cpp/fun_generator.cpp.o
[ 71%] Building CXX object tests/lab3/CMakeFiles/gcd_array_generator.dir/ta_gcd/gcd_array_generator.cpp.o
[ 67%] Building CXX object tests/lab3/CMakeFiles/stu_assign_generator.dir/stu_cpp/assign_generator.cpp.o
[ 71%] Built target common
Scanning dependencies of target test_logging
Scanning dependencies of target test_ast
Scanning dependencies of target parser
[ 73%] Building CXX object tests/CMakeFiles/test_ast.dir/test_ast.cpp.o
[ 75%] Building CXX object tests/CMakeFiles/test_logging.dir/test_logging.cpp.o
[ 77%] Building C object tests/lab2/CMakeFiles/parser.dir/main.c.o
[ 79%] Linking CXX executable ../../parser
[ 79%] Built target parser
[ 81%] Linking CXX executable ../test_logging
[ 81%] Built target test_logging
[ 83%] Linking CXX executable ../test_ast
[ 84%] Linking CXX executable ../../stu_while_generator
[ 86%] Linking CXX executable ../../stu_if_generator
[ 88%] Linking CXX executable ../../stu_assign_generator
[ 88%] Built target test_ast
[ 90%] Linking CXX executable ../../stu_fun_generator
[ 92%] Linking CXX executable ../../gcd_array_generator
[ 92%] Built target stu_if_generator
[ 94%] Linking CXX static library ../../libOP_lib.a
[ 94%] Built target stu_while_generator
[ 94%] Built target stu_assign_generator
[ 94%] Built target stu_fun_generator
[ 94%] Built target gcd_array_generator
[ 94%] Built target OP_lib
Scanning dependencies of target cminusfc
[ 96%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusfc.cpp.o
[ 98%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusf_builder.cpp.o
[100%] Linking CXX executable ../../cminusfc
[100%] Built target cminusfc
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$
make install(需要管理员权限)
root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build# make install
[ 5%] Built target flex
[ 15%] Built target syntax
[ 18%] Built target cminus_io
[ 26%] Built target common
[ 47%] Built target IR_lib
[ 60%] Built target OP_lib
[ 66%] Built target cminusfc
[ 69%] Built target test_logging
[ 73%] Built target test_ast
[ 77%] Built target lexer
[ 81%] Built target parser
[ 84%] Built target stu_while_generator
[ 88%] Built target stu_if_generator
[ 92%] Built target stu_assign_generator
[ 96%] Built target stu_fun_generator
[100%] Built target gcd_array_generator
Install the project...
-- Install configuration: "Debug"
-- Installing: /usr/local/lib/libcminus_io.a
-- Installing: /usr/local/bin/cminusfc
root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build#
结果验证
运行 python lab4_test.py
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab4$ python 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============
实验总结
本次实验主要的难点在于读懂实验需求,以及对代码的特殊细节进行处理,刚开始做的时候非常困惑,查阅了非常多的资料,包括实验本身给的文档、各类头文件,也查阅了别人的代码和注释,参照了lab3 中给出的gcd_generator,才勉强写出代码。大量时间用在阅读和理解上。 在debug的过程中尝试了多种思路,包括使用log等等,但还是很难debug,参照别人的代码时也发现了 自己考虑问题不全面,接口不熟悉等一些问题,希望以后这样的实验还是能与同学多交流吧
参考
编译原理:cminus_compiler-2021-fall Lab4_编译原理实验 lab4-CSDN博客
还有一个:岳麓山大小姐的
https://blog.csdn.net/qq_45795586/article/details/122593206
这个很管用