HNU编译原理实验四cminus_compiler-2022-fall

news2024/12/25 12:43:06

前言:原本想认认真真把这个实验给完成的,但是当时时间太赶了,一周要做三个实验,所以这次实验基本都是抄的了,有些地方也抄的不明不白,不过懂不懂这个对课程学习的帮助并不是很大,毕竟这个实验的难度对hun的学生来说确实挺大的。至于代码还有很大的优化空间,有时间的可以去优化一下。

Lab4 实验报告

实验要求

在理解cminus-f 语法与语义的基础上,参考cminusf_builder.hpp文件以及include中的文件,补充完成 cminusf_builder.cpp 中的16个函数,来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序。在自动产生 IR 的过程中,利用访问者模式自顶向下遍历抽象语法树的每一个结点,调用我们补充完成的函数对每一个抽象语法树的结点进行分析,如果程序是合法的则编译应该显示Success,否则编译不通过显示Fail

  1. 阅读cminus-f 的语义规则成为语言律师,我们将按照语义实现程度进行评分
  2. 阅读LightIR 核心类介绍
  3. 阅读实验框架,理解如何使用框架以及注意事项
  4. 修改 src/cminusfc/cminusf_builder.cpp 来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序
  5. report.md 中解释你们的设计,遇到的困难和解决方案

实验难点

  1. ASTFunDeclaration函数的设计

    在将函数名push到符号表的时候应该先退出当前函数的作用域,进入到全局作用域,因为函数名应该是全局都可以索引到的。push完成后再次进入函数的作用域(同时要注意此步骤应该放在push函数参数之前,否则随着退出当前作用域,push到符号表中的参数会消失

  2. 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。
  3. ASTVar函数的设计

    我们知道visit(ASTVar &node)的返回值为指向一个具体元素的指针,语义要求可以对一些数值进行强制类型转换,但是这个操作显然也不能太过非法,例如函数传参时,若形参是int,但是由传入参数为一个数组,这时要将它和一个普通变量区分开来,特设argload全局变量,1标识对于由visit(ASTVar &node)得到的指针能不能在传参中进行load操作。(因为它是从一个id为pointer或array,且无expression的var得到的

实验设计

0.全局变量的定义

由于函数都是void型,函数没有返回值,我们需要借助全局变量在上层或下层之间传递值 。这里设置了三个全局变量:

  1. ret用于存储返回值
  2. Ints用于储存参数的类型,以确定函数的类型
  3. 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} programdeclaration-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-specifierint  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-declarationtype-specifier ID ( params ) compound-stmt

params → param-list  ∣   void \text{params} \rightarrow \text{param-list}\ |\ \textbf{void} paramsparam-list  void

param-list → param-list  ,  param  ∣  param \text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param} param-listparam-list , param  param

param → type-specifier  ID   ∣  type-specifier  ID   [] \text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]} paramtype-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{[]} paramtype-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-stmtexpression ;  ;

若对应表达式存在,则处理该表达式

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-stmtwhile ( expression ) statement

说明如下:

while语句是 cminus-f 中唯一的迭代语句。它执行时,会不断对表达式进行求值,并且在对表达式的求值结果等于 0 前,循环执行执下面的语句

基本和if-else一样,但是三个模块loopJudgeloopBodyout都是必须的

首先跳转到条件判断块,处理条件判断对应的表达式,得到返回值存到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-stmtreturn ;  return expression ;

return语句可以返回值,也可以不返回值。
未声明为 void \textbf{void} void类型的函数必须返回和函数返回类型相同的值
return会将程序的控制转移给当前函数的调用者,而 main \textbf{main} main函数的return会使得程序终止

这个函数要求强制类型转换,所以要先得到return_type,对比rettype,就是一番常规的类型转化,(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{]} varID  ID [ expression]

说明:

