Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
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
0
Merge Requests
0
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
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/
)
*
[
lab1
](
./Documentations/1-parser/
)
+
DDL:2022-10-03 23:59:59 (UTC+8)
+
DDL:2022-10-03 23:59:59 (UTC+8)
-
[
report
](
./report/1-parser/
)
*
[
lab2
](
./Documentations/2-ir-gen-warmup/
)
*
[
lab2
](
./Documentations/2-ir-gen-warmup/
)
+
DDL:2022-10-23 23:59:59 (UTC+8)
+
DDL:2022-10-23 23:59:59 (UTC+8)
-
[
report
](
./report/2-ir-gen-warmup/report.md
)
*
[
lab3
](
./Documentations/3-ir-gen/
)
*
[
lab3
](
./Documentations/3-ir-gen/
)
+
DDL:2022-11-07 23:59:59 (UTC+8)
+
DDL:2022-11-07 23:59:59 (UTC+8)
-
[
report
](
./report/3-ir-gen/report.md
)
## FAQ: How to merge upstream remote branches
## FAQ: How to merge upstream remote branches
...
...
Reports/.gitignore
View file @
1306a415
1-parser/*.pdf
1-parser/*.pdf
2-ir-gen-warmup/*.pdf
2-ir-gen-warmup/*.pdf
3-ir-gen/*.pdf
Reports/3-ir-gen/report.md
View file @
1306a415
# lab3 实验报告
# lab3 实验报告
PB20111654 李晓奇
>
PB20111654 李晓奇
## 实验要求
## 实验要求
用访问者模式来实现 IR 的自动生成。
## 实验难点
## 实验难点
做实验时我把令我
**费解的部分**
都记录了下来,经过整理后如下:
1.
syntax_tree和ast的区别、为什需要ast?
1.
syntax_tree和ast的区别、为什需要ast?
阅读AST的构造函数,
`AST(syntax_tree *)`
阅读AST的构造函数,
`AST(syntax_tree *)`
实际上这部分在上个实验就有看calc_ast的代码,但是印象不深刻。
。
实际上这部分在上个实验就有看calc_ast的代码,但是印象不深刻。
2.
访问者模式究竟有什么用?
2.
访问者模式究竟有什么用?
...
@@ -41,11 +45,13 @@ PB20111654 李晓奇
...
@@ -41,11 +45,13 @@ PB20111654 李晓奇
假设就是vardeclaration,直接调用(在
`ASTPrinter::visit`
内)
`visit(dynamic_cast<ASTVarDeclaration *>(this))`
不是一样的吗?
假设就是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 李晓奇
...
@@ -53,110 +59,140 @@ PB20111654 李晓奇
加入一个全局变量保存语句的返回值。
加入一个全局变量保存语句的返回值。
5.
数组or指针?这个关乎地址的计算、类型转换……
5.
数组or指针?
答案在于:中间代码是如何生成的?
按我的理解,
这个关乎地址的计算、类型转换……
-
在翻译为中间代码时,函数内部声明的只能是数组,计算地址时会得到指针。
经过对比IR代码、反复测试,得出结论:数组类型只在变量声明中得到,函数传参一定是传一个指针过去。
-
在传递给函数时,只能传递数组,但是传递为指针。
6.
记录遇到的坑
记录
**遇到的坑**
:
-
形如这种产生式子
`term→term mulop factor ∣ factor`
,我为了图方便先求了
`factor`
,在根据
`term`
的有调用
`accept()`
,但是这样会导致左右操作数颠倒。
> 这次实验,配合算法的最后一个OJ,让我深刻认识到自己写bug的能力有多强。
-
我对于类型转换的控制比较严格,没考虑到的一率abort,因此很快发现了漏掉了bool类型的转换。
-
形如这种产生式子
`term→term mulop factor ∣ factor`
,我为了图方便先求了
`factor`
,再根据
`term`
的有无调用
`accept()`
,但是这样会导致左右操作数颠倒。
-
接上条,一些类型转换的bug:
`i32`
到
`i1`
也是需要转换的,开始做的时候还没意识到这些(主要是没意识到
`i1`
的存在)。
-
我对于类型转换的控制比较严格,没考虑到的一率abort,因此很快发现自己漏掉了bool类型的转换。
-
**核心问题**
:函数调用是值传递,比如
`f(a)`
,这里在中间代码看来,a返回的是一个指针,需要load出来再传过去。
-
接上条,一些类型转换的bug:
`i32`
到
`i1`
也是需要转换的,开始做的时候还没意识到这些(主要是没意识到
`i1`
的存在)。
但是对于字面量,又不需这样提取数据。可以使用
`dynamic_cast`
尝试解决。
-
函数传参
但是出现了加减乘除时,这个又不能动态类型转换。
这个卡了我一阵子,经过反复的debug和思索,更见坚信:函数传参只是值传递。(至少在cminus是的),所有出现的bug也好、逻辑上的死循环也好,都应该从ASTVar的访问寻找病因。
换句话说,访问
`ASTCall`
时,需要依次调用args的
`accept()`
,但是这个args!!!!expression应该返回的就是值才对。问题出现在变量的访问那里。
这个在访问
`ASTVar`
时解决掉:加入全局变量
`LV`
表式当前处理的是左值,所以要返回对应的地址,否则返回load出来的右值。
-
全局变量要特殊对待。
LV为
`true`
时,对应的是赋值语句的左式、函数的指针引用。
-
数组下标为负时要调用
`neg_idx_except()`
,这个函数的call后边也要加上br。
另外嵌套递归调用可能导致LV发生变化,要注意。
-
神奇,bool变量不能比较,所以在比较、计算的时候要做类型转换。
> 关于函数传参的探讨
-
store的顺序颠倒了!!
>
> - 值传递:由于LV是false,所以访问ASTVar的时候会load出来。可能需要类型转换。
-
GEP和Load的翻译有问题。
>
> - 指针传递:设置LV是true,这时ASTVar会返回一个指针,**指向基本元素类型**(int|float)。
定位问题:源操作数不是array类型!
>
> 所以保证ASTVar返回的Value*变量符合要求,应该可以拿来直接使用,否则抛出异常。
那一定要看声明时push进scope的是什么,发现声明的数组类型其实也是指针类型(指向数组的指针),并不是数组??
-
针对ASTVar的探讨
但后来发现,原来问题出在调用GEP,我直接把0代替
`CONST_INT(0)`
传入了,“胡闹吗这不是”。
-
LV:如果标记为true,则做左值,返回数据地址,否则返回提取出来的数据。
-
不要乱给lable命名,因为lable的命名不会自动为自定义名称的label去重。
-
node.expression:如果非空,则有数组下标
-
发现ifelse语句忘了无条件跳转了……
-
Var的类型:这里断言是指针类型,所有push操作都应该维护这个性质。其指向的类型应该有三种:基本类型(int
\*
, float
\*
),数组类型([i32 x n]
\*
, [float x n]
\*
),二级指针类型(int
\*\*
, float
\*\*
)。
-
返现布尔表达式必须使用i1类型,类型要求真的太严格了!!
每种类型都有不同的对待逻辑,如何区分不同的Var类型呢?
-
知道了为什么clang生成的中间代码,会把实参声明一遍再拷贝了。
-
全局变量要特殊对待
在我看来方便编译器代码的生成,因为重新声明,push给scope的值是指针类型而不是纯值,这在后边访问Var中有用。
-
数组下标为负时要调用
`neg_idx_except()`
,这个函数的call后边也要加上br
-
发现ret会有乱跳转的问题,这个在最后一个测试的abs中发现。
-
神奇,bool变量不能比较,所以在比较、计算的时候要做类型转换
问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。
-
store的顺序颠倒了!!
后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。
-
GEP和Load的翻译有问题。
-
最后一个测试文件能够正常执行,但是输出有错。
定位问题:源操作数不是array类型!
> 这个测试文件的调试吃了我三个多小时的时间。
那一定要看声明时push进scope的是什么,发现声明的数组类型其实也是指针类型(指向数组的指针),并不是数组??
尝试了:
但后来发现,原来问题出在调用GEP,我直接吧0传入了,“胡闹吗这不是”。
-
和clang生成的替换(只能替换整个函数),定位问题出在函数
`gauss`
上。
-
不要乱给lable命名,因为lable的命名不会自动(针对自定义)去重。
-
不断缩减guass函数的语句范围,测试输出是否与clang编译出的输出一致。但是最后缩减到的语句还是很复杂,IR还是有200+行。
-
发现ifelse语句忘了无条件跳转了……
-
寻找可以单步调试ll文件的调试器,考虑到没用,放弃。
-
返现布尔表达式必须使用i1类型,类型要求真的太严格了!!
-
利用opt、dot生成基本块的跳转图,还是大的离谱。
-
知道了为什么clang生成的中间代码,会把实参声明一遍再拷贝了。
-
助教推荐的creduce,最开始以为是助教没理解我的问题,觉得永不上,舍弃,现在回想巨亏。
在我看来方便编译器代码的生成,因为重新声明,push给scope的值是指针类型而不是纯值,这在后边访问Var中有用。
最后,找同学看一下IR代码,二十分钟就找到问题了,是把float变量声明成int了。
-
发现ret会有乱跳转的问题,这个在最后一个测试的abs中发现。
问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。
后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。
-
最后一个测试文件能够正常执行,但是输出有错,
发现一个好的debug方法:
利用裁剪定位问题,即注释掉一部分,分别利用clang、cminusfc生成可执行文件,查看输出是否一致。
## 实验设计
## 实验设计
请写明为了顺利完成本次实验,加入了哪些亮点设计,并对这些设计进行解释。
### 1)全局变量的设计
可能的阐述方向有:
有两个全局变量:
`Value* cur_value`
和
`bool LV`
分别表示最近一个表达式返回值是多少、是否需要左值(赋值时使用)。
### 2)难点解决 ASTVar的访问
最困难的函数当属
`CminusfBuilder::visit(ASTVar &node)`
,这个函数需要完成的主要任务是产生访问的局部变量的数值 or 地址。
根据是否需要左值、是否有下标索引、Var的类型,IR产生的逻辑各不相同。
我的逻辑如下:
首先断言访问到的节点如果合法,在scope中找到的一定是一个指针类型,指向的是基本类型(int|float|array)或者指针类型,指针类型特指函数声明中保存实参数值的情况,且参数类型为指针。所有push操作都应该维护这个性质。
1.
如何设计全局变量
其次,根据是否需要左值区分不同的逻辑。
2.
遇到的难点以及解决方案
3.
如何降低生成 IR 中的冗余
-
根据我的设计,左值只有赋值语句出现,由于array和指针都不可以赋值,所以合法情况下,返回int|float的地址。
4.
...
-
如果不是左值,根据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) {
...
@@ -313,9 +313,6 @@ void CminusfBuilder::visit(ASTCompoundStmt &node) {
// You may need to add some code here
// You may need to add some code here
// to deal with complex statements.
// 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
();
scope
.
enter
();
for
(
auto
&
decl
:
node
.
local_declarations
)
{
for
(
auto
&
decl
:
node
.
local_declarations
)
{
...
@@ -714,11 +711,6 @@ void CminusfBuilder::visit(ASTCall &node) {
...
@@ -714,11 +711,6 @@ void CminusfBuilder::visit(ASTCall &node) {
else
else
error_exit
(
"BUG HERE: function param needs weird pointer type"
);
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
())
{
}
else
if
(
param_type
->
is_integer_type
()
or
param_type
->
is_float_type
())
{
// need type cast between int and float
// 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
())
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