# lab2 实验报告 PB20111654 李晓奇 ## 问题1: getelementptr 请给出 `IR.md` 中提到的两种 getelementptr 用法的区别,并稍加解释: - `%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0` %2是`i32*`类型。 我的理解是,这条指令相当于`%2=&%1[0][%0]`,其中将`%1`看成指向至少有两层结构的指针,GEP指令最后边的两个数字一是用来指定进入的层数,一是用来指定每层的偏移量。 第一个`i32 0`表示指向第一个`[10 x i32]`块 第二个`i32 %0`表示在第一个`[10 x i32]`块中指向第`%0`个元素 GEP指令返回这个元素的指针,赋值给%2 - `%2 = getelementptr i32, i32* %1 i32 %0` %2同样是`i32*`类型。 %1是指针,不同于上式(%1指向数组),本式%1指向`i32`类型,所以填入一个偏移数字,即GEP指令返回`%1[%0]`的指针,赋值给%2。 ## 问题2: cpp 与 .ll 的对应 请说明你的 cpp 代码片段和 .ll 的每个 BasicBlock 的对应关系。 ### assign.c ![](figures/assign.png) ### fun.c ![](figures/fun.png) ### if.c ![](figures/if.png) ### while.c ![](figures/while.png) ## 问题3: Visitor Pattern 分析 `calc` 程序在输入为 `4 * (8 + 4 - 1) / 2` 时的行为: 1. 请画出该表达式对应的抽象语法树(使用 `calc_ast.hpp` 中的 `CalcAST*` 类型和在该类型中存储的值来表示),并给节点使用数字编号。 AST如下,略去了CalcAST中的root: ![](figures/CALC_AST.png) 2. 请指出示例代码在用访问者模式遍历该语法树时的遍历顺序。 CalcBuilder类从AST的树根开始,访问每个节点,具体行为如下: - 如果节点是中间节点,则visit会触发该节点内部的每一个子节点的accept函数,经过一次或多次调用又触发CalcBuilder的visit,所以进行递归。 - 如果节点是叶子节点,即CalcASTNum类型,则可以直接读出这个节点的数值,保存在CalcBuilder类中的val中,回溯时会使用到。 所以CalcBuilder访问AST是个深度优先递归遍历的过程。 遍历顺序是: 1->2->3->4->5->7->8->9->11->14->16->12->15->10->13->6 > 个人感觉这个过程像是根据分析树构建语法树。过程中通过val保存一次visit调用的结果,只有在涉及到翻译的节点,即加减乘除运算时才做输出。 ## 实验难点 描述在实验中遇到的问题、分析和解决方案。 - 理解GEP指令和指针的关系:阅读[资料](https://llvm.org/docs/GetElementPtr.html#what-effect-do-address-spaces-have-on-geps)。 - lightIR API的使用方法:相比[代码](http://202.38.79.174/compiler_staff/2022fall-compiler_cminus/-/tree/master/include/lightir),[文档](http://202.38.79.174/compiler_staff/2022fall-compiler_cminus/-/blob/master/Documentations/common/LightIR.md#c-apis)中有更多有助于理解的信息。所以先扫一遍文档获得宏观上的理解,再读[示例](http://202.38.79.174/compiler_staff/2022fall-compiler_cminus/-/blob/master/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp)。对于API中的每个类,文档和代码配合着看,大致就能理解了。 - 理解CalcAST和CalcBuilder的行为:读一遍代码就能懂了。 ## 实验反馈 暂无~