编译实践学习 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

解决方案

FuncTypeBType 统一为一种类型就行了。

注意,你不能定义一个 void 类型的变量(这也是 FuncTypeBType 间最大的差异),因此在 cpp 部分要加特判。


生成完汇编开测,发现 WA 4/12.

进入函数时:

  • 上层符号表加入函数名

  • 新建一层符号表

  • 本层符号表加入函数参数

发现生成汇编时会在调用完 void 函数后试图获取返回值。本来想着再建个符号表,但实际上只要看 kind.data.call.callee->ty->data.function.ret->tag != KOOPA_RTT_UNIT

posted @ 2024-07-06 17:01  383494  阅读(9)  评论(0编辑  收藏  举报