编译实践学习 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;
}
大为震撼。