var 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。
数组的下标值是整型,它的值是表达式计算结果或结果进行类型转换后的整型值
一个负的下标会导致程序终止,需要调用框架中的内置函数neg_idx_except (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。

  • 我们从语法中得知例a = b这样的表达式,等式左值和右值都是var,所以限制了visit(ASTVar &node)的返回值为指向一个具体元素的指针,var→ID ∣ ID [ expression],这里最重要的是对每一种情况都进行分析,不遗漏

  • 对数组下标,有强制类型转换,有小于0的异常处理(需要生成下标正常和非正常的BB)

id类型expressionargload正常出现的地方
array1基本所有地方读取数组元素
pointer1子函数使用传入的数组
其他1不应该出现
array0调用子函数时,将数组作为传入参数
pointer0将传入的数组作为参数传入子函数
int1基本所有地方
float1基本所有地方

首先从域中取出对应变量, 接着判断有无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} expressionvar = 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-expressionadditive-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-expressionadditive-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} termterm 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{)} callID ( 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来表示

主要逻辑如下

  1. 有中生无,cpp文件中几乎对于每一个visit函数(除了前几个),都要在函数开头加上 对return_flag的判断,如果是1,则抛弃子节点,不翻译
  2. 无中生有,在 visit(ASTFunDeclaration &node)的最后,整个函数都翻译完毕的时候,如果return_flag为0,根据return_type补上类型正确的return语句,最后将return_flag置为0
  3. 维护这个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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue 弹窗 惯性滚动 加速滚动

惯性滚动组件 新建文件 components/scroll-viwe <template><div v-if"visiable"><div class"mapbox-result-scroll-hidden"><div class"mapbox-result-wrap" ref"resultWrap"><div class"mapbox-resu…

服务了可口可乐、海底捞、某头部商业银行,我有这些体会

我非常喜欢巴西队的内马尔&#xff0c;他曾说&#xff1a;“你可能会看到我一秒钟、一分钟、一天不开心&#xff0c;但第二天你会看到我的笑脸。” 在 Authing 工作两年多了&#xff0c;在这期间&#xff0c;我为可口可乐、海底捞、某头部商业银行等客户做了交付&#xff0c;在…

jq实现倒计时功能

效果如下&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>jq实现…

10 IO实例

IO 1 流 流可以认为是一条通道&#xff0c;它可以将数据从源端传送到目的地。 例如将程序中的某些数据写入文件&#xff0c;或将文件中的某些数据读入程序。 Java中数据的操作是以“流”的方式进行。 Java中的“流”是一个具体的Java对象&#xff0c;该对象提供一些方法进行…

组件的概念

文章目录组件&#xff1f;从UI层面看组件化组件&#xff1f; 等下&#xff0c;你有没有留意到我说了一个很关键的词&#xff0c;叫组件。组件&#xff1f;直观的理解组件是一个什么东西&#xff1f;可拼接&#xff0c;可组合&#xff0c;搭积木&#xff0c;乐高积木? 对&…

Springboot定时任务调度的实现原理

前言 源码的世界是一片汪洋大海&#xff0c;springboot的源码更是如此&#xff0c;虽然用的时候似乎很简单&#xff0c;然而正是因为其内部的设计巧妙、复杂&#xff0c;才造就了其使用上的简单易上手。罗马不是一天建起来的&#xff0c;要完全理解它也并非一时的事&#xff0c…

webdriver的尝试:一 【webdriver自动打开浏览器与页面】

文章目录Webdriver尝试使用步骤1&#xff1a;安装类库2&#xff1a;安装驱动3&#xff1a;配置环境3&#xff1a;编写脚本4&#xff1a;执行脚本Webdriver 网站地址 Selenium webdriver 简单介绍&#xff1a;webdriver是一个api和协议。支持多种语言。主要功能&#xff0c;通…

大米新闻微信小程序和Springboot新闻管理系统项目源码

介绍 本项目分为大米news小程序端和springboot新闻管理系统后台项目。小程序主要用来新闻展示&#xff0c;后台管理系统用于提供相关新闻API。 项目源码 参考&#xff1a;https://www.bilibili.com/video/BV1TD4y1j7g3/?spm_id_from333.337.search-card.all.click&vd_s…

day08 常用API

1.API 1.1 API概述-帮助文档的使用 什么是API ​ API (Application Programming Interface) &#xff1a;应用程序编程接口 java中的API ​ 指的就是 JDK 中提供的各种功能的 Java类&#xff0c;这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&a…

两个链表的第一个公共结点

今天为大家带来一道题目&#xff1a; 这个题目先来看看我自己写的错误版本 public class Solution {public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {ListNode tmp1pHead1;ListNode tmp2pHead2;while(pHead1!null&&pHead2!null){ListNode cur…

Axure8.0动态面板使用

Axure动态面板是最常使用的&#xff0c;今天我们就来详细介绍一下。 动态面板是Axure中一个非常强大的高级元件&#xff0c;用于实现多个状态的切换展示&#xff0c;可以将其看成一个容器&#xff0c;可以容纳多种不同状态&#xff0c;通过各种交互触发其状态发生变化。 通过以…

年终盘点丨2022边缘计算大事记

2022年进入尾声了&#xff0c;每年到了年底&#xff0c;边缘计算社区都会盘点过去一年边缘计算领域发生的值得您关注的事情。今年的边缘计算领域发生很多不一样的精彩&#xff1a;加强面向特定场景的边缘计算能力刷屏一整年&#xff0c;安波福43亿美元收购风河&#xff0c;全球…

C++图论 最短路问题总结

目录 最短路问题 图的存储 一、单源最短路 ① 朴素Dijkstra O(n^2) 练习题 代码 ② 堆优化Dijkstra O(mlogn) 练习题 代码 ③ Bellman_ford O(nm) 练习题 代码 ④ Spfa O(n) - O(nm) 练习题 ​代码 二、多源最短路 Floyd O(n^3) 练习题 代码 最短路问题 图…

C# 数据库访问方法

一 访问数据的两种基本方式 1 方式1&#xff1a;DataAdapter及DataSet ① 适合于“离线”处理&#xff1b; ② 自动建立Command对象&#xff1b; 方式2&#xff1a;DataReader ① 适合于只读数据&#xff0c;效率较高 它们都要使用Connection及Command 二 Connection对象…

Android解析服务器响应数据

文章目录Android解析服务器响应数据解析XML格式数据Pull解析方式SAX解析方式解析JSON数据使用JSONObject使用GSON的方式来解析JSON数据Android解析服务器响应数据 解析XML格式数据 通常情况下,每一个需要访问网络的应用程序都会有一个自己的服务器,我们可以向服务器提交自己的…

多线程——概念及线程安全

文章目录多线程概念进程vs线程多线程的优势/好处/使用场景线程的状态创建线程的方式线程的启动Thread中,start()和run()有什么区别?Thread类中的常用方法join()获取当前线程引用线程休眠线程中断线程的属性多线程效率局部变量在多线程中的使用线程安全问题1.什么情况会产生线程…

replit搭建

本文章用于快速搭建“出去”的节点&#xff0c;很简单 每个月只有100G流量中间可能会停止运行&#xff0c;需要手动进入项目开启 1、需要注册一个Replit账号 点击注册 支持Github登录&#xff0c;其他登录也行 2、使用这个模板项目 随便起个名字 3、运行 进行完第二步&am…

【开源项目】第三方登录框架JustAuth入门使用和源码分析

第三方登录框架JustAuth入门使用和源码分析 项目介绍 JustAuth&#xff0c;如你所见&#xff0c;它仅仅是一个第三方授权登录的工具类库&#xff0c;它可以让我们脱离繁琐的第三方登录 SDK&#xff0c;让登录变得So easy! JustAuth 集成了诸如&#xff1a;Github、Gitee、支付…

九、kubernetes中Namespace详解、实例

1、概述 Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下&#xff0c;kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中&#xff0c;可能不想让两个Pod之间进行互相的访…

花费数小时,带你学透Java数组,这些常用方法你还记得吗?

推荐学习专栏&#xff1a;Java 编程进阶之路【从入门到精通】 文章目录1. 数组2. 一维数组2.1 声明2.2 初始化2.3 使用3. 二维数组3.1 声明3.2 初始化3.3 使用4. 数组在内存中的分布5. 数组常用的方法5.1 Arrays.toString方法5.2 Arrays.copyOf方法5.3 Arrays.copyOfRange方法5…