HNU-编译原理实验-cminus_compiler-2021-fall-master【4】-Lab4

Lab4 实验报告

一、实验要求

  • 阅读cminus-f 的语义规则成为语言律师,我们将按照语义实现程度进行评分

  • 阅读LightIR 核心类介绍

  • 阅读实验框架,理解如何使用框架以及注意事项

  • 修改 src/cminusfc/cminusf_builder.cpp 来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序

  • report.md 中解释你们的设计,遇到的困难和解决方案

二、实验难点

  • 在执行make install指令的时候,权限不够:

    在这里插入图片描述

    解决方法:进入 root 页面,即可获得权限

    输入指令su root,随后输入密码,即可提高权限。

    如果是 WSL,则需要创建一个密码:

    输入指令sudo passwd,随后输入 Windows 密码,正确之后,即可为 Ubuntu 创建一个超级用户。

    随后,再输入指令su root,输入之前设置的密码,即可提高权限。

    在这里插入图片描述

  • 熟悉的段错误:

在这里插入图片描述

解决方法:

实际上问题特别多,经过找 bug,调用 LOG 反复测试,找出了大量的 bug。

有涉及到函数引用、有针对表达式语义翻译的,修修改改,解决了大量 bug。

这里贴出其中针对这个问题的 bug:

在这里插入图片描述

这种写法下,在 if 语句内的仅有node.term->accept(*this);,而没有return;。因此,导致了没法正确地退出,而导致在无后续的情况下,仍旧对后续进行操作(即对空指针进行访问处理等操作),从而导致了违规访问(即段错误)。

需要将语句用大括号括起来,即:

在这里插入图片描述

这样才能正确return

  • 类型转换

    在生成代码时,许多语义制导都需要考虑数值的类型,需要对不符合要求的数值进行类型转换(例如,赋值语句中,浮点数赋值给整数)。

    下图是简单的样例(具体看我编写的代码):

    在这里插入图片描述

  • var,即变量结点

    这个结点的处理,不仅要考虑a[]的情况(即expression为空),还要考虑指针指向的是数组的情况(即int* a[10])。代码非常复杂,还很容易出错。

    下图为针对数组指针的问题,其他详见我编写的代码:

    在这里插入图片描述

    附上数组指针的问题(详见上一个实验):

    请给出IR.md中提到的两种getelementptr用法的区别,并稍加解释:

  • %2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0
  • %2 = getelementptr i32, i32* %1 i32 %0

对于前一种,指针的类型为[10 x i32]*,代码中的第一个i32 0是指,对于指针所指向的[10 x i32]*这一数组([10 x i32][]),偏移量为0,即就是当前指向的地址(若不为0,例如为5,则其指向的地址是[10 x i32][5],也就是往后偏移5个单位的[10 x i32]);而第二个i32 %0,则是表示在[10 x i32]这一数组中,偏移量为%0,即指向的地址是[%0]

因此,若存在某二维数组,形如i32 a[][10],则getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0取出的是a[0][%0];若存在某三维数组,形如i32 a[][20][10],则getelementptr [20 x [10 x i32]], [20 x [10 x i32]]* %1, i32 0, i32 0, i32 %0取出的是a[0][0][%0];同理,多维数组以此类推。

对于一维数组i32 a[10],则可通过置第一个偏移参数为0,置第二个偏移参数为所需要的%0,利用[10 x i32]*型的指针,取出a[%0](例如第一行代码所示)。

而对于后一种,指针类型为i32*,即可直接对应i32 a[]的数组,通过设置偏移量参数为%0,即可直接取出a[%0]中的值。

因此,这两种getelementptr实际上是针对不同的指针类型[10 x i32]*i32*,同样取出a[%0]的方法。

此外,这两种的用法实际上能算作是同一种用法,区别是在于前者是[10 x i32]*型,指针所指向的地址仍旧可以视为指针,从而除指针自身以[10 x i32]为单位的偏移外,还有偏移单位为i32的偏移;而后者是i32*,仅存在i32这一单位的偏移。

而且,这种不仅适用于数组的寻址,也适用于结构体、向量等数据组织方式的寻址,方法和数组的类似。

  • 各结点中处理需要相互传递的信息

    这些信息我用全局变量进行存储,以便于各函数处理时能相互传递信息:

    在这里插入图片描述

三、实验设计

从实验说明中知道,要编写的代码是cminusf_builder.cpp,其内含各个 visit 函数,我们需要补全这些 visit 的函数。

具体思考过程我就不在这里写了,属实一把辛酸泪。这次几乎没有什么可以参考的样例代码(打印抽象语法树的算法的代码,真心参考价值不多,虽然的确大致提供了一些思路,但是真的和我们需要写的代码有“亿点点”差距,带点“女娲补天”的感觉了)。

