编译原理Lab4-使用LightIR框架自动产生cminus-f语言的LLVM IR

本文主要是介绍编译原理Lab4-使用LightIR框架自动产生cminus-f语言的LLVM IR,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • [[#实验框架|实验框架]]
  • [[#实验过程|实验过程]]
    • [[#实验过程#全局变量的设计|全局变量的设计]]
    • [[#实验过程#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;}// 函数参数的vectorstd::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);elsebuilder->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-stmtif(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));elsebuilder->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));//currentFunctionauto 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);//falseBBbuilder->set_insert_point(falseBB);node.else_statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr){ // no return inside the blockinsertedflag = 1;nextBB = BasicBlock::create(module.get(), "", currentFunc);builder->create_br(nextBB);}return_flag = 0;//tureBBbuilder->set_insert_point(trueBB);node.if_statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr){ // no return inside the blockif (insertedflag == 0){insertedflag = 1;nextBB = BasicBlock::create(module.get(), "", currentFunc);}builder->create_br(nextBB);}return_flag = !insertedflag;//nextBBif (insertedflag == 1){builder->set_insert_point(nextBB);}}else //无else{//tureBBnextBB = 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;//nextBBbuilder->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());//currentFunctionauto 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 BBbuilder->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 BBbuilder->set_insert_point(loopBody);node.statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr)builder->create_br(loopJudge);return_flag = 0;//outloop BBbuilder->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());//currentFunctionauto 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;//转化为intif (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 exphandBBauto 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);//normalBBbuilder->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 funcauto 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);//exphandBBbuilder->set_insert_point(exphandBB);Value * call_error = scope.find("neg_idx_except");builder->create_call(call_error, {});builder->create_br(outBB);//outBBbuilder->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-expressionif (!node.additive_expression_r) //简单加法表达式,通过accept调用下一层级{node.additive_expression_l->accept(*this);}//simple-expression -> additive-expression relop additive- expressionelse //关系表达式,运算结果为整型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);elseAdditiveLoad_l = ret;node.additive_expression_r->accept(*this);if (ret->get_type()->is_pointer_type())AdditiveLoad_r = builder->create_load(ret);elseAdditiveLoad_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 -> termif (node.additive_expression == nullptr) //如果只是简单的项,转到下一层{node.term->accept(*this);}//additive-expression -> additive-expression addop termelse{node.additive_expression->accept(*this);if (ret->get_type()->is_pointer_type())AdditiveExpression = builder->create_load(ret);elseAdditiveExpression = ret;node.term->accept(*this);if (ret->get_type()->is_pointer_type())Term = builder->create_load(ret);elseTerm = 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 -> factorif (!node.term){node.factor->accept(*this);}//term -> term mulop factorelse{node.term->accept(*this);if (ret->get_type()->is_pointer_type())Term = builder->create_load(ret);elseTerm = ret;node.factor->accept(*this);if (ret->get_type()->is_pointer_type())Factor = builder->create_load(ret);elseFactor = 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 funcret = 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

上面
这个很管用

这篇关于编译原理Lab4-使用LightIR框架自动产生cminus-f语言的LLVM IR的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/581877

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl