Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
2
2022fall-Compiler_CMinus
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
6
Merge Requests
6
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
李晓奇
2022fall-Compiler_CMinus
Commits
1306a415
You need to sign in or sign up before continuing.
Commit
1306a415
authored
Oct 31, 2022
by
李晓奇
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update report
parent
698a4326
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
138 additions
and
125 deletions
+138
-125
README.md
README.md
+3
-19
Reports/.gitignore
Reports/.gitignore
+1
-0
Reports/3-ir-gen/report.md
Reports/3-ir-gen/report.md
+134
-98
src/cminusfc/cminusf_builder.cpp
src/cminusfc/cminusf_builder.cpp
+0
-8
No files found.
README.md
View file @
1306a415
# 重现问题
> 这个在运行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
...
...
Reports/.gitignore
View file @
1306a415
1-parser/*.pdf
2-ir-gen-warmup/*.pdf
3-ir-gen/*.pdf
Reports/3-ir-gen/report.md
View file @
1306a415
# 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方法,发现了新工具。
-
对中间代码有了深刻的认识。
不管多困难,先开始。~~因为更多的困难在动手前是永远想不到的~~
。
-
对中间代码的生成有了亲身体验
。
### 实验反馈 (可选 不计入评分)
对本次实验的建议
加入调试的指导会让大家学到更多,实验更顺利。
src/cminusfc/cminusf_builder.cpp
View file @
1306a415
...
...
@@ -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
())
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment