编译实践学习 Part8
License: CC BY-NC-SA 4.0
lv 8.1
你可以把全局范围内所有的函数 (包括之后章节中会出现的全局变量) 都放在同一个作用域内, 即全局作用域.
于是我加入了 Koopa_val_global_func
.
class Koopa_val_global_func : public Koopa_val_base {
private:
FuncDefAST const * func;
public:
bool is_void() const { return func->func_typ->is_void; }
Koopa_val_global_func(FuncDefAST const * func) { this->func = func; }
std::string get_str() const override { return "@" + func->ident; }
Koopa_value_type val_type() const override { return KOOPA_VALUE_TYPE_GLOBAL_FUNCTION; }
};
某人把
FuncDefParam:
BType IDENT {
auto ast = new FuncDefParamsAST();
ast->params.push_back({cast_ast<BTypeAST>($1), *$2});
$$ = ast;
}
;
写成了
FuncDefParam:
BType IDENT {
auto ast = new FuncDefParamsAST();
ast->params.push_back({cast_ast<BTypeAST>($1), *$2});
$$ = $1;
}
;
然后 debug 了大半天,是谁我不说。
某人又把函数参数忘放进符号表了。
然后是生成汇编。
重写了原来的 Asm_val
类(原来只有堆栈值/立即值的分类,现在加了寄存器值,来获取参数)。
判断函数调用其他函数是否有其他参数时先 dfs 一次,错误方式:
int get_function_max_call_param(const koopa_raw_value_t &val) {
if(val->ty->tag != KOOPA_RTT_FUNCTION) {
return -1;
}
return val->ty->data.function.params.len;
}
正确方式:
int get_function_max_call_param(const koopa_raw_value_t &val) {
if(val->kind.tag != KOOPA_RVT_CALL) {
return -1;
}
return val->kind.data.call.args.len;
}
写完 lv 8.1 后突发奇想想写个递归。
int main(){ return main(); }
报错了,怎么会逝呢?
然后修好了。
lv 8.2
在生成 Koopa IR 时无脑加几行:
decl @getint(): i32
decl @getch(): i32
decl @getarray(*i32): i32
decl @putint(i32)
decl @putch(i32)
decl @putarray(i32, *i32)
decl @starttime()
decl @stoptime()
但这样还不够,
根据 SysY 的规定, SysY 库函数可以不加声明就直接使用, 所以你可能需要预先在全局符号表中添加和库函数相关的符号定义, 以防无法正确处理相关内容.
把之前 Koopa_val_global_func
改了改。顺利通过。
lv 8.3
遇到了 reduce/reduce conflict.
折腾过程
发现有人遇到过同样的问题,链接在这。
问了 AI 但没得到什么有用的信息,参考了 github 上 BerkinChen/pku-compiler
但仍然报错。
翻阅 Bison 文档。发现可以让 Bison 生成示例。
然后 google 了一通并找到了 StackOverflow 的帖子。
报错原理
考虑以下输出 int foo
,编译器在读入 foo
时就要判断前面的 int
属于哪种规则。
它不知道完整的内容是 int foo;
还是 int foo(){}
,因此有多种规约 int
的方式,就出错了。
原来的语法规则是 FuncType IDENT '(' ')' // ...
和 BType IDENT ';'
,Bison 读到 IDENT
时不知道之前的 INT
该给 FuncType
还是 BType
。
解决方案
将 FuncType
和 BType
统一为一种类型就行了。
注意,你不能定义一个 void
类型的变量(这也是 FuncType
和 BType
间最大的差异),因此在 cpp 部分要加特判。
生成完汇编开测,发现 WA 4/12.
进入函数时:
-
上层符号表加入函数名
-
新建一层符号表
-
本层符号表加入函数参数
发现生成汇编时会在调用完 void
函数后试图获取返回值。本来想着再建个符号表,但实际上只要看 kind.data.call.callee->ty->data.function.ret->tag != KOOPA_RTT_UNIT
。