From f34f7df5ef207668348c61e82013996267c7f360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?202108010329-=E5=BC=A0=E6=AD=A3=E9=98=B3?= <12721456+zzy8268s7cx@user.noreply.gitee.com> Date: Sat, 16 Dec 2023 12:06:48 +0000 Subject: [PATCH] update Reports/lab4/report.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 202108010329-张正阳 <12721456+zzy8268s7cx@user.noreply.gitee.com> --- Reports/lab4/report.md | 601 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 582 insertions(+), 19 deletions(-) diff --git a/Reports/lab4/report.md b/Reports/lab4/report.md index 5557c6c..27da47c 100755 --- a/Reports/lab4/report.md +++ b/Reports/lab4/report.md @@ -1,34 +1,597 @@ -# Lab4 实验报告 +## 一、实验要求 +1. 阅读cminus-f 的语义规则成为语言律师,我们将按照语义实现程度进行评分 +2. 阅读LightIR 核心类介绍 +3. 阅读实验框架,理解如何使用框架以及注意事项 +4. 修改 src/cminusfc/cminusf_builder.cpp 来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序 +5. 在 report.md 中解释你们的设计,遇到的困难和解决方案 +## 三、实验设计 +从实验说明中知道,要编写的代码是cminusf_builder.cpp,其内含各个 visit 函数,我们需要补全这些 visit 的函数。 +在写代码之前,要好好看看打印抽象语法树的算法的代码(虽然参考价值不多,但其中的 visit 的代码还是很有启发性的,需要仔细阅读),掌握 logging 工具(虽然掌握了之后还是特别难 debug),仔细看 Light IR 文档(主要看 C++ APIs 部分),仔细看cminus-f 的语法与语义(这个特别重要)。 +宏定义和一些全局变量 +```cpp +// 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 +```cpp +/* Program, 程序, program->declaration-list */ +void CminusfBuilder::visit(ASTProgram &node) { + for (auto decl : node.declarations) /* 遍历declaration-list */ + decl->accept(*this); /* 处理每一个declaration */ +} -## 实验设计 +``` +Num,数值 +```cpp +/* 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 ] ; +要注意: +全局变量需要初始化为全 0 。 +cminus-f 的基础类型只有整型(int)、浮点型(float)和 void。而在变量声明中,只有整型和浮点型可以使用,void 仅用于函数声明。 +一个变量声明定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。 +数组变量在声明时,INTEGER \textbf{INTEGER}INTEGER 应当大于0。 +一次只能声明一个变量。 +```cpp +/* 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); /* 将获得变量加入域 */ + } +} -1. 如何设计全局变量 -2. 遇到的难点以及解决方案 -3. 如何降低生成 IR 中的冗余 -4. ... +``` +Fun-Declaration, 函数声明 +fun-declaration → type-specifier ID ( params ) compound-stmt +要注意: +函数声明包含了返回类型,标识符,由逗号分隔的形参列表,还有一个复合语句。 +当函数的返回类型是 void 时,函数不返回任何值。 +函数的参数可以是 void ,也可以是一个列表。当函数的形参是void时,调用该函数时不用传入任何参数。 +形参中跟着中括号代表数组参数,它们可以有不同长度。 +整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。 +函数的形参拥有和函数声明的复合语句相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致) +函数可以递归调用。 +```cpp +/* 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 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 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 [] +```cpp +/* 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 } +```cpp +/* 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 ; ∣ ; +```cpp +/* 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 +注意: +if语句中的表达式将被求值,若结果的值等于0,则第二个语句执行(如果存在的话),否则第一个语句会执行。 +为了避免歧义,else将会匹配最近的if +```cpp +/* 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 +注意: +while语句是 cminus-f 中唯一的迭代语句。它执行时,会不断对表达式进行求值,并且在对表达式的求值结果等于 0 前,循环执行执下面的语句 +```cpp +/* 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 ; +注意: +return语句可以返回值,也可以不返回值。 +未声明为void 类型的函数必须返回和函数返回类型相同的值 +```cpp +/* 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 ] +注意: +var 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。 +数组的下标值是整型,它的值是表达式计算结果或结果进行类型转换后的整型值 +一个负的下标会导致程序终止,需要调用框架中的内置函数neg_idx_except (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。 +```cpp +/* 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(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 中不允许进行指针算数。 +```cpp +/* 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 +注意: +一个简单表达式是一个加法表达式或者两个加法表达式的关系运算。当它是加法表达式时,它的值就是加法表达式的值。而当它是关系运算时,如果关系运算结果为真则值为整型值 1,反之则值为整型值 0。 +```cpp +/* 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 +注意: +浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型 +```cpp +/* 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 +注意: +浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型 +```cpp +/* 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 ) +注意: +函数调用由一个函数的标识符与一组括号包围的实参组成。实参可以为空,也可以是由逗号分隔的的表达式组成的列表,这些表达式代表着函数调用时,传给形参的值。函数调用时的实参数量和类型必须与函数声明中的形参一致,必要时需要进行类型转换。 +```cpp +/* Call, 函数调用, call -> ID (args) */ +void CminusfBuilder::visit(ASTCall &node) { + auto function = static_cast(scope.find(node.id)); /* 获取需要调用的函数 */ + auto paramType = function->get_function_type()->param_begin(); /* 获取其参数类型 */ + std::vector 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), args); /* 创建函数调用 */ +} + +``` +## 四、实验结果验证 +进入build文件夹 +输入指令:cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/ +![image.png](https://cdn.nlark.com/yuque/0/2023/png/38957757/1702476232937-e53403e9-61e1-4eb9-b1fb-37b5b5010143.png#averageHue=%23300a24&clientId=u619af0ce-6bf4-4&from=paste&height=215&id=ud1dca314&originHeight=484&originWidth=1214&originalType=binary&ratio=1.875&rotation=0&showTitle=false&size=121953&status=done&style=none&taskId=u360e41d3-ecfe-42c8-a7a5-fd09a43f83b&title=&width=539.5555555555555) +输入指令make -j +![image.png](https://cdn.nlark.com/yuque/0/2023/png/38957757/1702476250045-42e4dd65-c24d-4605-895e-75912533b976.png#averageHue=%23350d2c&clientId=u619af0ce-6bf4-4&from=paste&height=332&id=u639d71b3&originHeight=747&originWidth=1211&originalType=binary&ratio=1.875&rotation=0&showTitle=false&size=219373&status=done&style=none&taskId=u8cbf92dc-224f-4109-95d5-603b7c54872&title=&width=538.2222222222222) +最后输入指令make install +![image.png](https://cdn.nlark.com/yuque/0/2023/png/38957757/1702476267560-c17c22ac-5982-4e66-a6e8-5512eaccdab5.png#averageHue=%23330c29&clientId=u619af0ce-6bf4-4&from=paste&height=333&id=u819ab00f&originHeight=750&originWidth=1184&originalType=binary&ratio=1.875&rotation=0&showTitle=false&size=183537&status=done&style=none&taskId=u927ffa19-2b2b-412b-ab00-5269d78a7c9&title=&width=526.2222222222222) +编译成功 +运行自动测试程序,测试样例 +在目录tests/lab4下输入指令./lab4_test.py: +![image.png](https://cdn.nlark.com/yuque/0/2023/png/38957757/1702476348787-9d653612-c49a-4cfa-9a08-8a7be849d03a.png#averageHue=%23300a24&clientId=u619af0ce-6bf4-4&from=paste&height=273&id=u33d81421&originHeight=614&originWidth=1212&originalType=binary&ratio=1.875&rotation=0&showTitle=false&size=138241&status=done&style=none&taskId=u3ebcc69e-c9ad-4d74-ab1d-cec6fe6233d&title=&width=538.6666666666666) +可以看到,通过所有样例,运行正确。 -- Gitee