riscv64: Only emit jumps at the end of basic blocks (#5381)

This PR fixes two bugs in the riscv64 backend, where branch instructions were emitted in the middle of a basic block:

Constant emission, where the constants are inlined into the vcode and are jumped over at runtime,
The BrTableCheck pseudo-instruction, which was always emitted before a BrTable instruction, and would handle jumping to the default label.
The first bug was resolved by introducing two new psuedo instructions, LoadConst32 and LoadConst64. Both of these instructions serve to delay the original encoding to emission time, after regalloc2 has run.

The second bug was fixed by removing the BrTableCheck instruction. As it was always emitted directly before BrTable, it was easier to remove it and merge the two into a single instruction.
This commit is contained in:
Trevor Elliott
2022-12-06 10:54:10 -08:00
committed by GitHub
parent feaa7ca75f
commit 293bb5b334
5 changed files with 88 additions and 115 deletions

View File

@@ -635,6 +635,20 @@ impl MachInstEmit for Inst {
let x: u32 = 0b0110111 | reg_to_gpr_num(rd.to_reg()) << 7 | (imm.as_u32() << 12);
sink.put4(x);
}
&Inst::LoadConst32 { rd, imm } => {
let rd = allocs.next_writable(rd);
LoadConstant::U32(imm)
.load_constant(rd, &mut |_| rd)
.into_iter()
.for_each(|inst| inst.emit(&[], sink, emit_info, state));
}
&Inst::LoadConst64 { rd, imm } => {
let rd = allocs.next_writable(rd);
LoadConstant::U64(imm)
.load_constant(rd, &mut |_| rd)
.into_iter()
.for_each(|inst| inst.emit(&[], sink, emit_info, state));
}
&Inst::FpuRR {
frm,
alu_op,
@@ -1109,14 +1123,15 @@ impl MachInstEmit for Inst {
}
}
}
&Inst::BrTableCheck {
&Inst::BrTable {
index,
targets_len,
default_,
tmp1,
ref targets,
} => {
let index = allocs.next(index);
// load
Inst::load_constant_u32(writable_spilltmp_reg(), targets_len as u64, &mut |_| {
let tmp1 = allocs.next_writable(tmp1);
Inst::load_constant_u32(writable_spilltmp_reg(), targets.len() as u64, &mut |_| {
writable_spilltmp_reg()
})
.iter()
@@ -1133,20 +1148,13 @@ impl MachInstEmit for Inst {
.emit(&[], sink, emit_info, state);
sink.use_label_at_offset(
sink.cur_offset(),
default_.as_label().unwrap(),
targets[0].as_label().unwrap(),
LabelUse::PCRel32,
);
Inst::construct_auipc_and_jalr(None, writable_spilltmp_reg(), 0)
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
}
&Inst::BrTable {
index,
tmp1,
ref targets,
} => {
let index = allocs.next(index);
let tmp1 = allocs.next_writable(tmp1);
let mut insts = SmallInstVec::new();
// get current pc.
insts.push(Inst::Auipc {
@@ -1175,7 +1183,7 @@ impl MachInstEmit for Inst {
// here is all the jumps.
let mut need_label_use = vec![];
for t in targets {
for t in targets.iter().skip(1) {
need_label_use.push((insts.len(), t.clone()));
insts.extend(Inst::construct_auipc_and_jalr(
None,