前言:原本想认认真真把这个实验给完成的,但是当时时间太赶了,一周要做三个实验,所以这次实验基本都是抄的了,有些地方也抄的不明不白,不过懂不懂这个对课程学习的帮助并不是很大,毕竟这个实验的难度对hun的学生来说确实挺大的。至于代码还有很大的优化空间,有时间的可以去优化一下。
Lab4 实验报告
实验要求
在理解cminus-f
语法与语义的基础上,参考cminusf_builder.hpp文件以及include中的文件,补充完成 cminusf_builder.cpp
中的16个函数,来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f
程序。在自动产生 IR 的过程中,利用访问者模式自顶向下遍历抽象语法树的每一个结点,调用我们补充完成的函数对每一个抽象语法树的结点进行分析,如果程序是合法的则编译应该显示Success
,否则编译不通过显示Fail
。
- 阅读cminus-f 的语义规则
成为语言律师,我们将按照语义实现程度进行评分 - 阅读LightIR 核心类介绍
- 阅读实验框架,理解如何使用框架以及注意事项
- 修改
src/cminusfc/cminusf_builder.cpp
来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序 - 在
report.md
中解释你们的设计,遇到的困难和解决方案
实验难点
-
ASTFunDeclaration函数的设计
在将函数名push到符号表的时候应该先退出当前函数的作用域,进入到全局作用域,因为函数名应该是全局都可以索引到的。push完成后再次进入函数的作用域(同时要注意此步骤应该放在push函数参数之前,否则随着退出当前作用域,push到符号表中的参数会消失)
-
ASTSelectionStmt函数的设计
在该函数的设计中遇到了许多问题:
- **
if-else
不配对出现。**即只有if
的情况,最初考虑不论else
是否出现,都生成flaseBB这个block(尽管这可能会产生冗余),这条冗余无法解决某种特殊情况,后将冗余去掉,实验设计中详述。 - **
if-else
的嵌套问题。**考虑到else
总是和最靠近的if
配对,为了保证嵌套中每对if-else
生成的nextBB是按序生成,所以先处理else
中的动作。 - **
if-else
中含有return。**如果if-else
都含有return
语句,那么就不应该生成nextBB,如果if-else
只有一个含有return
语句,则应该生成nextBB;如果只有if
且含有return
语句,也应该生成nextBB。
- **
-
ASTVar函数的设计
我们知道
visit(ASTVar &node)
的返回值为指向一个具体元素的指针,语义要求可以对一些数值进行强制类型转换,但是这个操作显然也不能太过非法,例如函数传参时,若形参是int,但是由传入参数为一个数组,这时要将它和一个普通变量区分开来,特设argload全局变量,1标识对于由visit(ASTVar &node)
得到的指针能不能在传参中进行load操作。(因为它是从一个id为pointer或array,且无expression的var得到的)
实验设计
0.全局变量的定义
由于函数都是void型,函数没有返回值,我们需要借助全局变量在上层或下层之间传递值 。这里设置了三个全局变量:
- ret用于存储返回值
- Ints用于储存参数的类型,以确定函数的类型
- return_flag用于全局变量标识当前模块是否已经有return语句,在最后的同学交流模块有分析
Value *ret; //存储返回值
std::vector<Type *> Ints; //储存参数的类型,以确定函数的类型
int return_flag = 0; //全局变量标识当前模块是否已经有return语句
1.ASTProgram
函数
对应文法规则: program → declaration-list \text{program} \rightarrow \text{declaration-list} program→declaration-list
其主要功能是遍历program
下层的declaration-list
中的声明。故可以用一个for循环遍历declaration-list
,然后处理每一个declaration
。具体代码如下:
/* Program, 程序, program->declaration-list */
void CminusfBuilder::visit(ASTProgram &node) {
for (auto decl : node.declarations) /* 遍历declaration-list */
decl->accept(*this); /* 处理每一个declaration */
}
2.ASTNum
函数
主要功能是判断node的类型(整型或浮点型),将将相应的value值存入,并将地址赋给ret。代码如下:
void CminusfBuilder::visit(ASTNum &node)
{
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);//获取结点中存储的浮点型数值
}
}
3.ASTVarDeclaration
函数
对应的语法规则为: var-declaration → type-specifier ID ; ∣ type-specifier ID [ INTEGER ] ; \text{var-declaration}\ \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{;}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[}\ \textbf{INTEGER}\ \textbf{]}\ \textbf{;} var-declaration →type-specifier ID ; ∣ type-specifier ID [ INTEGER ] ;
type-specifier → int ∣ float ∣ void \text{type-specifier} \rightarrow \textbf{int}\ |\ \textbf{float}\ |\ \textbf{void} type-specifier→int ∣ float ∣ void
此语法规则有如下说明:
全局变量需要初始化为全 0
cminus-f
的基础类型只有整型(int)、浮点型(float)和void
。而在变量声明中,只有整型和浮点型可以使用,void
仅用于函数声明。
一个变量声明
定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。
数组变量在声明时, INTEGER \textbf{INTEGER} INTEGER 应当大于0。
一次只能声明一个变量。
首先通过scope.in_global
判断声明的变量是全局变量还是局部变量
如果是局部变量,先根据node.num判断是否为数组(或变量)
- 若为数组类型,首先要需开辟的对应大小的空间的类型指针,接着为数组分配空间,最后将获得的数组变量加入域
- 若为变量类型,则申请局部空间,然后将获得变量加入域
若是全局变量,先根据node.num判断是否为数组(或变量)
- 若为数组类型,首先要需开辟的对应大小的空间的类型指针,接着将变量初始化为0, 开辟全局数组,最后将获得的数组变量加入域
- 若为变量类型,首先初始化为0,然后申请全局空间,最后将获得的数组变量加入域
void CminusfBuilder::visit(ASTVarDeclaration &node)
{
if(return_flag)//如果返回标志位1
return;
auto TyInt32 = Type::get_int32_type(module.get());//获取int32类型
auto TyFloat = Type::get_float_type(module.get());//获取float类型
//局部变量,不能是空类型,push作用将id储存进作用域,以便之后赋值时查找是否声明
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);//获取对应的数组Type
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);//获取对应的数组Type
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);//获取对应的数组Type
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);//获取对应的数组Type
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);//将获得变量加入域
}
}
}
}
4.ASTFunDeclaration
函数
对应语法规则:
fun-declaration → type-specifier ID ( params ) compound-stmt \text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt} fun-declaration→type-specifier ID ( params ) compound-stmt
params → param-list ∣ void \text{params} \rightarrow \text{param-list}\ |\ \textbf{void} params→param-list ∣ void
param-list → param-list , param ∣ param \text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param} param-list→param-list , param ∣ param
param → type-specifier ID ∣ type-specifier ID [] \text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]} param→type-specifier ID ∣ type-specifier ID []
此语法规则有如下说明:
函数声明
包含了返回类型,标识符,由逗号分隔的形参
列表,还有一个复合语句
。
当函数的返回类型是void
时,函数不返回任何值。
函数的参数可以是void
,也可以是一个列表。当函数的形参
是void
时,调用该函数时不用传入任何参数。
形参
中跟着中括号代表数组参数,它们可以有不同长度。
整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。
函数的形参
拥有和函数声明
的复合语句
相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致)
函数可以递归调用。
首先进入函数的作用域 ,根据不同的返回类型,设置funType
,根据函数声明,构造形参列表(此处的形参即参数的类型),遍历Fundeclaration下的param节点获得参数类型,然后根据参数类型和返回值类型创建 函数的BasicBlock,然后将函数的名字push到作用域中,此时应该先退出当前作用域, 进入到全局的作用域,之后再次进入函数的作用域,且此动作应该再push其他内容前进行,否则会将其其他内容丢弃。然后根据参数的类型分配空间,并将函数名push到符号表中(参数分配空间不能在param节点中执行,因为那时候还没有创建函数,没有执行builder->set_insert_point(bb)
)。
参数列表为空时,只需先退出当前作用域, 进入到全局的作用域,之后再次进入函数的作用域,且此动作应该再push其他内容前进行,否 则会将其其他内容丢弃。
接着处理函数体内语句compound-stmt,最后退出此函数作用域
void CminusfBuilder::visit(ASTFunDeclaration &node)
{
scope.enter(); //进入函数的作用域
Type *TYPE32 = Type::get_int32_type(module.get());//获取int32类型
Type *TyFloat = Type::get_float_type(module.get());//获取float类型
Type *TYPEV = Type::get_void_type(module.get());//获取void类型
Type *TYPEARRAY_32 = PointerType::get_int32_ptr_type(module.get());
Type *funType; //返回类型
Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());//获取int32数组类型
Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());获取float数组类型
if (node.type == TYPE_FLOAT)//如果是浮点型
funType = TyFloat;
else
{
funType = (node.type == TYPE_VOID) ? TYPEV : TYPE32;//不是void就是int
}
// 函数参数的vector,根据函数声明,构造形参列表(此处的形参即参数的类型)
std::vector<Value *> args;
if (node.params.size() > 0) //参数列表非空
{
for (auto param : node.params)
{
param->accept(*this); //得到参数类型并分配空间
}
// 由函数类型得到函数
auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get());//创建函数
// BB的名字在生成中无所谓,但是可以方便阅读
auto bb = BasicBlock::create(module.get(), "entry", fun);
builder->set_insert_point(bb);//将基本块bb加入到builder中
scope.exit();//退出此函数作用域
scope.push(node.id, fun); //函数名放进作用域
scope.enter();//进入此函数作用域
for (auto param : node.params) //alloca
{
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);//将实参加入vector
}
int i = 0;
//将参数store下来
for (auto param : node.params)
{
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());
// BB的名字在生成中无所谓,但是可以方便阅读
auto bb = BasicBlock::create(module.get(), "entry", fun);//创建基本块
builder->set_insert_point(bb);//将基本块bb加入到builder中
scope.exit();//退出函数作用域
scope.push(node.id, fun); //函数名放进作用域
scope.enter();//进入函数作用域
}
node.compound_stmt->accept(*this);//处理函数体内语句compound-stmt
if(return_flag == 0)
{
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();
}
5.ASTParam
函数
对于语法规则: param → type-specifier ID ∣ type-specifier ID [] \text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]} param→type-specifier ID ∣ type-specifier ID []
主要功能:对于每一个传进来的参数node,将其类型存入到全局变量Ints中。
首先返回参数类型并分配空间,若为整型数组,浮点数组,整型数或浮点数,直接将参数push到域中。
void CminusfBuilder::visit(ASTParam &node)
{
//param -> type-specifier ID | type-specifier ID []
Type *TYPE32 = Type::get_int32_type(module.get());//获取int32类型
Type *TyFloat = Type::get_float_type(module.get());//获取float类型
Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());//获取int32数组类型
Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());//获取float数组类型
//返回参数类型并分配空间
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;
}
6.ASTCompoundStmt
函数
对应语法规则: compound-stmt → { local-declarations statement-list } \text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}} compound-stmt→{ local-declarations statement-list}
说明如下:
一个
复合语句
由一对大括号和其中的局部声明
与语句列表
组成
复合语句
的执行时,对包含着的语句按照语句列表
中的顺序执行
局部声明
拥有和复合语句
中的语句列表
一样的作用域,且其优先级高于任何同名的全局声明(常见的静态作用域)
首先进入函数的作用域,接着遍历并处理每个局部声明,再遍历和处理每个语句,最后退出函数作用域
void CminusfBuilder::visit(ASTCompoundStmt &node)
{
if(return_flag)//返回标志为1
return;
scope.enter();//进入函数体内的作用域
for (auto loc_decl : node.local_declarations)//遍历
{
loc_decl->accept(*this);//处理每一个局部声明
}
for (auto stmt : node.statement_list)//遍历
{
stmt->accept(*this);//处理每一个语句
}
scope.exit();//退出作用域
}
7.ASTExpressionStmt
函数
对应语法规则: expression-stmt → expression ; ∣ ; \text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;} expression-stmt→expression ; ∣ ;
若对应表达式存在,则处理该表达式
void CminusfBuilder::visit(ASTExpressionStmt &node)
{
if(return_flag)//返回标志为1
return;
if (node.expression != nullptr)//若对应表达式存在
{
node.expression->accept(*this);//则处理该表达式
}
}
8.ASTSelectionStmt
函数
对应语法规则: selection-stmt → if ( expression ) statement ∣ if ( expression ) statement else statement \begin{aligned}\text{selection-stmt} \rightarrow\ &\textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\\ &|\ \textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\ \textbf{else}\ \text{statement}\end{aligned} selection-stmt→ if ( expression ) statement∣ if ( expression ) statement else statement
说明如下:
if
语句中的表达式
将被求值,若结果的值等于0,则第二个语句
执行(如果存在的话),否则第一个语句
会执行。为了避免歧义, else 将会匹配最近的将会匹配最近的 if \textbf{else}将会匹配最近的将会匹配最近的\textbf{if} else将会匹配最近的将会匹配最近的if
创建if(true)分支, 根据是否有else分支进行选择处理
- 有else分支
- 创建false分支及条件转移
- 进入到false分支的BB,然后进入到else中的
statement
- 如果else中没有
return
,将标记insertedflag
置为1,然后创建nextBB和无条件转移。 - 进入到true分支的BB,然后进入到if中的
statement
- 如果if中没有
return
,且else中有return
(通过insertedflag==0
判断),将标记insertedflag
置为1,然后创建nextBB**(因为if中没有return时,nextBB一定存在,故无论else中是否有return
都要无条件转移到nextBB**)。 - 最后当
insertedflag
为一时,即创建了nextBB,进入到此BasicBlock中。
- 没有else分支
- 没有else分支时,一定会有nextBB,因此创建nextBB,并创建trueBB和nextBB的条件转移指令
- 进入到trueBB,然后进入到if中的
statement
- 无条件转移到nextBB,最后进入到nextBB
void CminusfBuilder::visit(ASTSelectionStmt &node)
{
if(return_flag)//如果返回标志为1
return;
Type *TYPE32 = Type::get_int32_type(module.get());//获取int32类型
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);
BasicBlock *falseBB;
BasicBlock *nextBB;
BranchInst *br;
int insertedflag = 0;
//falseBB,假分支放在前面是为了保证在ifelse嵌套时,nextbb的序号按序
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
{
//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);
}
}
9.ASTIterationStmt
函数
对应语法规则: iteration-stmt → while ( expression ) statement \text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement} iteration-stmt→while ( expression ) statement
说明如下:
while
语句是cminus-f
中唯一的迭代语句
。它执行时,会不断对表达式
进行求值,并且在对表达式
的求值结果等于 0 前,循环执行执下面的语句
基本和if-else
一样,但是三个模块loopJudge
,loopBody
,out
都是必须的
首先跳转到条件判断块,处理条件判断对应的表达式,得到返回值存到expression中,获取表达式得到的结果类型,若结果为整型,则针对整型进行处理(bool类型视为整型), 若结果为浮点型,则针对浮点数进行处理,接着设置条件跳转语句。
对于循环体内容,首先循环语句执行块,然后执行对应的语句 ,若无返回,则补充跳转 ,最后跳转到条件判断语句
最后进入 outloop BB 执行余下内容
void CminusfBuilder::visit(ASTIterationStmt &node)
{
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);//处理条件判断对应的表达式,得到返回值存到expression中
if (ret->get_type()->is_pointer_type())//指针型
ret = builder->create_load(ret);
if (ret->get_type()->is_float_type())//float型
ret = builder->create_fcmp_ne(ret, CONST_FP(0));
else if (ret->get_type() == TYPE32)//int型
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); //return块(即后续语句)
}
10.ASTReturnStmt
函数
对应语法规则: return-stmt → return ; ∣ return expression ; \text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;} return-stmt→return ; ∣ return expression ;
return
语句可以返回值,也可以不返回值。
未声明为 void \textbf{void} void类型的函数必须返回和函数返回类型相同的值
return
会将程序的控制转移给当前函数的调用者,而 main \textbf{main} main函数的return
会使得程序终止
这个函数要求强制类型转换,所以要先得到return_type
,对比ret
的type
,就是一番常规的类型转化,(pointer,i 1,i32,float)-> (void, i 32, float)
如果返回类型是void,则创建void返回,随后return,无需后续操作
接着处理非void的情况,处理条件判断对应的表达式,得到返回值存到expression中。获取表达式得到的结果类型。处理expression返回的结果和需要return的结果类型不匹配的问题,最后创建return,将expression的结果进行返回。
void CminusfBuilder::visit(ASTReturnStmt &node)
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());//获取int32类型
Type *TYPE1 = Type::get_int1_type(module.get());//获取int1类型
Type *TyFloat = Type::get_float_type(module.get());//获取float类型
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);//处理条件判断对应的表达式,得到返回值存到expression中
if (return_type->is_void_type())//如果是void
{
printf("return_type is void, but expression is not empty\n");
builder->create_void_ret();//则创建void返回,随后return,无需后续操作
return;
}
if (ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
//处理expression返回的结果和需要return的结果类型不匹配的问题
if (return_type == TYPE32)
{
if (ret->get_type() == TYPE1)
ret = builder->create_zext(ret, TYPE32);//将其操作数零扩展为type2类型
else if (ret->get_type() == TyFloat)
ret = builder->create_fptosi(ret, TYPE32);//将浮点值转换为type2(整数)类型。
}
if (return_type == TyFloat)
{
if (ret->get_type()->is_integer_type())
ret = builder->create_sitofp(ret, TyFloat);//将有符号整数转换为type2(浮点数)类型
}
builder->create_ret(ret);//创建return,将expression的结果进行返回
}
return_flag = 1;//return标志位置1
}
11.ASTVar
函数
对应语法规则: var → ID ∣ ID [ expression ] \text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]} var→ID ∣ ID [ expression]
说明:
var
可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。
数组的下标值是整型,它的值是表达式计算结果或结果进行类型转换后的整型值
一个负的下标会导致程序终止,需要调用框架中的内置函数neg_idx_except
(该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。
-
我们从语法中得知例
a = b
这样的表达式,等式左值和右值都是var,所以限制了visit(ASTVar &node)
的返回值为指向一个具体元素的指针,var→ID ∣ ID [ expression]
,这里最重要的是对每一种情况都进行分析,不遗漏 -
对数组下标,有强制类型转换,有小于0的异常处理(需要生成下标正常和非正常的BB)
id类型 | expression | argload | 正常出现的地方 |
---|---|---|---|
array | 有 | 1 | 基本所有地方读取数组元素 |
pointer | 有 | 1 | 子函数使用传入的数组 |
其他 | 有 | 1 | 不应该出现 |
array | 无 | 0 | 调用子函数时,将数组作为传入参数 |
pointer | 无 | 0 | 将传入的数组作为参数传入子函数 |
int | 无 | 1 | 基本所有地方 |
float | 无 | 1 | 基本所有地方 |
首先从域中取出对应变量, 接着判断有无expression,
- 若有expression,处理expression,
void CminusfBuilder::visit(ASTVar &node)
{
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);//从域中取出对应变量
argload = 1;
if (var)
{
if (node.expression != nullptr)//若有expression
//id is an array
{
// printf("\t\tvar-expression\n");
node.expression->accept(*this);//处理expression,得到结果ret
Value *num = ret;
//transfer num to 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});//进行一层寻址(因为此时并非指向数组)
// printf("var-exp-array in sub func\n");
}
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});//则进行两层寻址
// printf("var-exp-arrary\n");
}
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");//取出neg_idx_except函数
builder->create_call(call_error, {});//调用neg_idx_except函数进行处理
builder->create_br(outBB);//跳转到outBB块
//outBB
builder->set_insert_point(outBB);//进入outBB块
}
else
{
if (var->get_type()->get_pointer_element_type()->is_float_type() || var->get_type()->get_pointer_element_type()->is_integer_type())
{//若要返回值
argload = 1;
}
else if (var->get_type()->get_pointer_element_type()->is_array_type())//若指向数组
{
var = builder->create_gep(var, {CONST_INT(0), CONST_INT(0)});//则寻址
// printf("arrary_arg\n");
argload = 0;
}
else
{
var = builder->create_load(var);//否则则进行load
argload = 0;
}
ret = var;
}
}
else
{
printf("cannot find the var\n");
return;
}
}
12.ASTAssignExpression
函数
对应语法规则: expression → var = expression ∣ simple-expression \text{expression} \rightarrow \text{var}\ \textbf{=}\ \text{expression}\ |\ \text{simple-expression} expression→var = expression ∣ simple-expression
说明:
赋值语义为:先找到
var
代表的变量地址(如果是数组,需要先对下标表达式求值),然后对右侧的表达式进行求值,求值结果将在转换成变量类型后存储在先前找到的地址中。同时,存储在var
中的值将作为赋值表达式的求值结果。在
C
中,赋值对象(即var
)必须是左值,而左值可以通过多种方式获得。cminus-f
中,唯一的左值就是通过var
的语法得到的,因此cminus-f
通过语法限制了var
为左值,而不是像C
中一样通过类型检查,这也是为什么cminus-f
中不允许进行指针算数。
首先处理左var,获取地址,处理右expression,获取var的类型,若赋值语句左右类型不匹配,则进行匹配,最后进行赋值即可
void CminusfBuilder::visit(ASTAssignExpression &node)
{
if(return_flag)
return;
Type *TYPE32 = Type::get_int32_type(module.get());//获取int32类型
Type *TYPE1 = Type::get_int1_type(module.get());//获取int1类型
Type *TYPEFLOAT = Type::get_float_type(module.get());//获取float类型
node.var.get()->accept(*this);//处理左var
Value *var = ret;//获取地址
node.expression.get()->accept(*this);//处理右expression
if (var->get_type()->get_pointer_element_type()->is_float_type())//获取var的类型
{
//若赋值语句左右类型不匹配,则进行匹配
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
{
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);//进行赋值
}
}
13.SimpleExpression
函数
对应文法规则: simple-expression → additive-expression relop additive-expression ∣ additive-expression \text{simple-expression} \rightarrow \text{additive-expression}\ \text{relop}\ \text{additive-expression}\ |\ \text{additive-expression} simple-expression→additive-expression relop additive-expression ∣ additive-expression
说明:
一个
简单表达式
是一个加法表达式
或者两个加法表达式
的关系运算。当它是加法表达式
时,它的值就是加法表达式
的值。而当它是关系运算时,如果关系运算结果为真则值为整型值 1,反之则值为整型值 0。
如果是简单加法表达式,通过accept调用下一层级
否则先获取左值再获取右值。如果是指针,load上来;否则,直接赋值给相应的表达式
判断是不是浮点数,如果两个数中有且仅有一个是浮点数,那么将另外一个数转换成浮点数。并且在检测到浮点数时,将标志flag设为1,便于以后选择是否浮点数比较。同时,如果是布尔型表达式,则转换成32位整数。
根据op的值选择相应的比较,将结果赋值给icmp,最后将icmp赋值给返回值ret
void CminusfBuilder::visit(ASTSimpleExpression &node)
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());//获取int32类型
Type *FloatType = Type::get_float_type(module.get());//获取float类型
Type *Int1Type = Type::get_int1_type(module.get());//获取int1类型
//简单加法表达式,通过accept调用下一层级
if (!node.additive_expression_r)
{
node.additive_expression_l->accept(*this);
}
//simple-expression -> additive-expression relop additive- expression
//关系表达式,运算结果为整型1 或者 0
else
{
//获取左值和右值
Value *AdditiveLoad_l;
Value *AdditiveLoad_r;
Value *icmp;//运算结果
node.additive_expression_l->accept(*this);//处理左边的expression
if (ret->get_type()->is_pointer_type())//是指针
AdditiveLoad_l = builder->create_load(ret);
else
AdditiveLoad_l = ret;
node.additive_expression_r->accept(*this);//处理右边的expression
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())
{
flag = 1;
if (AdditiveLoad_r->get_type()->is_integer_type())//右边为int型
AdditiveLoad_r = builder->create_sitofp(AdditiveLoad_r, FloatType);//将右部转为float型
}
else
{
if (AdditiveLoad_r->get_type()->is_float_type())//右边为float型
{
flag = 1;
AdditiveLoad_l = builder->create_sitofp(AdditiveLoad_l, FloatType);//将左部转为float型
}
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)
{
switch (node.op)//根据不同的比较操作,调用fcmp进行处理
{
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
{
switch (node.op)//根据不同的比较操作,调用fcmp进行处理
{
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;
}
}
14.AdditiveExpression
函数
对应语法规则: additive-expression → additive-expression addop term ∣ term \text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term} additive-expression→additive-expression addop term ∣ term
说明:
加法表达式
表现出了四则运算的结合性质与优先级顺序,四则运算的含义和C
中的整型运算一致。
浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型
如果没有additive_expression,则通过accept调用下一层;如果有,则先获取左值,再获取右值。判断是否为浮点数,如果是,将flag置1,根据flag和op选择计算,并返回结果。
void CminusfBuilder::visit(ASTAdditiveExpression &node)
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());//获取int32类型
Type *Int1Type = Type::get_int1_type(module.get());//获取int1类型
Type *FloatType = Type::get_float_type(module.get());//获取float类型
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);//处理左expression
if (ret->get_type()->is_pointer_type())//是指针
AdditiveExpression = builder->create_load(ret);
else
AdditiveExpression = ret;
node.term->accept(*this);//处理右term
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())//右部是浮点数
{
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)
{
if (node.op == OP_PLUS)//根据对应加法或是减法,调用fadd或是fsub进行处理
{
icmp = builder->create_fadd(AdditiveExpression, Term);
}
else
{
icmp = builder->create_fsub(AdditiveExpression, Term);
}
}
else
{
if (node.op == OP_PLUS)//根据对应加法或是减法,调用iadd或是isub进行处理
{
icmp = builder->create_iadd(AdditiveExpression, Term);
}
else
{
icmp = builder->create_isub(AdditiveExpression, Term);
}
}
ret = icmp;//返回运算结果
}
}
15.ASTTerm
函数
对应语法规则: term → term mulop factor ∣ factor \text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor} term→term mulop factor ∣ factor
说明:
浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型
如果没有term,通过accept调用下一层;如果有term,先获取左值,再获取右值。判断是否为浮点数,如果是,将flag置1,根据flag和op选择计算,并返回结果
void CminusfBuilder::visit(ASTTerm &node)
{
if(return_flag)
return;
Type *Int32Type = Type::get_int32_type(module.get());//获取int32类型
Type *Int1Type = Type::get_int1_type(module.get());//获取int1类型
Type *FloatType = Type::get_float_type(module.get());//获取float类型
Value *Term;
Value *Factor;
Value *icmp;
//term -> factor
if (!node.term)//若无乘法运算
{
node.factor->accept(*this);//则直接去处理元素
}
//term -> term mulop factor
else
{
node.term->accept(*this);//处理左term
if (ret->get_type()->is_pointer_type())//是指针
Term = builder->create_load(ret);
else
Term = ret;
node.factor->accept(*this);//处理右factor
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)//根据对应乘法或是除法,调用fmul或是fdiv进行处理
{
icmp = builder->create_fmul(Term, Factor);
}
else
{
icmp = builder->create_fdiv(Term, Factor);
}
}
else
{
if (node.op == OP_MUL)//根据对应乘法或是除法,调用imul或是idiv进行处理
{
icmp = builder->create_imul(Term, Factor);
}
else
{
icmp = builder->create_isdiv(Term, Factor);
}
}
ret = icmp;//保存返回结果
}
}
16.ASTCall
函数
对应语法规则: call → ID ( args ) \text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)} call→ID ( args)
说明:
函数调用
由一个函数的标识符
与一组括号包围的实参
组成。实参
可以为空,也可以是由逗号分隔的的表达式
组成的列表,这些表达式代表着函数调用时,传给形参
的值。函数调用时
的实参
数量和类型必须与函数声明
中的形参
一致,必要时需要进行类型转换。
调用scope.find()找ID对应的值,如果没有找到,直接返回。
检查函数类型,如果不匹配,直接返回。
检查参数类型是否匹配。设置计数器i,统计参数的数量。每接受一个参数,判断参数类型,如果不匹配,则进行强制类型转换,将i++。最后比较i和要求参数数量,如果不相等,则退出。最后生成调用语句。
void CminusfBuilder::visit(ASTCall &node)
{
if(return_flag)
return;
Value *value;
value = scope.find(node.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);//对每一个参数进行处理,获取参数对应的值
//如果ret是布尔型,ret先转换成32位整型
if (ret->get_type() == Int1Type)
{
ret = builder->create_zext(ret, Int32Type);
}
//要求的参数为整型
if (arg_type == Int32Type)
{
if (argload && ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
if (ret->get_type()->is_pointer_type())
return;
else if (ret->get_type() == FloatType)
ret = builder->create_fptosi(ret, Int32Type);
value_args = ret;
}
//要求的参数为浮点数
else if (arg_type == FloatType)
{//若不是指针,则需要判断形参和实参的类型是否符合。若不符合则需要类型转换
if (argload && ret->get_type()->is_pointer_type())
ret = builder->create_load(ret);
if (ret->get_type()->is_pointer_type())
return;
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 || argload)
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);//创建函数调用
}
17.结果验证
进入build文件夹
输入指令:cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/
输入指令make -j
最后输入指令make install
编译成功
运行自动测试程序,测试样例
在目录tests/lab4
下输入指令./lab4_test.py
:
可以看到,通过所有样例,运行正确。
18.测试程序
一个简单的冒泡排序,由于cminus文法规则里没有for循环的用法,这里使用while循环替换了
void main(void) {
int a[20];
int num;
int n;
int t;
int i;
int j;
n = 0;
i=0;
num = input();
while(n < num)
{
a[n] = input();
n = n + 1;
}
while(i<num){
j=num-1;
while(j>i){
if(a[j]<a[j-1])
{
t=a[j-1];
a[j-1]=a[j];
a[j]=t;
}
j=j-1;
}
i=i+1;
}
n = 0;
while(n < num)
{
output(a[n]);
n = n + 1;
}
return;
}
运行结果如下:
可以看到,对于输入的六个数的数组[5,7,3,9,1,6],测试程序能够正确将其由小到大排序并正确输出结果
假如将main函数中的参数void删除,会报错,报错的位置正好是main函数参数的位置
因此程序符合cminus的语法规则
实验反馈 (可选 不会评分)
通过本次实验,首先,我们从单个结点入手,更加熟悉和理解语法树的生成方式;其次,我们熟悉了cminus语法,学会通过类来查找函数的属性;再者,我们明白了scope域和全局变量的相关使用;最后,我们也学会了如何与同组队员进行及时讨论交流,相互学习。整个过程中,我们不断考虑极端样例进行测试,力求代码更加智能,能够尽量容忍程序的出错,而不是遇到错误就退出编译。总之,通过这次实验,我们的能力均有提升,也更加明白编译原理这门课的重要性!
同学交流 (可选)
例如对于如下代码,存在源程序中有冗余代码和缺少return语句的处理 :
void main(void){
int i;
i = 0;
if(i)
{
return ;
i = 7;
}
while(i >= 7)
{
i = 0;
}
output(i);
}
生成的ll文件的主体内容如下:
define void @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1, align 4
%2 = load i32, i32* %1, align 4
%3 = icmp ne i32 %2, 0
br i1 %3, label %4, label %5
; <label>:4: ; preds = %0
br label %13
; <label>:5: ; preds = %0
br label %6
; <label>:6: ; preds = %9, %5
%7 = load i32, i32* %1, align 4
%8 = icmp sge i32 %7, 7
br i1 %8, label %9, label %10
; <label>:9: ; preds = %6
store i32 0, i32* %1, align 4
br label %6
; <label>:10: ; preds = %6
%11 = load i32, i32* %1, align 4
%12 = call i32 (i32, ...) bitcast (i32 (...)* @output to i32 (i32, ...)*)(i32 %11)
br label %13
; <label>:13: ; preds = %10, %4
ret void
}
i = 7是一条多余的代码,我们可以在翻译的时候不予翻译(有中生无),对于output(i)后面已经没有代码了,但对于整个函数要补全一条return代码(无中生有)
自然地,解决这个问题需要知道当前模块已经翻译的代码中是不是已经有完整的return,我们用一个全局变量return_flag来表示
主要逻辑如下
- 有中生无,cpp文件中几乎对于每一个visit函数(除了前几个),都要在函数开头加上 对return_flag的判断,如果是1,则抛弃子节点,不翻译
- 无中生有,在
visit(ASTFunDeclaration &node)
的最后,整个函数都翻译完毕的时候,如果return_flag为0,根据return_type补上类型正确的return语句,最后将return_flag置为0 - 维护这个return_flag,除了
visit(ASTFunDeclaration &node)
和visit(ASTReturnStmt &node)
对return_flag有操作以外,要注意if-else,while结构中的return_flag,while模块翻译完毕之后,return—flag置为0,因为return_flag是标识当前模块的,相应地,if-else模块结束后,只有if和else都有return时,才使return_flag为1