Commit 1306a415 authored by 李晓奇's avatar 李晓奇

update report

parent 698a4326
# 重现问题
> 这个在运行py脚本或者gdb运行时不会出现。
>
> 只有单独使用`cminusfc`生成可执行文件会出错。
- 构建`cminusfc`
- ```
$ # at path `2022fall-compiler_cminus`
$ cd tests/3-ir-gen/testcases/lv3/
$ cminusfc complex4.cminus
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::erase: __pos (which is 18446744073709551615) > this->size() (which is 8)
[1] 22357 IOT instruction (core dumped) cminusfc complex4.cminus
```
# 实验说明
请 fork 此 repo 到自己的仓库下,随后在自己的仓库中完成实验,请确保自己的 repo 为 Private。
## 目前已布置的实验
* [lab1](./Documentations/1-parser/)
+ DDL:2022-10-03 23:59:59 (UTC+8)
- [report](./report/1-parser/)
* [lab2](./Documentations/2-ir-gen-warmup/)
+ DDL:2022-10-23 23:59:59 (UTC+8)
- [report](./report/2-ir-gen-warmup/report.md)
* [lab3](./Documentations/3-ir-gen/)
+ DDL:2022-11-07 23:59:59 (UTC+8)
- [report](./report/3-ir-gen/report.md)
## FAQ: How to merge upstream remote branches
......
1-parser/*.pdf
2-ir-gen-warmup/*.pdf
3-ir-gen/*.pdf
# lab3 实验报告
PB20111654 李晓奇
> PB20111654 李晓奇
## 实验要求
用访问者模式来实现 IR 的自动生成。
## 实验难点
做实验时我把令我**费解的部分**都记录了下来,经过整理后如下:
1. syntax_tree和ast的区别、为什需要ast?
阅读AST的构造函数,`AST(syntax_tree *)`
实际上这部分在上个实验就有看calc_ast的代码,但是印象不深刻。
实际上这部分在上个实验就有看calc_ast的代码,但是印象不深刻。
2. 访问者模式究竟有什么用?
......@@ -41,11 +45,13 @@ PB20111654 李晓奇
假设就是vardeclaration,直接调用(在`ASTPrinter::visit`内)`visit(dynamic_cast<ASTVarDeclaration *>(this))`不是一样的吗?
参考[攻略](https://www.jianshu.com/p/1f1049d0a0f4)接受一点,感觉还是不太明白,但是不影响写实验。因为经过反复的阅读,我已经知道了我们要实现的`CminusfBuilder`是一个访问者类!
参考[攻略](https://www.jianshu.com/p/1f1049d0a0f4)我对这种设计模式 理解/接受 了一些。也在这里意识到要做的事:实现一个访问者类,和`ASTPrinter`类似。
3. 无从下手
3. 无从下手
从变量开始!
每个节点的访问都要做什么?比如我访问`ASTFunDeclaration`时,我要为参数做什么?我有什么?
答案:从变量开始!
自底向上做自然多了,不然像在摸黑拧魔方。
......@@ -53,110 +59,140 @@ PB20111654 李晓奇
加入一个全局变量保存语句的返回值。
5. 数组or指针?这个关乎地址的计算、类型转换……
答案在于:中间代码是如何生成的?
5. 数组or指针?
按我的理解,
这个关乎地址的计算、类型转换……
- 在翻译为中间代码时,函数内部声明的只能是数组,计算地址时会得到指针。
- 在传递给函数时,只能传递数组,但是传递为指针。
经过对比IR代码、反复测试,得出结论:数组类型只在变量声明中得到,函数传参一定是传一个指针过去。
6. 记录遇到的坑
- 形如这种产生式子`term→term mulop factor ∣ factor`,我为了图方便先求了`factor`,在根据`term`的有调用`accept()`,但是这样会导致左右操作数颠倒。
- 我对于类型转换的控制比较严格,没考虑到的一率abort,因此很快发现了漏掉了bool类型的转换。
- 接上条,一些类型转换的bug:`i32``i1`也是需要转换的,开始做的时候还没意识到这些(主要是没意识到`i1`的存在)。
- **核心问题**:函数调用是值传递,比如`f(a)`,这里在中间代码看来,a返回的是一个指针,需要load出来再传过去。
但是对于字面量,又不需这样提取数据。可以使用`dynamic_cast`尝试解决。
但是出现了加减乘除时,这个又不能动态类型转换。
换句话说,访问`ASTCall`时,需要依次调用args的`accept()`,但是这个args!!!!expression应该返回的就是值才对。问题出现在变量的访问那里。
这个在访问`ASTVar`时解决掉:加入全局变量`LV`表式当前处理的是左值,所以要返回对应的地址,否则返回load出来的右值。
LV为`true`时,对应的是赋值语句的左式、函数的指针引用。
另外嵌套递归调用可能导致LV发生变化,要注意。
> 关于函数传参的探讨
>
> - 值传递:由于LV是false,所以访问ASTVar的时候会load出来。可能需要类型转换。
>
> - 指针传递:设置LV是true,这时ASTVar会返回一个指针,**指向基本元素类型**(int|float)。
>
> 所以保证ASTVar返回的Value*变量符合要求,应该可以拿来直接使用,否则抛出异常。
- 针对ASTVar的探讨
- LV:如果标记为true,则做左值,返回数据地址,否则返回提取出来的数据。
- node.expression:如果非空,则有数组下标
- Var的类型:这里断言是指针类型,所有push操作都应该维护这个性质。其指向的类型应该有三种:基本类型(int\*, float\*),数组类型([i32 x n]\*, [float x n]\*),二级指针类型(int\*\*, float\*\*)。
每种类型都有不同的对待逻辑,如何区分不同的Var类型呢?
- 全局变量要特殊对待
- 数组下标为负时要调用`neg_idx_except()`,这个函数的call后边也要加上br
- 神奇,bool变量不能比较,所以在比较、计算的时候要做类型转换
- store的顺序颠倒了!!
- GEP和Load的翻译有问题。
定位问题:源操作数不是array类型!
那一定要看声明时push进scope的是什么,发现声明的数组类型其实也是指针类型(指向数组的指针),并不是数组??
但后来发现,原来问题出在调用GEP,我直接吧0传入了,“胡闹吗这不是”。
- 不要乱给lable命名,因为lable的命名不会自动(针对自定义)去重。
- 发现ifelse语句忘了无条件跳转了……
- 返现布尔表达式必须使用i1类型,类型要求真的太严格了!!
- 知道了为什么clang生成的中间代码,会把实参声明一遍再拷贝了。
在我看来方便编译器代码的生成,因为重新声明,push给scope的值是指针类型而不是纯值,这在后边访问Var中有用。
- 发现ret会有乱跳转的问题,这个在最后一个测试的abs中发现。
问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。
后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。
- 最后一个测试文件能够正常执行,但是输出有错,
发现一个好的debug方法:
利用裁剪定位问题,即注释掉一部分,分别利用clang、cminusfc生成可执行文件,查看输出是否一致。
记录**遇到的坑**
> 这次实验,配合算法的最后一个OJ,让我深刻认识到自己写bug的能力有多强。
- 形如这种产生式子`term→term mulop factor ∣ factor`,我为了图方便先求了`factor`,再根据`term`的有无调用`accept()`,但是这样会导致左右操作数颠倒。
- 我对于类型转换的控制比较严格,没考虑到的一率abort,因此很快发现自己漏掉了bool类型的转换。
- 接上条,一些类型转换的bug:`i32``i1`也是需要转换的,开始做的时候还没意识到这些(主要是没意识到`i1`的存在)。
- 函数传参
这个卡了我一阵子,经过反复的debug和思索,更见坚信:函数传参只是值传递。(至少在cminus是的),所有出现的bug也好、逻辑上的死循环也好,都应该从ASTVar的访问寻找病因。
- 全局变量要特殊对待。
- 数组下标为负时要调用`neg_idx_except()`,这个函数的call后边也要加上br。
- 神奇,bool变量不能比较,所以在比较、计算的时候要做类型转换。
- store的顺序颠倒了!!
- GEP和Load的翻译有问题。
定位问题:源操作数不是array类型!
那一定要看声明时push进scope的是什么,发现声明的数组类型其实也是指针类型(指向数组的指针),并不是数组??
但后来发现,原来问题出在调用GEP,我直接把0代替`CONST_INT(0)`传入了,“胡闹吗这不是”。
- 不要乱给lable命名,因为lable的命名不会自动为自定义名称的label去重。
- 发现ifelse语句忘了无条件跳转了……
- 返现布尔表达式必须使用i1类型,类型要求真的太严格了!!
- 知道了为什么clang生成的中间代码,会把实参声明一遍再拷贝了。
在我看来方便编译器代码的生成,因为重新声明,push给scope的值是指针类型而不是纯值,这在后边访问Var中有用。
- 发现ret会有乱跳转的问题,这个在最后一个测试的abs中发现。
问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。
后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。
- 最后一个测试文件能够正常执行,但是输出有错。
> 这个测试文件的调试吃了我三个多小时的时间。
尝试了:
- 和clang生成的替换(只能替换整个函数),定位问题出在函数`gauss`上。
- 不断缩减guass函数的语句范围,测试输出是否与clang编译出的输出一致。但是最后缩减到的语句还是很复杂,IR还是有200+行。
- 寻找可以单步调试ll文件的调试器,考虑到没用,放弃。
- 利用opt、dot生成基本块的跳转图,还是大的离谱。
- 助教推荐的creduce,最开始以为是助教没理解我的问题,觉得永不上,舍弃,现在回想巨亏。
最后,找同学看一下IR代码,二十分钟就找到问题了,是把float变量声明成int了。
## 实验设计
请写明为了顺利完成本次实验,加入了哪些亮点设计,并对这些设计进行解释。
可能的阐述方向有:
### 1)全局变量的设计
有两个全局变量:`Value* cur_value``bool LV`
分别表示最近一个表达式返回值是多少、是否需要左值(赋值时使用)。
### 2)难点解决 ASTVar的访问
最困难的函数当属`CminusfBuilder::visit(ASTVar &node)`,这个函数需要完成的主要任务是产生访问的局部变量的数值 or 地址。
根据是否需要左值、是否有下标索引、Var的类型,IR产生的逻辑各不相同。
我的逻辑如下:
首先断言访问到的节点如果合法,在scope中找到的一定是一个指针类型,指向的是基本类型(int|float|array)或者指针类型,指针类型特指函数声明中保存实参数值的情况,且参数类型为指针。所有push操作都应该维护这个性质。
1. 如何设计全局变量
2. 遇到的难点以及解决方案
3. 如何降低生成 IR 中的冗余
4. ...
其次,根据是否需要左值区分不同的逻辑。
- 根据我的设计,左值只有赋值语句出现,由于array和指针都不可以赋值,所以合法情况下,返回int|float的地址。
- 如果不是左值,根据var对应的类型区分:
- int|float:返回对应的数值。
- array:返回首地址或者索引后的元素的地址,即可能传递指针也可能是传递内部某一个元素的值。
- 指针:返回指针或者索引后的元素地址。
以上对于int|float类型的访问,如果存在索引都是非法。
这里的难点主要是:
1. 确定var的可能类型:关于指针类型(名义上)我踩了很多坑才意识到。
2. 合理组织代码:写了不少bug才该正确。
3. 调用好API判断类型。
### 3)IR冗余
只是探讨一下可能的方向。
- `neg_idx_except`的调用,如果一个函数中有很多数组索引,那么每次都建立一个基本块跳转到这个函数就显得比较冗余,考虑一个函数维护至多一个基本块来调用`neg_idx_except`
- 或许也可以不为函数实参声明额外空间做保存。这里我还是不理解alloca在什么时候起作用(除了搞出一堆指针来折磨我之外),但是不为实参alloca新空间拷贝一下,IR代码至少是能跑的。我这么写是为了代码好写,或许可以改进。
### 实验总结
此次实验有什么收获
> 充满哲理性的总结:
>
> 不管多困难,先开始。~~因为更多的困难在动手前是永远想不到的~~。
收获:
- 最后和助教交流debug方法,发现了新工具。
- 对中间代码有了深刻的认识。
不管多困难,先开始。~~因为更多的困难在动手前是永远想不到的~~
- 对中间代码的生成有了亲身体验
### 实验反馈 (可选 不计入评分)
对本次实验的建议
加入调试的指导会让大家学到更多,实验更顺利。
......@@ -313,9 +313,6 @@ void CminusfBuilder::visit(ASTCompoundStmt &node) {
// You may need to add some code here
// to deal with complex statements.
/* auto bb = BasicBlock::create(builder->get_module(), "", cur_fun);
* builder->create_br(bb);
* builder->set_insert_point(bb); */
scope.enter();
for (auto &decl : node.local_declarations) {
......@@ -714,11 +711,6 @@ void CminusfBuilder::visit(ASTCall &node) {
else
error_exit("BUG HERE: function param needs weird pointer type");
/* if (not Type::is_eq_type(param_type->get_pointer_element_type(),
* cur_value->get_type()->get_pointer_element_type()->get_array_element_type()))
* error_exit("expected right pointer type");
* // int[] to int* or float[] to flot*
* cur_value = builder->create_gep(cur_value, {CONST_INT(0), CONST_INT(0)}); */
} else if (param_type->is_integer_type() or param_type->is_float_type()) {
// need type cast between int and float
if (not cur_value->get_type()->is_integer_type() and not cur_value->get_type()->is_float_type())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment