Improve prologue/epilogue detection

This commit is contained in:
Luke Street 2023-08-03 18:51:15 -04:00
parent b267b79f7b
commit 8660984d40
1 changed files with 70 additions and 37 deletions

View File

@ -40,6 +40,37 @@ pub enum TailCallResult {
type BlockRange = Range<u32>; type BlockRange = Range<u32>;
#[inline(always)]
fn next_ins(section: &ObjSection, ins: &Ins) -> Option<Ins> { disassemble(section, ins.addr + 4) }
type InsCheck = dyn Fn(&Ins) -> bool;
#[inline(always)]
fn check_sequence(
section: &ObjSection,
ins: &Ins,
sequence: &[(&InsCheck, &InsCheck)],
) -> Result<bool> {
let mut found = false;
for &(first, second) in sequence {
if first(ins) {
if let Some(next) = next_ins(section, ins) {
if second(&next)
// Also check the following instruction, in case the scheduler
// put something in between.
|| (!next.is_branch() && matches!(next_ins(section, &next), Some(ins) if second(&ins)))
{
found = true;
break;
}
}
}
}
Ok(found)
}
impl FunctionSlices { impl FunctionSlices {
pub fn end(&self) -> u32 { self.blocks.last_key_value().map(|(_, &end)| end).unwrap_or(0) } pub fn end(&self) -> u32 { self.blocks.last_key_value().map(|(_, &end)| end).unwrap_or(0) }
@ -68,56 +99,58 @@ impl FunctionSlices {
} }
fn check_prologue(&mut self, section: &ObjSection, ins: &Ins) -> Result<()> { fn check_prologue(&mut self, section: &ObjSection, ins: &Ins) -> Result<()> {
let next_ins = match disassemble(section, ins.addr + 4) { #[inline(always)]
Some(ins) => ins, fn is_mflr(ins: &Ins) -> bool {
None => return Ok(()), // mfspr r0, LR
}; ins.op == Opcode::Mfspr && ins.field_rD() == 0 && ins.field_spr() == 8
}
#[inline(always)]
fn is_stwu(ins: &Ins) -> bool {
// stwu r1, d(r1) // stwu r1, d(r1)
// mfspr r0, LR ins.op == Opcode::Stwu && ins.field_rS() == 1 && ins.field_rA() == 1
if ((ins.op == Opcode::Stwu && ins.field_rS() == 1 && ins.field_rA() == 1) }
&& (next_ins.op == Opcode::Mfspr #[inline(always)]
&& next_ins.field_rD() == 0 fn is_stw(ins: &Ins) -> bool {
&& next_ins.field_spr() == 8))
// mfspr r0, LR
// stw r0, d(r1) // stw r0, d(r1)
|| ((ins.op == Opcode::Mfspr && ins.field_rD() == 0 && ins.field_spr() == 8) ins.op == Opcode::Stw && ins.field_rS() == 0 && ins.field_rA() == 1
&& (next_ins.op == Opcode::Stw }
&& next_ins.field_rS() == 0
&& next_ins.field_rA() == 1)) if check_sequence(section, ins, &[(&is_stwu, &is_mflr), (&is_mflr, &is_stw)])? {
{ if let Some(prologue) = self.prologue {
match self.prologue { if prologue != ins.addr && prologue != ins.addr - 4 {
Some(prologue) if prologue != ins.addr && prologue != ins.addr - 4 => {
bail!("Found duplicate prologue: {:#010X} and {:#010X}", prologue, ins.addr) bail!("Found duplicate prologue: {:#010X} and {:#010X}", prologue, ins.addr)
} }
_ => self.prologue = Some(ins.addr), } else {
self.prologue = Some(ins.addr);
} }
} }
Ok(()) Ok(())
} }
fn check_epilogue(&mut self, section: &ObjSection, ins: &Ins) -> Result<()> { fn check_epilogue(&mut self, section: &ObjSection, ins: &Ins) -> Result<()> {
let next_ins = match disassemble(section, ins.addr + 4) { #[inline(always)]
Some(ins) => ins, fn is_mtlr(ins: &Ins) -> bool {
None => return Ok(()), // mtspr LR, r0
}; ins.op == Opcode::Mtspr && ins.field_rS() == 0 && ins.field_spr() == 8
// mtspr SPR, r0 }
// addi rD, rA, SIMM #[inline(always)]
if ((ins.op == Opcode::Mtspr && ins.field_rS() == 0 && ins.field_spr() == 8) fn is_addi(ins: &Ins) -> bool {
&& (next_ins.op == Opcode::Addi // addi r1, r1, SIMM
&& next_ins.field_rD() == 1 ins.op == Opcode::Addi && ins.field_rD() == 1 && ins.field_rA() == 1
&& next_ins.field_rA() == 1)) }
#[inline(always)]
fn is_or(ins: &Ins) -> bool {
// or r1, rA, rB // or r1, rA, rB
// mtspr SPR, r0 ins.op == Opcode::Or && ins.field_rD() == 1
|| ((ins.op == Opcode::Or && ins.field_rA() == 1) }
&& (next_ins.op == Opcode::Mtspr
&& next_ins.field_rS() == 0 if check_sequence(section, ins, &[(&is_mtlr, &is_addi), (&is_or, &is_mtlr)])? {
&& next_ins.field_spr() == 8)) if let Some(epilogue) = self.epilogue {
{ if epilogue != ins.addr {
match self.epilogue {
Some(epilogue) if epilogue != ins.addr => {
bail!("Found duplicate epilogue: {:#010X} and {:#010X}", epilogue, ins.addr) bail!("Found duplicate epilogue: {:#010X} and {:#010X}", epilogue, ins.addr)
} }
_ => self.epilogue = Some(ins.addr), } else {
self.epilogue = Some(ins.addr);
} }
} }
Ok(()) Ok(())