cranelift: refactor unwind logic to accommodate multiple backends (#2357)
* Make cranelift_codegen::isa::unwind::input public * Move UnwindCode's common offset field out of the structure * Make MachCompileResult::unwind_info more generic * Record initial stack pointer offset
This commit is contained in:
@@ -1,36 +1,125 @@
|
||||
use super::Inst;
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator, UnwindInfoKind};
|
||||
use crate::isa::unwind::input::UnwindInfo;
|
||||
use crate::isa::x64::inst::{
|
||||
args::{AluRmiROpcode, Amode, RegMemImm, SyntheticAmode},
|
||||
regs, Inst,
|
||||
};
|
||||
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator};
|
||||
use crate::result::CodegenResult;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::Reg;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub use self::systemv::create_cie;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
mod systemv;
|
||||
pub(crate) mod systemv;
|
||||
|
||||
pub struct X64UnwindInfo;
|
||||
|
||||
impl UnwindInfoGenerator<Inst> for X64UnwindInfo {
|
||||
#[allow(unused_variables)]
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<Inst>,
|
||||
kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
// Assumption: RBP is being used as the frame pointer for both calling conventions
|
||||
// In the future, we should be omitting frame pointer as an optimization, so this will change
|
||||
Ok(match kind {
|
||||
#[cfg(feature = "unwind")]
|
||||
UnwindInfoKind::SystemV => {
|
||||
const WORD_SIZE: u8 = 8;
|
||||
systemv::create_unwind_info(context, WORD_SIZE)?.map(UnwindInfo::SystemV)
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
use crate::isa::unwind::input::{self, UnwindCode};
|
||||
let mut codes = Vec::new();
|
||||
const WORD_SIZE: u8 = 8;
|
||||
|
||||
for i in context.prologue.clone() {
|
||||
let i = i as usize;
|
||||
let inst = &context.insts[i];
|
||||
let offset = context.insts_layout[i];
|
||||
|
||||
match inst {
|
||||
Inst::Push64 {
|
||||
src: RegMemImm::Reg { reg },
|
||||
} => {
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::StackAlloc {
|
||||
size: WORD_SIZE.into(),
|
||||
},
|
||||
));
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *reg,
|
||||
stack_offset: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
Inst::MovRR { src, dst, .. } => {
|
||||
if *src == regs::rsp() {
|
||||
codes.push((offset, UnwindCode::SetFramePointer { reg: dst.to_reg() }));
|
||||
}
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Sub,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push((offset, UnwindCode::StackAlloc { size: imm }));
|
||||
}
|
||||
Inst::MovRM {
|
||||
src,
|
||||
dst: SyntheticAmode::Real(Amode::ImmReg { simm32, base }),
|
||||
..
|
||||
} if *base == regs::rsp() => {
|
||||
// `mov reg, imm(rsp)`
|
||||
let imm = *simm32;
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *src,
|
||||
stack_offset: imm,
|
||||
},
|
||||
));
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Add,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push((offset, UnwindCode::StackDealloc { size: imm }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
#[cfg(feature = "unwind")]
|
||||
UnwindInfoKind::Windows => {
|
||||
//TODO winx64::create_unwind_info(context)?.map(|u| UnwindInfo::WindowsX64(u))
|
||||
panic!();
|
||||
}
|
||||
UnwindInfoKind::None => None,
|
||||
})
|
||||
}
|
||||
|
||||
let last_epilogue_end = context.len;
|
||||
let epilogues_unwind_codes = context
|
||||
.epilogues
|
||||
.iter()
|
||||
.map(|epilogue| {
|
||||
// TODO add logic to process epilogue instruction instead of
|
||||
// returning empty array.
|
||||
let end = epilogue.end as usize - 1;
|
||||
let end_offset = context.insts_layout[end];
|
||||
if end_offset == last_epilogue_end {
|
||||
// Do not remember/restore for very last epilogue.
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let start = epilogue.start as usize;
|
||||
let offset = context.insts_layout[start];
|
||||
vec![
|
||||
(offset, UnwindCode::RememberState),
|
||||
// TODO epilogue instructions
|
||||
(end_offset, UnwindCode::RestoreState),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let prologue_size = context.insts_layout[context.prologue.end as usize];
|
||||
Ok(Some(input::UnwindInfo {
|
||||
prologue_size,
|
||||
prologue_unwind_codes: codes,
|
||||
epilogues_unwind_codes,
|
||||
function_size: context.len,
|
||||
word_size: WORD_SIZE,
|
||||
initial_sp_offset: WORD_SIZE,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
//! Unwind information for System V ABI (x86-64).
|
||||
|
||||
use crate::isa::unwind::input;
|
||||
use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo};
|
||||
use crate::isa::x64::inst::{
|
||||
args::{AluRmiROpcode, Amode, RegMemImm, SyntheticAmode},
|
||||
regs, Inst,
|
||||
};
|
||||
use crate::machinst::UnwindInfoContext;
|
||||
use crate::result::CodegenResult;
|
||||
use alloc::vec::Vec;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64};
|
||||
use regalloc::{Reg, RegClass};
|
||||
|
||||
@@ -88,113 +83,15 @@ pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
|
||||
}
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
context: UnwindInfoContext<Inst>,
|
||||
word_size: u8,
|
||||
unwind: input::UnwindInfo<Reg>,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
use crate::isa::unwind::input::{self, UnwindCode};
|
||||
let mut codes = Vec::new();
|
||||
|
||||
for i in context.prologue.clone() {
|
||||
let i = i as usize;
|
||||
let inst = &context.insts[i];
|
||||
let offset = context.insts_layout[i];
|
||||
|
||||
match inst {
|
||||
Inst::Push64 {
|
||||
src: RegMemImm::Reg { reg },
|
||||
} => {
|
||||
codes.push(UnwindCode::StackAlloc {
|
||||
offset,
|
||||
size: word_size.into(),
|
||||
});
|
||||
codes.push(UnwindCode::SaveRegister {
|
||||
offset,
|
||||
reg: *reg,
|
||||
stack_offset: 0,
|
||||
});
|
||||
}
|
||||
Inst::MovRR { src, dst, .. } => {
|
||||
if *src == regs::rsp() {
|
||||
codes.push(UnwindCode::SetFramePointer {
|
||||
offset,
|
||||
reg: dst.to_reg(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Sub,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push(UnwindCode::StackAlloc { offset, size: imm });
|
||||
}
|
||||
Inst::MovRM {
|
||||
src,
|
||||
dst: SyntheticAmode::Real(Amode::ImmReg { simm32, base }),
|
||||
..
|
||||
} if *base == regs::rsp() => {
|
||||
// `mov reg, imm(rsp)`
|
||||
let imm = *simm32;
|
||||
codes.push(UnwindCode::SaveRegister {
|
||||
offset,
|
||||
reg: *src,
|
||||
stack_offset: imm,
|
||||
});
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Add,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push(UnwindCode::StackDealloc { offset, size: imm });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let last_epilogue_end = context.len;
|
||||
let epilogues_unwind_codes = context
|
||||
.epilogues
|
||||
.iter()
|
||||
.map(|epilogue| {
|
||||
let end = epilogue.end as usize - 1;
|
||||
let end_offset = context.insts_layout[end];
|
||||
if end_offset == last_epilogue_end {
|
||||
// Do not remember/restore for very last epilogue.
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let start = epilogue.start as usize;
|
||||
let offset = context.insts_layout[start];
|
||||
vec![
|
||||
UnwindCode::RememberState { offset },
|
||||
UnwindCode::RestoreState { offset: end_offset },
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let prologue_size = context.insts_layout[context.prologue.end as usize];
|
||||
let unwind = input::UnwindInfo {
|
||||
prologue_size,
|
||||
prologue_unwind_codes: codes,
|
||||
epilogues_unwind_codes,
|
||||
function_size: context.len,
|
||||
word_size,
|
||||
};
|
||||
|
||||
struct RegisterMapper;
|
||||
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
||||
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(reg)?.0)
|
||||
}
|
||||
fn rsp(&self) -> u16 {
|
||||
map_reg(regs::rsp()).unwrap().0
|
||||
fn sp(&self) -> u16 {
|
||||
X86_64::RSP.0
|
||||
}
|
||||
}
|
||||
let map = RegisterMapper;
|
||||
|
||||
@@ -103,10 +103,29 @@ impl MachBackend for X64Backend {
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
result: &MachCompileResult,
|
||||
kind: crate::machinst::UnwindInfoKind,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
Ok(match (result.unwind_info.as_ref(), kind) {
|
||||
(Some(info), UnwindInfoKind::SystemV) => {
|
||||
inst::unwind::systemv::create_unwind_info(info.clone())?.map(UnwindInfo::SystemV)
|
||||
}
|
||||
(Some(_info), UnwindInfoKind::Windows) => {
|
||||
//TODO inst::unwind::winx64::create_unwind_info(info.clone())?.map(|u| UnwindInfo::WindowsX64(u))
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
// By default, an ISA cannot create a System V CIE
|
||||
Some(inst::unwind::create_cie())
|
||||
Some(inst::unwind::systemv::create_cie())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user