use binemit::CodeOffset;
use cursor::{Cursor, FuncCursor};
use ir::{Function, InstructionData, Opcode};
use isa::{EncInfo, TargetIsa};
use iterators::IteratorExtras;
use regalloc::RegDiversions;
use timing;
use CodegenResult;
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
let _tt = timing::relax_branches();
let encinfo = isa.encoding_info();
func.offsets.clear();
func.offsets.resize(func.dfg.num_ebbs());
fallthroughs(func);
let mut offset = 0;
let mut divert = RegDiversions::new();
let mut go_again = true;
while go_again {
go_again = false;
offset = 0;
let mut cur = FuncCursor::new(func);
while let Some(ebb) = cur.next_ebb() {
divert.clear();
if cur.func.offsets[ebb] != offset {
debug_assert!(
cur.func.offsets[ebb] < offset,
"Code shrinking during relaxation"
);
cur.func.offsets[ebb] = offset;
go_again = true;
}
while let Some(inst) = cur.next_inst() {
divert.apply(&cur.func.dfg[inst]);
let enc = cur.func.encodings[inst];
if let Some(range) = encinfo.branch_range(enc) {
if let Some(dest) = cur.func.dfg[inst].branch_destination() {
let dest_offset = cur.func.offsets[dest];
if !range.contains(offset, dest_offset)
&& (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
{
offset +=
relax_branch(&mut cur, &divert, offset, dest_offset, &encinfo, isa);
continue;
}
}
}
offset += encinfo.byte_size(enc, inst, &divert, &cur.func);
}
}
}
for (jt, jt_data) in func.jump_tables.iter() {
func.jt_offsets[jt] = offset;
offset += jt_data.len() as u32 * 4;
}
Ok(offset)
}
fn fallthroughs(func: &mut Function) {
for (ebb, succ) in func.layout.ebbs().adjacent_pairs() {
let term = func.layout.last_inst(ebb).expect("EBB has no terminator.");
if let InstructionData::Jump {
ref mut opcode,
destination,
..
} = func.dfg[term]
{
match *opcode {
Opcode::Fallthrough => {
debug_assert_eq!(destination, succ, "Illegal fall-through in {}", ebb)
}
Opcode::Jump => {
if destination == succ {
*opcode = Opcode::Fallthrough;
func.encodings[term] = Default::default();
}
}
_ => {}
}
}
}
}
fn relax_branch(
cur: &mut FuncCursor,
divert: &RegDiversions,
offset: CodeOffset,
dest_offset: CodeOffset,
encinfo: &EncInfo,
isa: &TargetIsa,
) -> CodeOffset {
let inst = cur.current_inst().unwrap();
debug!(
"Relaxing [{}] {} for {:#x}-{:#x} range",
encinfo.display(cur.func.encodings[inst]),
cur.func.dfg.display_inst(inst, isa),
offset,
dest_offset
);
let dfg = &cur.func.dfg;
let ctrl_type = dfg.ctrl_typevar(inst);
if let Some(enc) = isa
.legal_encodings(cur.func, &dfg[inst], ctrl_type)
.find(|&enc| {
let range = encinfo.branch_range(enc).expect("Branch with no range");
if !range.contains(offset, dest_offset) {
debug!(" trying [{}]: out of range", encinfo.display(enc));
false
} else if encinfo.operand_constraints(enc)
!= encinfo.operand_constraints(cur.func.encodings[inst])
{
debug!(" trying [{}]: constraints differ", encinfo.display(enc));
false
} else {
debug!(" trying [{}]: OK", encinfo.display(enc));
true
}
}) {
cur.func.encodings[inst] = enc;
return encinfo.byte_size(enc, inst, &divert, &cur.func);
}
panic!("No branch in range for {:#x}-{:#x}", offset, dest_offset);
}