在写代码之前,要好好看看打印抽象语法树的算法的代码(虽然参考价值不多,但其中的 visit 的代码还是很有启发性的,需要仔细阅读),掌握 logging 工具(虽然掌握了之后还是特别难 debug),仔细看 Light IR 文档(主要看 C++ APIs 部分),仔细看cminus-f 的语法与语义(这个特别重要)

  • 宏定义和一些全局变量

    // use these macros to get constant value
    #define CONST_INT(num) \
     ConstantInt::get(num, module.get())     /* 增加一个有关整型的宏 */
    #define CONST_FP(num) \
     ConstantFP::get((float)num, module.get())
    #define CONST_ZERO(type) \
     ConstantZero::get(type, module.get())   /* 此处要修改为type */
    
    #define Int32Type \
     Type::get_int32_type(module.get())      /* 获取int32类型 */
    #define FloatType \
     Type::get_float_type(module.get())      /* 获取float类型 */
    
    #define checkInt(num) \
     num->get_type()->is_integer_type()      /* 整型判断 */
    #define checkFloat(num) \
     num->get_type()->is_float_type()        /* 浮点型判断 */
    #define checkPointer(num) \
     num->get_type()->is_pointer_type()      /* 指针类型判断 */
    
    // You can define global variables here
    // to store state
    Value* Res;                     /* 存储返回的结果 */
    Value* arg;                     /* 存储参数指针,用于Param的处理 */
    bool need_as_address = false;   /* 标志是返回值还是返回地址 */
    
  • Program, 程序

    program → declaration-list \text{program} \rightarrow \text{declaration-list} programdeclaration-list

    /* Program, 程序, program->declaration-list */
    void CminusfBuilder::visit(ASTProgram &node) {  
     for (auto decl : node.declarations)         /* 遍历declaration-list */
         decl->accept(*this);                    /* 处理每一个declaration */
    }
    
  • Num,数值

    /* Num,数值 */
    void CminusfBuilder::visit(ASTNum &node) {      
     if (node.type == TYPE_INT)                  /* 若为整型 */
         Res = ConstantInt::get(node.i_val, module.get());   /* 获取结点中存储的整型数值 */
     else if (node.type == TYPE_FLOAT)           /* 若为浮点型 */
         Res = ConstantFP::get(node.f_val, module.get());    /* 获取结点中存储的浮点型数值 */
    }
    
  • Var-Declaration, 变量声明

    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 ] ;

    要注意:

    全局变量需要初始化为全 0 。

    cminus-f 的基础类型只有整型(int)、浮点型(float)和 void。而在变量声明中,只有整型和浮点型可以使用,void 仅用于函数声明。

    一个变量声明定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。

    数组变量在声明时, INTEGER \textbf{INTEGER} INTEGER 应当大于0。

    一次只能声明一个变量。

    /* Var-Declaration, 变量声明, var-declaration -> type-specifier ID
     *                                             | type-specifier ID [INTEGER] */
    void CminusfBuilder::visit(ASTVarDeclaration &node) {              
        Type* tmpType;                  /* 类型暂存变量,用于存储变量的类型,用于后续申请空间 */
        if (node.type == TYPE_INT)      /* 若为整型 */
            tmpType = Int32Type;        /* 则type为整数类型 */
        else if (node.type == TYPE_FLOAT)   /* 则为浮点型 */
            tmpType = FloatType;            /* 则type为浮点类型 */
        if (node.num != nullptr) {          /* 若为数组类型 */
            /* 获取需开辟的对应大小的空间的类型指针 */
            auto* arrayType = ArrayType::get(tmpType, node.num->i_val); /* 获取对应的数组Type */
            auto initializer = CONST_ZERO(tmpType);                     /* 全局变量初始化为0 */
            Value* arrayAlloca;             /* 存储申请到的数组空间的地址 */
            if (scope.in_global())          /* 若为全局数组,则开辟全局数组 */
                arrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer);
            else                            /* 若不是全局数组,则开辟局部数组 */
                arrayAlloca = builder->create_alloca(arrayType);
            scope.push(node.id, arrayAlloca);/* 将获得的数组变量加入域 */
        }
        else {                              /* 若不是数组类型 */
            auto initializer = CONST_ZERO(tmpType); /* 全局变量初始化为0 */
            Value* varAlloca;               /* 存储申请到的变量空间的地址 */
            if (scope.in_global())          /* 若为全局变量,则申请全局空间 */
                varAlloca = GlobalVariable::create(node.id, module.get(), tmpType, false, initializer);
            else                            /* 若不是全局变量,则申请局部空间 */
                varAlloca = builder->create_alloca(tmpType);
            scope.push(node.id, varAlloca); /* 将获得变量加入域 */
        }
    }
    
  • Fun-Declaration, 函数声明

    fun-declaration → type-specifier  ID   (  params  )  compound-stmt \text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt} fun-declarationtype-specifier ID ( params ) compound-stmt

    要注意:

    函数声明包含了返回类型,标识符,由逗号分隔的形参列表,还有一个复合语句

    当函数的返回类型是 void 时,函数不返回任何值。

    函数的参数可以是 void ,也可以是一个列表。当函数的形参void时,调用该函数时不用传入任何参数。

    形参中跟着中括号代表数组参数,它们可以有不同长度。

    整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。

    函数的形参拥有和函数声明复合语句相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致)

    函数可以递归调用。

    /* Fun-Declaration, 函数声明, fun-declaration -> type-specifier ID ( params ) compound-stmt */
    void CminusfBuilder::visit(ASTFunDeclaration &node) {   
        Type* retType;               /* 返回类型 */
        /* 根据不同的返回类型,设置retType */
        if (node.type == TYPE_INT) { retType = Int32Type; }
        if (node.type == TYPE_FLOAT) { retType = FloatType; }
        if (node.type == TYPE_VOID) { retType = Type::get_void_type(module.get()); }
        /* 根据函数声明,构造形参列表(此处的形参即参数的类型) */
        std::vector<Type*> paramsType;  /* 参数类型列表 */
        for (auto param : node.params) {
            if (param->isarray) {       /* 若参数为数组形式,则存入首地址指针 */
                if (param->type == TYPE_INT)        /* 若为整型 */
                    paramsType.push_back(Type::get_int32_ptr_type(module.get()));
                else if(param->type == TYPE_FLOAT)  /* 若为浮点型 */
                    paramsType.push_back(Type::get_float_ptr_type(module.get()));
            }
            else {                  /* 若为单个变量形式,则存入对应类型 */
                if (param->type == TYPE_INT)        /* 若为整型 */
                    paramsType.push_back(Int32Type);
                else if (param->type == TYPE_FLOAT) /* 若为浮点型 */
                    paramsType.push_back(FloatType);
            }
        }
        auto funType = FunctionType::get(retType, paramsType);              /* 函数结构 */
        auto function = Function::create(funType, node.id, module.get());   /* 创建函数 */
        scope.push(node.id, function);  /* 将函数加入到域 */
        scope.enter();                  /* 进入此函数作用域 */
        auto bb = BasicBlock::create(module.get(), node.id + "_entry", function);/* 创建基本块 */
        builder->set_insert_point(bb);  /* 将基本块加入到builder中 */
        /* 将实参和形参进行匹配 */
        std::vector<Value*> args;       /* 创建vector存储实参 */
        for (auto arg = function->arg_begin();arg != function->arg_end();arg++) {/* 遍历实参列表 */
            args.push_back(*arg);       /* 将实参加入vector */
        }
        for (int i = 0;i < node.params.size();i++) {    /* 遍历形参列表(=遍历实参列表) */
            auto param = node.params[i];        /* 取出对应形参 */
            arg = args[i];                      /* 取出对应实参 */
            param->accept(*this);           /* 调用param的accept进行处理 */
        }
        node.compound_stmt->accept(*this);      /* 处理函数体内语句compound-stmt */
        if (builder->get_insert_block()->get_terminator() == nullptr) {
            if (function->get_return_type()->is_void_type())
                builder->create_void_ret();
            else if (function->get_return_type()->is_float_type())
                builder->create_ret(CONST_FP(0.));
            else
                builder->create_ret(CONST_INT(0));
        }
        scope.exit();                       /* 退出此函数作用域 */
    }
    
  • 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 []

    /* Param, 参数 */
    void CminusfBuilder::visit(ASTParam &node) {        
     Value* paramAlloca; /* 该参数的存储空间 */
     if (node.isarray) { /* 若为数组 */
         if (node.type == TYPE_INT)        /* 若为整型数组,则开辟整型数组存储空间 */
             paramAlloca = builder->create_alloca(Type::get_int32_ptr_type(module.get()));
         else if (node.type == TYPE_FLOAT) /* 若为浮点数数组,则开辟浮点数数组存储空间 */
             paramAlloca = builder->create_alloca(Type::get_float_ptr_type(module.get()));
     }
     else {              /* 若不是数组 */
         if (node.type == TYPE_INT)        /* 若为整型,则开辟整型存储空间 */
             paramAlloca = builder->create_alloca(Int32Type);
         else if (node.type == TYPE_FLOAT) /* 若为浮点数,则开辟浮点数存储空间 */
             paramAlloca = builder->create_alloca(FloatType);
     }
     builder->create_store(arg, paramAlloca);    /* 将实参load到开辟的存储空间中 */
     scope.push(node.id, paramAlloca);           /* 将参数push到域中 */
    }
    
  • CompoundStmt, 函数体语句

    compound-stmt → {  local-declarations statement-list } \text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}} compound-stmt{ local-declarations statement-list}

    /* CompoundStmt, 函数体语句, compound-stmt -> {local-declarations statement-list} */
    void CminusfBuilder::visit(ASTCompoundStmt &node) {
     scope.enter();      /* 进入函数体内的作用域 */
     for (auto local_declaration : node.local_declarations)   /* 遍历 */
         local_declaration->accept(*this);   /* 处理每一个局部声明 */
     for (auto statement : node.statement_list)              /* 遍历 */
         statement->accept(*this);           /* 处理每一个语句 */
     scope.exit();       /* 退出作用域 */
    }
    
  • ExpressionStmt, 表达式语句

    expression-stmt → expression  ;   ∣   ; \text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;} expression-stmtexpression ;  ;

    /* ExpressionStmt, 表达式语句, expression-stmt -> expression;
     *                                              | ; */
    void CminusfBuilder::visit(ASTExpressionStmt &node) {       /*  */
        if (node.expression != nullptr)     /* 若对应表达式存在 */
            node.expression->accept(*this); /* 则处理该表达式 */
    }
    
  • SelectionStmt, if 语句

    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 \textbf{else} else将会匹配最近的 if \textbf{if} if

    /* SelectionStmt, if语句, selection-stmt -> if (expression) statement
     *                                        | if (expression) statement else statement */
    void CminusfBuilder::visit(ASTSelectionStmt &node) {        
        auto function = builder->get_insert_block()->get_parent();  /* 获得当前所对应的函数 */
        node.expression->accept(*this);         /* 处理条件判断对应的表达式,得到返回值存到expression中 */
        auto resType = Res->get_type();         /* 获取表达式得到的结果类型 */
        Value* TrueFalse;                       /* 存储if判断的结果 */
        if (resType->is_integer_type()) {       /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
            auto intType = Int32Type;  
            TrueFalse = builder->create_icmp_gt(Res, CONST_ZERO(intType));  /* 大于0视为true */
        }
        else if (resType->is_float_type()) {    /* 若结果为浮点型,则针对浮点数进行处理 */
            auto floatType = FloatType;
            TrueFalse = builder->create_fcmp_gt(Res, CONST_ZERO(floatType));/* 大于0视为true */
        }
        if (node.else_statement != nullptr) {   /* 若存在else语句 */
            auto trueBB = BasicBlock::create(module.get(), "true", function);   /* 创建符合条件块 */
            auto falseBB = BasicBlock::create(module.get(), "false", function); /* 创建else块 */
            builder->create_cond_br(TrueFalse, trueBB, falseBB);    /* 设置跳转语句 */
    
            builder->set_insert_point(trueBB);  /* 符合if条件的块 */
            node.if_statement->accept(*this);   /* 处理符合条件后要执行的语句 */
            auto curTrueBB = builder->get_insert_block();   /* 将块加入 */
    
            builder->set_insert_point(falseBB); /* else的块 */
            node.else_statement->accept(*this); /* 处理else语句 */
            auto curFalseBB = builder->get_insert_block();  /* 将块加入 */
    
            /* 处理返回,避免跳转到对应块后无return */
            auto trueTerm = builder->get_insert_block()->get_terminator();  /* 判断true语句中是否存在ret语句 */
            auto falseTerm = builder->get_insert_block()->get_terminator(); /* else语句中是否存在ret语句 */
            BasicBlock* retBB;
            if (trueTerm == nullptr || falseTerm == nullptr) {  /* 若有一方不存在return语句,则需要创建返回块 */
                retBB = BasicBlock::create(module.get(), "ret", function);  /* 创建return块 */
                builder->set_insert_point(retBB);               /* return块(即后续语句) */
            }
            if (trueTerm == nullptr) {          /* 若符号条件后要执行的语句中不存在return */
                builder->set_insert_point(curTrueBB);   /* 则设置跳转 */
                builder->create_br(retBB);              /* 跳转到刚刚设置的return块 */
            }
            if (falseTerm == nullptr) {         /* 若else语句中不存在return */
                builder->set_insert_point(curFalseBB);  /* 则设置跳转 */
                builder->create_br(retBB);              /* 跳转到刚刚设置的return块 */
            }
        }
        else {  /* 若不存在else语句,则只需要设置true语句块和后续语句块即可 */
            auto trueBB = BasicBlock::create(module.get(), "true", function);   /* true语句块 */
            auto retBB = BasicBlock::create(module.get(), "ret", function);     /* 后续语句块 */
            builder->create_cond_br(TrueFalse, trueBB, retBB);  /* 根据条件设置跳转指令 */
    
            builder->set_insert_point(trueBB);  /* true语句块 */
            node.if_statement->accept(*this);   /* 执行条件符合后要执行的语句 */
            if (builder->get_insert_block()->get_terminator() == nullptr)   /* 补充return(同上) */
                builder->create_br(retBB);      /* 跳转到刚刚设置的return块 */
            builder->set_insert_point(retBB);   /* return块(即后续语句) */
        }
    }
    
  • IterationStmt, while 语句

    iteration-stmt → while   (  expression  )  statement \text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement} iteration-stmtwhile ( expression ) statement

    注意:

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

    /* IterationStmt, while语句, iteration-stmt -> while (expression) statement */
    void CminusfBuilder::visit(ASTIterationStmt &node) {    
        auto function = builder->get_insert_block()->get_parent();  /* 获得当前所对应的函数 */
        auto conditionBB = BasicBlock::create(module.get(), "condition", function); /* 创建条件判断块 */
        auto loopBB = BasicBlock::create(module.get(), "loop", function);           /* 创建循环语句块 */
        auto retBB = BasicBlock::create(module.get(), "ret", function);             /* 创建后续语句块 */
        builder->create_br(conditionBB);        /* 跳转到条件判断块 */
    
        builder->set_insert_point(conditionBB); /* 条件判断块 */
        node.expression->accept(*this);         /* 处理条件判断对应的表达式,得到返回值存到expression中 */
        auto resType = Res->get_type();         /* 获取表达式得到的结果类型 */
        Value* TrueFalse;                       /* 存储if判断的结果 */
        if (resType->is_integer_type()) {       /* 若结果为整型,则针对整型进行处理(bool类型视为整型) */
            auto intType = Int32Type;
            TrueFalse = builder->create_icmp_gt(Res, CONST_ZERO(intType));  /* 大于0视为true */
        }
        else if (resType->is_float_type()) {    /* 若结果为浮点型,则针对浮点数进行处理 */
            auto floatType = FloatType;
            TrueFalse = builder->create_fcmp_gt(Res, CONST_ZERO(floatType));/* 大于0视为true */
        }
        builder->create_cond_br(TrueFalse, loopBB, retBB);  /* 设置条件跳转语句 */
    
        builder->set_insert_point(loopBB);      /* 循环语句执行块 */
        node.statement->accept(*this);          /* 执行对应的语句 */
        if (builder->get_insert_block()->get_terminator() == nullptr)   /* 若无返回,则补充跳转 */
            builder->create_br(conditionBB);    /* 跳转到条件判断语句 */
    
        builder->set_insert_point(retBB);       /* return块(即后续语句) */
    }
    
  • ReturnStmt, 返回语句

    return-stmt → return   ;   ∣   return  expression  ; \text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;} return-stmtreturn ;  return expression ;

    注意:

    return语句可以返回值,也可以不返回值。

    未声明为 void \textbf{void} void类型的函数必须返回和函数返回类型相同的值

    /* ReturnStmt, 返回语句, return-stmt -> return;
     *                                    | return expression; */
    void CminusfBuilder::visit(ASTReturnStmt &node) {   
        auto function = builder->get_insert_block()->get_parent();  /* 获得当前所对应的函数 */
        auto retType = function->get_return_type();     /* 获取返回类型 */
        if (retType->is_void_type()) {      /* 如果是void */
            builder->create_void_ret();     /* 则创建void返回,随后return,无需后续操作 */
            return;
        }
        /* 处理非void的情况 */
        node.expression->accept(*this);     /* 处理条件判断对应的表达式,得到返回值存到expression中 */
        auto resType = Res->get_type();     /* 获取表达式得到的结果类型 */
        /* 处理expression返回的结果和需要return的结果类型不匹配的问题 */
        if (retType->is_integer_type() && resType->is_float_type()) 
            Res = builder->create_fptosi(Res, Int32Type);
        if (retType->is_float_type() && resType->is_integer_type())
            Res = builder->create_sitofp(Res, Int32Type);
        builder->create_ret(Res);           /* 创建return,将expression的结果进行返回 */
    }
    
  • Var, 变量引用

    var → ID   ∣   ID   [  expression ] \text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]} varID  ID [ expression]

    注意:

    var 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。

    数组的下标值是整型,它的值是表达式计算结果或结果进行类型转换后的整型值

    一个负的下标会导致程序终止,需要调用框架中的内置函数neg_idx_except (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。

    /* Var, 变量引用, var -> ID
     *                     | ID [expression] */
    void CminusfBuilder::visit(ASTVar &node) {     
        auto var = scope.find(node.id);             /* 从域中取出对应变量 */
        bool should_return_lvalue = need_as_address;    /* 判断是否需要返回地址(即是否由赋值语句调用) */
        need_as_address = false;            /* 重置全局变量need_as_address */
        Value* index = CONST_INT(0);        /* 初始化index */
        if (node.expression != nullptr) {   /* 若有expression */
            node.expression->accept(*this); /* 处理expression,得到结果Res */
            auto res = Res;                 /* 存储结果 */
            if (checkFloat(res))            /* 判断结果是否为浮点数 */
                res = builder->create_fptosi(res, Int32Type);   /* 若是,则矫正为整数 */
            index = res;                    /* 赋值给index,表示数组下标 */
            /* 判断下标是否为负数。若是,则调用neg_idx_except函数进行处理 */
            auto function = builder->get_insert_block()->get_parent();  /* 获取当前函数 */
            auto indexTest = builder->create_icmp_lt(index, CONST_ZERO(Int32Type)); /* test是否为负数 */
            auto failBB = BasicBlock::create(module.get(), node.id + "_failTest", function);/* fail块 */
            auto passBB = BasicBlock::create(module.get(), node.id + "_passTest", function);/* pass块 */
            builder->create_cond_br(indexTest, failBB, passBB); /* 设置跳转语句 */
    
            builder->set_insert_point(failBB);  /* fail块,即下标为负数 */
            auto fail = scope.find("neg_idx_except");               /* 取出neg_idx_except函数 */
            builder->create_call(static_cast<Function*>(fail), {}); /* 调用neg_idx_except函数进行处理 */
            builder->create_br(passBB);         /* 跳转到pass块 */
    
            builder->set_insert_point(passBB);  /* pass块 */
            if (var->get_type()->get_pointer_element_type()->is_array_type())   /* 若为指向数组的指针 */
                var = builder->create_gep(var, { CONST_INT(0), index });    /* 则进行两层寻址(原因在上一实验中已说明) */
            else {
                if (var->get_type()->get_pointer_element_type()->is_pointer_type()) /* 若为指针 */
                    var = builder->create_load(var);        /* 则取出指针指向的元素 */
                var = builder->create_gep(var, { index });  /* 进行一层寻址(因为此时并非指向数组) */
            }
            if (should_return_lvalue) {         /* 若要返回值 */
                Res = var;                      /* 则返回var对应的地址 */
                need_as_address = false;        /* 并重置全局变量need_as_address */
            }
            else 
                Res = builder->create_load(var);/* 否则则进行load */
            return;
        }
        /* 处理无expression的情况 */
        if (should_return_lvalue) { /* 若要返回值 */
            Res = var;              /* 则返回var对应的地址 */
            need_as_address = false;/* 并重置全局变量need_as_address */
        }
        else {                      /* 否则 */
            if (var->get_type()->get_pointer_element_type()->is_array_type())   /* 若指向数组 */
                Res = builder->create_gep(var, { CONST_INT(0), CONST_INT(0) }); /* 则寻址 */
            else
                Res = builder->create_load(var);/* 否则则进行load */
        }
    }
    
  • AssignExpression, 赋值语句

    注意:

    赋值语义为:先找到 var 代表的变量地址(如果是数组,需要先对下标表达式求值),然后对右侧的表达式进行求值,求值结果将在转换成变量类型后存储在先前找到的地址中。同时,存储在 var 中的值将作为赋值表达式的求值结果。

    C 中,赋值对象(即 var )必须是左值,而左值可以通过多种方式获得。cminus-f中,唯一的左值就是通过 var 的语法得到的,因此 cminus-f 通过语法限制了 var 为左值,而不是像 C 中一样通过类型检查,这也是为什么 cminus-f 中不允许进行指针算数。

    /* AssignExpression, 赋值语句, var = expression */
    void CminusfBuilder::visit(ASTAssignExpression &node) {     
        need_as_address = true;         /* 设置need_as_address,表示需要返回值 */
        node.var->accept(*this);        /* 处理左var */
        auto var = Res;                 /* 获取地址 */
        node.expression->accept(*this); /* 处理右expression */
        auto res = Res;                 /* 获得结果 */
        auto varType = var->get_type()->get_pointer_element_type(); /* 获取var的类型 */
        /* 若赋值语句左右类型不匹配,则进行匹配 */
        if (varType == FloatType && checkInt(res))
            res = builder->create_sitofp(res, FloatType);
        if (varType == Int32Type && checkFloat(res))
            res = builder->create_fptosi(res, Int32Type);
        builder->create_store(res, var);/* 进行赋值 */
    }
    
  • 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。

    /* SimpleExpression, 比较表达式, simple-expression -> additive-expression relop additive-expression
     *                                                  | additive-expression */
    void CminusfBuilder::visit(ASTSimpleExpression &node) {    
        node.additive_expression_l->accept(*this);  /* 处理左边的expression */
        auto lres = Res;                            /* 获取结果存到lres中 */
        if (node.additive_expression_r == nullptr) { return; }  //* 若不存在右expression,则直接返回 */ 
        node.additive_expression_r->accept(*this);  /* 处理右边的expression */
        auto rres = Res;                            /* 结果存到rres中 */
        if (checkInt(lres) && checkInt(rres)) {     /* 确保两边都是整数 */
            switch (node.op) {  /* 根据不同的比较操作,调用icmp进行处理 */
            case OP_LE:
                Res = builder->create_icmp_le(lres, rres);break;
            case OP_LT:
                Res = builder->create_icmp_lt(lres, rres);break;
            case OP_GT:
                Res = builder->create_icmp_gt(lres, rres);break;
            case OP_GE:
                Res = builder->create_icmp_ge(lres, rres);break;
            case OP_EQ:
                Res = builder->create_icmp_eq(lres, rres);break;
            case OP_NEQ:
                Res = builder->create_icmp_ne(lres, rres);break;
            }
        }
        else {  /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行比较 */
            if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
                lres = builder->create_sitofp(lres, FloatType);
            if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
                rres = builder->create_sitofp(rres, FloatType);
            switch (node.op) {  /* 根据不同的比较操作,调用fcmp进行处理 */
            case OP_LE:
                Res = builder->create_fcmp_le(lres, rres);break;
            case OP_LT:
                Res = builder->create_fcmp_lt(lres, rres);break;
            case OP_GT:
                Res = builder->create_fcmp_gt(lres, rres);break;
            case OP_GE:
                Res = builder->create_fcmp_ge(lres, rres);break;
            case OP_EQ:
                Res = builder->create_fcmp_eq(lres, rres);break;
            case OP_NEQ:
                Res = builder->create_fcmp_ne(lres, rres);break;
            }
        }
        Res = builder->create_zext(Res, Int32Type); /* 将结果作为整数保存(可作为判断语句中的判断条件) */
    }
    
  • AdditiveExpression, 加法表达式

    additive-expression → additive-expression addop term  ∣  term \text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term} additive-expressionadditive-expression addop term  term

    注意:

    浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型

    /* AdditiveExpression, 加法表达式, additive-expression -> additive-expression addop term
     *                                                      | term */
    void CminusfBuilder::visit(ASTAdditiveExpression &node) {   
        if (node.additive_expression == nullptr) {  /* 若无加减法运算 */
            node.term->accept(*this);return;        /* 则直接去做乘除法 */
        }
        node.additive_expression->accept(*this);    /* 处理左expression */
        auto lres = Res;                            /* 结果保存在lres中 */
        node.term->accept(*this);                   /* 处理右term */
        auto rres = Res;                            /* 结果保存在rres中 */
        if (checkInt(lres) && checkInt(rres)) {     /* 确保两边都是整数 */
            switch (node.op) {  /* 根据对应加法或是减法,调用iadd或是isub进行处理 */
            case OP_PLUS:
                Res = builder->create_iadd(lres, rres);break;
            case OP_MINUS:
                Res = builder->create_isub(lres, rres);break;
            }
        }
        else {  /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
            if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
                lres = builder->create_sitofp(lres, FloatType);
            if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
                rres = builder->create_sitofp(rres, FloatType);
            switch (node.op) {  /* 根据对应加法或是减法,调用fadd或是fsub进行处理 */
            case OP_PLUS:
                Res = builder->create_fadd(lres, rres);break;
            case OP_MINUS:
                Res = builder->create_fsub(lres, rres);break;
            }
        }
    }
    
  • Term, 乘除法语句

    term → term mulop factor  ∣  factor \text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor} termterm mulop factor  factor

    注意:

    浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型

    /* Term, 乘除法语句, Term -> term mulop factor
     *                         | factor */
    void CminusfBuilder::visit(ASTTerm &node) {     
        if (node.term == nullptr) {             /* 若无乘法运算 */
            node.factor->accept(*this);return;  /* 则直接去处理元素 */
        }
        node.term->accept(*this);   /* 处理左term */
        auto lres = Res;            /* 结果保存在lres中 */
        node.factor->accept(*this); /* 处理右factor */
        auto rres = Res;            /* 结果保存在rres中 */
        if (checkInt(lres) && checkInt(rres)) { /* 确保两边都是整数 */
            switch (node.op) {  /* 根据对应乘法或是除法,调用imul或是idiv进行处理 */
            case OP_MUL:
                Res = builder->create_imul(lres, rres);break;
            case OP_DIV:
                Res = builder->create_isdiv(lres, rres);break;
            }
        }
        else {  /* 若有一边是浮点类型,则需要先将另一边也转为浮点数,再进行处理 */
            if (checkInt(lres)) /* 若左边是整数,则将其转为浮点数 */
                lres = builder->create_sitofp(lres, FloatType);
            if (checkInt(rres)) /* 若右边是整数,则将其转为浮点数 */
                rres = builder->create_sitofp(rres, FloatType);
            switch (node.op) {  /* 根据对应乘法或是除法,调用fmul或是fdiv进行处理 */
            case OP_MUL:
                Res = builder->create_fmul(lres, rres);break;
            case OP_DIV:
                Res = builder->create_fdiv(lres, rres);break;
            }
        }
    }
    
  • Call, 函数调用

    call → ID   (  args ) \text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)} callID ( args)

    注意:

    函数调用由一个函数的标识符与一组括号包围的实参组成。实参可以为空,也可以是由逗号分隔的的表达式组成的列表,这些表达式代表着函数调用时,传给形参的值。函数调用时实参数量和类型必须与函数声明中的形参一致,必要时需要进行类型转换。

    /* Call, 函数调用, call -> ID (args) */
    void CminusfBuilder::visit(ASTCall &node) {    
        auto function = static_cast<Function*>(scope.find(node.id));    /* 获取需要调用的函数 */
        auto paramType = function->get_function_type()->param_begin();  /* 获取其参数类型 */
        std::vector<Value*> args;       /* 创建args用于存储函数参数的值,构建调用函数的参数列表 */
        for (auto arg : node.args) {    /* 遍历形参列表 */
            arg->accept(*this);         /* 对每一个参数进行处理,获取参数对应的值 */
            if (Res->get_type()->is_pointer_type()) {   /* 若参数是指针 */
                args.push_back(Res);    /* 则直接将值加入到参数列表 */
            }
            else {  /* 若不是指针,则需要判断形参和实参的类型是否符合。若不符合则需要类型转换 */
                if (*paramType==FloatType && checkInt(Res))
                    Res = builder->create_sitofp(Res, FloatType);
                if (*paramType==Int32Type && checkFloat(Res))
                    Res = builder->create_fptosi(Res, Int32Type);
                args.push_back(Res);
            }
            paramType++;                /* 查看下一个形参 */
        }
        Res = builder->create_call(static_cast<Function*>(function), args); /* 创建函数调用 */
    }
    

