Improve prologue/epilogue detection
This commit is contained in:
parent
b267b79f7b
commit
8660984d40
|
@ -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(()),
|
|
||||||
};
|
|
||||||
// stwu r1, d(r1)
|
|
||||||
// mfspr r0, LR
|
|
||||||
if ((ins.op == Opcode::Stwu && ins.field_rS() == 1 && ins.field_rA() == 1)
|
|
||||||
&& (next_ins.op == Opcode::Mfspr
|
|
||||||
&& next_ins.field_rD() == 0
|
|
||||||
&& next_ins.field_spr() == 8))
|
|
||||||
// mfspr r0, LR
|
// 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)
|
||||||
|
ins.op == Opcode::Stwu && ins.field_rS() == 1 && ins.field_rA() == 1
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_stw(ins: &Ins) -> bool {
|
||||||
// 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(())
|
||||||
|
|
Loading…
Reference in New Issue