编译实践学习 Part6

License: CC BY-NC-SA 4.0

lv 6.1

这一部分引入了 if/else,于是就有了 shift/reduce conflict.

在符合 EBNF 语法定义的前提下, 我们可以找到不止一种语法的推导 (或规约) 方法.

例如:

if(a) if(b) x; else y;

查阅官方文档发现:

Bison is designed to resolve these conflicts by choosing to shift, unless otherwise directed by operator precedence declarations.

也就是说,Bison 在面对这种困境时默认应用靠后的规则。

找了一堆方法都没有解决报 Warning 的问题,最后还是求助了 ChatGPT:

%token IF ELSE
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE

%%

If:
	IF '(' Exp ')' Stmt %prec LOWER_THAN_ELSE{
		// ...
	} | IF '(' Exp ')' Stmt ELSE Stmt {
		// ...
	};

在生成 Koopa IR 时发现每个块后不能有东西。先是用了一下异常,然后发现这样会出问题(如作用域进出带来的符号表变化),于是决定自己包装 std::ostringstream,加一个不接受输入的功能。

/// Before
using Ost = std::ostringstream;
/// After
class Ost {
public:
	std::ostringstream *stream;
	bool muted;
	Ost(std::ostringstream &s) {
		stream = &s;
		muted = false;
	}
	Ost &operator<<(auto x) {
		if(!muted) {
			(*stream) << x;
		}
		return *this;
	}
	void mute() { muted = true; }
	void unmute() { muted = false; }
};

生成汇编:

用一个 unordered_map 记录每个基本块对应的名称,然后要用的时候直接跳转就行了。

// basic_block: id map
std::unordered_map<koopa_raw_basic_block_t, std::string> blk_id_mp;

void dfs_ir(const koopa_raw_function_t &func, Outp &outstr) {
	// ...
	for(size_t i = 0; i < func->bbs.len; i++) {
		assert(func->bbs.kind == KOOPA_RSIK_BASIC_BLOCK);
		koopa_raw_basic_block_t blk = (koopa_raw_basic_block_t)func->bbs.buffer[i];
		blk_id_mp[blk] = "block_" + std::to_string(Global_State::basic_blk_cnt);
		Global_State::basic_blk_cnt++;
	}
	for(size_t i = 0; i < func->bbs.len; i++) {
		assert(func->bbs.kind == KOOPA_RSIK_BASIC_BLOCK);
		koopa_raw_basic_block_t blk = (koopa_raw_basic_block_t)func->bbs.buffer[i];
		dfs_ir(blk, outstr);
	}
	// ...
}

void dfs_ir(const koopa_raw_basic_block_t &blk, Outp &outstr) {
	outstr << blk_id_mp[blk] << ":\n";
	// ...
}

void dfs_ir(const koopa_raw_value_t &val, Outp &outstr) {
	// ...
	case KOOPA_RVT_BRANCH:
		dfs_ir(kind.data.branch.cond, outstr);
		valmp[(void *)kind.data.branch.cond]->load(outstr, "t0");
		assert(blk_id_mp.contains(kind.data.branch.true_bb));
		assert(blk_id_mp.contains(kind.data.branch.false_bb));
		outstr << "bnez t0, " << blk_id_mp[kind.data.branch.true_bb] << "\n";
		outstr << "j " << blk_id_mp[kind.data.branch.false_bb] << "\n";
		break;
	case KOOPA_RVT_JUMP:
		assert(blk_id_mp.contains(kind.data.jump.target));
		outstr << "j " << blk_id_mp[kind.data.jump.target] << "\n";
		break;
	// ...
}

lv 6.2

对之前写的 BinaryExpAST_Base<T, U>::output 加个特判。

template<typename T, typename U>
void BinaryExpAST_Base<T, U>::output(Ost& outstr, std::string prefix) const {
	if(!binary_op.has_value()) {
		return nxt_level->output(outstr, prefix);
	}
	if(binary_op.value()->is_logic_op()) {
		now_level.value()->output(outstr, prefix);
		Koopa_val lhs = stmt_val.top();
		stmt_val.pop();
		lhs.prepare(outstr, prefix);
		int cur_if_cnt = if_cnt;
		if_cnt++;
		outstr << prefix << "br " << lhs << ", %then_short" << cur_if_cnt
			   << ", %else_short" << cur_if_cnt << "\n";
		if(binary_op.value()->op == OP_LOR) {
			outstr << "%then_short" << cur_if_cnt << ":\n";
			outstr << prefix << "store 1, " << SHORT_TMP_VAR_NAME << "\n";
			// outstr << prefix << "%" << now_var << " = or 0, 1\n";
			outstr << prefix << "jump %end_short" << cur_if_cnt << "\n";
			outstr << "%else_short" << cur_if_cnt << ":\n";
		} else {
			outstr << "%else_short" << cur_if_cnt << ":\n";
			outstr << prefix << "store 0, " << SHORT_TMP_VAR_NAME << "\n";
			outstr << prefix << "jump %end_short" << cur_if_cnt << "\n";
			outstr << "%then_short" << cur_if_cnt << ":\n";
		}

		nxt_level->output(outstr, prefix);
		Koopa_val rhs = stmt_val.top();
		stmt_val.pop();
		rhs.prepare(outstr, prefix);
		int now_var = unnamed_var_cnt;
		unnamed_var_cnt++;
		outstr << prefix << "%" << now_var << " = ne 0, " << rhs << "\n";
		outstr << prefix << "store %" << now_var << ", " << SHORT_TMP_VAR_NAME << "\n";
		// outstr << "%" << now_var << " = or 0, " << rhs << "\n";
		outstr << prefix << "jump %end_short" << cur_if_cnt << "\n";
		outstr << "%end_short" << cur_if_cnt << ":\n";
		now_var = unnamed_var_cnt;
		unnamed_var_cnt++;
		outstr << prefix << "%" << now_var << " = load " << SHORT_TMP_VAR_NAME << "\n";
		stmt_val.push(new Koopa_val_temp_symbol(now_var));
		return;
	}
	now_level.value()->output(outstr, prefix);
	Koopa_val lhs = stmt_val.top();
	stmt_val.pop();
	nxt_level->output(outstr, prefix);
	Koopa_val rhs = stmt_val.top();
	stmt_val.pop();
	lhs.prepare(outstr, prefix);
	rhs.prepare(outstr, prefix);
	// if(binary_op.value()->is_logic_op()) {
	// 	int now_var = unnamed_var_cnt;
	// 	unnamed_var_cnt++;
	// 	outstr << prefix << "%" << now_var << " = ne 0, " << lhs;
	// 	lhs = new Koopa_val_temp_symbol(now_var);
	// 	now_var = unnamed_var_cnt;
	// 	unnamed_var_cnt++;
	// 	outstr << prefix << "%" << now_var << " = ne 0, " << rhs;
	// 	rhs = new Koopa_val_temp_symbol(now_var);
	// }
	int now_var = unnamed_var_cnt;
	unnamed_var_cnt++;
	outstr << prefix << "%" << now_var << " = ";
	binary_op.value()->output(outstr, "");
	outstr << lhs << ", " << rhs << '\n';
	stmt_val.push(new Koopa_val_temp_symbol(now_var));
}

喜提 WA。

发现自己被这个例子 Hack 了:

int main() {
  int c = 1;
  return !c;
}

大为震撼。

posted @ 2024-07-02 15:18  383494  阅读(7)  评论(0编辑  收藏  举报