四、实验结果验证

  • 先要获取权限(以确保共享库能复制到 usr 下)

    输入指令su root,输入密码,提高权限:

    在这里插入图片描述

    (如果是 WSL,先看之前“实验难点”中所写的)

  • 编译

    输入指令mkdir build && cd build

    在这里插入图片描述

    再输入指令cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/

    在这里插入图片描述

    再输入指令make -j

    在这里插入图片描述

    最后输入指令make install

    在这里插入图片描述

    (若复制文件失败,提示缺少权限,则先去提高权限)

    可以看到,编译成功。

  • 运行自动测试程序,测试样例

    在目录tests/lab4下输入指令./lab4_test.py

    在这里插入图片描述

    可以看到,通过所有样例,运行正确。

五、实验反馈

属实,有“亿点点”难,主要给的启发信息有点少。(虽然有 lab3 的铺垫,但我一开始也完全不知道这个代码该怎么补,花了一下午加晚上认真看了几个参考文档,才有了一些思路)

设计思路也都写在了注释中(注释非常详细)。

写了两天,终归是做完了。

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Lab1-Lab4分别完成了词法分析,语法分析;语义分析;中间代码生成和目标代码生成. Lab1实验报告 词法分析: 能够查出C--源代码中词法未定义的字符以及任何不符合词法单元定义的字符; 识别合法的八进制,如035、072; 识别合法的十六进制数,如0x23、0X4a; 识别合法的指数形式的浮点数,如1.2、.2、1.2e+4; 语法分析: 能够查出C--源代码中的语法错误; 没有词法和语错误的情况,则打印语法树 Lab2实验报告 语义分析: 可对输入的*.cmm文件进行语义分析,并检查如下类型的错误: 错误类型1:变量在使用时未定义。 错误类型2:函数在调用时未经定义。 错误类型3:变量出现重复定义,或变量与前面定义过的结构体名字重复。 错误类型4:函数出现重复定义。 错误类型5:赋值号两边的表达式类型不匹配。 错误类型6:赋值号左边出现一个只有右值的表达式。 错误类型7:操作数类型不匹配或操作数类型与操作符不匹配。 错误类型8:return语句的返回类型与函数定义的返回类型不匹配。 错误类型9:函数调用时实参与形参的数目或类型不匹配。 错误类型10:对非数组型变量使用“[…]”(数组访问)操作符。 错误类型11:对普通变量使用“(…)”或“()”(函数调用)操作符。 错误类型12:数组访问操作符“[…]”中出现非整数。 错误类型13:对非结构体型变量使用“.”操作符。 错误类型14:访问结构体中未定义过的域。 错误类型15:结构体中域名重复定义(同一结构体中),或在定义时对域进行初始化。 错误类型16:结构体的名字与前面定义过的结构体或变量的名字重复。 错误类型17:直接使用未定义过的结构体来定义变量。 Lab3实验报告 中间代码生成: 在词法分析、语法分析和语义分析的基础上,可对输入的*.cmm文件进行中间代码生成。但不支持结构体类型的变量,不支持高维数组的变量以及不支持一维数组作为函数参数出现。 Lab4实验报告 目标代码生成: 在词法分析、语法分析、语义分析和中间代码生成程序的基础上,将C--源代码翻译为MIPS32指令序列(可以包含伪指令),并在SPIM Simulator上运行。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值